mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Fix expression scope calculation when within macro expansions
This commit is contained in:
parent
882ae7105d
commit
bfb187aacd
7 changed files with 99 additions and 33 deletions
|
@ -10,9 +10,7 @@ license = "MIT OR Apache-2.0"
|
||||||
authors = ["rust-analyzer team"]
|
authors = ["rust-analyzer team"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
# Disabling debug info speeds up builds a bunch,
|
debug = 1
|
||||||
# and we don't rely on it for debugging that much.
|
|
||||||
debug = 0
|
|
||||||
|
|
||||||
[profile.dev.package]
|
[profile.dev.package]
|
||||||
# These speed up local tests.
|
# These speed up local tests.
|
||||||
|
|
|
@ -158,6 +158,40 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
|
||||||
// region:specific impls
|
// region:specific impls
|
||||||
|
|
||||||
impl InFile<&SyntaxNode> {
|
impl InFile<&SyntaxNode> {
|
||||||
|
pub fn parent_ancestors_with_macros(
|
||||||
|
self,
|
||||||
|
db: &dyn db::ExpandDatabase,
|
||||||
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
||||||
|
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||||
|
Some(parent) => Some(node.with_value(parent)),
|
||||||
|
None => db
|
||||||
|
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||||
|
.to_node_item(db)
|
||||||
|
.syntax()
|
||||||
|
.cloned()
|
||||||
|
.map(|node| node.parent())
|
||||||
|
.transpose(),
|
||||||
|
};
|
||||||
|
std::iter::successors(succ(&self.cloned()), succ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ancestors_with_macros(
|
||||||
|
self,
|
||||||
|
db: &dyn db::ExpandDatabase,
|
||||||
|
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
|
||||||
|
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
|
||||||
|
Some(parent) => Some(node.with_value(parent)),
|
||||||
|
None => db
|
||||||
|
.lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id)
|
||||||
|
.to_node_item(db)
|
||||||
|
.syntax()
|
||||||
|
.cloned()
|
||||||
|
.map(|node| node.parent())
|
||||||
|
.transpose(),
|
||||||
|
};
|
||||||
|
std::iter::successors(Some(self.cloned()), succ)
|
||||||
|
}
|
||||||
|
|
||||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||||
///
|
///
|
||||||
/// For attributes and derives, this will point back to the attribute only.
|
/// For attributes and derives, this will point back to the attribute only.
|
||||||
|
|
|
@ -68,38 +68,44 @@ impl SourceAnalyzer {
|
||||||
pub(crate) fn new_for_body(
|
pub(crate) fn new_for_body(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
def: DefWithBodyId,
|
def: DefWithBodyId,
|
||||||
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
|
node: InFile<&SyntaxNode>,
|
||||||
offset: Option<TextSize>,
|
offset: Option<TextSize>,
|
||||||
) -> SourceAnalyzer {
|
) -> SourceAnalyzer {
|
||||||
let (body, source_map) = db.body_with_source_map(def);
|
Self::new_for_body_(db, def, node, offset, Some(db.infer(def)))
|
||||||
let scopes = db.expr_scopes(def);
|
|
||||||
let scope = match offset {
|
|
||||||
None => scope_for(&scopes, &source_map, node),
|
|
||||||
Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset),
|
|
||||||
};
|
|
||||||
let resolver = resolver_for_scope(db.upcast(), def, scope);
|
|
||||||
SourceAnalyzer {
|
|
||||||
resolver,
|
|
||||||
def: Some((def, body, source_map)),
|
|
||||||
infer: Some(db.infer(def)),
|
|
||||||
file_id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_for_body_no_infer(
|
pub(crate) fn new_for_body_no_infer(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
def: DefWithBodyId,
|
||||||
|
node: InFile<&SyntaxNode>,
|
||||||
|
offset: Option<TextSize>,
|
||||||
|
) -> SourceAnalyzer {
|
||||||
|
Self::new_for_body_(db, def, node, offset, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new_for_body_(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
def: DefWithBodyId,
|
def: DefWithBodyId,
|
||||||
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
|
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
|
||||||
offset: Option<TextSize>,
|
offset: Option<TextSize>,
|
||||||
|
infer: Option<Arc<InferenceResult>>,
|
||||||
) -> SourceAnalyzer {
|
) -> SourceAnalyzer {
|
||||||
let (body, source_map) = db.body_with_source_map(def);
|
let (body, source_map) = db.body_with_source_map(def);
|
||||||
let scopes = db.expr_scopes(def);
|
let scopes = db.expr_scopes(def);
|
||||||
let scope = match offset {
|
let scope = match offset {
|
||||||
None => scope_for(&scopes, &source_map, node),
|
None => scope_for(db, &scopes, &source_map, node),
|
||||||
Some(offset) => scope_for_offset(db, &scopes, &source_map, node.file_id, offset),
|
Some(offset) => {
|
||||||
|
debug_assert!(
|
||||||
|
node.value.text_range().contains_inclusive(offset),
|
||||||
|
"{:?} not in {:?}",
|
||||||
|
offset,
|
||||||
|
node.value.text_range()
|
||||||
|
);
|
||||||
|
scope_for_offset(db, &scopes, &source_map, node.file_id, offset)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let resolver = resolver_for_scope(db.upcast(), def, scope);
|
let resolver = resolver_for_scope(db.upcast(), def, scope);
|
||||||
SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer: None, file_id }
|
SourceAnalyzer { resolver, def: Some((def, body, source_map)), infer, file_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_for_resolver(
|
pub(crate) fn new_for_resolver(
|
||||||
|
@ -662,7 +668,6 @@ impl SourceAnalyzer {
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This must be a normal source file rather than macro file.
|
|
||||||
let ctx = LowerCtx::new(db.upcast(), self.file_id);
|
let ctx = LowerCtx::new(db.upcast(), self.file_id);
|
||||||
let hir_path = Path::from_src(&ctx, path.clone())?;
|
let hir_path = Path::from_src(&ctx, path.clone())?;
|
||||||
|
|
||||||
|
@ -955,14 +960,17 @@ impl SourceAnalyzer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_for(
|
fn scope_for(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
scopes: &ExprScopes,
|
scopes: &ExprScopes,
|
||||||
source_map: &BodySourceMap,
|
source_map: &BodySourceMap,
|
||||||
node: InFile<&SyntaxNode>,
|
node: InFile<&SyntaxNode>,
|
||||||
) -> Option<ScopeId> {
|
) -> Option<ScopeId> {
|
||||||
node.value
|
node.ancestors_with_macros(db.upcast())
|
||||||
.ancestors()
|
.take_while(|it| {
|
||||||
.filter_map(ast::Expr::cast)
|
!ast::Item::can_cast(it.value.kind()) || ast::MacroCall::can_cast(it.value.kind())
|
||||||
.filter_map(|it| source_map.node_expr(InFile::new(node.file_id, &it)))
|
})
|
||||||
|
.filter_map(|it| it.map(ast::Expr::cast).transpose())
|
||||||
|
.filter_map(|it| source_map.node_expr(it.as_ref()))
|
||||||
.find_map(|it| scopes.scope_for(it))
|
.find_map(|it| scopes.scope_for(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,11 @@ pub(crate) fn bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
||||||
|
|
||||||
let usages = definition.usages(&ctx.sema).all();
|
let usages = definition.usages(&ctx.sema).all();
|
||||||
add_enum_def(edit, ctx, &usages, target_node, &target_module);
|
add_enum_def(edit, ctx, &usages, target_node, &target_module);
|
||||||
replace_usages(edit, ctx, usages, definition, &target_module);
|
let mut delayed_mutations = Vec::new();
|
||||||
|
replace_usages(edit, ctx, usages, definition, &target_module, &mut delayed_mutations);
|
||||||
|
for (scope, path) in delayed_mutations {
|
||||||
|
insert_use(&scope, path, &ctx.config.insert_use);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -197,6 +201,7 @@ fn replace_usages(
|
||||||
usages: UsageSearchResult,
|
usages: UsageSearchResult,
|
||||||
target_definition: Definition,
|
target_definition: Definition,
|
||||||
target_module: &hir::Module,
|
target_module: &hir::Module,
|
||||||
|
delayed_mutations: &mut Vec<(ImportScope, ast::Path)>,
|
||||||
) {
|
) {
|
||||||
for (file_id, references) in usages {
|
for (file_id, references) in usages {
|
||||||
edit.edit_file(file_id);
|
edit.edit_file(file_id);
|
||||||
|
@ -217,6 +222,7 @@ fn replace_usages(
|
||||||
def.usages(&ctx.sema).all(),
|
def.usages(&ctx.sema).all(),
|
||||||
target_definition,
|
target_definition,
|
||||||
target_module,
|
target_module,
|
||||||
|
delayed_mutations,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if let Some(initializer) = find_assignment_usage(&name) {
|
} else if let Some(initializer) = find_assignment_usage(&name) {
|
||||||
|
@ -255,6 +261,7 @@ fn replace_usages(
|
||||||
def.usages(&ctx.sema).all(),
|
def.usages(&ctx.sema).all(),
|
||||||
target_definition,
|
target_definition,
|
||||||
target_module,
|
target_module,
|
||||||
|
delayed_mutations,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,7 +313,7 @@ fn replace_usages(
|
||||||
ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)),
|
ImportScope::Module(it) => ImportScope::Module(edit.make_mut(it)),
|
||||||
ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)),
|
ImportScope::Block(it) => ImportScope::Block(edit.make_mut(it)),
|
||||||
};
|
};
|
||||||
insert_use(&scope, path, &ctx.config.insert_use);
|
delayed_mutations.push((scope, path));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -78,7 +78,6 @@ impl RunnableKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Runnable {
|
impl Runnable {
|
||||||
// test package::module::testname
|
|
||||||
pub fn label(&self, target: Option<&str>) -> String {
|
pub fn label(&self, target: Option<&str>) -> String {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
RunnableKind::Test { test_id, .. } => format!("test {test_id}"),
|
RunnableKind::Test { test_id, .. } => format!("test {test_id}"),
|
||||||
|
@ -86,7 +85,7 @@ impl Runnable {
|
||||||
RunnableKind::Bench { test_id } => format!("bench {test_id}"),
|
RunnableKind::Bench { test_id } => format!("bench {test_id}"),
|
||||||
RunnableKind::DocTest { test_id, .. } => format!("doctest {test_id}"),
|
RunnableKind::DocTest { test_id, .. } => format!("doctest {test_id}"),
|
||||||
RunnableKind::Bin => {
|
RunnableKind::Bin => {
|
||||||
target.map_or_else(|| "run binary".to_owned(), |t| format!("run {t}"))
|
format!("run {}", target.unwrap_or("binary"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -513,11 +512,11 @@ impl TestAttr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
|
|
||||||
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
|
|
||||||
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
|
|
||||||
|
|
||||||
fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
|
fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
|
||||||
|
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
|
||||||
|
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
|
||||||
|
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
|
||||||
|
|
||||||
docs_from_attrs(attrs).map_or(false, |doc| {
|
docs_from_attrs(attrs).map_or(false, |doc| {
|
||||||
let mut in_code_block = false;
|
let mut in_code_block = false;
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,20 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||||
<span class="brace">}</span>
|
<span class="brace">}</span>
|
||||||
<span class="brace">}</span>
|
<span class="brace">}</span>
|
||||||
|
|
||||||
|
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span>
|
||||||
|
<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">></span> <span class="brace">{</span>
|
||||||
|
<span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span>
|
||||||
|
<span class="brace">}</span><span class="semicolon">;</span>
|
||||||
|
<span class="brace">}</span>
|
||||||
|
|
||||||
<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>
|
<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>
|
||||||
|
|
||||||
|
<span class="keyword">struct</span> <span class="struct declaration">S</span><span class="angle"><</span><span class="type_param declaration">T</span><span class="angle">></span><span class="parenthesis">(</span><span class="type_param">T</span><span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||||
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
||||||
|
<span class="keyword">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span>
|
||||||
|
<span class="comment">// regression test, TestLocal here used to not resolve</span>
|
||||||
|
<span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle"><</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">></span><span class="semicolon">;</span>
|
||||||
|
|
||||||
<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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
|
|
|
@ -102,10 +102,20 @@ macro without_args {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! id {
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
$($tt)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
include!(concat!("foo/", "foo.rs"));
|
include!(concat!("foo/", "foo.rs"));
|
||||||
|
|
||||||
|
struct S<T>(T);
|
||||||
fn main() {
|
fn main() {
|
||||||
|
struct TestLocal;
|
||||||
|
// regression test, TestLocal here used to not resolve
|
||||||
|
let _: S<id![TestLocal]>;
|
||||||
|
|
||||||
format_args!("Hello, {}!", (92,).0);
|
format_args!("Hello, {}!", (92,).0);
|
||||||
dont_color_me_braces!();
|
dont_color_me_braces!();
|
||||||
noop!(noop!(1));
|
noop!(noop!(1));
|
||||||
|
|
Loading…
Reference in a new issue