Skip to content

Commit 96db0d8

Browse files
committed
Add Semantics::original_ast_node for upmapping nodes out of macro files
1 parent b9fa37f commit 96db0d8

File tree

14 files changed

+182
-48
lines changed

14 files changed

+182
-48
lines changed

crates/hir/src/semantics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
208208
self.imp.original_range_opt(node)
209209
}
210210

211+
pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
212+
self.imp.original_ast_node(node)
213+
}
214+
211215
pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
212216
self.imp.diagnostics_display_range(diagnostics)
213217
}
@@ -660,6 +664,11 @@ impl<'db> SemanticsImpl<'db> {
660664
node.as_ref().original_file_range_opt(self.db.upcast())
661665
}
662666

667+
fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
668+
let file = self.find_file(node.syntax().clone());
669+
file.with_value(node).original_ast_node(self.db.upcast()).map(|it| it.value)
670+
}
671+
663672
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
664673
let root = self.db.parse_or_expand(src.file_id).unwrap();
665674
let node = src.value.to_node(&root);

crates/hir_expand/src/lib.rs

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ use std::{hash::Hash, iter, sync::Arc};
2424

2525
use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
2626
use syntax::{
27-
algo::skip_trivia_token,
27+
algo::{self, skip_trivia_token},
2828
ast::{self, AstNode, HasAttrs},
29-
Direction, SyntaxNode, SyntaxToken, TextRange,
29+
Direction, SyntaxNode, SyntaxToken,
3030
};
3131

