Skip to content

Commit fa06e3d

Browse files
bors[bot]Veykril
andauthored
Merge #11911
11911: fix: Fix SearchScope using incorrect text ranges for macro-emitted inline modules r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents 2366d8e + d9f6cee commit fa06e3d

File tree

2 files changed

+89
-59
lines changed

2 files changed

+89
-59
lines changed

crates/hir_expand/src/lib.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,17 @@ impl HirFileId {
175175
/// For macro-expansion files, returns the file original source file the
176176
/// expansion originated from.
177177
pub fn original_file(self, db: &dyn db::AstDatabase) -> FileId {
178-
match self.0 {
179-
HirFileIdRepr::FileId(file_id) => file_id,
180-
HirFileIdRepr::MacroFile(macro_file) => {
181-
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
182-
let file_id = match loc.eager {
183-
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
184-
_ => loc.kind.file_id(),
185-
};
186-
file_id.original_file(db)
178+
let mut file_id = self;
179+
loop {
180+
match file_id.0 {
181+
HirFileIdRepr::FileId(id) => break id,
182+
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
183+
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
184+
file_id = match loc.eager {
185+
Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
186+
_ => loc.kind.file_id(),
187+
};
188+
}
187189
}
188190
}
189191
}
@@ -211,6 +213,24 @@ impl HirFileId {
211213
}
212214
}
213215

216+
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
217+
pub fn original_call_node(self, db: &dyn db::AstDatabase) -> Option<(FileId, SyntaxNode)> {
218+
let mut call = match self.0 {
219+
HirFileIdRepr::FileId(_) => return None,
220+
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
221+
db.lookup_intern_macro_call(macro_call_id).kind.to_node(db)
222+
}
223+
};
224+
loop {
225+
match call.file_id.0 {
226+
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
227+
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
228+
call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
229+
}
230+
}
231+
}
232+
}
233+
214234
/// Return expansion information if it is a macro-expansion file
215235
pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
216236
match self.0 {
@@ -675,9 +695,11 @@ impl<T> InFile<T> {
675695
pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
676696
InFile::new(self.file_id, f(self.value))
677697
}
698+
678699
pub fn as_ref(&self) -> InFile<&T> {
679700
self.with_value(&self.value)
680701
}
702+
681703
pub fn file_syntax(&self, db: &dyn db::AstDatabase) -> SyntaxNode {
682704
db.parse_or_expand(self.file_id).expect("source created from invalid file")
683705
}

crates/ide_db/src/search.rs

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl SearchScope {
8585
SearchScope { entries }
8686
}
8787

88+
/// Build a search scope spanning the entire crate graph of files.
8889
fn crate_graph(db: &RootDatabase) -> SearchScope {
8990
let mut entries = FxHashMap::default();
9091

@@ -98,6 +99,7 @@ impl SearchScope {
9899
SearchScope { entries }
99100
}
100101

102+
/// Build a search scope spanning all the reverse dependencies of the given crate.
101103
fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
102104
let mut entries = FxHashMap::default();
103105
for rev_dep in of.transitive_reverse_dependencies(db) {
@@ -109,6 +111,7 @@ impl SearchScope {
109111
SearchScope { entries }
110112
}
111113

114+
/// Build a search scope spanning the given crate.
112115
fn krate(db: &RootDatabase, of: hir::Crate) -> SearchScope {
113116
let root_file = of.root_file(db);
114117
let source_root_id = db.file_source_root(root_file);
@@ -118,55 +121,55 @@ impl SearchScope {
118121
}
119122
}
120123

121-
fn module(db: &RootDatabase, module: hir::Module) -> SearchScope {
124+
/// Build a search scope spanning the given module and all its submodules.
125+
fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
122126
let mut entries = FxHashMap::default();
123127

124-
let mut to_visit = vec![module];
125-
let mut is_first = true;
128+
let (file_id, range) = {
129+
let InFile { file_id, value } = module.definition_source(db);
130+
if let Some((file_id, call_source)) = file_id.original_call_node(db) {
131+
(file_id, Some(call_source.text_range()))
132+
} else {
133+
(
134+
file_id.original_file(db),
135+
match value {
136+
ModuleSource::SourceFile(_) => None,
137+
ModuleSource::Module(it) => Some(it.syntax().text_range()),
138+
ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()),
139+
},
140+
)
141+
}
142+
};
143+
entries.insert(file_id, range);
144+
145+
let mut to_visit: Vec<_> = module.children(db).collect();
126146
while let Some(module) = to_visit.pop() {
127-
let src = module.definition_source(db);
128-
let file_id = src.file_id.original_file(db);
129-
match src.value {
130-
ModuleSource::Module(m) => {
131-
if is_first {
132-
let range = Some(m.syntax().text_range());
133-
entries.insert(file_id, range);
134-
} else {
135-
// We have already added the enclosing file to the search scope,
136-
// so do nothing.
137-
}
138-
}
139-
ModuleSource::BlockExpr(b) => {
140-
if is_first {
141-
let range = Some(b.syntax().text_range());
142-
entries.insert(file_id, range);
143-
} else {
144-
// We have already added the enclosing file to the search scope,
145-
// so do nothing.
146-
}
147-
}
148-
ModuleSource::SourceFile(_) => {
149-
entries.insert(file_id, None);
150-
}
151-
};
152-
is_first = false;
147+
if let InFile { file_id, value: ModuleSource::SourceFile(_) } =
148+
module.definition_source(db)
149+
{
150+
entries.insert(file_id.original_file(db), None);
151+
}
153152
to_visit.extend(module.children(db));
154153
}
155154
SearchScope { entries }
156155
}
157156

157+
/// Build an empty search scope.
158158
pub fn empty() -> SearchScope {
159159
SearchScope::new(FxHashMap::default())
160160
}
161161

162+
/// Build a empty search scope spanning the given file.
162163
pub fn single_file(file: FileId) -> SearchScope {
163164
SearchScope::new(std::iter::once((file, None)).collect())
164165
}
165166

167+
/// Build a empty search scope spanning the text range of the given file.
166168
pub fn file_range(range: FileRange) -> SearchScope {
167169
SearchScope::new(std::iter::once((range.file_id, Some(range.range))).collect())
168170
}
169171

172+
/// Build a empty search scope spanning the given files.
170173
pub fn files(files: &[FileId]) -> SearchScope {
171174
SearchScope::new(files.iter().map(|f| (*f, None)).collect())
172175
}
@@ -177,29 +180,23 @@ impl SearchScope {
177180
mem::swap(&mut small, &mut large)
178181
}
179182

183+
let intersect_ranges =
184+
|r1: Option<TextRange>, r2: Option<TextRange>| -> Option<Option<TextRange>> {
185+
match (r1, r2) {
186+
(None, r) | (r, None) => Some(r),
187+
(Some(r1), Some(r2)) => r1.intersect(r2).map(Some),
188+
}
189+
};
180190
let res = small
181191
.iter()
182-
.filter_map(|(file_id, r1)| {
183-
let r2 = large.get(file_id)?;
184-
let r = intersect_ranges(*r1, *r2)?;
185-
Some((*file_id, r))
192+
.filter_map(|(&file_id, &r1)| {
193+
let &r2 = large.get(&file_id)?;
194+
let r = intersect_ranges(r1, r2)?;
195+
Some((file_id, r))
186196
})
187197
.collect();
188198

189-
return SearchScope::new(res);
190-
191-
fn intersect_ranges(
192-
r1: Option<TextRange>,
193-
r2: Option<TextRange>,
194-
) -> Option<Option<TextRange>> {
195-
match (r1, r2) {
196-
(None, r) | (r, None) => Some(r),
197-
(Some(r1), Some(r2)) => {
198-
let r = r1.intersect(r2)?;
199-
Some(Some(r))
200-
}
201-
}
202-
}
199+
SearchScope::new(res)
203200
}
204201
}
205202

@@ -282,7 +279,8 @@ impl Definition {
282279
hir::MacroKind::BuiltIn => SearchScope::crate_graph(db),
283280
// FIXME: We don't actually see derives in derive attributes as these do not
284281
// expand to something that references the derive macro in the output.
285-
// We could get around this by emitting dummy `use DeriveMacroPathHere as _;` items maybe?
282+
// We could get around this by doing pseudo expansions for proc_macro_derive like we
283+
// do for the derive attribute
286284
hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => {
287285
SearchScope::reverse_dependencies(db, module.krate())
288286
}
@@ -294,7 +292,7 @@ impl Definition {
294292
return SearchScope::reverse_dependencies(db, module.krate());
295293
}
296294
if let Some(Visibility::Module(module)) = vis {
297-
return SearchScope::module(db, module.into());
295+
return SearchScope::module_and_children(db, module.into());
298296
}
299297

300298
let range = match module_source {
@@ -341,10 +339,12 @@ impl<'a> FindUsages<'a> {
341339
self
342340
}
343341

342+
/// Limit the search to a given [`SearchScope`].
344343
pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
345344
self.set_scope(Some(scope))
346345
}
347346

347+
/// Limit the search to a given [`SearchScope`].
348348
pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
349349
assert!(self.scope.is_none());
350350
self.scope = scope;
@@ -420,6 +420,7 @@ impl<'a> FindUsages<'a> {
420420
Some(offset)
421421
})
422422
}
423+
423424
fn scope_files<'a>(
424425
sema: &'a Semantics<RootDatabase>,
425426
scope: &'a SearchScope,
@@ -433,6 +434,12 @@ impl<'a> FindUsages<'a> {
433434
})
434435
}
435436

437+
// FIXME: There should be optimization potential here
438+
// Currently we try to descend everything we find which
439+
// means we call `Semantics::descend_into_macros` on
440+
// every textual hit. That function is notoriously
441+
// expensive even for things that do not get down mapped
442+
// into macros.
436443
for (text, file_id, search_range) in scope_files(sema, &search_scope) {
437444
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
438445

@@ -463,7 +470,8 @@ impl<'a> FindUsages<'a> {
463470
// Search for `super` and `crate` resolving to our module
464471
match self.def {
465472
Definition::Module(module) => {
466-
let scope = search_scope.intersection(&SearchScope::module(self.sema.db, module));
473+
let scope = search_scope
474+
.intersection(&SearchScope::module_and_children(self.sema.db, module));
467475

468476
let is_crate_root = module.is_crate_root(self.sema.db);
469477

0 commit comments

Comments
 (0)