Skip to content

Commit 7ee25a0

Browse files
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.
1 parent fa59a76 commit 7ee25a0

File tree

23 files changed

+394
-124
lines changed

23 files changed

+394
-124
lines changed

src/tools/rust-analyzer/Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ dependencies = [
556556
"syntax-bridge",
557557
"test-fixture",
558558
"test-utils",
559+
"text-size",
559560
"tracing",
560561
"triomphe",
561562
"tt",

src/tools/rust-analyzer/crates/hir-def/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ smallvec.workspace = true
2929
hashbrown.workspace = true
3030
triomphe.workspace = true
3131
rustc_apfloat = "0.2.0"
32+
text-size.workspace = true
3233

3334
ra-ap-rustc_parse_format.workspace = true
3435
ra-ap-rustc_abi.workspace = true

src/tools/rust-analyzer/crates/hir-def/src/body.rs

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ use crate::{
3333
BlockId, DefWithBodyId, HasModule, Lookup,
3434
};
3535

36+
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
37+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38+
pub struct HygieneId(span::SyntaxContextId);
39+
40+
impl HygieneId {
41+
pub const ROOT: Self = Self(span::SyntaxContextId::ROOT);
42+
43+
pub fn new(ctx: span::SyntaxContextId) -> Self {
44+
Self(ctx)
45+
}
46+
47+
fn is_root(self) -> bool {
48+
self.0.is_root()
49+
}
50+
}
51+
3652
/// The body of an item (function, const etc.).
3753
#[derive(Debug, Eq, PartialEq)]
3854
pub struct Body {
@@ -55,6 +71,22 @@ pub struct Body {
5571
pub body_expr: ExprId,
5672
/// Block expressions in this body that may contain inner items.
5773
block_scopes: Vec<BlockId>,
74+
75+
/// A map from binding to its hygiene ID.
76+
///
77+
/// Bindings that don't come from macro expansion are not allocated to save space, so not all bindings appear here.
78+
/// If a binding does not appear here it has `SyntaxContextId::ROOT`.
79+
///
80+
/// Note that this may not be the direct `SyntaxContextId` of the binding's expansion, because transparent
81+
/// expansions are attributed to their parent expansion (recursively).
82+
binding_hygiene: FxHashMap<BindingId, HygieneId>,
83+
/// A map from an variable usages to their hygiene ID.
84+
///
85+
/// Expressions that can be recorded here are single segment path, although not all single segments path refer
86+
/// to variables and have hygiene (some refer to items, we don't know at this stage).
87+
expr_hygiene: FxHashMap<ExprId, HygieneId>,
88+
/// A map from a destructuring assignment possible variable usages to their hygiene ID.
89+
pat_hygiene: FxHashMap<PatId, HygieneId>,
5890
}
5991

6092
pub type ExprPtr = AstPtr<ast::Expr>;
@@ -107,10 +139,11 @@ pub struct BodySourceMap {
107139
field_map_back: FxHashMap<ExprId, FieldSource>,
108140
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
109141

142+
// FIXME: Make this a sane struct.
110143
template_map: Option<
111144
Box<(
112145
// format_args!
113-
FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
146+
FxHashMap<ExprId, (HygieneId, Vec<(syntax::TextRange, Name)>)>,
114147
// asm!
115148
FxHashMap<ExprId, Vec<Vec<(syntax::TextRange, usize)>>>,
116149
)>,
@@ -268,13 +301,19 @@ impl Body {
268301
pats,
269302
bindings,
270303
binding_owners,
304+
binding_hygiene,
305+
expr_hygiene,
306+
pat_hygiene,
271307
} = self;
272308
block_scopes.shrink_to_fit();
273309
exprs.shrink_to_fit();
274310
labels.shrink_to_fit();
275311
pats.shrink_to_fit();
276312
bindings.shrink_to_fit();
277313
binding_owners.shrink_to_fit();
314+
binding_hygiene.shrink_to_fit();
315+
expr_hygiene.shrink_to_fit();
316+
pat_hygiene.shrink_to_fit();
278317
}
279318

280319
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -467,6 +506,25 @@ impl Body {
467506
}
468507
});
469508
}
509+
510+
fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
511+
self.binding_hygiene.get(&binding).copied().unwrap_or(HygieneId::ROOT)
512+
}
513+
514+
pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
515+
self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT)
516+
}
517+
518+
pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
519+
self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT)
520+
}
521+
522+
pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
523+
match id {
524+
ExprOrPatId::ExprId(id) => self.expr_path_hygiene(id),
525+
ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
526+
}
527+
}
470528
}
471529

472530
impl Default for Body {
@@ -481,6 +539,9 @@ impl Default for Body {
481539
block_scopes: Default::default(),
482540
binding_owners: Default::default(),
483541
self_param: Default::default(),
542+
binding_hygiene: Default::default(),
543+
expr_hygiene: Default::default(),
544+
pat_hygiene: Default::default(),
484545
}
485546
}
486547
}
@@ -594,13 +655,11 @@ impl BodySourceMap {
594655
pub fn implicit_format_args(
595656
&self,
596657
node: InFile<&ast::FormatArgsExpr>,
597-
) -> Option<&[(syntax::TextRange, Name)]> {
658+
) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
598659
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
599-
self.template_map
600-
.as_ref()?
601-
.0
602-
.get(&self.expr_map.get(&src)?.as_expr()?)
603-
.map(std::ops::Deref::deref)
660+
let (hygiene, names) =
661+
self.template_map.as_ref()?.0.get(&self.expr_map.get(&src)?.as_expr()?)?;
662+
Some((*hygiene, &**names))
604663
}
605664

606665
pub fn asm_template_args(
@@ -649,13 +708,4 @@ impl BodySourceMap {
649708
diagnostics.shrink_to_fit();
650709
binding_definitions.shrink_to_fit();
651710
}
652-
653-
pub fn template_map(
654-
&self,
655-
) -> Option<&(
656-
FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
657-
FxHashMap<Idx<Expr>, Vec<Vec<(tt::TextRange, usize)>>>,
658-
)> {
659-
self.template_map.as_deref()
660-
}
661711
}

0 commit comments

Comments
 (0)