Using the rustc_interface crate

OK, I have a basic example working: https://github.com/georgewfraser/hello_rustc

If any rustc experts see room for improvement in that example, please comment or make a PR!

1 Like

So I am able to parse successfully, but when I try to run the query lower_to_hir: https://github.com/georgewfraser/hello_rustc/blob/c503f5bc96c6966c777de43d5aebdbb77bbe8635/src/main.rs#L79

I get the error message:

error[E0463]: can't find crate for `std`

I'm guessing I need to change some of the options in rustc_interface::interface::Config but there doesn't seem to be any documentation and the way rustc itself uses Config is hard to follow. Perhaps someone can point me in the right direction?

You might need to run expansion (the expansion query) after HIR lowering. IIRC, expansion inserts an implicit extern crate core; extern crate std; at the top of the crate.

Not sure if this is helpful, but you can see how the compiler uses the interface here: https://github.com/rust-lang/rust/blob/45ebd5808afd3df7ba842797c0fcd4447ddf30fb/src/librustc_driver/lib.rs#L147-L431

Specifically, note line 299 which does expansion after calling global_ctxt, which forces lower_to_hir.

The error is happening when I call lower_to_hir, so it's hard to see how calling expansion after I call lower_to_hir is going to fix the error that already happened. But I tried making the order of operations more similar to the compiler:

interface::run_compiler(config, |compiler| {
    let sess = compiler.session();
    compiler.enter(|queries| {
        queries.parse().unwrap();
        println!("ran parse");
        queries.register_plugins().unwrap();
        println!("ran register_plugins");
        queries.global_ctxt().unwrap();
        println!("ran global_ctxt");
        queries.expansion().unwrap();
        println!("ran expansion");
    });
});

And I get the same error, after ran register_plugins is printed. I also tried just running parse and expansion:

interface::run_compiler(config, |compiler| {
    let sess = compiler.session();
    compiler.enter(|queries| {
        queries.parse().unwrap();
        println!("ran parse");
        queries.expansion().unwrap();
        println!("ran expansion");
    });
});

Same error.

You may want to pass a --sysroot argument with a value equal to the result of rustc --print sysroot.

Thanks @bjorn3! My example is in pretty good shape, hopefully this will be helpful to anyone else trying to do the same thing. https://github.com/georgewfraser/hello_rustc

3 Likes

Would you be interested in updating the rustc-dev-guide chapter you pointed to in the OP?

Done https://github.com/rust-lang/rustc-dev-guide/pull/621

1 Like

So now that we're merrily improving the rustc docs, I have a second question: how can I get the type of a span that isn't a definition? Suppose I want to get the type of message inside println("{}", message); in the below program:

fn main() {
  let message = "Hello, world!";
  println("{}", message);
}

Complete example here: https://gist.github.com/georgewfraser/2dc8af432b4766749ee51d32844b8e20#file-main-rs-L86

(I am happy to continue submitting PRs with additions to the docs, if the experts will keep answering my noob questions)

You can do this in different ways: you could traverse the AST before or after expansion (before is easier to recognize the println) or you could traverse the HIR. The span should then be stored next to the variable use.

https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/struct.Expr.html

https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Expr.html

Ok thank you, but how do I get from the span to the type? I'm not seeing any Span -> Ty function in TyCtxt or Queries, the closest thing is type_of: DefId -> Ty, which only seems to make sense for getting types top-level definitions.

First use https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/struct.TyCtxt.html#method.typeck_tables_of with the DefId of the current function, then call https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/struct.TypeckTables.html#method.node_type on the result with the HirId of the expression as argument I think.

Thanks @bjorn3, I've added an example to the docs: https://rustc-dev-guide.rust-lang.org/rustc-driver-interacting-with-the-ast.html

Now I'm trying to figure out how to get compiler error messages. I'm guessing I need to use the global_ctx() query to get a TyCtxt, similar to how I get the type of an expression, but it's not clear where I go for a list of errors. Can you point me in the right direction?

It used to be possible to give your own emitter, but it seems that it is now hard coded: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_session/session.rs.html#1004. You could try to set Config.diagnostic_output to DiagnosticOutput::Raw and set the error format to json to get the errors in json format.

It's kinda strange that in order to tap into the error stream, I have to get it in JSON format and re-parse it. Ideally there would be a way to intercept the stream of typed events before they get serialized to JSON. Should I create an issue in the rustc repo?

Yeah, it used to be possible to get the error by passing your own error emitter. Creating an issue makes sense.

OK, I've made the issue: https://github.com/rust-lang/rust/issues/71770

I'd be happy to work on this, would just like some guidance from rust maintainers on the right approach.

@georgewfraser I would imagine that zulip might be the best place to ask for such guidance: https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp

Perhaps ping Zoxc there?

OK, I've made another docs example, this time of how to intercept diagnostics: https://github.com/rust-lang/rustc-dev-guide/pull/693

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.