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, 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
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<Either<hir::SelfParam, hir::Param<'db>>>,
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    MacroSegment,
393}
394
395/// Information about the field or method access we are completing.
396#[derive(Debug)]
397pub(crate) struct DotAccess<'db> {
398    pub(crate) receiver: Option<ast::Expr>,
399    pub(crate) receiver_ty: Option<TypeInfo<'db>>,
400    pub(crate) kind: DotAccessKind,
401    pub(crate) ctx: DotAccessExprCtx,
402}
403
404#[derive(Debug, Clone, Copy)]
405pub(crate) enum DotAccessKind {
406    Field {
407        /// True if the receiver is an integer and there is no ident in the original file after it yet
408        /// like `0.$0`
409        receiver_is_ambiguous_float_literal: bool,
410    },
411    Method,
412}
413
414#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub(crate) struct DotAccessExprCtx {
416    pub(crate) in_block_expr: bool,
417    pub(crate) in_breakable: Option<BreakableKind>,
418}
419
420#[derive(Copy, Clone, Debug, PartialEq, Eq)]
421pub(crate) enum BreakableKind {
422    Loop,
423    For,
424    While,
425    Block,
426}
427
428#[derive(Clone, Debug, PartialEq, Eq)]
429pub(crate) enum ParamKind {
430    Function(ast::Fn),
431    Closure(ast::ClosureExpr),
432}
433
434/// `CompletionContext` is created early during completion to figure out, where
435/// exactly is the cursor, syntax-wise.
436#[derive(Debug)]
437pub(crate) struct CompletionContext<'a> {
438    pub(crate) sema: Semantics<'a, RootDatabase>,
439    pub(crate) scope: SemanticsScope<'a>,
440    pub(crate) db: &'a RootDatabase,
441    pub(crate) config: &'a CompletionConfig<'a>,
442    pub(crate) position: FilePosition,
443
444    pub(crate) trigger_character: Option<char>,
445    /// The token before the cursor, in the original file.
446    pub(crate) original_token: SyntaxToken,
447    /// The token before the cursor, in the macro-expanded file.
448    pub(crate) token: SyntaxToken,
449    /// The crate of the current file.
450    pub(crate) krate: hir::Crate,
451    pub(crate) display_target: DisplayTarget,
452    /// The module of the `scope`.
453    pub(crate) module: hir::Module,
454    /// The function where we're completing, if inside a function.
455    pub(crate) containing_function: Option<hir::Function>,
456    /// Whether nightly toolchain is used. Cached since this is looked up a lot.
457    pub(crate) is_nightly: bool,
458    /// The edition of the current crate
459    // FIXME: This should probably be the crate of the current token?
460    pub(crate) edition: Edition,
461
462    /// The expected name of what we are completing.
463    /// This is usually the parameter name of the function argument we are completing.
464    pub(crate) expected_name: Option<NameOrNameRef>,
465    /// The expected type of what we are completing.
466    pub(crate) expected_type: Option<Type<'a>>,
467
468    pub(crate) qualifier_ctx: QualifierCtx,
469
470    pub(crate) locals: FxHashMap<Name, Local>,
471
472    /// The module depth of the current module of the cursor position.
473    /// - crate-root
474    ///  - mod foo
475    ///   - mod bar
476    ///
477    /// Here depth will be 2
478    pub(crate) depth_from_crate_root: usize,
479
480    /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest
481    /// importing those traits.
482    ///
483    /// Note the trait *themselves* are not excluded, only their methods are.
484    pub(crate) exclude_flyimport: FxHashMap<ModuleDef, AutoImportExclusionType>,
485    /// Traits whose methods should always be excluded, even when in scope (compare `exclude_flyimport_traits`).
486    /// They will *not* be excluded, however, if they are available as a generic bound.
487    ///
488    /// Note the trait *themselves* are not excluded, only their methods are.
489    pub(crate) exclude_traits: FxHashSet<hir::Trait>,
490
491    /// Whether and how to complete semicolon for unit-returning functions.
492    pub(crate) complete_semicolon: CompleteSemicolon,
493}
494
495#[derive(Debug)]
496pub(crate) enum CompleteSemicolon {
497    DoNotComplete,
498    CompleteSemi,
499    CompleteComma,
500}
501
502impl CompletionContext<'_> {
503    /// The range of the identifier that is being completed.
504    pub(crate) fn source_range(&self) -> TextRange {
505        let kind = self.original_token.kind();
506        match kind {
507            CHAR => {
508                // assume we are completing a lifetime but the user has only typed the '
509                cov_mark::hit!(completes_if_lifetime_without_idents);
510                TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
511            }
512            LIFETIME_IDENT | UNDERSCORE | INT_NUMBER => self.original_token.text_range(),
513            // We want to consider all keywords in all editions.
514            _ if kind.is_any_identifier() => self.original_token.text_range(),
515            _ => TextRange::empty(self.position.offset),
516        }
517    }
518
519    pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
520        FamousDefs(&self.sema, self.krate)
521    }
522
523    /// Checks if an item is visible and not `doc(hidden)` at the completion site.
524    pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
525        match item {
526            ScopeDef::ModuleDef(def) => match def {
527                hir::ModuleDef::Module(it) => self.is_visible(it),
528                hir::ModuleDef::Function(it) => self.is_visible(it),
529                hir::ModuleDef::Adt(it) => self.is_visible(it),
530                hir::ModuleDef::Variant(it) => self.is_visible(it),
531                hir::ModuleDef::Const(it) => self.is_visible(it),
532                hir::ModuleDef::Static(it) => self.is_visible(it),
533                hir::ModuleDef::Trait(it) => self.is_visible(it),
534                hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
535                hir::ModuleDef::Macro(it) => self.is_visible(it),
536                hir::ModuleDef::BuiltinType(_) => Visible::Yes,
537            },
538            ScopeDef::GenericParam(_)
539            | ScopeDef::ImplSelfType(_)
540            | ScopeDef::AdtSelfType(_)
541            | ScopeDef::Local(_)
542            | ScopeDef::Label(_)
543            | ScopeDef::Unknown => Visible::Yes,
544        }
545    }
546
547    /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site.
548    pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
549    where
550        I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
551    {
552        let vis = item.visibility(self.db);
553        let attrs = item.attrs(self.db);
554        self.is_visible_impl(&vis, &attrs, item.krate(self.db))
555    }
556
557    pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
558    where
559        I: hir::HasAttrs + Copy,
560    {
561        let attrs = item.attrs(self.db);
562        attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
563    }
564
565    /// Check if an item is `#[doc(hidden)]`.
566    pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
567        let attrs = item.attrs(self.db);
568        let krate = item.krate(self.db);
569        match (attrs, krate) {
570            (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
571            _ => false,
572        }
573    }
574
575    /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
576    pub(crate) fn check_stability(&self, attrs: Option<&hir::AttrsWithOwner>) -> bool {
577        let Some(attrs) = attrs else {
578            return true;
579        };
580        !attrs.is_unstable() || self.is_nightly
581    }
582
583    pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
584    where
585        I: hir::HasAttrs + hir::HasCrate,
586    {
587        let defining_crate = item.krate(self.db);
588        let attrs = item.attrs(self.db);
589        self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)
590    }
591
592    /// Whether the given trait is an operator trait or not.
593    pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
594        match trait_.attrs(self.db).lang(self.db) {
595            Some(lang) => OP_TRAIT_LANG.contains(&lang),
596            None => false,
597        }
598    }
599
600    /// Whether the given trait has `#[doc(notable_trait)]`
601    pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {
602        trait_.attrs(self.db).is_doc_notable_trait()
603    }
604
605    /// Returns the traits in scope, with the [`Drop`] trait removed.
606    pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
607        let mut traits_in_scope = self.scope.visible_traits();
608        if let Some(drop) = self.famous_defs().core_ops_Drop() {
609            traits_in_scope.0.remove(&drop.into());
610        }
611        traits_in_scope
612    }
613
614    pub(crate) fn iterate_path_candidates(
615        &self,
616        ty: &hir::Type<'_>,
617        mut cb: impl FnMut(hir::AssocItem),
618    ) {
619        let mut seen = FxHashSet::default();
620        ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {
621            // We might iterate candidates of a trait multiple times here, so deduplicate
622            // them.
623            if seen.insert(item) {
624                cb(item)
625            }
626            None::<()>
627        });
628    }
629
630    /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
631    /// passes all doc-aliases along, to funnel it into `Completions::add_path_resolution`.
632    pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
633        let _p = tracing::info_span!("CompletionContext::process_all_names").entered();
634        self.scope.process_all_names(&mut |name, def| {
635            if self.is_scope_def_hidden(def) {
636                return;
637            }
638            let doc_aliases = self.doc_aliases_in_scope(def);
639            f(name, def, doc_aliases);
640        });
641    }
642
643    pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
644        let _p = tracing::info_span!("CompletionContext::process_all_names_raw").entered();
645        self.scope.process_all_names(f);
646    }
647
648    fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
649        if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
650            return self.is_doc_hidden(&attrs, krate);
651        }
652
653        false
654    }
655
656    fn is_visible_impl(
657        &self,
658        vis: &hir::Visibility,
659        attrs: &hir::AttrsWithOwner,
660        defining_crate: hir::Crate,
661    ) -> Visible {
662        if !self.check_stability(Some(attrs)) {
663            return Visible::No;
664        }
665
666        if !vis.is_visible_from(self.db, self.module.into()) {
667            if !self.config.enable_private_editable {
668                return Visible::No;
669            }
670            // If the definition location is editable, also show private items
671            return if is_editable_crate(defining_crate, self.db) {
672                Visible::Editable
673            } else {
674                Visible::No
675            };
676        }
677
678        if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }
679    }
680
681    pub(crate) fn is_doc_hidden(
682        &self,
683        attrs: &hir::AttrsWithOwner,
684        defining_crate: hir::Crate,
685    ) -> bool {
686        // `doc(hidden)` items are only completed within the defining crate.
687        self.krate != defining_crate && attrs.is_doc_hidden()
688    }
689
690    pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
691        if let Some(attrs) = scope_def.attrs(self.db) {
692            attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()
693        } else {
694            vec![]
695        }
696    }
697}
698
699// CompletionContext construction
700impl<'db> CompletionContext<'db> {
701    pub(crate) fn new(
702        db: &'db RootDatabase,
703        position @ FilePosition { file_id, offset }: FilePosition,
704        config: &'db CompletionConfig<'db>,
705        trigger_character: Option<char>,
706    ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
707        let _p = tracing::info_span!("CompletionContext::new").entered();
708        let sema = Semantics::new(db);
709
710        let editioned_file_id = sema.attach_first_edition(file_id);
711        let original_file = sema.parse(editioned_file_id);
712
713        // Insert a fake ident to get a valid parse tree. We will use this file
714        // to determine context, though the original_file will be used for
715        // actual completion.
716        let file_with_fake_ident = {
717            let (_, edition) = editioned_file_id.unpack(db);
718            let parse = db.parse(editioned_file_id);
719            parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()
720        };
721
722        // always pick the token to the immediate left of the cursor, as that is what we are actually
723        // completing on
724        let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
725
726        // try to skip completions on path with invalid colons
727        // this approach works in normal path and inside token tree
728        if original_token.kind() == T![:] {
729            // return if no prev token before colon
730            let prev_token = original_token.prev_token()?;
731
732            // only has a single colon
733            if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {
734                return None;
735            }
736
737            // has 3 colon or 2 coloncolon in a row
738            // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205
739            // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751
740            if prev_token
741                .prev_token()
742                .map(|t| t.kind() == T![:] || t.kind() == T![::])
743                .unwrap_or(false)
744            {
745                return None;
746            }
747        }
748
749        let AnalysisResult {
750            analysis,
751            expected: (expected_type, expected_name),
752            qualifier_ctx,
753            token,
754            original_offset,
755        } = expand_and_analyze(
756            &sema,
757            InFile::new(editioned_file_id.into(), original_file.syntax().clone()),
758            file_with_fake_ident.syntax().clone(),
759            offset,
760            &original_token,
761        )?;
762
763        // adjust for macro input, this still fails if there is no token written yet
764        let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;
765
766        let krate = scope.krate();
767        let module = scope.module();
768        let containing_function = scope.containing_function();
769        let edition = krate.edition(db);
770
771        let toolchain = db.toolchain_channel(krate.into());
772        // `toolchain == None` means we're in some detached files. Since we have no information on
773        // the toolchain being used, let's just allow unstable items to be listed.
774        let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
775
776        let mut locals = FxHashMap::default();
777        scope.process_all_names(&mut |name, scope| {
778            if let ScopeDef::Local(local) = scope {
779                // synthetic names currently leak out as we lack synthetic hygiene, so filter them
780                // out here
781                if name.as_str().starts_with('<') {
782                    return;
783                }
784                locals.insert(name, local);
785            }
786        });
787
788        let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
789            // `BlockExpr` modules do not count towards module depth
790            .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
791            .count()
792            // exclude `m` itself
793            .saturating_sub(1);
794
795        let exclude_traits: FxHashSet<_> = config
796            .exclude_traits
797            .iter()
798            .filter_map(|path| {
799                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map(
800                    |it| match it {
801                        hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
802                        _ => None,
803                    },
804                )
805            })
806            .collect();
807
808        let mut exclude_flyimport: FxHashMap<_, _> = config
809            .exclude_flyimport
810            .iter()
811            .flat_map(|(path, kind)| {
812                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern))
813                    .map(|it| (it.into_module_def(), *kind))
814            })
815            .collect();
816        exclude_flyimport
817            .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));
818
819        // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze`
820        let complete_semicolon = if !config.add_semicolon_to_unit {
821            CompleteSemicolon::DoNotComplete
822        } else if let Some(term_node) =
823            sema.token_ancestors_with_macros(token.clone()).find(|node| {
824                matches!(node.kind(), BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR)
825            })
826        {
827            let next_token = iter::successors(token.next_token(), |it| it.next_token())
828                .map(|it| it.kind())
829                .find(|kind| !kind.is_trivia());
830            match term_node.kind() {
831                MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,
832                BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,
833                _ => CompleteSemicolon::DoNotComplete,
834            }
835        } else {
836            CompleteSemicolon::DoNotComplete
837        };
838
839        let display_target = krate.to_display_target(db);
840        let ctx = CompletionContext {
841            sema,
842            scope,
843            db,
844            config,
845            position,
846            trigger_character,
847            original_token,
848            token,
849            krate,
850            module,
851            containing_function,
852            is_nightly,
853            edition,
854            expected_name,
855            expected_type,
856            qualifier_ctx,
857            locals,
858            depth_from_crate_root,
859            exclude_flyimport,
860            exclude_traits,
861            complete_semicolon,
862            display_target,
863        };
864        Some((ctx, analysis))
865    }
866}
867
868const OP_TRAIT_LANG: &[hir::LangItem] = &[
869    hir::LangItem::AddAssign,
870    hir::LangItem::Add,
871    hir::LangItem::BitAndAssign,
872    hir::LangItem::BitAnd,
873    hir::LangItem::BitOrAssign,
874    hir::LangItem::BitOr,
875    hir::LangItem::BitXorAssign,
876    hir::LangItem::BitXor,
877    hir::LangItem::DerefMut,
878    hir::LangItem::Deref,
879    hir::LangItem::DivAssign,
880    hir::LangItem::Div,
881    hir::LangItem::PartialEq,
882    hir::LangItem::FnMut,
883    hir::LangItem::FnOnce,
884    hir::LangItem::Fn,
885    hir::LangItem::IndexMut,
886    hir::LangItem::Index,
887    hir::LangItem::MulAssign,
888    hir::LangItem::Mul,
889    hir::LangItem::Neg,
890    hir::LangItem::Not,
891    hir::LangItem::PartialOrd,
892    hir::LangItem::RemAssign,
893    hir::LangItem::Rem,
894    hir::LangItem::ShlAssign,
895    hir::LangItem::Shl,
896    hir::LangItem::ShrAssign,
897    hir::LangItem::Shr,
898    hir::LangItem::Sub,
899];