Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--Cargo.lock1
-rw-r--r--crates/hir-def/Cargo.toml1
-rw-r--r--crates/hir-def/src/body.rs82
-rw-r--r--crates/hir-def/src/body/lower.rs206
-rw-r--r--crates/hir-def/src/body/scope.rs23
-rw-r--r--crates/hir-def/src/expander.rs4
-rw-r--r--crates/hir-def/src/resolver.rs18
-rw-r--r--crates/hir-expand/src/name.rs2
-rw-r--r--crates/hir-ty/src/consteval.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs10
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs3
-rw-r--r--crates/hir-ty/src/infer.rs4
-rw-r--r--crates/hir-ty/src/infer/closure.rs10
-rw-r--r--crates/hir-ty/src/infer/expr.rs6
-rw-r--r--crates/hir-ty/src/infer/path.rs3
-rw-r--r--crates/hir-ty/src/mir/eval.rs2
-rw-r--r--crates/hir-ty/src/mir/lower.rs7
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs4
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs3
-rw-r--r--crates/hir-ty/src/tests/simple.rs17
-rw-r--r--crates/hir/src/semantics.rs13
-rw-r--r--crates/hir/src/source_analyzer.rs75
-rw-r--r--crates/ide/src/goto_definition.rs20
23 files changed, 394 insertions, 124 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fd569571b3..368e182895 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -556,6 +556,7 @@ dependencies = [
"syntax-bridge",
"test-fixture",
"test-utils",
+ "text-size",
"tracing",
"triomphe",
"tt",
diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml
index c8ba5da449..375f18d9fe 100644
--- a/crates/hir-def/Cargo.toml
+++ b/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/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 684eaf1c3b..27fe5c87cc 100644
--- a/crates/hir-def/src/body.rs
+++ b/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<BlockId>,
+
+ /// 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<BindingId, HygieneId>,
+ /// 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<ExprId, HygieneId>,
+ /// A map from a destructuring assignment possible variable usages to their hygiene ID.
+ pat_hygiene: FxHashMap<PatId, HygieneId>,
}
pub type ExprPtr = AstPtr<ast::Expr>;
@@ -107,10 +139,11 @@ pub struct BodySourceMap {
field_map_back: FxHashMap<ExprId, FieldSource>,
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
+ // FIXME: Make this a sane struct.
template_map: Option<
Box<(
// format_args!
- FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
+ FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
// asm!
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
)>,
@@ -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::<ast::Expr>);
- 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<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
- FxHashMap<Idx<Expr>, Vec<Vec<(tt::TextRange, usize)>>>,
- )> {
- self.template_map.as_deref()
- }
}
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index 4b74028b83..cdc7b14171 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/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<Arc<ExpansionSpanMap>>,
+
current_try_block_label: Option<LabelId>,
// points to the expression that a try expression will target (replaces current_try_block_label)
// catch_scope: Option<ExprId>,
@@ -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<Name, BindingId>,
+ map: FxHashMap<(Name, HygieneId), BindingId>,
is_used: FxHashMap<BindingId, bool>,
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<ast::Expr>) -> 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 = <cond>; _t }`
/// to preserve drop semantics. We should probably do the same in future.
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, 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 <pat> in <head> <body>` 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<LabelId>,
+ label: Option<(HygieneId, LabelId)>,
expr: Option<ast::BlockExpr>,
) -> 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<ast::Lifetime>,
) -> Result<Option<LabelId>, 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<T>(&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<T>(
+ &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<T>(
&mut self,
- label: Option<LabelId>,
+ 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<syntax::SyntaxToken>) -> bool {
diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs
index c6967961b3..7802bfe51a 100644
--- a/crates/hir-def/src/body/scope.rs
+++ b/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/crates/hir-def/src/expander.rs b/crates/hir-def/src/expander.rs
index 6d8b4445f7..6f200021ba 100644
--- a/crates/hir-def/src/expander.rs
+++ b/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/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index f0f2210ec2..cf93958ecb 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/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<ResolveValueResult> {
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<ValueNs> {
- 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/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 54313904a7..267d545833 100644
--- a/crates/hir-expand/src/name.rs
+++ b/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/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 6a85988844..9b0044f5c4 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/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<Const> {
- 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/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 75dce77831..92404e3a10 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/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/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 492262c7a4..c7f7fb7ad3 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/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/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 6859c2d9c3..3cb0d89e01 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/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/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 91ba2af85e..6fc82f6743 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/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/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 5c822fd22e..12b3ab671a 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/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/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index e4841c7b15..44c496c054 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/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/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 7580f042ad..b7b565dece 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/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/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index a8a927c2c5..6343406b83 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/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/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 91086e8057..420f2aaff4 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/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/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 63bb336777..d985a6451b 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/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/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index e6ef56b80d..7ea666986a 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/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/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index d34a0d8f51..860754d5e7 100644
--- a/crates/hir/src/semantics.rs
+++ b/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<PathResolution> {
+ pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option<PathResolution> {
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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 430b646e97..f5b57d57df 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/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<PathResolution>)> {
- 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<impl Iterator<Item = (TextRange, Option<PathResolution>)> + '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<PathResolution> {
- 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<PathResolution> {
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<DefWithBodyId>,
path: &Path,
+ hygiene: HygieneId,
) -> Option<PathResolution> {
- 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/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 1d42a66995..363f852e0e 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -3002,4 +3002,24 @@ 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);
+ }
+}
+"#,
+ );
+ }
}