Skip to main content

ide_db/
search.rs

1//! Implementation of find-usages functionality.
2//!
3//! It is based on the standard ide trick: first, we run a fast text search to
4//! get a super-set of matches. Then, we confirm each match using precise
5//! name resolution.
6
7use std::mem;
8use std::{cell::LazyCell, cmp::Reverse};
9
10use base_db::{SourceDatabase, all_crates};
11use either::Either;
12use hir::{
13    Adt, AsAssocItem, DefWithBody, EditionedFileId, ExpressionStoreOwner, FileRange,
14    FileRangeWrapper, HasAttrs, HasContainer, HasSource, InFile, InFileWrapper, InRealFile,
15    InlineAsmOperand, ItemContainer, ModuleSource, PathResolution, Semantics, Visibility,
16};
17use memchr::memmem::Finder;
18use parser::SyntaxKind;
19use rustc_hash::{FxHashMap, FxHashSet};
20use salsa::Database;
21use syntax::{
22    AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize, ToSmolStr,
23    ast::{self, HasName, Rename},
24    match_ast,
25};
26use triomphe::Arc;
27
28use crate::{
29    RootDatabase,
30    defs::{Definition, NameClass, NameRefClass},
31    traits::{as_trait_assoc_def, convert_to_def_in_trait},
32};
33
34#[derive(Debug, Default, Clone)]
35pub struct UsageSearchResult {
36    pub references: FxHashMap<EditionedFileId, Vec<FileReference>>,
37}
38
39impl UsageSearchResult {
40    pub fn is_empty(&self) -> bool {
41        self.references.is_empty()
42    }
43
44    pub fn len(&self) -> usize {
45        self.references.len()
46    }
47
48    pub fn iter(&self) -> impl Iterator<Item = (EditionedFileId, &[FileReference])> + '_ {
49        self.references.iter().map(|(&file_id, refs)| (file_id, &**refs))
50    }
51
52    pub fn file_ranges(&self) -> impl Iterator<Item = FileRange> + '_ {
53        self.references.iter().flat_map(|(&file_id, refs)| {
54            refs.iter().map(move |&FileReference { range, .. }| FileRange { file_id, range })
55        })
56    }
57}
58
59impl IntoIterator for UsageSearchResult {
60    type Item = (EditionedFileId, Vec<FileReference>);
61    type IntoIter = <FxHashMap<EditionedFileId, Vec<FileReference>> as IntoIterator>::IntoIter;
62
63    fn into_iter(self) -> Self::IntoIter {
64        self.references.into_iter()
65    }
66}
67
68#[derive(Debug, Clone)]
69pub struct FileReference {
70    /// The range of the reference in the original file
71    pub range: TextRange,
72    /// The node of the reference in the (macro-)file
73    pub name: FileReferenceNode,
74    pub category: ReferenceCategory,
75}
76
77#[derive(Debug, Clone)]
78pub enum FileReferenceNode {
79    Name(ast::Name),
80    NameRef(ast::NameRef),
81    Lifetime(ast::Lifetime),
82    FormatStringEntry(ast::String, TextRange),
83}
84
85impl FileReferenceNode {
86    pub fn text_range(&self) -> TextRange {
87        match self {
88            FileReferenceNode::Name(it) => it.syntax().text_range(),
89            FileReferenceNode::NameRef(it) => it.syntax().text_range(),
90            FileReferenceNode::Lifetime(it) => it.syntax().text_range(),
91            FileReferenceNode::FormatStringEntry(_, range) => *range,
92        }
93    }
94    pub fn syntax(&self) -> SyntaxElement {
95        match self {
96            FileReferenceNode::Name(it) => it.syntax().clone().into(),
97            FileReferenceNode::NameRef(it) => it.syntax().clone().into(),
98            FileReferenceNode::Lifetime(it) => it.syntax().clone().into(),
99            FileReferenceNode::FormatStringEntry(it, _) => it.syntax().clone().into(),
100        }
101    }
102    pub fn into_name_like(self) -> Option<ast::NameLike> {
103        match self {
104            FileReferenceNode::Name(it) => Some(ast::NameLike::Name(it)),
105            FileReferenceNode::NameRef(it) => Some(ast::NameLike::NameRef(it)),
106            FileReferenceNode::Lifetime(it) => Some(ast::NameLike::Lifetime(it)),
107            FileReferenceNode::FormatStringEntry(_, _) => None,
108        }
109    }
110    pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
111        match self {
112            FileReferenceNode::NameRef(name_ref) => Some(name_ref),
113            _ => None,
114        }
115    }
116    pub fn as_lifetime(&self) -> Option<&ast::Lifetime> {
117        match self {
118            FileReferenceNode::Lifetime(lifetime) => Some(lifetime),
119            _ => None,
120        }
121    }
122    pub fn text(&self) -> syntax::TokenText<'_> {
123        match self {
124            FileReferenceNode::NameRef(name_ref) => name_ref.text(),
125            FileReferenceNode::Name(name) => name.text(),
126            FileReferenceNode::Lifetime(lifetime) => lifetime.text(),
127            FileReferenceNode::FormatStringEntry(it, range) => {
128                syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()])
129            }
130        }
131    }
132}
133
134bitflags::bitflags! {
135    #[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
136    pub struct ReferenceCategory: u8 {
137        // FIXME: Add this variant and delete the `retain_adt_literal_usages` function.
138        // const CREATE = 1 << 0;
139        const WRITE = 1 << 0;
140        const READ = 1 << 1;
141        const IMPORT = 1 << 2;
142        const TEST = 1 << 3;
143    }
144}
145
146/// Generally, `search_scope` returns files that might contain references for the element.
147/// For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates.
148/// In some cases, the location of the references is known to within a `TextRange`,
149/// e.g. for things like local variables.
150#[derive(Clone, Debug)]
151pub struct SearchScope {
152    entries: FxHashMap<EditionedFileId, Option<TextRange>>,
153}
154
155impl SearchScope {
156    fn new(entries: FxHashMap<EditionedFileId, Option<TextRange>>) -> SearchScope {
157        SearchScope { entries }
158    }
159
160    /// Build a search scope spanning the entire crate graph of files.
161    fn crate_graph(db: &RootDatabase) -> SearchScope {
162        let mut entries = FxHashMap::default();
163
164        let all_crates = all_crates(db);
165        for &krate in all_crates.iter() {
166            let crate_data = krate.data(db);
167            let source_root = db.file_source_root(crate_data.root_file_id).source_root_id(db);
168            let source_root = db.source_root(source_root).source_root(db);
169            entries.extend(
170                source_root
171                    .iter()
172                    .map(|id| (EditionedFileId::new(db, id, crate_data.edition), None)),
173            );
174        }
175        SearchScope { entries }
176    }
177
178    /// Build a search scope spanning all the reverse dependencies of the given crate.
179    fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
180        let mut entries = FxHashMap::default();
181        for rev_dep in of.transitive_reverse_dependencies(db) {
182            let root_file = rev_dep.root_file(db);
183
184            let source_root = db.file_source_root(root_file).source_root_id(db);
185            let source_root = db.source_root(source_root).source_root(db);
186            entries.extend(
187                source_root
188                    .iter()
189                    .map(|id| (EditionedFileId::new(db, id, rev_dep.edition(db)), None)),
190            );
191        }
192        SearchScope { entries }
193    }
194
195    /// Build a search scope spanning the given crate.
196    fn krate(db: &RootDatabase, of: hir::Crate) -> SearchScope {
197        let root_file = of.root_file(db);
198
199        let source_root_id = db.file_source_root(root_file).source_root_id(db);
200        let source_root = db.source_root(source_root_id).source_root(db);
201        SearchScope {
202            entries: source_root
203                .iter()
204                .map(|id| (EditionedFileId::new(db, id, of.edition(db)), None))
205                .collect(),
206        }
207    }
208
209    /// Build a search scope spanning the given module and all its submodules.
210    pub fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
211        let mut entries = FxHashMap::default();
212
213        let (file_id, range) = {
214            let InFile { file_id, value } = module.definition_source_range(db);
215            if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db)
216            {
217                (file_id, Some(call_source.text_range()))
218            } else {
219                (file_id.original_file(db), Some(value))
220            }
221        };
222        entries.entry(file_id).or_insert(range);
223
224        let mut to_visit: Vec<_> = module.children(db).collect();
225        while let Some(module) = to_visit.pop() {
226            if let Some(file_id) = module.as_source_file_id(db) {
227                entries.insert(file_id, None);
228            }
229            to_visit.extend(module.children(db));
230        }
231        SearchScope { entries }
232    }
233
234    /// Build an empty search scope.
235    pub fn empty() -> SearchScope {
236        SearchScope::new(FxHashMap::default())
237    }
238
239    /// Build a empty search scope spanning the given file.
240    pub fn single_file(file: EditionedFileId) -> SearchScope {
241        SearchScope::new(std::iter::once((file, None)).collect())
242    }
243
244    /// Build a empty search scope spanning the text range of the given file.
245    pub fn file_range(range: FileRange) -> SearchScope {
246        SearchScope::new(std::iter::once((range.file_id, Some(range.range))).collect())
247    }
248
249    /// Build a empty search scope spanning the given files.
250    pub fn files(files: &[EditionedFileId]) -> SearchScope {
251        SearchScope::new(files.iter().map(|f| (*f, None)).collect())
252    }
253
254    pub fn intersection(&self, other: &SearchScope) -> SearchScope {
255        let (mut small, mut large) = (&self.entries, &other.entries);
256        if small.len() > large.len() {
257            mem::swap(&mut small, &mut large)
258        }
259
260        let intersect_ranges =
261            |r1: Option<TextRange>, r2: Option<TextRange>| -> Option<Option<TextRange>> {
262                match (r1, r2) {
263                    (None, r) | (r, None) => Some(r),
264                    (Some(r1), Some(r2)) => r1.intersect(r2).map(Some),
265                }
266            };
267        let res = small
268            .iter()
269            .filter_map(|(&file_id, &r1)| {
270                let &r2 = large.get(&file_id)?;
271                let r = intersect_ranges(r1, r2)?;
272                Some((file_id, r))
273            })
274            .collect();
275
276        SearchScope::new(res)
277    }
278}
279
280impl IntoIterator for SearchScope {
281    type Item = (EditionedFileId, Option<TextRange>);
282    type IntoIter = std::collections::hash_map::IntoIter<EditionedFileId, Option<TextRange>>;
283
284    fn into_iter(self) -> Self::IntoIter {
285        self.entries.into_iter()
286    }
287}
288
289impl Definition {
290    fn search_scope(&self, db: &RootDatabase) -> SearchScope {
291        let _p = tracing::info_span!("search_scope").entered();
292
293        if let Definition::BuiltinType(_) = self {
294            return SearchScope::crate_graph(db);
295        }
296
297        // def is crate root
298        if let &Definition::Module(module) = self
299            && module.is_crate_root(db)
300        {
301            return SearchScope::reverse_dependencies(db, module.krate(db));
302        }
303
304        let module = match self.module(db) {
305            Some(it) => it,
306            None => return SearchScope::empty(),
307        };
308        let InFile { file_id, value: module_source } = module.definition_source(db);
309        let file_id = file_id.original_file(db);
310
311        if let Definition::Local(var) = self {
312            let def = match var.parent(db) {
313                ExpressionStoreOwner::Body(def) => match def {
314                    DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
315                    DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
316                    DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
317                    DefWithBody::EnumVariant(v) => v.source(db).map(|src| src.syntax().cloned()),
318                },
319                ExpressionStoreOwner::Signature(def) => match def {
320                    hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
321                    hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
322                    hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
323                    hir::GenericDef::TypeAlias(it) => {
324                        it.source(db).map(|src| src.syntax().cloned())
325                    }
326                    hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
327                    hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
328                    hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
329                },
330                ExpressionStoreOwner::VariantFields(it) => {
331                    it.source(db).map(|src| src.syntax().cloned())
332                }
333            };
334            return match def {
335                Some(def) => SearchScope::file_range(
336                    def.as_ref().original_file_range_with_macro_call_input(db),
337                ),
338                None => SearchScope::single_file(file_id),
339            };
340        }
341
342        if let Definition::InlineAsmOperand(op) = self {
343            let def = match op.parent(db) {
344                ExpressionStoreOwner::Body(def) => match def {
345                    DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
346                    DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
347                    DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
348                    DefWithBody::EnumVariant(v) => v.source(db).map(|src| src.syntax().cloned()),
349                },
350                ExpressionStoreOwner::Signature(def) => match def {
351                    hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
352                    hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
353                    hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
354                    hir::GenericDef::TypeAlias(it) => {
355                        it.source(db).map(|src| src.syntax().cloned())
356                    }
357                    hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
358                    hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
359                    hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
360                },
361                ExpressionStoreOwner::VariantFields(it) => {
362                    it.source(db).map(|src| src.syntax().cloned())
363                }
364            };
365            return match def {
366                Some(def) => SearchScope::file_range(
367                    def.as_ref().original_file_range_with_macro_call_input(db),
368                ),
369                None => SearchScope::single_file(file_id),
370            };
371        }
372
373        if let Definition::SelfType(impl_) = self {
374            return match impl_.source(db).map(|src| src.syntax().cloned()) {
375                Some(def) => SearchScope::file_range(
376                    def.as_ref().original_file_range_with_macro_call_input(db),
377                ),
378                None => SearchScope::single_file(file_id),
379            };
380        }
381
382        if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
383            let def = match param.parent(db) {
384                hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()),
385                hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()),
386                hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()),
387                hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()),
388                hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()),
389                hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()),
390                hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()),
391            };
392            return match def {
393                Some(def) => SearchScope::file_range(
394                    def.as_ref().original_file_range_with_macro_call_input(db),
395                ),
396                None => SearchScope::single_file(file_id),
397            };
398        }
399
400        if let Definition::Macro(macro_def) = self {
401            return match macro_def.kind(db) {
402                hir::MacroKind::Declarative => {
403                    if macro_def.attrs(db).is_macro_export() {
404                        SearchScope::reverse_dependencies(db, module.krate(db))
405                    } else {
406                        SearchScope::krate(db, module.krate(db))
407                    }
408                }
409                hir::MacroKind::AttrBuiltIn
410                | hir::MacroKind::DeriveBuiltIn
411                | hir::MacroKind::DeclarativeBuiltIn => SearchScope::crate_graph(db),
412                hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => {
413                    SearchScope::reverse_dependencies(db, module.krate(db))
414                }
415            };
416        }
417
418        if let Definition::DeriveHelper(_) = self {
419            return SearchScope::reverse_dependencies(db, module.krate(db));
420        }
421
422        if let Some(vis) = self.visibility(db) {
423            return match vis {
424                Visibility::Module(module, _) => {
425                    SearchScope::module_and_children(db, module.into())
426                }
427                Visibility::PubCrate(krate) => SearchScope::krate(db, krate.into()),
428                Visibility::Public => SearchScope::reverse_dependencies(db, module.krate(db)),
429            };
430        }
431
432        let range = match module_source {
433            ModuleSource::Module(m) => Some(m.syntax().text_range()),
434            ModuleSource::BlockExpr(b) => Some(b.syntax().text_range()),
435            ModuleSource::SourceFile(_) => None,
436        };
437        match range {
438            Some(range) => SearchScope::file_range(FileRange { file_id, range }),
439            None => SearchScope::single_file(file_id),
440        }
441    }
442
443    pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> {
444        FindUsages {
445            def: self,
446            rename: None,
447            assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)),
448            sema,
449            scope: None,
450            include_self_kw_refs: None,
451            search_self_mod: false,
452            included_categories: ReferenceCategory::all(),
453            exclude_library_files: false,
454        }
455    }
456}
457
458#[derive(Clone)]
459pub struct FindUsages<'a> {
460    def: Definition,
461    rename: Option<&'a Rename>,
462    sema: &'a Semantics<'a, RootDatabase>,
463    scope: Option<&'a SearchScope>,
464    /// The container of our definition should it be an assoc item
465    assoc_item_container: Option<hir::AssocItemContainer>,
466    /// whether to search for the `Self` type of the definition
467    include_self_kw_refs: Option<hir::Type<'a>>,
468    /// whether to search for the `self` module
469    search_self_mod: bool,
470    /// categories to include while collecting usages
471    included_categories: ReferenceCategory,
472    /// whether to skip files from library source roots
473    exclude_library_files: bool,
474}
475
476impl<'a> FindUsages<'a> {
477    /// Enable searching for `Self` when the definition is a type or `self` for modules.
478    pub fn include_self_refs(mut self) -> Self {
479        self.include_self_kw_refs = def_to_ty(self.sema, &self.def);
480        self.search_self_mod = true;
481        self
482    }
483
484    /// Limit the search to a given [`SearchScope`].
485    pub fn in_scope(self, scope: &'a SearchScope) -> Self {
486        self.set_scope(Some(scope))
487    }
488
489    /// Limit the search to a given [`SearchScope`].
490    pub fn set_scope(mut self, scope: Option<&'a SearchScope>) -> Self {
491        assert!(self.scope.is_none());
492        self.scope = scope;
493        self
494    }
495
496    // FIXME: This is just a temporary fix for not handling import aliases like
497    // `use Foo as Bar`. We need to support them in a proper way.
498    // See issue #14079
499    pub fn with_rename(mut self, rename: Option<&'a Rename>) -> Self {
500        self.rename = rename;
501        self
502    }
503
504    pub fn set_included_categories(mut self, categories: ReferenceCategory) -> Self {
505        self.included_categories = categories;
506        self
507    }
508
509    pub fn set_exclude_library_files(mut self, exclude_library_files: bool) -> Self {
510        self.exclude_library_files = exclude_library_files;
511        self
512    }
513
514    pub fn at_least_one(&self) -> bool {
515        let mut found = false;
516        self.search(&mut |_, _| {
517            found = true;
518            true
519        });
520        found
521    }
522
523    pub fn all(self) -> UsageSearchResult {
524        let mut res = UsageSearchResult::default();
525        self.search(&mut |file_id, reference| {
526            res.references.entry(file_id).or_default().push(reference);
527            false
528        });
529        res
530    }
531
532    fn scope_files<'b>(
533        db: &'b RootDatabase,
534        scope: &'b SearchScope,
535        exclude_library_files: bool,
536    ) -> impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)> + 'b {
537        scope
538            .entries
539            .iter()
540            .filter(move |(file_id, _)| {
541                !exclude_library_files || !is_library_file(db, file_id.file_id(db))
542            })
543            .map(|(&file_id, &search_range)| {
544                let text = db.file_text(file_id.file_id(db)).text(db);
545                let search_range =
546                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
547
548                (text.clone(), file_id, search_range)
549            })
550    }
551
552    fn match_indices<'b>(
553        text: &'b str,
554        finder: &'b Finder<'b>,
555        search_range: TextRange,
556    ) -> impl Iterator<Item = TextSize> + 'b {
557        finder.find_iter(text.as_bytes()).filter_map(move |idx| {
558            let offset: TextSize = idx.try_into().unwrap();
559            if !search_range.contains_inclusive(offset) {
560                return None;
561            }
562            // If this is not a word boundary, that means this is only part of an identifier,
563            // so it can't be what we're looking for.
564            // This speeds up short identifiers significantly.
565            if text[..idx]
566                .chars()
567                .next_back()
568                .is_some_and(|ch| matches!(ch, 'A'..='Z' | 'a'..='z' | '_'))
569                || text[idx + finder.needle().len()..]
570                    .chars()
571                    .next()
572                    .is_some_and(|ch| matches!(ch, 'A'..='Z' | 'a'..='z' | '_' | '0'..='9'))
573            {
574                return None;
575            }
576            Some(offset)
577        })
578    }
579
580    fn find_nodes<'b>(
581        sema: &'b Semantics<'_, RootDatabase>,
582        name: &str,
583        file_id: EditionedFileId,
584        node: &syntax::SyntaxNode,
585        offset: TextSize,
586    ) -> impl Iterator<Item = SyntaxNode> + 'b {
587        node.token_at_offset(offset)
588            .find(|it| {
589                // `name` is stripped of raw ident prefix. See the comment on name retrieval below.
590                it.text().trim_start_matches('\'').trim_start_matches("r#") == name
591            })
592            .into_iter()
593            .flat_map(move |token| {
594                if sema.is_inside_macro_call(InFile::new(file_id.into(), &token)) {
595                    sema.descend_into_macros_exact(token)
596                } else {
597                    <_>::from([token])
598                }
599                .into_iter()
600                .filter_map(|it| it.parent())
601            })
602    }
603
604    /// Performs a special fast search for associated functions. This is mainly intended
605    /// to speed up `new()` which can take a long time.
606    ///
607    /// The trick is instead of searching for `func_name` search for `TypeThatContainsContainerName::func_name`.
608    /// We cannot search exactly that (not even in tokens), because `ContainerName` may be aliased.
609    /// Instead, we perform a textual search for `ContainerName`. Then, we look for all cases where
610    /// `ContainerName` may be aliased (that includes `use ContainerName as Xyz` and
611    /// `type Xyz = ContainerName`). We collect a list of all possible aliases of `ContainerName`.
612    /// The list can have false positives (because there may be multiple types named `ContainerName`),
613    /// but it cannot have false negatives. Then, we look for `TypeThatContainsContainerNameOrAnyAlias::func_name`.
614    /// Those that will be found are of high chance to be actual hits (of course, we will need to verify
615    /// that).
616    ///
617    /// Returns true if completed the search.
618    // FIXME: Extend this to other cases, such as associated types/consts/enum variants (note those can be `use`d).
619    fn short_associated_function_fast_search(
620        &self,
621        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
622        search_scope: &SearchScope,
623        name: &str,
624    ) -> bool {
625        if self.scope.is_some() {
626            return false;
627        }
628
629        let _p = tracing::info_span!("short_associated_function_fast_search").entered();
630
631        let container = (|| {
632            let Definition::Function(function) = self.def else {
633                return None;
634            };
635            if function.has_self_param(self.sema.db) {
636                return None;
637            }
638            match function.container(self.sema.db) {
639                // Only freestanding `impl`s qualify; methods from trait
640                // can be called from within subtraits and bounds.
641                ItemContainer::Impl(impl_) => {
642                    let has_trait = impl_.trait_(self.sema.db).is_some();
643                    if has_trait {
644                        return None;
645                    }
646                    let adt = impl_.self_ty(self.sema.db).as_adt()?;
647                    Some(adt)
648                }
649                _ => None,
650            }
651        })();
652        let Some(container) = container else {
653            return false;
654        };
655
656        fn has_any_name(node: &SyntaxNode, mut predicate: impl FnMut(&str) -> bool) -> bool {
657            node.descendants().any(|node| {
658                match_ast! {
659                    match node {
660                        ast::Name(it) => predicate(it.text().trim_start_matches("r#")),
661                        ast::NameRef(it) => predicate(it.text().trim_start_matches("r#")),
662                        _ => false
663                    }
664                }
665            })
666        }
667
668        // This is a fixpoint algorithm with O(number of aliases), but most types have no or few aliases,
669        // so this should stay fast.
670        //
671        /// Returns `(aliases, ranges_where_Self_can_refer_to_our_type)`.
672        fn collect_possible_aliases(
673            sema: &Semantics<'_, RootDatabase>,
674            container: Adt,
675            exclude_library_files: bool,
676        ) -> Option<(FxHashSet<SmolStr>, Vec<FileRangeWrapper<EditionedFileId>>)> {
677            fn insert_type_alias(
678                db: &RootDatabase,
679                to_process: &mut Vec<(SmolStr, SearchScope)>,
680                alias_name: &str,
681                def: Definition,
682            ) {
683                let alias = alias_name.trim_start_matches("r#").to_smolstr();
684                tracing::debug!("found alias: {alias}");
685                to_process.push((alias, def.search_scope(db)));
686            }
687
688            let _p = tracing::info_span!("collect_possible_aliases").entered();
689
690            let db = sema.db;
691            let container_name = container.name(db).as_str().to_smolstr();
692            let search_scope = Definition::from(container).search_scope(db);
693            let mut seen = FxHashSet::default();
694            let mut completed = FxHashSet::default();
695            let mut to_process = vec![(container_name, search_scope)];
696            let mut is_possibly_self = Vec::new();
697            let mut total_files_searched = 0;
698
699            while let Some((current_to_process, current_to_process_search_scope)) = to_process.pop()
700            {
701                let is_alias = |alias: &ast::TypeAlias| {
702                    let def = sema.to_def(alias)?;
703                    let ty = def.ty(db);
704                    let is_alias = ty.as_adt()? == container;
705                    is_alias.then_some(def)
706                };
707
708                let finder = Finder::new(current_to_process.as_bytes());
709                for (file_text, file_id, search_range) in FindUsages::scope_files(
710                    db,
711                    &current_to_process_search_scope,
712                    exclude_library_files,
713                ) {
714                    let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
715
716                    for offset in FindUsages::match_indices(&file_text, &finder, search_range) {
717                        let usages = FindUsages::find_nodes(
718                            sema,
719                            &current_to_process,
720                            file_id,
721                            &tree,
722                            offset,
723                        )
724                        .filter(|it| matches!(it.kind(), SyntaxKind::NAME | SyntaxKind::NAME_REF));
725                        for usage in usages {
726                            if let Some(alias) = usage.parent().and_then(|it| {
727                                let path = ast::PathSegment::cast(it)?.parent_path();
728                                let use_tree = ast::UseTree::cast(path.syntax().parent()?)?;
729                                use_tree.rename()?.name()
730                            }) {
731                                if seen.insert(InFileWrapper::new(
732                                    file_id,
733                                    alias.syntax().text_range(),
734                                )) {
735                                    tracing::debug!("found alias: {alias}");
736                                    cov_mark::hit!(container_use_rename);
737                                    // FIXME: `use`s have no easy way to determine their search scope, but they are rare.
738                                    to_process.push((
739                                        alias.text().to_smolstr(),
740                                        current_to_process_search_scope.clone(),
741                                    ));
742                                }
743                            } else if let Some(alias) =
744                                usage.ancestors().find_map(ast::TypeAlias::cast)
745                                && let Some(name) = alias.name()
746                                && seen
747                                    .insert(InFileWrapper::new(file_id, name.syntax().text_range()))
748                            {
749                                if let Some(def) = is_alias(&alias) {
750                                    cov_mark::hit!(container_type_alias);
751                                    insert_type_alias(
752                                        sema.db,
753                                        &mut to_process,
754                                        name.text().as_str(),
755                                        def.into(),
756                                    );
757                                } else {
758                                    cov_mark::hit!(same_name_different_def_type_alias);
759                                }
760                            }
761
762                            // We need to account for `Self`. It can only refer to our type inside an impl.
763                            let impl_ = 'impl_: {
764                                for ancestor in usage.ancestors() {
765                                    if let Some(parent) = ancestor.parent()
766                                        && let Some(parent) = ast::Impl::cast(parent)
767                                    {
768                                        // Only if the GENERIC_PARAM_LIST is directly under impl, otherwise it may be in the self ty.
769                                        if matches!(
770                                            ancestor.kind(),
771                                            SyntaxKind::ASSOC_ITEM_LIST
772                                                | SyntaxKind::WHERE_CLAUSE
773                                                | SyntaxKind::GENERIC_PARAM_LIST
774                                        ) {
775                                            break;
776                                        }
777                                        if parent
778                                            .trait_()
779                                            .is_some_and(|trait_| *trait_.syntax() == ancestor)
780                                        {
781                                            break;
782                                        }
783
784                                        // Otherwise, found an impl where its self ty may be our type.
785                                        break 'impl_ Some(parent);
786                                    }
787                                }
788                                None
789                            };
790                            (|| {
791                                let impl_ = impl_?;
792                                is_possibly_self.push(sema.original_range(impl_.syntax()));
793                                let assoc_items = impl_.assoc_item_list()?;
794                                let type_aliases = assoc_items
795                                    .syntax()
796                                    .descendants()
797                                    .filter_map(ast::TypeAlias::cast);
798                                for type_alias in type_aliases {
799                                    let Some(ty) = type_alias.ty() else { continue };
800                                    let Some(name) = type_alias.name() else { continue };
801                                    let contains_self = ty
802                                        .syntax()
803                                        .descendants_with_tokens()
804                                        .any(|node| node.kind() == SyntaxKind::SELF_TYPE_KW);
805                                    if !contains_self {
806                                        continue;
807                                    }
808                                    if seen.insert(InFileWrapper::new(
809                                        file_id,
810                                        name.syntax().text_range(),
811                                    )) {
812                                        if let Some(def) = is_alias(&type_alias) {
813                                            cov_mark::hit!(self_type_alias);
814                                            insert_type_alias(
815                                                sema.db,
816                                                &mut to_process,
817                                                name.text().as_str(),
818                                                def.into(),
819                                            );
820                                        } else {
821                                            cov_mark::hit!(same_name_different_def_type_alias);
822                                        }
823                                    }
824                                }
825                                Some(())
826                            })();
827                        }
828                    }
829                }
830
831                completed.insert(current_to_process);
832
833                total_files_searched += current_to_process_search_scope.entries.len();
834                // FIXME: Maybe this needs to be relative to the project size, or at least to the initial search scope?
835                if total_files_searched > 20_000 && completed.len() > 100 {
836                    // This case is extremely unlikely (even searching for `Vec::new()` on rust-analyzer does not enter
837                    // here - it searches less than 10,000 files, and it does so in five seconds), but if we get here,
838                    // we at a risk of entering an almost-infinite loop of growing the aliases list. So just stop and
839                    // let normal search handle this case.
840                    tracing::info!(aliases_count = %completed.len(), "too much aliases; leaving fast path");
841                    return None;
842                }
843            }
844
845            // Impls can contain each other, so we need to deduplicate their ranges.
846            is_possibly_self.sort_unstable_by_key(|position| {
847                (position.file_id, position.range.start(), Reverse(position.range.end()))
848            });
849            is_possibly_self.dedup_by(|pos2, pos1| {
850                pos1.file_id == pos2.file_id
851                    && pos1.range.start() <= pos2.range.start()
852                    && pos1.range.end() >= pos2.range.end()
853            });
854
855            tracing::info!(aliases_count = %completed.len(), "aliases search completed");
856
857            Some((completed, is_possibly_self))
858        }
859
860        fn search(
861            this: &FindUsages<'_>,
862            finder: &Finder<'_>,
863            name: &str,
864            files: impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)>,
865            mut container_predicate: impl FnMut(
866                &SyntaxNode,
867                InFileWrapper<EditionedFileId, TextRange>,
868            ) -> bool,
869            sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
870        ) {
871            for (file_text, file_id, search_range) in files {
872                let tree = LazyCell::new(move || this.sema.parse(file_id).syntax().clone());
873
874                for offset in FindUsages::match_indices(&file_text, finder, search_range) {
875                    let usages = FindUsages::find_nodes(this.sema, name, file_id, &tree, offset)
876                        .filter_map(ast::NameRef::cast);
877                    for usage in usages {
878                        let found_usage = usage
879                            .syntax()
880                            .parent()
881                            .and_then(ast::PathSegment::cast)
882                            .map(|path_segment| {
883                                container_predicate(
884                                    path_segment.parent_path().syntax(),
885                                    InFileWrapper::new(file_id, usage.syntax().text_range()),
886                                )
887                            })
888                            .unwrap_or(false);
889                        if found_usage {
890                            this.found_name_ref(&usage, sink);
891                        }
892                    }
893                }
894            }
895        }
896
897        let Some((container_possible_aliases, is_possibly_self)) =
898            collect_possible_aliases(self.sema, container, self.exclude_library_files)
899        else {
900            return false;
901        };
902
903        cov_mark::hit!(short_associated_function_fast_search);
904
905        // FIXME: If Rust ever gains the ability to `use Struct::method` we'll also need to account for free
906        // functions.
907        let finder = Finder::new(name.as_bytes());
908        // The search for `Self` may return duplicate results with `ContainerName`, so deduplicate them.
909        let mut self_positions = FxHashSet::default();
910        tracing::info_span!("Self_search").in_scope(|| {
911            search(
912                self,
913                &finder,
914                name,
915                is_possibly_self.into_iter().map(|position| {
916                    (position.file_text(self.sema.db).clone(), position.file_id, position.range)
917                }),
918                |path, name_position| {
919                    let has_self = path
920                        .descendants_with_tokens()
921                        .any(|node| node.kind() == SyntaxKind::SELF_TYPE_KW);
922                    if has_self {
923                        self_positions.insert(name_position);
924                    }
925                    has_self
926                },
927                sink,
928            )
929        });
930        tracing::info_span!("aliases_search").in_scope(|| {
931            search(
932                self,
933                &finder,
934                name,
935                FindUsages::scope_files(self.sema.db, search_scope, self.exclude_library_files),
936                |path, name_position| {
937                    has_any_name(path, |name| container_possible_aliases.contains(name))
938                        && !self_positions.contains(&name_position)
939                },
940                sink,
941            )
942        });
943
944        true
945    }
946
947    pub fn search(&self, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool) {
948        let _p = tracing::info_span!("FindUsages:search").entered();
949        let sema = self.sema;
950
951        let search_scope = {
952            // FIXME: Is the trait scope needed for trait impl assoc items?
953            let base =
954                as_trait_assoc_def(sema.db, self.def).unwrap_or(self.def).search_scope(sema.db);
955            match &self.scope {
956                None => base,
957                Some(scope) => base.intersection(scope),
958            }
959        };
960        if search_scope.entries.is_empty() {
961            return;
962        }
963
964        let name = match (self.rename, self.def) {
965            (Some(rename), _) => {
966                if rename.underscore_token().is_some() {
967                    None
968                } else {
969                    rename.name().map(|n| n.to_smolstr())
970                }
971            }
972            // special case crate modules as these do not have a proper name
973            (_, Definition::Module(module)) if module.is_crate_root(self.sema.db) => {
974                // FIXME: This assumes the crate name is always equal to its display name when it
975                // really isn't
976                // we should instead look at the dependency edge name and recursively search our way
977                // up the ancestors
978                module
979                    .krate(self.sema.db)
980                    .display_name(self.sema.db)
981                    .map(|crate_name| crate_name.crate_name().symbol().as_str().into())
982            }
983            _ => {
984                let self_kw_refs = || {
985                    self.include_self_kw_refs.as_ref().and_then(|ty| {
986                        ty.as_adt()
987                            .map(|adt| adt.name(self.sema.db))
988                            .or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
989                    })
990                };
991                // We need to search without the `r#`, hence `as_str` access.
992                // We strip `'` from lifetimes and labels as otherwise they may not match with raw-escaped ones,
993                // e.g. if we search `'foo` we won't find `'r#foo`.
994                self.def
995                    .name(sema.db)
996                    .or_else(self_kw_refs)
997                    .map(|it| it.as_str().trim_start_matches('\'').to_smolstr())
998            }
999        };
1000        let name = match &name {
1001            Some(s) => s.as_str(),
1002            None => return,
1003        };
1004
1005        // FIXME: This should probably depend on the number of the results (specifically, the number of false results).
1006        if name.len() <= 7 && self.short_associated_function_fast_search(sink, &search_scope, name)
1007        {
1008            return;
1009        }
1010
1011        let finder = &Finder::new(name);
1012        let include_self_kw_refs =
1013            self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
1014        for (text, file_id, search_range) in
1015            Self::scope_files(sema.db, &search_scope, self.exclude_library_files)
1016        {
1017            let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
1018
1019            // Search for occurrences of the items name
1020            for offset in Self::match_indices(&text, finder, search_range) {
1021                let ret = tree.token_at_offset(offset).any(|token| {
1022                    if let Some((range, _frange, string_token, Some(nameres))) =
1023                        sema.check_for_format_args_template(token.clone(), offset)
1024                    {
1025                        return self.found_format_args_ref(
1026                            file_id,
1027                            range,
1028                            string_token,
1029                            nameres,
1030                            sink,
1031                        );
1032                    }
1033                    false
1034                });
1035                if ret {
1036                    return;
1037                }
1038
1039                for name in Self::find_nodes(sema, name, file_id, &tree, offset)
1040                    .filter_map(ast::NameLike::cast)
1041                {
1042                    if match name {
1043                        ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
1044                        ast::NameLike::Name(name) => self.found_name(&name, sink),
1045                        ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
1046                    } {
1047                        return;
1048                    }
1049                }
1050            }
1051            // Search for occurrences of the `Self` referring to our type
1052            if let Some((self_ty, finder)) = &include_self_kw_refs {
1053                for offset in Self::match_indices(&text, finder, search_range) {
1054                    for name_ref in Self::find_nodes(sema, "Self", file_id, &tree, offset)
1055                        .filter_map(ast::NameRef::cast)
1056                    {
1057                        if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
1058                            return;
1059                        }
1060                    }
1061                }
1062            }
1063        }
1064
1065        // Search for `super` and `crate` resolving to our module
1066        if let Definition::Module(module) = self.def {
1067            let scope =
1068                search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module));
1069
1070            let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
1071            let finder = &Finder::new("super");
1072
1073            for (text, file_id, search_range) in
1074                Self::scope_files(sema.db, &scope, self.exclude_library_files)
1075            {
1076                self.sema.db.unwind_if_revision_cancelled();
1077
1078                let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
1079
1080                for offset in Self::match_indices(&text, finder, search_range) {
1081                    for name_ref in Self::find_nodes(sema, "super", file_id, &tree, offset)
1082                        .filter_map(ast::NameRef::cast)
1083                    {
1084                        if self.found_name_ref(&name_ref, sink) {
1085                            return;
1086                        }
1087                    }
1088                }
1089                if let Some(finder) = &is_crate_root {
1090                    for offset in Self::match_indices(&text, finder, search_range) {
1091                        for name_ref in Self::find_nodes(sema, "crate", file_id, &tree, offset)
1092                            .filter_map(ast::NameRef::cast)
1093                        {
1094                            if self.found_name_ref(&name_ref, sink) {
1095                                return;
1096                            }
1097                        }
1098                    }
1099                }
1100            }
1101        }
1102
1103        // search for module `self` references in our module's definition source
1104        match self.def {
1105            Definition::Module(module) if self.search_self_mod => {
1106                let src = module.definition_source(sema.db);
1107                let file_id = src.file_id.original_file(sema.db);
1108                let (file_id, search_range) = match src.value {
1109                    ModuleSource::Module(m) => (file_id, Some(m.syntax().text_range())),
1110                    ModuleSource::BlockExpr(b) => (file_id, Some(b.syntax().text_range())),
1111                    ModuleSource::SourceFile(_) => (file_id, None),
1112                };
1113
1114                let search_range = if let Some(&range) = search_scope.entries.get(&file_id) {
1115                    match (range, search_range) {
1116                        (None, range) | (range, None) => range,
1117                        (Some(range), Some(search_range)) => match range.intersect(search_range) {
1118                            Some(range) => Some(range),
1119                            None => return,
1120                        },
1121                    }
1122                } else {
1123                    return;
1124                };
1125
1126                let file_text = sema.db.file_text(file_id.file_id(self.sema.db));
1127                let text = file_text.text(sema.db);
1128                let search_range =
1129                    search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
1130
1131                let tree = LazyCell::new(|| sema.parse(file_id).syntax().clone());
1132                let finder = &Finder::new("self");
1133
1134                for offset in Self::match_indices(text, finder, search_range) {
1135                    for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset)
1136                        .filter_map(ast::NameRef::cast)
1137                    {
1138                        if self.found_self_module_name_ref(&name_ref, sink) {
1139                            return;
1140                        }
1141                    }
1142                }
1143            }
1144            _ => {}
1145        }
1146    }
1147
1148    fn found_self_ty_name_ref(
1149        &self,
1150        self_ty: &hir::Type<'_>,
1151        name_ref: &ast::NameRef,
1152        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1153    ) -> bool {
1154        if self.is_excluded_name_ref(name_ref) {
1155            return false;
1156        }
1157
1158        // See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845
1159        let ty_eq = |ty: hir::Type<'_>| match (ty.as_adt(), self_ty.as_adt()) {
1160            (Some(ty), Some(self_ty)) => ty == self_ty,
1161            (None, None) => ty == *self_ty,
1162            _ => false,
1163        };
1164
1165        match NameRefClass::classify(self.sema, name_ref) {
1166            Some(NameRefClass::Definition(Definition::SelfType(impl_), _))
1167                if ty_eq(impl_.self_ty(self.sema.db)) =>
1168            {
1169                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1170                let reference = FileReference {
1171                    range,
1172                    name: FileReferenceNode::NameRef(name_ref.clone()),
1173                    category: ReferenceCategory::empty(),
1174                };
1175                sink(file_id, reference)
1176            }
1177            _ => false,
1178        }
1179    }
1180
1181    fn found_self_module_name_ref(
1182        &self,
1183        name_ref: &ast::NameRef,
1184        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1185    ) -> bool {
1186        if self.is_excluded_name_ref(name_ref) {
1187            return false;
1188        }
1189
1190        match NameRefClass::classify(self.sema, name_ref) {
1191            Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => {
1192                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1193                let category = if is_name_ref_in_import(name_ref) {
1194                    ReferenceCategory::IMPORT
1195                } else {
1196                    ReferenceCategory::empty()
1197                };
1198                let reference = FileReference {
1199                    range,
1200                    name: FileReferenceNode::NameRef(name_ref.clone()),
1201                    category,
1202                };
1203                sink(file_id, reference)
1204            }
1205            _ => false,
1206        }
1207    }
1208
1209    fn found_format_args_ref(
1210        &self,
1211        file_id: EditionedFileId,
1212        range: TextRange,
1213        token: ast::String,
1214        res: Either<PathResolution, InlineAsmOperand>,
1215        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1216    ) -> bool {
1217        let def = res.either(Definition::from, Definition::from);
1218        if def == self.def {
1219            let reference = FileReference {
1220                range,
1221                name: FileReferenceNode::FormatStringEntry(token, range),
1222                category: ReferenceCategory::READ,
1223            };
1224            sink(file_id, reference)
1225        } else {
1226            false
1227        }
1228    }
1229
1230    fn found_lifetime(
1231        &self,
1232        lifetime: &ast::Lifetime,
1233        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1234    ) -> bool {
1235        match NameRefClass::classify_lifetime(self.sema, lifetime) {
1236            Some(NameRefClass::Definition(def, _)) if def == self.def => {
1237                let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
1238                let reference = FileReference {
1239                    range,
1240                    name: FileReferenceNode::Lifetime(lifetime.clone()),
1241                    category: ReferenceCategory::empty(),
1242                };
1243                sink(file_id, reference)
1244            }
1245            _ => false,
1246        }
1247    }
1248
1249    fn found_name_ref(
1250        &self,
1251        name_ref: &ast::NameRef,
1252        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1253    ) -> bool {
1254        if self.is_excluded_name_ref(name_ref) {
1255            return false;
1256        }
1257
1258        match NameRefClass::classify(self.sema, name_ref) {
1259            Some(NameRefClass::Definition(def, _))
1260                if self.def == def
1261                    // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait
1262                    || matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_)))
1263                        && convert_to_def_in_trait(self.sema.db, def) == self.def =>
1264            {
1265                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1266                let reference = FileReference {
1267                    range,
1268                    name: FileReferenceNode::NameRef(name_ref.clone()),
1269                    category: ReferenceCategory::new(self.sema, &def, name_ref),
1270                };
1271                sink(file_id, reference)
1272            }
1273            // FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions
1274            // so we always resolve all assoc type aliases to both their trait def and impl defs
1275            Some(NameRefClass::Definition(def, _))
1276                if self.assoc_item_container.is_some()
1277                    && matches!(self.def, Definition::TypeAlias(_))
1278                    && convert_to_def_in_trait(self.sema.db, def)
1279                        == convert_to_def_in_trait(self.sema.db, self.def) =>
1280            {
1281                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1282                let reference = FileReference {
1283                    range,
1284                    name: FileReferenceNode::NameRef(name_ref.clone()),
1285                    category: ReferenceCategory::new(self.sema, &def, name_ref),
1286                };
1287                sink(file_id, reference)
1288            }
1289            Some(NameRefClass::Definition(def, _))
1290                if self.include_self_kw_refs.is_some()
1291                    && self.include_self_kw_refs == def_to_ty(self.sema, &def) =>
1292            {
1293                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1294                let reference = FileReference {
1295                    range,
1296                    name: FileReferenceNode::NameRef(name_ref.clone()),
1297                    category: ReferenceCategory::new(self.sema, &def, name_ref),
1298                };
1299                sink(file_id, reference)
1300            }
1301            Some(NameRefClass::FieldShorthand {
1302                local_ref: local,
1303                field_ref: field,
1304                adt_subst: _,
1305            }) => {
1306                let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
1307
1308                let field = Definition::Field(field);
1309                let local = Definition::Local(local);
1310                let access = match self.def {
1311                    Definition::Field(_) if field == self.def => {
1312                        ReferenceCategory::new(self.sema, &field, name_ref)
1313                    }
1314                    Definition::Local(_) if local == self.def => {
1315                        ReferenceCategory::new(self.sema, &local, name_ref)
1316                    }
1317                    _ => return false,
1318                };
1319                let reference = FileReference {
1320                    range,
1321                    name: FileReferenceNode::NameRef(name_ref.clone()),
1322                    category: access,
1323                };
1324                sink(file_id, reference)
1325            }
1326            _ => false,
1327        }
1328    }
1329
1330    fn is_excluded_name_ref(&self, name_ref: &ast::NameRef) -> bool {
1331        (!self.included_categories.contains(ReferenceCategory::TEST)
1332            && is_name_ref_in_test(self.sema, name_ref))
1333            || (!self.included_categories.contains(ReferenceCategory::IMPORT)
1334                && is_name_ref_in_import(name_ref))
1335    }
1336
1337    fn found_name(
1338        &self,
1339        name: &ast::Name,
1340        sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
1341    ) -> bool {
1342        match NameClass::classify(self.sema, name) {
1343            Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ })
1344                if matches!(
1345                    self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
1346                ) =>
1347            {
1348                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
1349                let reference = FileReference {
1350                    range,
1351                    name: FileReferenceNode::Name(name.clone()),
1352                    // FIXME: mutable patterns should have `Write` access
1353                    category: ReferenceCategory::READ,
1354                };
1355                sink(file_id, reference)
1356            }
1357            Some(NameClass::ConstReference(def)) if self.def == def => {
1358                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
1359                let reference = FileReference {
1360                    range,
1361                    name: FileReferenceNode::Name(name.clone()),
1362                    category: ReferenceCategory::empty(),
1363                };
1364                sink(file_id, reference)
1365            }
1366            Some(NameClass::Definition(def)) if def != self.def => {
1367                match (&self.assoc_item_container, self.def) {
1368                    // for type aliases we always want to reference the trait def and all the trait impl counterparts
1369                    // FIXME: only until we can resolve them correctly, see FIXME above
1370                    (Some(_), Definition::TypeAlias(_))
1371                        if convert_to_def_in_trait(self.sema.db, def)
1372                            != convert_to_def_in_trait(self.sema.db, self.def) =>
1373                    {
1374                        return false;
1375                    }
1376                    (Some(_), Definition::TypeAlias(_)) => {}
1377                    // We looking at an assoc item of a trait definition, so reference all the
1378                    // corresponding assoc items belonging to this trait's trait implementations
1379                    (Some(hir::AssocItemContainer::Trait(_)), _)
1380                        if convert_to_def_in_trait(self.sema.db, def) == self.def => {}
1381                    _ => return false,
1382                }
1383                let FileRange { file_id, range } = self.sema.original_range(name.syntax());
1384                let reference = FileReference {
1385                    range,
1386                    name: FileReferenceNode::Name(name.clone()),
1387                    category: ReferenceCategory::empty(),
1388                };
1389                sink(file_id, reference)
1390            }
1391            _ => false,
1392        }
1393    }
1394}
1395
1396fn def_to_ty<'db>(sema: &Semantics<'db, RootDatabase>, def: &Definition) -> Option<hir::Type<'db>> {
1397    match def {
1398        Definition::Adt(adt) => Some(adt.ty(sema.db)),
1399        Definition::TypeAlias(it) => Some(it.ty(sema.db)),
1400        Definition::BuiltinType(it) => Some(it.ty(sema.db)),
1401        Definition::SelfType(it) => Some(it.self_ty(sema.db)),
1402        _ => None,
1403    }
1404}
1405
1406impl ReferenceCategory {
1407    fn new(
1408        sema: &Semantics<'_, RootDatabase>,
1409        def: &Definition,
1410        r: &ast::NameRef,
1411    ) -> ReferenceCategory {
1412        let mut result = ReferenceCategory::empty();
1413        if is_name_ref_in_test(sema, r) {
1414            result |= ReferenceCategory::TEST;
1415        }
1416
1417        // Only Locals and Fields have accesses for now.
1418        if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
1419            if is_name_ref_in_import(r) {
1420                result |= ReferenceCategory::IMPORT;
1421            }
1422            return result;
1423        }
1424
1425        let mode = r.syntax().ancestors().find_map(|node| {
1426            match_ast! {
1427                match node {
1428                    ast::BinExpr(expr) => {
1429                        if matches!(expr.op_kind()?, ast::BinaryOp::Assignment { .. }) {
1430                            // If the variable or field ends on the LHS's end then it's a Write
1431                            // (covers fields and locals). FIXME: This is not terribly accurate.
1432                            if let Some(lhs) = expr.lhs()
1433                            && lhs.syntax().text_range().contains_range(r.syntax().text_range()) {
1434                                    return Some(ReferenceCategory::WRITE)
1435                                }
1436                        }
1437                        Some(ReferenceCategory::READ)
1438                    },
1439                    _ => None,
1440                }
1441            }
1442        }).unwrap_or(ReferenceCategory::READ);
1443
1444        result | mode
1445    }
1446}
1447
1448fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool {
1449    name_ref
1450        .syntax()
1451        .parent()
1452        .and_then(ast::PathSegment::cast)
1453        .and_then(|it| it.parent_path().top_path().syntax().parent())
1454        .is_some_and(|it| it.kind() == SyntaxKind::USE_TREE)
1455}
1456
1457fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool {
1458    sema.ancestors_with_macros(name_ref.syntax().clone()).any(|node| match ast::Fn::cast(node) {
1459        Some(it) => sema.to_def(&it).is_some_and(|func| func.is_test(sema.db)),
1460        None => false,
1461    })
1462}
1463
1464fn is_library_file(db: &RootDatabase, file_id: span::FileId) -> bool {
1465    let source_root = db.file_source_root(file_id).source_root_id(db);
1466    db.source_root(source_root).source_root(db).is_library
1467}