From 9f8d05f29f9df6ff46455d7e9e3b25464c1e28ec Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 26 Dec 2023 16:30:31 +0300 Subject: macro_rules: Preserve all metavariable spans in a global side table --- compiler/rustc_span/src/lib.rs | 73 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 14 deletions(-) (limited to 'compiler/rustc_span/src') diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 49ae9f16c8e..616a7ccc7c6 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -72,6 +72,7 @@ pub mod fatal_error; pub mod profiling; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{Hash128, Hash64, HashStable, StableHasher}; use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc}; @@ -98,6 +99,9 @@ mod tests; pub struct SessionGlobals { symbol_interner: symbol::Interner, span_interner: Lock, + /// Maps a macro argument token into use of the corresponding metavariable in the macro body. + /// Collisions are possible and processed in `maybe_use_metavar_location` on best effort basis. + metavar_spans: Lock>, hygiene_data: Lock, /// A reference to the source map in the `Session`. It's an `Option` @@ -115,6 +119,7 @@ impl SessionGlobals { SessionGlobals { symbol_interner: symbol::Interner::fresh(), span_interner: Lock::new(span_encoding::SpanInterner::default()), + metavar_spans: Default::default(), hygiene_data: Lock::new(hygiene::HygieneData::new(edition)), source_map: Lock::new(None), } @@ -168,6 +173,11 @@ pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { // deserialization. scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals); +#[inline] +pub fn with_metavar_spans(f: impl FnOnce(&mut FxHashMap) -> R) -> R { + with_session_globals(|session_globals| f(&mut session_globals.metavar_spans.lock())) +} + // FIXME: We should use this enum or something like it to get rid of the // use of magic `/rust/1.x/...` paths across the board. #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Decodable)] @@ -824,29 +834,64 @@ impl Span { ) } + /// Check if you can select metavar spans for the given spans to get matching contexts. + fn try_metavars(a: SpanData, b: SpanData, a_orig: Span, b_orig: Span) -> (SpanData, SpanData) { + let get = |mspans: &FxHashMap<_, _>, s| mspans.get(&s).copied(); + match with_metavar_spans(|mspans| (get(mspans, a_orig), get(mspans, b_orig))) { + (None, None) => {} + (Some(meta_a), None) => { + let meta_a = meta_a.data(); + if meta_a.ctxt == b.ctxt { + return (meta_a, b); + } + } + (None, Some(meta_b)) => { + let meta_b = meta_b.data(); + if a.ctxt == meta_b.ctxt { + return (a, meta_b); + } + } + (Some(meta_a), Some(meta_b)) => { + let meta_b = meta_b.data(); + if a.ctxt == meta_b.ctxt { + return (a, meta_b); + } + let meta_a = meta_a.data(); + if meta_a.ctxt == b.ctxt { + return (meta_a, b); + } else if meta_a.ctxt == meta_b.ctxt { + return (meta_a, meta_b); + } + } + } + + (a, b) + } + /// Prepare two spans to a combine operation like `to` or `between`. - /// FIXME: consider using declarative macro metavariable spans for the given spans if they are - /// better suitable for combining (#119412). fn prepare_to_combine( a_orig: Span, b_orig: Span, ) -> Result<(SpanData, SpanData, Option), Span> { let (a, b) = (a_orig.data(), b_orig.data()); + if a.ctxt == b.ctxt { + return Ok((a, b, if a.parent == b.parent { a.parent } else { None })); + } - if a.ctxt != b.ctxt { - // Context mismatches usually happen when procedural macros combine spans copied from - // the macro input with spans produced by the macro (`Span::*_site`). - // In that case we consider the combined span to be produced by the macro and return - // the original macro-produced span as the result. - // Otherwise we just fall back to returning the first span. - // Combining locations typically doesn't make sense in case of context mismatches. - // `is_root` here is a fast path optimization. - let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt(); - return Err(if a_is_callsite { b_orig } else { a_orig }); + let (a, b) = Span::try_metavars(a, b, a_orig, b_orig); + if a.ctxt == b.ctxt { + return Ok((a, b, if a.parent == b.parent { a.parent } else { None })); } - let parent = if a.parent == b.parent { a.parent } else { None }; - Ok((a, b, parent)) + // Context mismatches usually happen when procedural macros combine spans copied from + // the macro input with spans produced by the macro (`Span::*_site`). + // In that case we consider the combined span to be produced by the macro and return + // the original macro-produced span as the result. + // Otherwise we just fall back to returning the first span. + // Combining locations typically doesn't make sense in case of context mismatches. + // `is_root` here is a fast path optimization. + let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt(); + Err(if a_is_callsite { b_orig } else { a_orig }) } /// This span, but in a larger context, may switch to the metavariable span if suitable. -- cgit 1.4.1-3-g733a5