ide_completion/
context.rs

1//! See [`CompletionContext`] structure.
2
3mod analysis;
4#[cfg(test)]
5mod tests;
6
7use std::iter;
8
9use base_db::RootQueryDb as _;
10use hir::{
11    DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
12    ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
13};
14use ide_db::{
15    FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
16    helpers::is_editable_crate,
17};
18use itertools::Either;
19use syntax::{
20    AstNode, Edition, SmolStr,
21    SyntaxKind::{self, *},
22    SyntaxToken, T, TextRange, TextSize,
23    ast::{self, AttrKind, NameOrNameRef},
24};
25
26use crate::{
27    CompletionConfig,
28    config::AutoImportExclusionType,
29    context::analysis::{AnalysisResult, expand_and_analyze},
30};
31
32const COMPLETION_MARKER: &str = "raCompletionMarker";
33
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub(crate) enum PatternRefutability {
36    Refutable,
37    Irrefutable,
38}
39
40#[derive(Debug)]
41pub(crate) enum Visible {
42    Yes,
43    Editable,
44    No,
45}
46
47/// Existing qualifiers for the thing we are currently completing.
48#[derive(Debug, Default)]
49pub(crate) struct QualifierCtx {
50    // TODO: Add try_tok and default_tok
51    pub(crate) async_tok: Option<SyntaxToken>,
52    pub(crate) unsafe_tok: Option<SyntaxToken>,
53    pub(crate) safe_tok: Option<SyntaxToken>,
54    pub(crate) vis_node: Option<ast::Visibility>,
55    pub(crate) abi_node: Option<ast::Abi>,
56}
57
58impl QualifierCtx {
59    pub(crate) fn none(&self) -> bool {
60        self.async_tok.is_none()
61            && self.unsafe_tok.is_none()
62            && self.safe_tok.is_none()
63            && self.vis_node.is_none()
64            && self.abi_node.is_none()
65    }
66}
67
68/// The state of the path we are currently completing.
69#[derive(Debug)]
70pub(crate) struct PathCompletionCtx<'db> {
71    /// If this is a call with () already there (or {} in case of record patterns)
72    pub(crate) has_call_parens: bool,
73    /// If this has a macro call bang !
74    pub(crate) has_macro_bang: bool,
75    /// The qualifier of the current path.
76    pub(crate) qualified: Qualified<'db>,
77    /// The parent of the path we are completing.
78    pub(crate) parent: Option<ast::Path>,
79    #[allow(dead_code)]
80    /// The path of which we are completing the segment
81    pub(crate) path: ast::Path,
82    /// The path of which we are completing the segment in the original file
83    pub(crate) original_path: Option<ast::Path>,
84    pub(crate) kind: PathKind<'db>,
85    /// Whether the path segment has type args or not.
86    pub(crate) has_type_args: bool,
87    /// Whether the qualifier comes from a use tree parent or not
88    pub(crate) use_tree_parent: bool,
89}
90
91impl PathCompletionCtx<'_> {
92    pub(crate) fn is_trivial_path(&self) -> bool {
93        matches!(
94            self,
95            PathCompletionCtx {
96                has_call_parens: false,
97                has_macro_bang: false,
98                qualified: Qualified::No,
99                parent: None,
100                has_type_args: false,
101                ..
102            }
103        )
104    }
105}
106
107/// The kind of path we are completing right now.
108#[derive(Debug, PartialEq, Eq)]
109pub(crate) enum PathKind<'db> {
110    Expr {
111        expr_ctx: PathExprCtx<'db>,
112    },
113    Type {
114        location: TypeLocation,
115    },
116    Attr {
117        attr_ctx: AttrCtx,
118    },
119    Derive {
120        existing_derives: ExistingDerives,
121    },
122    /// Path in item position, that is inside an (Assoc)ItemList
123    Item {
124        kind: ItemListKind,
125    },
126    Pat {
127        pat_ctx: PatternContext,
128    },
129    Vis {
130        has_in_token: bool,
131    },
132    Use,
133}
134
135pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
136
137#[derive(Debug, PartialEq, Eq)]
138pub(crate) struct AttrCtx {
139    pub(crate) kind: AttrKind,
140    pub(crate) annotated_item_kind: Option<SyntaxKind>,
141    pub(crate) derive_helpers: Vec<(Symbol, Symbol)>,
142}
143
144#[derive(Debug, PartialEq, Eq)]
145pub(crate) struct PathExprCtx<'db> {
146    pub(crate) in_block_expr: bool,
147    pub(crate) in_breakable: Option<BreakableKind>,
148    pub(crate) after_if_expr: bool,
149    pub(crate) before_else_kw: bool,
150    /// Whether this expression is the direct condition of an if or while expression
151    pub(crate) in_condition: bool,
152    pub(crate) incomplete_let: bool,
153    pub(crate) after_incomplete_let: bool,
154    pub(crate) in_value: bool,
155    pub(crate) ref_expr_parent: Option<ast::RefExpr>,
156    pub(crate) after_amp: bool,
157    /// The surrounding RecordExpression we are completing a functional update
158    pub(crate) is_func_update: Option<ast::RecordExpr>,
159    pub(crate) self_param: Option<hir::SelfParam>,
160    pub(crate) innermost_ret_ty: Option<hir::Type<'db>>,
161    pub(crate) innermost_breakable_ty: Option<hir::Type<'db>>,
162    pub(crate) impl_: Option<ast::Impl>,
163    /// Whether this expression occurs in match arm guard position: before the
164    /// fat arrow token
165    pub(crate) in_match_guard: bool,
166}
167
168/// Original file ast nodes
169#[derive(Clone, Debug, PartialEq, Eq)]
170pub(crate) enum TypeLocation {
171    TupleField,
172    TypeAscription(TypeAscriptionTarget),
173    /// Generic argument position e.g. `Foo<$0>`
174    GenericArg {
175        /// The generic argument list containing the generic arg
176        args: Option<ast::GenericArgList>,
177        /// `Some(trait_)` if `trait_` is being instantiated with `args`
178        of_trait: Option<hir::Trait>,
179        /// The generic parameter being filled in by the generic arg
180        corresponding_param: Option<ast::GenericParam>,
181    },
182    /// Associated type equality constraint e.g. `Foo<Bar = $0>`
183    AssocTypeEq,
184    /// Associated constant equality constraint e.g. `Foo<X = $0>`
185    AssocConstEq,
186    TypeBound,
187    ImplTarget,
188    ImplTrait,
189    Other,
190}
191
192impl TypeLocation {
193    pub(crate) fn complete_lifetimes(&self) -> bool {
194        matches!(
195            self,
196            TypeLocation::GenericArg {
197                corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
198                ..
199            }
200        )
201    }
202
203    pub(crate) fn complete_consts(&self) -> bool {
204        matches!(
205            self,
206            TypeLocation::GenericArg {
207                corresponding_param: Some(ast::GenericParam::ConstParam(_)),
208                ..
209            } | TypeLocation::AssocConstEq
210        )
211    }
212
213    pub(crate) fn complete_types(&self) -> bool {
214        match self {
215            TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
216                matches!(param, ast::GenericParam::TypeParam(_))
217            }
218            TypeLocation::AssocConstEq => false,
219            TypeLocation::AssocTypeEq => true,
220            TypeLocation::ImplTrait => false,
221            _ => true,
222        }
223    }
224
225    pub(crate) fn complete_self_type(&self) -> bool {
226        self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
227    }
228}
229
230#[derive(Clone, Debug, PartialEq, Eq)]
231pub(crate) enum TypeAscriptionTarget {
232    Let(Option<ast::Pat>),
233    FnParam(Option<ast::Pat>),
234    RetType(Option<ast::Expr>),
235    Const(Option<ast::Expr>),
236}
237
238/// The kind of item list a [`PathKind::Item`] belongs to.
239#[derive(Debug, PartialEq, Eq)]
240pub(crate) enum ItemListKind {
241    SourceFile,
242    Module,
243    Impl,
244    TraitImpl(Option<ast::Impl>),
245    Trait,
246    ExternBlock { is_unsafe: bool },
247}
248
249#[derive(Debug)]
250pub(crate) enum Qualified<'db> {
251    No,
252    With {
253        path: ast::Path,
254        resolution: Option<PathResolution>,
255        /// How many `super` segments are present in the path
256        ///
257        /// This would be None, if path is not solely made of
258        /// `super` segments, e.g.
259        ///
260        /// ```ignore
261        /// use super::foo;
262        /// ```
263        ///
264        /// Otherwise it should be Some(count of `super`)
265        super_chain_len: Option<usize>,
266    },
267    /// <_>::
268    TypeAnchor {
269        ty: Option<hir::Type<'db>>,
270        trait_: Option<hir::Trait>,
271    },
272    /// Whether the path is an absolute path
273    Absolute,
274}
275
276/// The state of the pattern we are completing.
277#[derive(Debug, Clone, PartialEq, Eq)]
278pub(crate) struct PatternContext {
279    pub(crate) refutability: PatternRefutability,
280    pub(crate) param_ctx: Option<ParamContext>,
281    pub(crate) has_type_ascription: bool,
282    pub(crate) should_suggest_name: bool,
283    pub(crate) after_if_expr: bool,
284    pub(crate) parent_pat: Option<ast::Pat>,
285    pub(crate) ref_token: Option<SyntaxToken>,
286    pub(crate) mut_token: Option<SyntaxToken>,
287    /// The record pattern this name or ref is a field of
288    pub(crate) record_pat: Option<ast::RecordPat>,
289    pub(crate) impl_or_trait: Option<Either<ast::Impl, ast::Trait>>,
290    /// List of missing variants in a match expr
291    pub(crate) missing_variants: Vec<hir::Variant>,
292}
293
294#[derive(Debug, Clone, PartialEq, Eq)]
295pub(crate) struct ParamContext {
296    pub(crate) param_list: ast::ParamList,
297    pub(crate) param: ast::Param,
298    pub(crate) kind: ParamKind,
299}
300
301/// The state of the lifetime we are completing.
302#[derive(Debug)]
303pub(crate) struct LifetimeContext {
304    pub(crate) kind: LifetimeKind,
305}
306
307/// The kind of lifetime we are completing.
308#[derive(Debug)]
309pub(crate) enum LifetimeKind {
310    LifetimeParam,
311    Lifetime { in_lifetime_param_bound: bool, def: Option<hir::GenericDef> },
312    LabelRef,
313    LabelDef,
314}
315
316/// The state of the name we are completing.
317#[derive(Debug)]
318pub(crate) struct NameContext {
319    #[allow(dead_code)]
320    pub(crate) name: Option<ast::Name>,
321    pub(crate) kind: NameKind,
322}
323
324/// The kind of the name we are completing.
325#[derive(Debug)]
326#[allow(dead_code)]
327pub(crate) enum NameKind {
328    Const,
329    ConstParam,
330    Enum,
331    Function,
332    IdentPat(PatternContext),
333    MacroDef,
334    MacroRules,
335    /// Fake node
336    Module(ast::Module),
337    RecordField,
338    Rename,
339    SelfParam,
340    Static,
341    Struct,
342    Trait,
343    TypeAlias,
344    TypeParam,
345    Union,
346    Variant,
347}
348
349/// The state of the NameRef we are completing.
350#[derive(Debug)]
351pub(crate) struct NameRefContext<'db> {
352    /// NameRef syntax in the original file
353    pub(crate) nameref: Option<ast::NameRef>,
354    pub(crate) kind: NameRefKind<'db>,
355}
356
357/// The kind of the NameRef we are completing.
358#[derive(Debug)]
359pub(crate) enum NameRefKind<'db> {
360    Path(PathCompletionCtx<'db>),
361    DotAccess(DotAccess<'db>),
362    /// Position where we are only interested in keyword completions
363    Keyword(ast::Item),
364    /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
365    RecordExpr {
366        dot_prefix: bool,
367        expr: ast::RecordExpr,
368    },
369    Pattern(PatternContext),
370    ExternCrate,
371}
372
373/// The identifier we are currently completing.
374#[derive(Debug)]
375pub(crate) enum CompletionAnalysis<'db> {
376    Name(NameContext),
377    NameRef(NameRefContext<'db>),
378    Lifetime(LifetimeContext),
379    /// The string the cursor is currently inside
380    String {
381        /// original token
382        original: ast::String,
383        /// fake token
384        expanded: Option<ast::String>,
385    },
386    /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
387    UnexpandedAttrTT {
388        colon_prefix: bool,
389        fake_attribute_under_caret: Option<ast::Attr>,
390        extern_crate: Option<ast::ExternCrate>,
391    },
392}
393
394/// Information about the field or method access we are completing.
395#[derive(Debug)]
396pub(crate) struct DotAccess<'db> {
397    pub(crate) receiver: Option<ast::Expr>,
398    pub(crate) receiver_ty: Option<TypeInfo<'db>>,
399    pub(crate) kind: DotAccessKind,
400    pub(crate) ctx: DotAccessExprCtx,
401}
402
403#[derive(Debug, Clone, Copy)]
404pub(crate) enum DotAccessKind {
405    Field {
406        /// True if the receiver is an integer and there is no ident in the original file after it yet
407        /// like `0.$0`
408        receiver_is_ambiguous_float_literal: bool,
409    },
410    Method,
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq)]
414pub(crate) struct DotAccessExprCtx {
415    pub(crate) in_block_expr: bool,
416    pub(crate) in_breakable: Option<BreakableKind>,
417}
418
419#[derive(Copy, Clone, Debug, PartialEq, Eq)]
420pub(crate) enum BreakableKind {
421    Loop,
422    For,
423    While,
424    Block,
425}
426
427#[derive(Clone, Debug, PartialEq, Eq)]
428pub(crate) enum ParamKind {
429    Function(ast::Fn),
430    Closure(ast::ClosureExpr),
431}
432
433/// `CompletionContext` is created early during completion to figure out, where
434/// exactly is the cursor, syntax-wise.
435#[derive(Debug)]
436pub(crate) struct CompletionContext<'a> {
437    pub(crate) sema: Semantics<'a, RootDatabase>,
438    pub(crate) scope: SemanticsScope<'a>,
439    pub(crate) db: &'a RootDatabase,
440    pub(crate) config: &'a CompletionConfig<'a>,
441    pub(crate) position: FilePosition,
442
443    pub(crate) trigger_character: Option<char>,
444    /// The token before the cursor, in the original file.
445    pub(crate) original_token: SyntaxToken,
446    /// The token before the cursor, in the macro-expanded file.
447    pub(crate) token: SyntaxToken,
448    /// The crate of the current file.
449    pub(crate) krate: hir::Crate,
450    pub(crate) display_target: DisplayTarget,
451    /// The module of the `scope`.
452    pub(crate) module: hir::Module,
453    /// The function where we're completing, if inside a function.
454    pub(crate) containing_function: Option<hir::Function>,
455    /// Whether nightly toolchain is used. Cached since this is looked up a lot.
456    pub(crate) is_nightly: bool,
457    /// The edition of the current crate
458    // FIXME: This should probably be the crate of the current token?
459    pub(crate) edition: Edition,
460
461    /// The expected name of what we are completing.
462    /// This is usually the parameter name of the function argument we are completing.
463    pub(crate) expected_name: Option<NameOrNameRef>,
464    /// The expected type of what we are completing.
465    pub(crate) expected_type: Option<Type<'a>>,
466
467    pub(crate) qualifier_ctx: QualifierCtx,
468
469    pub(crate) locals: FxHashMap<Name, Local>,
470
471    /// The module depth of the current module of the cursor position.
472    /// - crate-root
473    ///  - mod foo
474    ///   - mod bar
475    ///
476    /// Here depth will be 2
477    pub(crate) depth_from_crate_root: usize,
478
479    /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest
480    /// importing those traits.
481    ///
482    /// Note the trait *themselves* are not excluded, only their methods are.
483    pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
484    /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`).
485    /// They will *not* be excluded, however, if they are available as a generic bound.
486    ///
487    /// Note the trait *themselves* are not excluded, only their methods are.
488    pub(crate) exclude_traits: FxHashSet<hir::Trait>,
489
490    /// Whether and how to complete semicolon for unit-returning functions.
491    pub(crate) complete_semicolon: CompleteSemicolon,
492}
493
494#[derive(Debug)]
495pub(crate) enum CompleteSemicolon {
496    DoNotComplete,
497    CompleteSemi,
498    CompleteComma,
499}
500
501impl CompletionContext<'_> {
502    /// The range of the identifier that is being completed.
503    pub(crate) fn source_range(&self) -> TextRange {
504        let kind = self.original_token.kind();
505        match kind {
506            CHAR => {
507                // assume we are completing a lifetime but the user has only typed the '
508                cov_mark::hit!(completes_if_lifetime_without_idents);
509                TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
510            }
511            LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
512            // We want to consider all keywords in all editions.
513            _ if kind.is_any_identifier() => self.original_token.text_range(),
514            _ => TextRange::empty(self.position.offset),
515        }
516    }
517
518    pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
519        FamousDefs(&self.sema, self.krate)
520    }
521
522    /// Checks if an item is visible and not `doc(hidden)` at the completion site.
523    pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
524        match item {
525            ScopeDef::ModuleDef(def) => match def {
526                hir::ModuleDef::Module(it) => self.is_visible(it),
527                hir::ModuleDef::Function(it) => self.is_visible(it),
528                hir::ModuleDef::Adt(it) => self.is_visible(it),
529                hir::ModuleDef::Variant(it) => self.is_visible(it),
530                hir::ModuleDef::Const(it) => self.is_visible(it),
531                hir::ModuleDef::Static(it) => self.is_visible(it),
532                hir::ModuleDef::Trait(it) => self.is_visible(it),
533                hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
534                hir::ModuleDef::Macro(it) => self.is_visible(it),
535                hir::ModuleDef::BuiltinType(_) => Visible::Yes,
536            },
537            ScopeDef::GenericParam(_)
538            | ScopeDef::ImplSelfType(_)
539            | ScopeDef::AdtSelfType(_)
540            | ScopeDef::Local(_)
541            | ScopeDef::Label(_)
542            | ScopeDef::Unknown => Visible::Yes,
543        }
544    }
545
546    /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site.
547    pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
548    where
549        I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
550    {
551        let vis = item.visibility(self.db);
552        let attrs = item.attrs(self.db);
553        self.is_visible_impl(&vis, &attrs, item.krate(self.db))
554    }
555
556    pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
557    where
558        I: hir::HasAttrs + Copy,
559    {
560        let attrs = item.attrs(self.db);
561        attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
562    }
563
564    /// Check if an item is `#[doc(hidden)]`.
565    pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
566        let attrs = item.attrs(self.db);
567        let krate = item.krate(self.db);
568        match (attrs, krate) {
569            (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
570            _ => false,
571        }
572    }
573
574    /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
575    pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
576        let Some(attrs) = attrs else {
577            return true;
578        };
579        !attrs.is_unstable() || self.is_nightly
580    }
581
582    pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
583    where
584        I: hir::HasAttrs + hir::HasCrate,
585    {
586        let defining_crate = item.krate(self.db);
587        let attrs = item.attrs(self.db);
588        self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
589    }
590
591    /// Whether the given trait is an operator trait or not.
592    pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
593        match trait_.attrs(self.db).lang(self.db) {
594            Some(lang) => OP_TRAIT_LANG.contains(&lang),
595            None => false,
596        }
597    }
598
599    /// Whether the given trait has `#[doc(notable_trait)]`
600    pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
601        trait_.attrs(self.db).is_doc_notable_trait()
602    }
603
604    /// Returns the traits in scope, with the [`Drop`] trait removed.
605    pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
606        let mut traits_in_scope = self.scope.visible_traits();
607        if let Some(drop) = self.famous_defs().core_ops_Drop() {
608            traits_in_scope.0.remove(&drop.into());
609        }
610        traits_in_scope
611    }
612
613    pub(crate) fn iterate_path_candidates(
614        &self,
615        ty: &hir::Type<'_>,
616        mut cb: impl FnMut(hir::AssocItem),
617    ) {
618        let mut seen = FxHashSet::default();
619        ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
620            // We might iterate candidates of a trait multiple times here, so deduplicate
621            // them.
622            if seen.insert(item) {
623                cb(item)
624            }
625            None::<()>
626        });
627    }
628
629    /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
630    /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`].
631    pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
632        let _p = tracing::info_span!("CompletionContext::process_all_names").entered();
633        self.scope.process_all_names(&mut |name, def| {
634            if self.is_scope_def_hidden(def) {
635                return;
636            }
637            let doc_aliases = self.doc_aliases_in_scope(def);
638            f(name, def, doc_aliases);
639        });
640    }
641
642    pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
643        let _p = tracing::info_span!("CompletionContext::process_all_names_raw").entered();
644        self.scope.process_all_names(f);
645    }
646
647    fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
648        if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
649            return self.is_doc_hidden(&attrs, krate);
650        }
651
652        false
653    }
654
655    fn is_visible_impl(
656        &self,
657        vis: &hir::Visibility,
658        attrs: &hir::AttrsWithOwner,
659        defining_crate: hir::Crate,
660    ) -> Visible {
661        if !self.check_stability(Some(attrs)) {
662            return Visible::No;
663        }
664
665        if !vis.is_visible_from(self.db, self.module.into()) {
666            if !self.config.enable_private_editable {
667                return Visible::No;
668            }
669            // If the definition location is editable, also show private items
670            return if is_editable_crate(defining_crate, self.db) {
671                Visible::Editable
672            } else {
673                Visible::No
674            };
675        }
676
677        if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
678    }
679
680    pub(crate) fn is_doc_hidden(
681        &self,
682        attrs: &hir::AttrsWithOwner,
683        defining_crate: hir::Crate,
684    ) -> bool {
685        // `doc(hidden)` items are only completed within the defining crate.
686        self.krate != defining_crate && attrs.is_doc_hidden()
687    }
688
689    pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
690        if let Some(attrs) = scope_def.attrs(self.db) {
691            attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
692        } else {
693            vec![]
694        }
695    }
696}
697
698// CompletionContext construction
699impl<'db> CompletionContext<'db> {
700    pub(crate) fn new(
701        db: &'db RootDatabase,
702        position @ FilePosition { file_id, offset }: FilePosition,
703        config: &'db CompletionConfig<'db>,
704        trigger_character: Option<char>,
705    ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
706        let _p = tracing::info_span!("CompletionContext::new").entered();
707        let sema = Semantics::new(db);
708
709        let editioned_file_id = sema.attach_first_edition(file_id);
710        let original_file = sema.parse(editioned_file_id);
711
712        // Insert a fake ident to get a valid parse tree. We will use this file
713        // to determine context, though the original_file will be used for
714        // actual completion.
715        let file_with_fake_ident = {
716            let (_, edition) = editioned_file_id.unpack(db);
717            let parse = db.parse(editioned_file_id);
718            parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()
719        };
720
721        // always pick the token to the immediate left of the cursor, as that is what we are actually
722        // completing on
723        let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
724
725        // try to skip completions on path with invalid colons
726        // this approach works in normal path and inside token tree
727        if original_token.kind() == T![:] {
728            // return if no prev token before colon
729            let prev_token = original_token.prev_token()?;
730
731            // only has a single colon
732            if prev_token.kind() != T![:] {
733                return None;
734            }
735
736            // has 3 colon or 2 coloncolon in a row
737            // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205
738            // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751
739            if prev_token
740                .prev_token()
741                .map(|t| t.kind() == T![:] || t.kind() == T![::])
742                .unwrap_or(false)
743            {
744                return None;
745            }
746        }
747
748        let AnalysisResult {
749            analysis,
750            expected: (expected_type, expected_name),
751            qualifier_ctx,
752            token,
753            original_offset,
754        } = expand_and_analyze(
755            &sema,
756            InFile::new(editioned_file_id.into(), original_file.syntax().clone()),
757            file_with_fake_ident.syntax().clone(),
758            offset,
759            &original_token,
760        )?;
761
762        // adjust for macro input, this still fails if there is no token written yet
763        let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;
764
765        let krate = scope.krate();
766        let module = scope.module();
767        let containing_function = scope.containing_function();
768        let edition = krate.edition(db);
769
770        let toolchain = db.toolchain_channel(krate.into());
771        // `toolchain == None` means we're in some detached files. Since we have no information on
772        // the toolchain being used, let's just allow unstable items to be listed.
773        let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
774
775        let mut locals = FxHashMap::default();
776        scope.process_all_names(&mut |name, scope| {
777            if let ScopeDef::Local(local) = scope {
778                // synthetic names currently leak out as we lack synthetic hygiene, so filter them
779                // out here
780                if name.as_str().starts_with('<') {
781                    return;
782                }
783                locals.insert(name, local);
784            }
785        });
786
787        let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
788            // `BlockExpr` modules do not count towards module depth
789            .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
790            .count()
791            // exclude `m` itself
792            .saturating_sub(1);
793
794        let exclude_traits: FxHashSet<_> = config
795            .exclude_traits
796            .iter()
797            .filter_map(|path| {
798                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map(
799                    |it| match it {
800                        hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
801                        _ => None,
802                    },
803                )
804            })
805            .collect();
806
807        let mut exclude_flyimport: FxHashMap<_, _> = config
808            .exclude_flyimport
809            .iter()
810            .flat_map(|(path, kind)| {
811                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern))
812                    .map(|it| (it.into_module_def(), *kind))
813            })
814            .collect();
815        exclude_flyimport
816            .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
817
818        // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze`
819        let complete_semicolon = if !config.add_semicolon_to_unit {
820            CompleteSemicolon::DoNotComplete
821        } else if let Some(term_node) =
822            sema.token_ancestors_with_macros(token.clone()).find(|node| {
823                matches!(node.kind(), BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR)
824            })
825        {
826            let next_token = iter::successors(token.next_token(), |it| it.next_token())
827                .map(|it| it.kind())
828                .find(|kind| !kind.is_trivia());
829            match term_node.kind() {
830                MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,
831                BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,
832                _ => CompleteSemicolon::DoNotComplete,
833            }
834        } else {
835            CompleteSemicolon::DoNotComplete
836        };
837
838        let display_target = krate.to_display_target(db);
839        let ctx = CompletionContext {
840            sema,
841            scope,
842            db,
843            config,
844            position,
845            trigger_character,
846            original_token,
847            token,
848            krate,
849            module,
850            containing_function,
851            is_nightly,
852            edition,
853            expected_name,
854            expected_type,
855            qualifier_ctx,
856            locals,
857            depth_from_crate_root,
858            exclude_flyimport,
859            exclude_traits,
860            complete_semicolon,
861            display_target,
862        };
863        Some((ctx, analysis))
864    }
865}
866
867const OP_TRAIT_LANG: &[hir::LangItem] = &[
868    hir::LangItem::AddAssign,
869    hir::LangItem::Add,
870    hir::LangItem::BitAndAssign,
871    hir::LangItem::BitAnd,
872    hir::LangItem::BitOrAssign,
873    hir::LangItem::BitOr,
874    hir::LangItem::BitXorAssign,
875    hir::LangItem::BitXor,
876    hir::LangItem::DerefMut,
877    hir::LangItem::Deref,
878    hir::LangItem::DivAssign,
879    hir::LangItem::Div,
880    hir::LangItem::PartialEq,
881    hir::LangItem::FnMut,
882    hir::LangItem::FnOnce,
883    hir::LangItem::Fn,
884    hir::LangItem::IndexMut,
885    hir::LangItem::Index,
886    hir::LangItem::MulAssign,
887    hir::LangItem::Mul,
888    hir::LangItem::Neg,
889    hir::LangItem::Not,
890    hir::LangItem::PartialOrd,
891    hir::LangItem::RemAssign,
892    hir::LangItem::Rem,
893    hir::LangItem::ShlAssign,
894    hir::LangItem::Shl,
895    hir::LangItem::ShrAssign,
896    hir::LangItem::Shr,
897    hir::LangItem::Sub,
898];