From 7ee25a0d70bb92b9e55c75c84775a3b6c378a49a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 7 Oct 2024 23:02:02 +0300 Subject: [PATCH] Implement semitransparent hygiene Or macro_rules hygiene, or mixed site hygiene. In other words, hygiene for variables and labels but not items. The realization that made me implement this was that while "full" hygiene (aka. def site hygiene) is really hard for us to implement, and will likely involve intrusive changes and performance losses, since every `Name` will have to carry hygiene, mixed site hygiene is very local: it applies only to bodies, and we very well can save it in a side map with minor losses. This fixes one diagnostic in r-a that was about `izip!()` using hygiene (yay!) but it introduces a huge number of others, because of #18262. Up until now this issue wasn't a major problem because it only affected few cases, but with hygiene identifiers referred by macros like that are not resolved at all. The next commit will fix that. --- src/tools/rust-analyzer/Cargo.lock | 1 + .../rust-analyzer/crates/hir-def/Cargo.toml | 1 + .../rust-analyzer/crates/hir-def/src/body.rs | 82 +++++-- .../crates/hir-def/src/body/lower.rs | 206 ++++++++++++++---- .../crates/hir-def/src/body/scope.rs | 23 +- .../crates/hir-def/src/expander.rs | 4 + .../crates/hir-def/src/resolver.rs | 18 +- .../crates/hir-expand/src/name.rs | 2 + .../crates/hir-ty/src/consteval.rs | 4 +- .../crates/hir-ty/src/diagnostics/expr.rs | 10 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 +- .../crates/hir-ty/src/infer/closure.rs | 10 +- .../crates/hir-ty/src/infer/expr.rs | 6 +- .../crates/hir-ty/src/infer/path.rs | 3 +- .../crates/hir-ty/src/mir/eval.rs | 2 + .../crates/hir-ty/src/mir/lower.rs | 7 +- .../crates/hir-ty/src/mir/lower/as_place.rs | 4 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 3 +- .../crates/hir-ty/src/tests/simple.rs | 17 ++ .../rust-analyzer/crates/hir/src/semantics.rs | 13 +- .../crates/hir/src/source_analyzer.rs | 75 ++++--- .../crates/ide/src/goto_definition.rs | 20 ++ 23 files changed, 394 insertions(+), 124 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index fd569571b38b..368e1828953b 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -556,6 +556,7 @@ dependencies = [ "syntax-bridge", "test-fixture", "test-utils", + "text-size", "tracing", "triomphe", "tt", diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml index c8ba5da449e9..375f18d9fe1f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml @@ -29,6 +29,7 @@ smallvec.workspace = true hashbrown.workspace = true triomphe.workspace = true rustc_apfloat = "0.2.0" +text-size.workspace = true ra-ap-rustc_parse_format.workspace = true ra-ap-rustc_abi.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index 684eaf1c3b08..27fe5c87ccba 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -33,6 +33,22 @@ use crate::{ BlockId, DefWithBodyId, HasModule, Lookup, }; +/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct HygieneId(span::SyntaxContextId); + +impl HygieneId { + pub const ROOT: Self = Self(span::SyntaxContextId::ROOT); + + pub fn new(ctx: span::SyntaxContextId) -> Self { + Self(ctx) + } + + fn is_root(self) -> bool { + self.0.is_root() + } +} + /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { @@ -55,6 +71,22 @@ pub struct Body { pub body_expr: ExprId, /// Block expressions in this body that may contain inner items. block_scopes: Vec, + + /// A map from binding to its hygiene ID. + /// + /// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here. + /// If a binding does not appear here it has `SyntaxContextId::ROOT`. + /// + /// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent + /// expansions are attributed to their parent expansion (recursively). + binding_hygiene: FxHashMap, + /// A map from an variable usages to their hygiene ID. + /// + /// Expressions that can be recorded here are single segment path, although not all single segments path refer + /// to variables and have hygiene (some refer to items, we don't know at this stage). + expr_hygiene: FxHashMap, + /// A map from a destructuring assignment possible variable usages to their hygiene ID. + pat_hygiene: FxHashMap, } pub type ExprPtr = AstPtr; @@ -107,10 +139,11 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, + // FIXME: Make this a sane struct. template_map: Option< Box<( // format_args! - FxHashMap>, + FxHashMap)>, // asm! FxHashMap>>, )>, @@ -268,6 +301,9 @@ impl Body { pats, bindings, binding_owners, + binding_hygiene, + expr_hygiene, + pat_hygiene, } = self; block_scopes.shrink_to_fit(); exprs.shrink_to_fit(); @@ -275,6 +311,9 @@ impl Body { pats.shrink_to_fit(); bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); + binding_hygiene.shrink_to_fit(); + expr_hygiene.shrink_to_fit(); + pat_hygiene.shrink_to_fit(); } pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { @@ -467,6 +506,25 @@ impl Body { } }); } + + fn binding_hygiene(&self, binding: BindingId) -> HygieneId { + self.binding_hygiene.get(&binding).copied().unwrap_or(HygieneId::ROOT) + } + + pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId { + self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT) + } + + pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId { + self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT) + } + + pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId { + match id { + ExprOrPatId::ExprId(id) => self.expr_path_hygiene(id), + ExprOrPatId::PatId(id) => self.pat_path_hygiene(id), + } + } } impl Default for Body { @@ -481,6 +539,9 @@ impl Default for Body { block_scopes: Default::default(), binding_owners: Default::default(), self_param: Default::default(), + binding_hygiene: Default::default(), + expr_hygiene: Default::default(), + pat_hygiene: Default::default(), } } } @@ -594,13 +655,11 @@ impl BodySourceMap { pub fn implicit_format_args( &self, node: InFile<&ast::FormatArgsExpr>, - ) -> Option<&[(syntax::TextRange, Name)]> { + ) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); - self.template_map - .as_ref()? - .0 - .get(&self.expr_map.get(&src)?.as_expr()?) - .map(std::ops::Deref::deref) + let (hygiene, names) = + self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?; + Some((*hygiene, &**names)) } pub fn asm_template_args( @@ -649,13 +708,4 @@ impl BodySourceMap { diagnostics.shrink_to_fit(); binding_definitions.shrink_to_fit(); } - - pub fn template_map( - &self, - ) -> Option<&( - FxHashMap, Vec<(tt::TextRange, Name)>>, - FxHashMap, Vec>>, - )> { - self.template_map.as_deref() - } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 4b74028b83a6..cdc7b141717f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -9,6 +9,7 @@ use base_db::CrateId; use either::Either; use hir_expand::{ name::{AsName, Name}, + span_map::{ExpansionSpanMap, SpanMap}, InFile, }; use intern::{sym, Interned, Symbol}; @@ -22,10 +23,11 @@ use syntax::{ }, AstNode, AstPtr, AstToken as _, SyntaxNodePtr, }; +use text_size::TextSize; use triomphe::Arc; use crate::{ - body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr}, + body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr}, builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, @@ -60,6 +62,17 @@ pub(super) fn lower( krate: CrateId, is_async_fn: bool, ) -> (Body, BodySourceMap) { + // We cannot leave the root span map empty and let any identifier from it be treated as root, + // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved + // with the inner macro, and that will cause confusion because they won't be the same as `ROOT` + // even though they should be the same. Also, when the body comes from multiple expansions, their + // hygiene is different. + let span_map = expander.current_file_id().macro_file().map(|_| { + let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else { + panic!("in a macro file there should be `ExpansionSpanMap`"); + }; + Arc::clone(span_map) + }); ExprCollector { db, owner, @@ -74,6 +87,7 @@ pub(super) fn lower( label_ribs: Vec::new(), current_binding_owner: None, awaitable_context: None, + current_span_map: span_map, } .collect(params, body, is_async_fn) } @@ -90,6 +104,8 @@ struct ExprCollector<'a> { is_lowering_coroutine: bool, + current_span_map: Option>, + current_try_block_label: Option, // points to the expression that a try expression will target (replaces current_try_block_label) // catch_scope: Option, @@ -109,14 +125,14 @@ struct ExprCollector<'a> { struct LabelRib { kind: RibKind, // Once we handle macro hygiene this will need to be a map - label: Option<(Name, LabelId)>, + label: Option<(Name, LabelId, HygieneId)>, } impl LabelRib { fn new(kind: RibKind) -> Self { LabelRib { kind, label: None } } - fn new_normal(label: (Name, LabelId)) -> Self { + fn new_normal(label: (Name, LabelId, HygieneId)) -> Self { LabelRib { kind: RibKind::Normal, label: Some(label) } } } @@ -145,7 +161,7 @@ enum Awaitable { #[derive(Debug, Default)] struct BindingList { - map: FxHashMap, + map: FxHashMap<(Name, HygieneId), BindingId>, is_used: FxHashMap, reject_new: bool, } @@ -155,9 +171,16 @@ impl BindingList { &mut self, ec: &mut ExprCollector<'_>, name: Name, + hygiene: HygieneId, mode: BindingAnnotation, ) -> BindingId { - let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode)); + let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| { + let id = ec.alloc_binding(name.clone(), mode); + if !hygiene.is_root() { + ec.body.binding_hygiene.insert(id, hygiene); + } + id + }); if ec.body.bindings[id].mode != mode { ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); } @@ -211,6 +234,13 @@ impl ExprCollector<'_> { Name::new_symbol_root(sym::self_.clone()), BindingAnnotation::new(is_mutable, false), ); + let hygiene = self_param + .name() + .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); + if !hygiene.is_root() { + self.body.binding_hygiene.insert(binding_id, hygiene); + } self.body.self_param = Some(binding_id); self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); } @@ -288,13 +318,14 @@ impl ExprCollector<'_> { }) } Some(ast::BlockModifier::Label(label)) => { - let label = self.collect_label(label); - self.with_labeled_rib(label, |this| { + let label_hygiene = self.hygiene_id_for(label.syntax().text_range().start()); + let label_id = self.collect_label(label); + self.with_labeled_rib(label_id, label_hygiene, |this| { this.collect_block_(e, |id, statements, tail| Expr::Block { id, statements, tail, - label: Some(label), + label: Some(label_id), }) }) } @@ -336,9 +367,14 @@ impl ExprCollector<'_> { None => self.collect_block(e), }, ast::Expr::LoopExpr(e) => { - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + ( + self.hygiene_id_for(label.syntax().text_range().start()), + self.collect_label(label), + ) + }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -398,12 +434,15 @@ impl ExprCollector<'_> { self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let path = e - .path() - .and_then(|path| self.expander.parse_path(self.db, path)) - .map(Expr::Path) - .unwrap_or(Expr::Missing); - self.alloc_expr(path, syntax_ptr) + let (path, hygiene) = self + .collect_expr_path(&e) + .map(|(path, hygiene)| (Expr::Path(path), hygiene)) + .unwrap_or((Expr::Missing, HygieneId::ROOT)); + let expr_id = self.alloc_expr(path, syntax_ptr); + if !hygiene.is_root() { + self.body.expr_hygiene.insert(expr_id, hygiene); + } + expr_id } ast::Expr::ContinueExpr(e) => { let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| { @@ -677,6 +716,24 @@ impl ExprCollector<'_> { }) } + fn collect_expr_path(&mut self, e: &ast::PathExpr) -> Option<(Path, HygieneId)> { + e.path().and_then(|path| { + let path = self.expander.parse_path(self.db, path)?; + let Path::Normal { type_anchor, mod_path, generic_args } = &path else { + panic!("path parsing produced a non-normal path"); + }; + // Need to enable `mod_path.len() < 1` for `self`. + let may_be_variable = + type_anchor.is_none() && mod_path.len() <= 1 && generic_args.is_none(); + let hygiene = if may_be_variable { + self.hygiene_id_for(e.syntax().text_range().start()) + } else { + HygieneId::ROOT + }; + Some((path, hygiene)) + }) + } + fn collect_expr_as_pat_opt(&mut self, expr: Option) -> PatId { match expr { Some(expr) => self.collect_expr_as_pat(expr), @@ -740,8 +797,15 @@ impl ExprCollector<'_> { self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr) } ast::Expr::PathExpr(e) => { - let path = Box::new(self.expander.parse_path(self.db, e.path()?)?); - self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr) + let (path, hygiene) = self + .collect_expr_path(e) + .map(|(path, hygiene)| (Pat::Path(Box::new(path)), hygiene)) + .unwrap_or((Pat::Missing, HygieneId::ROOT)); + let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); + if !hygiene.is_root() { + self.body.pat_hygiene.insert(pat_id, hygiene); + } + pat_id } ast::Expr::MacroExpr(e) => { let e = e.macro_call()?; @@ -889,7 +953,7 @@ impl ExprCollector<'_> { let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); - let (btail, expr_id) = self.with_labeled_rib(label, |this| { + let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| { let mut btail = None; let block = this.collect_block_(e, |id, statements, tail| { btail = tail; @@ -933,7 +997,9 @@ impl ExprCollector<'_> { /// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = ; _t }` /// to preserve drop semantics. We should probably do the same in future. fn collect_while_loop(&mut self, syntax_ptr: AstPtr, e: ast::WhileExpr) -> ExprId { - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label)) + }); let body = self.collect_labelled_block_opt(label, e.loop_body()); // Labels can also be used in the condition expression, like this: @@ -950,9 +1016,9 @@ impl ExprCollector<'_> { // } // ``` let condition = match label { - Some(label) => { - self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition())) - } + Some((label_hygiene, label)) => self.with_labeled_rib(label, label_hygiene, |this| { + this.collect_expr_opt(e.condition()) + }), None => self.collect_expr_opt(e.condition()), }; @@ -961,7 +1027,7 @@ impl ExprCollector<'_> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label }, syntax_ptr) + self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: @@ -1005,7 +1071,9 @@ impl ExprCollector<'_> { args: Box::new([self.collect_pat_top(e.pat())]), ellipsis: None, }; - let label = e.label().map(|label| self.collect_label(label)); + let label = e.label().map(|label| { + (self.hygiene_id_for(label.syntax().text_range().start()), self.collect_label(label)) + }); let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, @@ -1037,7 +1105,8 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr); + let loop_outer = self + .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); self.add_definition_to_binding(iter_binding, iter_pat); @@ -1194,7 +1263,14 @@ impl ExprCollector<'_> { // FIXME: Report parse errors here } + let SpanMap::ExpansionSpanMap(new_span_map) = self.expander.span_map(self.db) + else { + panic!("just expanded a macro, ExpansionSpanMap should be available"); + }; + let old_span_map = + mem::replace(&mut self.current_span_map, Some(new_span_map.clone())); let id = collector(self, Some(expansion.tree())); + self.current_span_map = old_span_map; self.ast_id_map = prev_ast_id_map; self.expander.exit(mark); id @@ -1357,11 +1433,13 @@ impl ExprCollector<'_> { fn collect_labelled_block_opt( &mut self, - label: Option, + label: Option<(HygieneId, LabelId)>, expr: Option, ) -> ExprId { match label { - Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)), + Some((hygiene, label)) => { + self.with_labeled_rib(label, hygiene, |this| this.collect_block_opt(expr)) + } None => self.collect_block_opt(expr), } } @@ -1379,6 +1457,10 @@ impl ExprCollector<'_> { let pattern = match &pat { ast::Pat::IdentPat(bp) => { let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + let hygiene = bp + .name() + .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); let annotation = BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some()); @@ -1414,12 +1496,12 @@ impl ExprCollector<'_> { } // shadowing statics is an error as well, so we just ignore that case here _ => { - let id = binding_list.find(self, name, annotation); + let id = binding_list.find(self, name, hygiene, annotation); (Some(id), Pat::Bind { id, subpat }) } } } else { - let id = binding_list.find(self, name, annotation); + let id = binding_list.find(self, name, hygiene, annotation); (Some(id), Pat::Bind { id, subpat }) }; @@ -1698,11 +1780,12 @@ impl ExprCollector<'_> { lifetime: Option, ) -> Result, BodyDiagnostic> { let Some(lifetime) = lifetime else { return Ok(None) }; + let hygiene = self.hygiene_id_for(lifetime.syntax().text_range().start()); let name = Name::new_lifetime(&lifetime); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { - if let Some((label_name, id)) = &rib.label { - if *label_name == name { + if let Some((label_name, id, label_hygiene)) = &rib.label { + if *label_name == name && *label_hygiene == hygiene { return if self.is_label_valid_from_rib(rib_idx) { Ok(Some(*id)) } else { @@ -1732,8 +1815,13 @@ impl ExprCollector<'_> { res } - fn with_labeled_rib(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T { - self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label))); + fn with_labeled_rib( + &mut self, + label: LabelId, + hygiene: HygieneId, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label, hygiene))); let res = f(self); self.label_ribs.pop(); res @@ -1741,12 +1829,12 @@ impl ExprCollector<'_> { fn with_opt_labeled_rib( &mut self, - label: Option, + label: Option<(HygieneId, LabelId)>, f: impl FnOnce(&mut Self) -> T, ) -> T { match label { None => f(self), - Some(label) => self.with_labeled_rib(label, f), + Some((hygiene, label)) => self.with_labeled_rib(label, hygiene, f), } } // endregion: labels @@ -1795,28 +1883,39 @@ impl ExprCollector<'_> { _ => None, }); let mut mappings = vec![]; - let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) { + let (fmt, hygiene) = match template.and_then(|it| self.expand_macros_to_string(it)) { Some((s, is_direct_literal)) => { let call_ctx = self.expander.syntax_context(); - format_args::parse( + let hygiene = self.hygiene_id_for(s.syntax().text_range().start()); + let fmt = format_args::parse( &s, fmt_snippet, args, is_direct_literal, - |name| self.alloc_expr_desugared(Expr::Path(Path::from(name))), + |name| { + let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name))); + if !hygiene.is_root() { + self.body.expr_hygiene.insert(expr_id, hygiene); + } + expr_id + }, |name, span| { if let Some(span) = span { mappings.push((span, name)) } }, call_ctx, - ) + ); + (fmt, hygiene) } - None => FormatArgs { - template: Default::default(), - arguments: args.finish(), - orphans: Default::default(), - }, + None => ( + FormatArgs { + template: Default::default(), + arguments: args.finish(), + orphans: Default::default(), + }, + HygieneId::ROOT, + ), }; // Create a list of all _unique_ (argument, format trait) combinations. @@ -1963,7 +2062,11 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings); + self.source_map + .template_map + .get_or_insert_with(Default::default) + .0 + .insert(idx, (hygiene, mappings)); idx } @@ -2264,6 +2367,17 @@ impl ExprCollector<'_> { self.awaitable_context = orig; res } + + /// If this returns `HygieneId::ROOT`, do not allocate to save space. + fn hygiene_id_for(&self, span_start: TextSize) -> HygieneId { + match &self.current_span_map { + None => HygieneId::ROOT, + Some(span_map) => { + let ctx = span_map.span_at(span_start).ctx; + HygieneId(self.db.lookup_intern_syntax_context(ctx).opaque_and_semitransparent) + } + } + } } fn comma_follows_token(t: Option) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs index c6967961b33f..7802bfe51a47 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs @@ -4,7 +4,7 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ - body::Body, + body::{Body, HygieneId}, db::DefDatabase, hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement}, BlockId, ConstBlockId, DefWithBodyId, @@ -22,6 +22,7 @@ pub struct ExprScopes { #[derive(Debug, PartialEq, Eq)] pub struct ScopeEntry { name: Name, + hygiene: HygieneId, binding: BindingId, } @@ -30,6 +31,10 @@ impl ScopeEntry { &self.name } + pub(crate) fn hygiene(&self) -> HygieneId { + self.hygiene + } + pub fn binding(&self) -> BindingId { self.binding } @@ -102,7 +107,7 @@ impl ExprScopes { }; let mut root = scopes.root_scope(); if let Some(self_param) = body.self_param { - scopes.add_bindings(body, root, self_param); + scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } scopes.add_params_bindings(body, root, &body.params); compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root, resolve_const_block); @@ -150,17 +155,23 @@ impl ExprScopes { }) } - fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) { + fn add_bindings( + &mut self, + body: &Body, + scope: ScopeId, + binding: BindingId, + hygiene: HygieneId, + ) { let Binding { name, .. } = &body.bindings[binding]; - let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding }); + let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene }); self.scopes[scope].entries = IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry); } fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { let pattern = &body[pat]; - if let Pat::Bind { id, .. } = pattern { - self.add_bindings(body, scope, *id); + if let Pat::Bind { id, .. } = *pattern { + self.add_bindings(body, scope, id, body.binding_hygiene(id)); } pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs index 6d8b4445f75b..6f200021baa9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs @@ -49,6 +49,10 @@ impl Expander { } } + pub(crate) fn span_map(&self, db: &dyn DefDatabase) -> &SpanMap { + self.span_map.get_or_init(|| db.span_map(self.current_file_id)) + } + pub fn krate(&self) -> CrateId { self.module.krate } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index f0f2210ec2c5..cf93958ecbb2 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -10,7 +10,10 @@ use smallvec::{smallvec, SmallVec}; use triomphe::Arc; use crate::{ - body::scope::{ExprScopes, ScopeId}, + body::{ + scope::{ExprScopes, ScopeId}, + HygieneId, + }, builtin_type::BuiltinType, data::ExternCrateDeclData, db::DefDatabase, @@ -257,6 +260,7 @@ impl Resolver { &self, db: &dyn DefDatabase, path: &Path, + hygiene: HygieneId, ) -> Option { let path = match path { Path::Normal { mod_path, .. } => mod_path, @@ -303,11 +307,10 @@ impl Resolver { for scope in self.scopes() { match scope { Scope::ExprScope(scope) => { - let entry = scope - .expr_scopes - .entries(scope.scope_id) - .iter() - .find(|entry| entry.name() == first_name); + let entry = + scope.expr_scopes.entries(scope.scope_id).iter().find(|entry| { + entry.name() == first_name && entry.hygiene() == hygiene + }); if let Some(e) = entry { return Some(ResolveValueResult::ValueNs( @@ -393,8 +396,9 @@ impl Resolver { &self, db: &dyn DefDatabase, path: &Path, + hygiene: HygieneId, ) -> Option { - match self.resolve_path_in_value_ns(db, path)? { + match self.resolve_path_in_value_ns(db, path, hygiene)? { ResolveValueResult::ValueNs(it, _) => Some(it), ResolveValueResult::Partial(..) => None, } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 54313904a7ec..267d54583338 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -18,6 +18,8 @@ use syntax::utils::is_raw_identifier; #[derive(Clone, PartialEq, Eq, Hash)] pub struct Name { symbol: Symbol, + // If you are making this carry actual hygiene, beware that the special handling for variables and labels + // in bodies can go. ctx: (), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 6a8598884404..9b0044f5c4f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -3,7 +3,7 @@ use base_db::{ra_salsa::Cycle, CrateId}; use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ - body::Body, + body::{Body, HygieneId}, hir::{Expr, ExprId}, path::Path, resolver::{Resolver, ValueNs}, @@ -80,7 +80,7 @@ pub(crate) fn path_to_const<'g>( debruijn: DebruijnIndex, expected_ty: Ty, ) -> Option { - match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { + match resolver.resolve_path_in_value_ns_fully(db.upcast(), path, HygieneId::ROOT) { Some(ValueNs::GenericParam(p)) => { let ty = db.const_param_ty(p); let value = match mode { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 75dce7783163..92404e3a10e2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -289,10 +289,12 @@ impl ExprValidator { match &self.body[scrutinee_expr] { Expr::UnaryOp { op: UnaryOp::Deref, .. } => false, Expr::Path(path) => { - let value_or_partial = self - .owner - .resolver(db.upcast()) - .resolve_path_in_value_ns_fully(db.upcast(), path); + let value_or_partial = + self.owner.resolver(db.upcast()).resolve_path_in_value_ns_fully( + db.upcast(), + path, + self.body.expr_path_hygiene(scrutinee_expr), + ); value_or_partial.map_or(true, |v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 492262c7a4cf..c7f7fb7ad3d3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -77,7 +77,8 @@ fn walk_unsafe( ) { let mut mark_unsafe_path = |path, node| { let g = resolver.update_to_inner_scope(db.upcast(), def, current); - let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); + let hygiene = body.expr_or_pat_path_hygiene(node); + let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path, hygiene); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { let static_data = db.static_data(id); if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 6859c2d9c342..3cb0d89e01c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -33,7 +33,7 @@ use chalk_ir::{ }; use either::Either; use hir_def::{ - body::Body, + body::{Body, HygieneId}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, @@ -1398,7 +1398,7 @@ impl<'a> InferenceContext<'a> { }; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); let (resolution, unresolved) = if value_ns { - match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) { + match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) { Some(ResolveValueResult::ValueNs(value, _)) => match value { ValueNs::EnumVariantId(var) => { let substs = ctx.substs_from_path(path, var.into(), true); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 91ba2af85e9e..6fc82f6743ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -514,8 +514,11 @@ impl InferenceContext<'_> { if path.type_anchor().is_some() { return None; } - let result = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), path).and_then( - |result| match result { + let hygiene = self.body.expr_or_pat_path_hygiene(id); + let result = self + .resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), path, hygiene) + .and_then(|result| match result { ValueNs::LocalBinding(binding) => { let mir_span = match id { ExprOrPatId::ExprId(id) => MirSpan::ExprId(id), @@ -525,8 +528,7 @@ impl InferenceContext<'_> { Some(HirPlace { local: binding, projections: Vec::new() }) } _ => None, - }, - ); + }); result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 5c822fd22e1c..12b3ab671ade 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -201,7 +201,11 @@ impl InferenceContext<'_> { Expr::Path(Path::Normal { type_anchor: Some(_), .. }) => false, Expr::Path(path) => self .resolver - .resolve_path_in_value_ns_fully(self.db.upcast(), path) + .resolve_path_in_value_ns_fully( + self.db.upcast(), + path, + self.body.expr_path_hygiene(expr), + ) .map_or(true, |res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), Expr::Underscore => true, Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index e4841c7b15b6..44c496c05421 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -164,9 +164,10 @@ impl InferenceContext<'_> { let ty = self.table.normalize_associated_types_in(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { + let hygiene = self.body.expr_or_pat_path_hygiene(id); // FIXME: report error, unresolved first path segment let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; + self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?; match value_or_partial { ResolveValueResult::ValueNs(it, _) => (it, None), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 7580f042adc7..b7b565dece8d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -6,6 +6,7 @@ use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability}; use either::Either; use hir_def::{ + body::HygieneId, builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, lang_item::LangItem, @@ -2953,6 +2954,7 @@ pub fn render_const_using_debug_impl( let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( db.upcast(), &hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]), + HygieneId::ROOT, ) else { not_supported!("std::fmt::format not found"); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index a8a927c2c5c7..6343406b83de 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -5,7 +5,7 @@ use std::{fmt::Write, iter, mem}; use base_db::ra_salsa::Cycle; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - body::Body, + body::{Body, HygieneId}, data::adt::{StructKind, VariantData}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, @@ -446,9 +446,10 @@ impl<'ctx> MirLowerCtx<'ctx> { } else { let resolver_guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id); + let hygiene = self.body.expr_path_hygiene(expr_id); let result = self .resolver - .resolve_path_in_value_ns_fully(self.db.upcast(), p) + .resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene) .ok_or_else(|| { MirLowerError::unresolved_path(self.db, p, self.edition()) })?; @@ -1361,7 +1362,7 @@ impl<'ctx> MirLowerCtx<'ctx> { || MirLowerError::unresolved_path(self.db, c.as_ref(), edition); let pr = self .resolver - .resolve_path_in_value_ns(self.db.upcast(), c.as_ref()) + .resolve_path_in_value_ns(self.db.upcast(), c.as_ref(), HygieneId::ROOT) .ok_or_else(unresolved_name)?; match pr { ResolveValueResult::ValueNs(v, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 91086e805798..420f2aaff46d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -137,7 +137,9 @@ impl MirLowerCtx<'_> { Expr::Path(p) => { let resolver_guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr_id); - let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p); + let hygiene = self.body.expr_path_hygiene(expr_id); + let resolved = + self.resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p, hygiene); self.resolver.reset_to_guard(resolver_guard); let Some(pr) = resolved else { return try_rvalue(self); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 63bb3367771f..d985a6451b7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -351,9 +351,10 @@ impl MirLowerCtx<'_> { None => { let unresolved_name = || MirLowerError::unresolved_path(self.db, p, self.edition()); + let hygiene = self.body.pat_path_hygiene(pattern); let pr = self .resolver - .resolve_path_in_value_ns(self.db.upcast(), p) + .resolve_path_in_value_ns(self.db.upcast(), p, hygiene) .ok_or_else(unresolved_name)?; if let ( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index e6ef56b80d93..7ea666986ab3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3720,3 +3720,20 @@ fn test() -> bool { "#]], ); } + +#[test] +fn macro_semitransparent_hygiene() { + check_types( + r#" +macro_rules! m { + () => { let bar: i32; }; +} +fn foo() { + let bar: bool; + m!(); + bar; + // ^^^ bool +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d34a0d8f51b7..860754d5e704 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -45,7 +45,7 @@ use syntax::{ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, - source_analyzer::{resolve_hir_path, SourceAnalyzer}, + source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, @@ -1952,10 +1952,15 @@ impl SemanticsScope<'_> { /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. - pub fn speculative_resolve(&self, path: &ast::Path) -> Option { + pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option { let ctx = LowerCtx::new(self.db.upcast(), self.file_id); - let path = Path::from_src(&ctx, path.clone())?; - resolve_hir_path(self.db, &self.resolver, &path) + let path = Path::from_src(&ctx, ast_path.clone())?; + resolve_hir_path( + self.db, + &self.resolver, + &path, + name_hygiene(self.db, InFile::new(self.file_id, ast_path.syntax())), + ) } /// Iterates over associated types that may be specified after the given path (using diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 430b646e97cb..f5b57d57dfc7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -16,7 +16,7 @@ use either::Either; use hir_def::{ body::{ scope::{ExprScopes, ScopeId}, - Body, BodySourceMap, + Body, BodySourceMap, HygieneId, }, hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId}, lang_item::LangItem, @@ -562,7 +562,8 @@ impl SourceAnalyzer { let expr = ast::Expr::from(record_expr); let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?; - let local_name = field.field_name()?.as_name(); + let ast_name = field.field_name()?; + let local_name = ast_name.as_name(); let local = if field.name_ref().is_some() { None } else { @@ -571,7 +572,11 @@ impl SourceAnalyzer { PathKind::Plain, once(local_name.clone()), )); - match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { + match self.resolver.resolve_path_in_value_ns_fully( + db.upcast(), + &path, + name_hygiene(db, InFile::new(self.file_id, ast_name.syntax())), + ) { Some(ValueNs::LocalBinding(binding_id)) => { Some(Local { binding_id, parent: self.resolver.body_owner()? }) } @@ -627,7 +632,7 @@ impl SourceAnalyzer { Pat::Path(path) => path, _ => return None, }; - let res = resolve_hir_path(db, &self.resolver, path)?; + let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT)?; match res { PathResolution::Def(def) => Some(def), _ => None, @@ -818,7 +823,13 @@ impl SourceAnalyzer { if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { resolve_hir_path_qualifier(db, &self.resolver, &hir_path) } else { - resolve_hir_path_(db, &self.resolver, &hir_path, prefer_value_ns) + resolve_hir_path_( + db, + &self.resolver, + &hir_path, + prefer_value_ns, + name_hygiene(db, InFile::new(self.file_id, path.syntax())), + ) } } @@ -944,7 +955,7 @@ impl SourceAnalyzer { format_args: InFile<&ast::FormatArgsExpr>, offset: TextSize, ) -> Option<(TextRange, Option)> { - let implicits = self.body_source_map()?.implicit_format_args(format_args)?; + let (hygiene, implicits) = self.body_source_map()?.implicit_format_args(format_args)?; implicits.iter().find(|(range, _)| range.contains_inclusive(offset)).map(|(range, name)| { ( *range, @@ -956,6 +967,7 @@ impl SourceAnalyzer { PathKind::Plain, Some(name.clone()), )), + hygiene, ), ) }) @@ -982,22 +994,22 @@ impl SourceAnalyzer { db: &'a dyn HirDatabase, format_args: InFile<&ast::FormatArgsExpr>, ) -> Option)> + 'a> { - Some(self.body_source_map()?.implicit_format_args(format_args)?.iter().map( - move |(range, name)| { - ( - *range, - resolve_hir_value_path( - db, - &self.resolver, - self.resolver.body_owner(), - &Path::from_known_path_with_no_generic(ModPath::from_segments( - PathKind::Plain, - Some(name.clone()), - )), - ), - ) - }, - )) + let (hygiene, names) = self.body_source_map()?.implicit_format_args(format_args)?; + Some(names.iter().map(move |(range, name)| { + ( + *range, + resolve_hir_value_path( + db, + &self.resolver, + self.resolver.body_owner(), + &Path::from_known_path_with_no_generic(ModPath::from_segments( + PathKind::Plain, + Some(name.clone()), + )), + hygiene, + ), + ) + })) } pub(crate) fn as_asm_parts( @@ -1143,8 +1155,9 @@ pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, resolver: &Resolver, path: &Path, + hygiene: HygieneId, ) -> Option { - resolve_hir_path_(db, resolver, path, false) + resolve_hir_path_(db, resolver, path, false, hygiene) } #[inline] @@ -1164,6 +1177,7 @@ fn resolve_hir_path_( resolver: &Resolver, path: &Path, prefer_value_ns: bool, + hygiene: HygieneId, ) -> Option { let types = || { let (ty, unresolved) = match path.type_anchor() { @@ -1229,7 +1243,7 @@ fn resolve_hir_path_( }; let body_owner = resolver.body_owner(); - let values = || resolve_hir_value_path(db, resolver, body_owner, path); + let values = || resolve_hir_value_path(db, resolver, body_owner, path, hygiene); let items = || { resolver @@ -1254,8 +1268,9 @@ fn resolve_hir_value_path( resolver: &Resolver, body_owner: Option, path: &Path, + hygiene: HygieneId, ) -> Option { - resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| { + resolver.resolve_path_in_value_ns_fully(db.upcast(), path, hygiene).and_then(|val| { let res = match val { ValueNs::LocalBinding(binding_id) => { let var = Local { parent: body_owner?, binding_id }; @@ -1360,3 +1375,13 @@ fn resolve_hir_path_qualifier( .map(|it| PathResolution::Def(it.into())) }) } + +pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> HygieneId { + let Some(macro_file) = name.file_id.macro_file() else { + return HygieneId::ROOT; + }; + let span_map = db.expansion_span_map(macro_file); + let ctx = span_map.span_at(name.value.text_range().start()).ctx; + let ctx = db.lookup_intern_syntax_context(ctx); + HygieneId::new(ctx.opaque_and_semitransparent) +} diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 1d42a66995b7..363f852e0e4b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -2999,6 +2999,26 @@ mod bar { mod m {} use foo::m; +"#, + ); + } + + #[test] + fn macro_label_hygiene() { + check( + r#" +macro_rules! m { + ($x:stmt) => { + 'bar: loop { $x } + }; +} + +fn foo() { + 'bar: loop { + // ^^^^ + m!(continue 'bar$0); + } +} "#, ); }