3232
use crate::{
@@ -600,13 +600,15 @@ impl<'a> InFile<&'a SyntaxNode> {
600600

601601
/// Attempts to map the syntax node back up its macro calls.
602602
pub fn original_file_range_opt(self, db: &dyn db::AstDatabase) -> Option<FileRange> {
603-
match original_range_opt(db, self) {
604-
Some(range) => {
605-
let original_file = range.file_id.original_file(db);
606-
if range.file_id != original_file.into() {
603+
match ascend_node_border_tokens(db, self) {
604+
Some(InFile { file_id, value: (first, last) }) => {
605+
let original_file = file_id.original_file(db);
606+
let range = first.text_range().cover(last.text_range());
607+
if file_id != original_file.into() {
607608
tracing::error!("Failed mapping up more for {:?}", range);
609+
return None;
608610
}
609-
Some(FileRange { file_id: original_file, range: range.value })
611+
Some(FileRange { file_id: original_file, range })
610612
}
611613
_ if !self.file_id.is_macro() => Some(FileRange {
612614
file_id: self.file_id.original_file(db),
@@ -617,28 +619,29 @@ impl<'a> InFile<&'a SyntaxNode> {
617619
}
618620
}
619621

620-
fn original_range_opt(
622+
fn ascend_node_border_tokens(
621623
db: &dyn db::AstDatabase,
622-
node: InFile<&SyntaxNode>,
623-
) -> Option<InFile<TextRange>> {
624-
let expansion = node.file_id.expansion_info(db)?;
624+
InFile { file_id, value: node }: InFile<&SyntaxNode>,
625+
) -> Option<InFile<(SyntaxToken, SyntaxToken)>> {
626+
let expansion = file_id.expansion_info(db)?;
625627

626628
// the input node has only one token ?
627-
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
628-
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
629+
let first = skip_trivia_token(node.first_token()?, Direction::Next)?;
630+
let last = skip_trivia_token(node.last_token()?, Direction::Prev)?;
631+
let is_single_token = first == last;
629632

630-
node.value.descendants().find_map(|it| {
633+
node.descendants().find_map(|it| {
631634
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
632-
let first = ascend_call_token(db, &expansion, node.with_value(first))?;
635+
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
633636

634637
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
635-
let last = ascend_call_token(db, &expansion, node.with_value(last))?;
638+
let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
636639

637-
if (!single && first == last) || (first.file_id != last.file_id) {
640+
if (!is_single_token && first == last) || (first.file_id != last.file_id) {
638641
return None;
639642
}
640643

641-
Some(first.with_value(first.value.text_range().cover(last.value.text_range())))
644+
Some(InFile::new(first.file_id, (first.value, last.value)))
642645
})
643646
}
644647

@@ -674,6 +677,23 @@ impl<N: AstNode> InFile<N> {
674677
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
675678
}
676679

680+
pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option<InFile<N>> {
681+
match ascend_node_border_tokens(db, self.syntax()) {
682+
Some(InFile { file_id, value: (first, last) }) => {
683+
let original_file = file_id.original_file(db);
684+
if file_id != original_file.into() {
685+
let range = first.text_range().cover(last.text_range());
686+
tracing::error!("Failed mapping up more for {:?}", range);
687+
return None;
688+
}
689+
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
690+
Some(InFile::new(file_id, anc.ancestors().find_map(N::cast)?))
691+
}
692+
_ if !self.file_id.is_macro() => Some(self),
693+
_ => None,
694+
}
695+
}
696+
677697
pub fn syntax(&self) -> InFile<&SyntaxNode> {
678698
self.with_value(self.value.syntax())
679699
}

crates/ide_assists/src/handlers/auto_import.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
9595
NodeOrToken::Token(token) => token.text_range(),
9696
};
9797
let group_label = group_label(import_assets.import_candidate());
98-
let scope = ImportScope::find_insert_use_container_with_macros(
98+
let scope = ImportScope::find_insert_use_container(
9999
&match syntax_under_caret {
100100
NodeOrToken::Node(it) => it,
101101
NodeOrToken::Token(it) => it.parent()?,
@@ -164,6 +164,60 @@ mod tests {
164164

165165
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
166166

167+
#[test]
168+
fn not_applicable_if_scope_inside_macro() {
169+
check_assist_not_applicable(
170+
auto_import,
171+
r"
172+
mod bar {
173+
pub struct Baz;
174+
}
175+
macro_rules! foo {
176+
($it:ident) => {
177+
mod __ {
178+
fn __(x: $it) {}
179+
}
180+
};
181+
}
182+
foo! {
183+
Baz$0
184+
}
185+
",
186+
);
187+
}
188+
189+
#[test]
190+
fn applicable_in_attributes() {
191+
check_assist(
192+
auto_import,
193+
r"
194+
//- proc_macros: identity
195+
#[proc_macros::identity]
196+
mod foo {
197+
mod bar {
198+
const _: Baz$0 = ();
199+
}
200+
}
201+
mod baz {
202+
pub struct Baz;
203+
}
204+
",
205+
r"
206+
#[proc_macros::identity]
207+
mod foo {
208+
mod bar {
209+
use crate::baz::Baz;
210+
211+
const _: Baz = ();
212+
}
213+
}
214+
mod baz {
215+
pub struct Baz;
216+
}
217+
",
218+
);
219+
}
220+
167221
#[test]
168222
fn applicable_when_found_an_import_partial() {
169223
check_assist(

crates/ide_assists/src/handlers/extract_function.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option
9191

9292
let target_range = body.text_range();
9393

94-
let scope = ImportScope::find_insert_use_container_with_macros(&node, &ctx.sema)?;
94+
let scope = ImportScope::find_insert_use_container(&node, &ctx.sema)?;
9595

9696
acc.add(
9797
AssistId("extract_function", crate::AssistKind::RefactorExtract),

crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ fn process_references(
314314
if let Some(mut mod_path) = mod_path {
315315
mod_path.pop_segment();
316316
mod_path.push_segment(variant_hir_name.clone());
317-
let scope = ImportScope::find_insert_use_container(&scope_node)?;
317+
let scope = ImportScope::find_insert_use_container(&scope_node, &ctx.sema)?;
318318
visited_modules.insert(module);
319319
return Some((segment, scope_node, Some((scope, mod_path))));
320320
}

crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub(crate) fn replace_qualified_name_with_use(
7070
})
7171
.flatten();
7272

73-
let scope = ImportScope::find_insert_use_container_with_macros(path.syntax(), &ctx.sema)?;
73+
let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
7474
let target = path.syntax().text_range();
7575
acc.add(
7676
AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),

crates/ide_assists/src/tests.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ mod sourcegen;
22
mod generated;
33

44
use expect_test::expect;
5-
use hir::Semantics;
5+
use hir::{db::DefDatabase, Semantics};
66
use ide_db::{
77
base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
88
helpers::{
@@ -117,7 +117,8 @@ enum ExpectedResult<'a> {
117117

118118
#[track_caller]
119119
fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: Option<&str>) {
120-
let (db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
120+
let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
121+
db.set_enable_proc_attr_macros(true);
121122
let text_without_caret = db.file_text(file_with_caret_id).to_string();
122123

123124
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };

crates/ide_completion/src/completions/attribute/derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ fn flyimport_attribute(ctx: &CompletionContext, acc: &mut Completions) -> Option
9898
&ctx.sema,
9999
parent.clone(),
100100
)?;
101-
let import_scope = ImportScope::find_insert_use_container_with_macros(&parent, &ctx.sema)?;
101+
let import_scope = ImportScope::find_insert_use_container(&parent, &ctx.sema)?;
102102
acc.add_all(
103103
import_assets
104104
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)

crates/ide_completion/src/completions/flyimport.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
129129

130130
let user_input_lowercased = potential_import_name.to_lowercase();
131131
let import_assets = import_assets(ctx, potential_import_name)?;
132-
let import_scope = ImportScope::find_insert_use_container_with_macros(
132+
let import_scope = ImportScope::find_insert_use_container(
133133
&position_for_import(ctx, Some(import_assets.import_candidate()))?,
134134
&ctx.sema,
135135
)?;

crates/ide_completion/src/completions/postfix.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ fn add_custom_postfix_completions(
244244
postfix_snippet: impl Fn(&str, &str, &str) -> Builder,
245245
receiver_text: &str,
246246
) -> Option<()> {
247-
let import_scope =
248-
ImportScope::find_insert_use_container_with_macros(&ctx.token.parent()?, &ctx.sema)?;
247+
let import_scope = ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?;
249248
ctx.config.postfix_snippets().filter(|(_, snip)| snip.scope == SnippetScope::Expr).for_each(
250249
|(trigger, snippet)| {
251250
let imports = match snippet.imports(ctx, &import_scope) {

0 commit comments

Comments
 (0)