Skip to content

Commit 4453fd4

Browse files
committed
Fix expression scope calculation when within macro expansions
1 parent 00fc758 commit 4453fd4

File tree

7 files changed

+99
-33
lines changed

7 files changed

+99
-33
lines changed

src/tools/rust-analyzer/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ license = "MIT OR Apache-2.0"
1010
authors = ["rust-analyzer team"]
1111

1212
[profile.dev]
13-
# Disabling debug info speeds up builds a bunch,
14-
# and we don't rely on it for debugging that much.
15-
debug = 0
13+
debug = 1
1614

1715
[profile.dev.package]
1816
# These speed up local tests.

src/tools/rust-analyzer/crates/hir-expand/src/files.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,40 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
158158
// region:specific impls
159159

160160
impl InFile<&SyntaxNode> {
161+
pub fn parent_ancestors_with_macros(
162+
self,
163+
db: &dyn db::ExpandDatabase,
164+
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
165+
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
166+
Some(parent) => Some(node.with_value(parent)),
167+
None => db
168+
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
169+
.to_node_item(db)
170+
.syntax()
171+
.cloned()
172+
.map(|node| node.parent())
173+
.transpose(),
174+
};
175+
std::iter::successors(succ(&self.cloned()), succ)
176+
}
177+
178+
pub fn ancestors_with_macros(
179+
self,
180+
db: &dyn db::ExpandDatabase,
181+
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
182+
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
183+
Some(parent) => Some(node.with_value(parent)),
184+
None => db
185+
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
186+
.to_node_item(db)
187+
.syntax()
188+
.cloned()
189+
.map(|node| node.parent())
190+
.transpose(),
191+
};
192+
std::iter::successors(Some(self.cloned()), succ)
193+
}
194+
161195
/// Falls back to the macro call range if the node cannot be mapped up fully.
162196
///
163197
/// For attributes and derives, this will point back to the attribute only.

src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -68,38 +68,44 @@ impl SourceAnalyzer {
6868
pub(crate) fn new_for_body(
6969
db: &dyn HirDatabase,
7070
def: DefWithBodyId,
71-
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
71+
node: InFile<&SyntaxNode>,
7272
offset: Option<TextSize>,
7373
) -> SourceAnalyzer {
74-
let (body, source_map) = db.body_with_source_map(def);
75-
let scopes = db.expr_scopes(def);
76-
let scope = match offset {
77-
None => scope_for(&scopes, &source_map, node),
78-
Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset),
79-
};
80-
let resolver = resolver_for_scope(db.upcast(), def, scope);
81-
SourceAnalyzer {
82-
resolver,
83-
def: Some((def, body, source_map)),
84-
infer: Some(db.infer(def)),
85-
file_id,
86-
}
74+
Self::new_for_body_(db, def, node, offset, Some(db.infer(def)))
8775
}
8876

8977
pub(crate) fn new_for_body_no_infer(
78+
db: &dyn HirDatabase,
79+
def: DefWithBodyId,
80+
node: InFile<&SyntaxNode>,
81+
offset: Option<TextSize>,
82+
) -> SourceAnalyzer {
83+
Self::new_for_body_(db, def, node, offset, None)
84+
}
85+
86+
pub(crate) fn new_for_body_(
9087
db: &dyn HirDatabase,
9188
def: DefWithBodyId,
9289
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
9390
offset: Option<TextSize>,
91+
infer: Option<Arc<InferenceResult>>,
9492
) -> SourceAnalyzer {
9593
let (body, source_map) = db.body_with_source_map(def);
9694
let scopes = db.expr_scopes(def);
9795
let scope = match offset {
98-
None => scope_for(&scopes, &source_map, node),
99-
Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset),
96+
None => scope_for(db, &scopes, &source_map, node),
97+
Some(offset) => {
98+
debug_assert!(
99+
node.value.text_range().contains_inclusive(offset),
100+
"{:?} not in {:?}",
101+
offset,
102+
node.value.text_range()
103+
);
104+
scope_for_offset(db, &scopes, &source_map, node.file_id, offset)
105+
}
100106
};
101107
let resolver = resolver_for_scope(db.upcast(), def, scope);
102-
SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer: None, file_id }
108+
SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer, file_id }
103109
}
104110

105111
pub(crate) fn new_for_resolver(
@@ -662,7 +668,6 @@ impl SourceAnalyzer {
662668
return resolved;
663669
}
664670

665-
// This must be a normal source file rather than macro file.
666671
let ctx = LowerCtx::new(db.upcast(), self.file_id);
667672
let hir_path = Path::from_src(&ctx, path.clone())?;
668673

@@ -955,14 +960,17 @@ impl SourceAnalyzer {
955960
}
956961

957962
fn scope_for(
963+
db: &dyn HirDatabase,
958964
scopes: &ExprScopes,
959965
source_map: &BodySourceMap,
960966
node: InFile<&SyntaxNode>,
961967
) -> Option<ScopeId> {
962-
node.value
963-
.ancestors()
964-
.filter_map(ast::Expr::cast)
965-
.filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it)))
968+
node.ancestors_with_macros(db.upcast())
969+
.take_while(|it| {
970+
!ast::Item::can_cast(it.value.kind()) || ast::MacroCall::can_cast(it.value.kind())
971+
})
972+
.filter_map(|it| it.map(ast::Expr::cast).transpose())
973+
.filter_map(|it| source_map.node_expr(it.as_ref()))
966974
.find_map(|it| scopes.scope_for(it))
967975
}
968976

