X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_expand%2Fsrc%2Fproc_macro_server.rs;h=7bf6502c976ca6d69fd49aa978ee6df9f0ae0406;hb=bc26def1d5d21527e70a1d161ed69bd00fb7fb4c;hp=1ea26b4eab44b3069ee73c8fcf0810a5016fe545;hpb=50489e3b0048af4df6f0a92bb43af8afce9b6652;p=rust.git diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 1ea26b4eab4..7bf6502c976 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,4 +1,4 @@ -use crate::base::ExtCtxt; +use crate::base::{ExtCtxt, ResolverExpand}; use rustc_ast as ast; use rustc_ast::token; @@ -7,6 +7,7 @@ use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens}; use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing}; use rustc_ast_pretty::pprust; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::Diagnostic; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; @@ -14,6 +15,8 @@ use rustc_parse::lexer::nfc_normalize; use rustc_parse::{nt_to_tokenstream, parse_stream_from_source_str}; use rustc_session::parse::ParseSess; +use rustc_span::def_id::CrateNum; +use rustc_span::hygiene::ExpnId; use rustc_span::hygiene::ExpnKind; use rustc_span::symbol::{self, kw, sym, Symbol}; use rustc_span::{BytePos, FileName, MultiSpan, Pos, RealFileName, SourceFile, Span}; @@ -355,22 +358,34 @@ pub struct Literal { } pub(crate) struct Rustc<'a> { + resolver: &'a dyn ResolverExpand, sess: &'a ParseSess, def_site: Span, call_site: Span, mixed_site: Span, span_debug: bool, + krate: CrateNum, + expn_id: ExpnId, + rebased_spans: FxHashMap, } impl<'a> Rustc<'a> { - pub fn new(cx: &'a ExtCtxt<'_>) -> Self { + pub fn new(cx: &'a ExtCtxt<'_>, krate: CrateNum) -> Self { let expn_data = cx.current_expansion.id.expn_data(); + let def_site = cx.with_def_site_ctxt(expn_data.def_site); + let call_site = cx.with_call_site_ctxt(expn_data.call_site); + let mixed_site = cx.with_mixed_site_ctxt(expn_data.call_site); + let sess = cx.parse_sess(); Rustc { - sess: &cx.sess.parse_sess, - def_site: cx.with_def_site_ctxt(expn_data.def_site), - call_site: cx.with_call_site_ctxt(expn_data.call_site), - mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site), + resolver: cx.resolver, + sess, + def_site, + call_site, + mixed_site, span_debug: cx.ecfg.span_debug, + krate, + expn_id: cx.current_expansion.id, + rebased_spans: FxHashMap::default(), } } @@ -623,10 +638,11 @@ fn path(&mut self, file: &Self::SourceFile) -> String { match file.name { FileName::Real(ref name) => name .local_path() + .expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`") .to_str() .expect("non-UTF8 file path in `proc_macro::SourceFile::path`") .to_string(), - _ => file.name.to_string(), + _ => file.name.prefer_local().to_string(), } } fn is_real(&mut self, file: &Self::SourceFile) -> bool { @@ -713,6 +729,51 @@ fn resolved_at(&mut self, span: Self::Span, at: Self::Span) -> Self::Span { fn source_text(&mut self, span: Self::Span) -> Option { self.sess.source_map().span_to_snippet(span).ok() } + /// Saves the provided span into the metadata of + /// *the crate we are currently compiling*, which must + /// be a proc-macro crate. This id can be passed to + /// `recover_proc_macro_span` when our current crate + /// is *run* as a proc-macro. + /// + /// Let's suppose that we have two crates - `my_client` + /// and `my_proc_macro`. The `my_proc_macro` crate + /// contains a procedural macro `my_macro`, which + /// is implemented as: `quote! { "hello" }` + /// + /// When we *compile* `my_proc_macro`, we will execute + /// the `quote` proc-macro. This will save the span of + /// "hello" into the metadata of `my_proc_macro`. As a result, + /// the body of `my_proc_macro` (after expansion) will end + /// up containg a call that looks like this: + /// `proc_macro::Ident::new("hello", proc_macro::Span::recover_proc_macro_span(0))` + /// + /// where `0` is the id returned by this function. + /// When `my_proc_macro` *executes* (during the compilation of `my_client`), + /// the call to `recover_proc_macro_span` will load the corresponding + /// span from the metadata of `my_proc_macro` (which we have access to, + /// since we've loaded `my_proc_macro` from disk in order to execute it). + /// In this way, we have obtained a span pointing into `my_proc_macro` + fn save_span(&mut self, mut span: Self::Span) -> usize { + // Throw away the `SyntaxContext`, since we currently + // skip serializing `SyntaxContext`s for proc-macro crates + span = span.with_ctxt(rustc_span::SyntaxContext::root()); + self.sess.save_proc_macro_span(span) + } + fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span { + let resolver = self.resolver; + let krate = self.krate; + let expn_id = self.expn_id; + *self.rebased_spans.entry(id).or_insert_with(|| { + let raw_span = resolver.get_proc_macro_quoted_span(krate, id); + // Ignore the deserialized `SyntaxContext` entirely. + // FIXME: Preserve the macro backtrace from the serialized span + // For example, if a proc-macro crate has code like + // `macro_one!() -> macro_two!() -> quote!()`, we might + // want to 'concatenate' this backtrace with the backtrace from + // our current call site. + raw_span.with_def_site_ctxt(expn_id) + }) + } } // See issue #74616 for details @@ -722,10 +783,10 @@ fn ident_name_compatibility_hack( rustc: &mut Rustc<'_>, ) -> Option<(rustc_span::symbol::Ident, bool)> { if let NtIdent(ident, is_raw) = nt { - if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { + if let ExpnKind::Macro { name: macro_name, .. } = orig_span.ctxt().outer_expn_data().kind { let source_map = rustc.sess.source_map(); let filename = source_map.span_to_filename(orig_span); - if let FileName::Real(RealFileName::Named(path)) = filename { + if let FileName::Real(RealFileName::LocalPath(path)) = filename { let matches_prefix = |prefix, filename| { // Check for a path that ends with 'prefix*/src/' let mut iter = path.components().rev(); @@ -788,7 +849,7 @@ fn ident_name_compatibility_hack( if macro_name == sym::tuple_from_req && matches_prefix("actix-web", "extract.rs") { let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$T") { - if let FileName::Real(RealFileName::Named(macro_path)) = + if let FileName::Real(RealFileName::LocalPath(macro_path)) = source_map.span_to_filename(rustc.def_site) { if macro_path.to_string_lossy().contains("pin-project-internal-0.") {