ide_completion/
context.rs

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