Skip to main content

ide_completion/
context.rs

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