src/tools/rust-analyzer/crates/ide-assists/src/handlers/bool_to_enum.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
7676

7777
let usages = definition.usages(&ctx.sema).all();
7878
add_enum_def(edit, ctx, &usages, target_node, &target_module);
79-
replace_usages(edit, ctx, usages, definition, &target_module);
79+
let mut delayed_mutations = Vec::new();
80+
replace_usages(edit, ctx, usages, definition, &target_module, &mut delayed_mutations);
81+
for (scope, path) in delayed_mutations {
82+
insert_use(&scope, path, &ctx.config.insert_use);
83+
}
8084
},
8185
)
8286
}
@@ -197,6 +201,7 @@ fn replace_usages(
197201
usages: UsageSearchResult,
198202
target_definition: Definition,
199203
target_module: &hir::Module,
204+
delayed_mutations: &mut Vec<(ImportScope, ast::Path)>,
200205
) {
201206
for (file_id, references) in usages {
202207
edit.edit_file(file_id);
@@ -217,6 +222,7 @@ fn replace_usages(
217222
def.usages(&ctx.sema).all(),
218223
target_definition,
219224
target_module,
225+
delayed_mutations,
220226
)
221227
}
222228
} else if let Some(initializer) = find_assignment_usage(&name) {
@@ -255,6 +261,7 @@ fn replace_usages(
255261
def.usages(&ctx.sema).all(),
256262
target_definition,
257263
target_module,
264+
delayed_mutations,
258265
)
259266
}
260267
}
@@ -306,7 +313,7 @@ fn replace_usages(
306313
ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)),
307314
ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)),
308315
};
309-
insert_use(&scope, path, &ctx.config.insert_use);
316+
delayed_mutations.push((scope, path));
310317
}
311318
},
312319
)

src/tools/rust-analyzer/crates/ide/src/runnables.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,15 +78,14 @@ impl RunnableKind {
7878
}
7979

8080
impl Runnable {
81-
// test package::module::testname
8281
pub fn label(&self, target: Option<&str>) -> String {
8382
match &self.kind {
8483
RunnableKind::Test { test_id, .. } => format!("test {test_id}"),
8584
RunnableKind::TestMod { path } => format!("test-mod {path}"),
8685
RunnableKind::Bench { test_id } => format!("bench {test_id}"),
8786
RunnableKind::DocTest { test_id, .. } => format!("doctest {test_id}"),
8887
RunnableKind::Bin => {
89-
target.map_or_else(|| "run binary".to_owned(), |t| format!("run {t}"))
88+
format!("run {}", target.unwrap_or("binary"))
9089
}
9190
}
9291
}
@@ -513,11 +512,11 @@ impl TestAttr {
513512
}
514513
}
515514

516-
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
517-
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
518-
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
519-
520515
fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
516+
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
517+
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
518+
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
519+
521520
docs_from_attrs(attrs).map_or(false, |doc| {
522521
let mut in_code_block = false;
523522

src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,20 @@
9494
<span class="brace">}</span>
9595
<span class="brace">}</span>
9696

97+
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
98+
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
99+
<span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
100+
<span class="brace">}</span><span class="semicolon">;</span>
101+
<span class="brace">}</span>
97102

98103
<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="string_literal macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
99104

105+
<span class="keyword">struct</span> <span class="struct declaration">S</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="semicolon">;</span>
100106
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
107+
<span class="keyword">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span>
108+
<span class="comment">// regression test, TestLocal here used to not resolve</span>
109+
<span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle">&lt;</span><span class="macro">id</span><span class="macro_bang">!</span><span class="bracket macro">[</span><span class="struct macro">TestLocal</span><span class="bracket macro">]</span><span class="angle">&gt;</span><span class="semicolon">;</span>
110+
101111
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
102112
<span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
103113
<span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>

src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,20 @@ macro without_args {
102102
}
103103
}
104104
105+
macro_rules! id {
106+
($($tt:tt)*) => {
107+
$($tt)*
108+
};
109+
}
105110
106111
include!(concat!("foo/", "foo.rs"));
107112
113+
struct S<T>(T);
108114
fn main() {
115+
struct TestLocal;
116+
// regression test, TestLocal here used to not resolve
117+
let _: S<id![TestLocal]>;
118+
109119
format_args!("Hello, {}!", (92,).0);
110120
dont_color_me_braces!();
111121
noop!(noop!(1));

0 commit comments

Comments
 (0)