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