src/tools/rust-analyzer/crates/ide-completion/src/context.rs RUST 1,013 lines View on github.com → Search inside
1//! See [`CompletionContext`] structure.23mod analysis;4#[cfg(test)]5mod tests;67use std::{iter, sync::LazyLock};89use 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};2526use crate::{27    CompletionConfig,28    config::AutoImportExclusionType,29    context::analysis::{AnalysisResult, expand_and_analyze},30};3132const COMPLETION_MARKER: &str = "raCompletionMarker";3334#[derive(Copy, Clone, Debug, PartialEq, Eq)]35pub(crate) enum PatternRefutability {36    Refutable,37    Irrefutable,38}3940#[derive(Debug)]41pub(crate) enum Visible {42    Yes,43    Editable,44    No,45}4647/// Existing qualifiers for the thing we are currently completing.48#[derive(Debug, Default)]49pub(crate) struct QualifierCtx {50    // TODO: Add try_tok and default_tok51    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}5758impl 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}6768/// 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 segment81    pub(crate) path: ast::Path,82    /// The path of which we are completing the segment in the original file83    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 not88    pub(crate) use_tree_parent: bool,89}9091impl 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    }105106    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.kind114        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}128129/// 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)ItemList145    Item {146        kind: ItemListKind,147    },148    Pat {149        pat_ctx: PatternContext,150    },151    Vis {152        has_in_token: bool,153    },154    Use,155}156157pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;158159#[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}165166#[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 expression173    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 update180    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 the186    /// fat arrow token187    pub(crate) in_match_guard: bool,188}189190/// Original file ast nodes191#[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 arg198        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 arg202        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}213214impl 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    }224225    pub(crate) fn complete_consts(&self) -> bool {226        matches!(227            self,228            TypeLocation::GenericArg {229                corresponding_param: Some(ast::GenericParam::ConstParam(_)),230                ..231            } | TypeLocation::AssocConstEq232        )233    }234235    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    }246247    pub(crate) fn complete_self_type(&self) -> bool {248        self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)249    }250}251252#[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}259260/// 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}270271#[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 path278        ///279        /// This would be None, if path is not solely made of280        /// `super` segments, e.g.281        ///282        /// ```ignore283        /// 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 path295    Absolute,296}297298/// 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 of310    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 expr313    pub(crate) missing_variants: Vec<hir::EnumVariant>,314}315316#[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}322323/// The state of the lifetime we are completing.324#[derive(Debug)]325pub(crate) struct LifetimeContext {326    pub(crate) kind: LifetimeKind,327}328329/// 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}337338/// 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}345346/// 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 node358    Module(ast::Module),359    RecordField,360    Rename,361    SelfParam,362    Static,363    Struct,364    Trait,365    TypeAlias,366    TypeParam,367    Union,368    Variant,369}370371/// The state of the NameRef we are completing.372#[derive(Debug)]373pub(crate) struct NameRefContext<'db> {374    /// NameRef syntax in the original file375    pub(crate) nameref: Option<ast::NameRef>,376    pub(crate) kind: NameRefKind<'db>,377}378379/// 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 completions385    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}394395/// 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 inside402    String {403        /// original token404        original: ast::String,405        /// fake token406        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}418419/// 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}427428#[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 yet432        /// like `0.$0`433        receiver_is_ambiguous_float_literal: bool,434    },435    Method,436}437438#[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}443444#[derive(Copy, Clone, Debug, PartialEq, Eq)]445pub(crate) enum BreakableKind {446    Loop,447    For,448    While,449    Block,450}451452#[derive(Clone, Debug, PartialEq, Eq)]453pub(crate) enum ParamKind {454    Function(ast::Fn),455    Closure(ast::ClosureExpr),456}457458/// `CompletionContext` is created early during completion to figure out, where459/// exactly is the cursor, syntax-wise.460#[derive(Debug)]461pub(crate) struct CompletionContext<'a, 'db> {462    pub(crate) sema: Semantics<'db, RootDatabase>,463    pub(crate) scope: SemanticsScope<'db>,464    pub(crate) db: &'db RootDatabase,465    pub(crate) config: &'a CompletionConfig<'a>,466    pub(crate) position: FilePosition,467468    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 crate483    // FIXME: This should probably be the crate of the current token?484    pub(crate) edition: Edition,485486    /// 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<'db>>,491492    pub(crate) qualifier_ctx: QualifierCtx,493494    pub(crate) locals: FxHashMap<Name, Local>,495496    /// The module depth of the current module of the cursor position.497    /// - crate-root498    ///  - mod foo499    ///   - mod bar500    ///501    /// Here depth will be 2502    pub(crate) depth_from_crate_root: usize,503504    /// Traits whose methods will be excluded from flyimport. Flyimport should not suggest505    /// 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>,514515    /// Whether and how to complete semicolon for unit-returning functions.516    pub(crate) complete_semicolon: CompleteSemicolon,517}518519#[derive(Debug)]520pub(crate) enum CompleteSemicolon {521    DoNotComplete,522    CompleteSemi,523    CompleteComma,524}525526impl<'db> CompletionContext<'_, 'db> {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    }542543    pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> {544        FamousDefs(&self.sema, self.krate)545    }546547    /// 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    }570571    /// 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) -> Visible573    where574        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    }580581    pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>582    where583        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    }588589    /// 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    }598599    /// 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        if !attrs.is_unstable() {605            return true;606        }607        if !self.is_nightly {608            return false;609        }610        // Unstable on nightly, but we still don't want to suggest internal features, unless the feature flag is enabled.611        let Some(unstable_feature) = attrs.unstable_feature(self.db) else {612            return true;613        };614        !is_internal_feature(&unstable_feature)615            || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)616    }617618    pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool619    where620        I: hir::HasAttrs + hir::HasCrate,621    {622        let defining_crate = item.krate(self.db);623        let attrs = item.attrs(self.db);624        self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate)625    }626627    /// Whether the given trait is an operator trait or not.628    pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {629        match trait_.attrs(self.db).lang(self.db) {630            Some(lang) => OP_TRAIT_LANG.contains(&lang),631            None => false,632        }633    }634635    /// Whether the given trait has `#[doc(notable_trait)]`636    pub(crate) fn is_doc_notable_trait(&self, trait_: hir::Trait) -> bool {637        trait_.attrs(self.db).is_doc_notable_trait()638    }639640    /// Returns the traits in scope, with the [`Drop`] trait removed.641    pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {642        let mut traits_in_scope = self.scope.visible_traits();643        if let Some(drop) = self.famous_defs().core_ops_Drop() {644            traits_in_scope.0.remove(&drop.into());645        }646        traits_in_scope647    }648649    pub(crate) fn iterate_path_candidates(650        &self,651        ty: &hir::Type<'_>,652        mut cb: impl FnMut(hir::AssocItem),653    ) {654        let mut seen = FxHashSet::default();655        ty.iterate_path_candidates(self.db, &self.scope, &self.traits_in_scope(), None, |item| {656            // We might iterate candidates of a trait multiple times here, so deduplicate657            // them.658            if seen.insert(item) {659                cb(item)660            }661            None::<()>662        });663    }664665    /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and666    /// passes all doc-aliases along, to funnel it into `Completions::add_path_resolution`.667    pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {668        let _p = tracing::info_span!("CompletionContext::process_all_names").entered();669        self.scope.process_all_names(&mut |name, def| {670            if self.is_scope_def_hidden(def) {671                return;672            }673            let doc_aliases = self.doc_aliases_in_scope(def);674            f(name, def, doc_aliases);675        });676    }677678    pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {679        let _p = tracing::info_span!("CompletionContext::process_all_names_raw").entered();680        self.scope.process_all_names(f);681    }682683    fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {684        if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {685            return self.is_doc_hidden(&attrs, krate);686        }687688        false689    }690691    fn is_visible_impl(692        &self,693        vis: &hir::Visibility,694        attrs: &hir::AttrsWithOwner,695        defining_crate: hir::Crate,696    ) -> Visible {697        if !self.check_stability(Some(attrs)) {698            return Visible::No;699        }700701        if !vis.is_visible_from(self.db, self.module.into()) {702            if !self.config.enable_private_editable {703                return Visible::No;704            }705            // If the definition location is editable, also show private items706            return if is_editable_crate(defining_crate, self.db) {707                Visible::Editable708            } else {709                Visible::No710            };711        }712713        if self.is_doc_hidden(attrs, defining_crate) { Visible::No } else { Visible::Yes }714    }715716    pub(crate) fn is_doc_hidden(717        &self,718        attrs: &hir::AttrsWithOwner,719        defining_crate: hir::Crate,720    ) -> bool {721        // `doc(hidden)` items are only completed within the defining crate.722        self.krate != defining_crate && attrs.is_doc_hidden()723    }724725    pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {726        if let Some(attrs) = scope_def.attrs(self.db) {727            attrs.doc_aliases(self.db).iter().map(|it| it.as_str().into()).collect()728        } else {729            vec![]730        }731    }732733    pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> {734        self.scope735            .generic_def()736            .and_then(|def| ty.try_rebase_into_owner(self.db, def))737            .unwrap_or_else(|| ty.instantiate_with_errors())738    }739}740741// CompletionContext construction742impl<'a, 'db> CompletionContext<'a, 'db> {743    pub(crate) fn new(744        db: &'db RootDatabase,745        position @ FilePosition { file_id, offset }: FilePosition,746        config: &'a CompletionConfig<'a>,747        trigger_character: Option<char>,748    ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> {749        let _p = tracing::info_span!("CompletionContext::new").entered();750        let sema = Semantics::new(db);751752        let editioned_file_id = sema.attach_first_edition(file_id);753        let original_file = sema.parse(editioned_file_id);754755        // Insert a fake ident to get a valid parse tree. We will use this file756        // to determine context, though the original_file will be used for757        // actual completion.758        let file_with_fake_ident = {759            let (_, edition) = editioned_file_id.unpack(db);760            let parse = editioned_file_id.parse(db);761            parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()762        };763764        // always pick the token to the immediate left of the cursor, as that is what we are actually765        // completing on766        let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;767768        // try to skip completions on path with invalid colons769        // this approach works in normal path and inside token tree770        if original_token.kind() == T![:] {771            // return if no prev token before colon772            let prev_token = original_token.prev_token()?;773774            // only has a single colon775            if prev_token.kind() != T![:] && !is_in_macro_matcher(&original_token) {776                return None;777            }778779            // has 3 colon or 2 coloncolon in a row780            // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205781            // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751782            if prev_token783                .prev_token()784                .map(|t| t.kind() == T![:] || t.kind() == T![::])785                .unwrap_or(false)786            {787                return None;788            }789        }790791        let AnalysisResult {792            analysis,793            expected: (expected_type, expected_name),794            qualifier_ctx,795            token,796            original_offset,797        } = expand_and_analyze(798            &sema,799            InFile::new(editioned_file_id.into(), original_file.syntax().clone()),800            file_with_fake_ident.syntax().clone(),801            offset,802            &original_token,803        )?;804805        // adjust for macro input, this still fails if there is no token written yet806        let scope = sema.scope_at_offset(&token.parent()?, original_offset)?;807808        let krate = scope.krate();809        let module = scope.module();810        let containing_function = scope.containing_function();811        let edition = krate.edition(db);812813        let toolchain = toolchain_channel(db, krate.into());814        // `toolchain == None` means we're in some detached files. Since we have no information on815        // the toolchain being used, let's just allow unstable items to be listed.816        let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);817818        let mut locals = FxHashMap::default();819        scope.process_all_names(&mut |name, scope| {820            if let ScopeDef::Local(local) = scope {821                // synthetic names currently leak out as we lack synthetic hygiene, so filter them822                // out here823                if name.as_str().starts_with('<') {824                    return;825                }826                locals.insert(name, local);827            }828        });829830        let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))831            // `BlockExpr` modules do not count towards module depth832            .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))833            .count()834            // exclude `m` itself835            .saturating_sub(1);836837        let exclude_traits: FxHashSet<_> = config838            .exclude_traits839            .iter()840            .filter_map(|path| {841                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern)).find_map(842                    |it| match it {843                        hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),844                        _ => None,845                    },846                )847            })848            .collect();849850        let mut exclude_flyimport: FxHashMap<_, _> = config851            .exclude_flyimport852            .iter()853            .flat_map(|(path, kind)| {854                hir::resolve_absolute_path(db, path.split("::").map(Symbol::intern))855                    .map(|it| (it.into_module_def(), *kind))856            })857            .collect();858        let exclude_subitems = exclude_flyimport859            .iter()860            .flat_map(|it| match it {861                (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => {862                    module.scope(db, None)863                }864                _ => vec![],865            })866            .filter_map(|(_, def)| match def {867                ScopeDef::ModuleDef(module_def) => Some(module_def),868                _ => None,869            })870            .collect::<Vec<_>>();871        exclude_flyimport872            .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always)));873        exclude_flyimport874            .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always)));875876        // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze`877        let complete_semicolon = if !config.add_semicolon_to_unit {878            CompleteSemicolon::DoNotComplete879        } else if let Some(term_node) =880            sema.token_ancestors_with_macros(token.clone()).find(|node| {881                matches!(882                    node.kind(),883                    BLOCK_EXPR884                        | MATCH_ARM885                        | CLOSURE_EXPR886                        | ARG_LIST887                        | PAREN_EXPR888                        | ARRAY_EXPR889                        | MATCH_EXPR890                )891            })892        {893            let next_token = iter::successors(token.next_token(), |it| it.next_token())894                .map(|it| it.kind())895                .find(|kind| !kind.is_trivia());896            match term_node.kind() {897                MATCH_ARM if next_token != Some(T![,]) => CompleteSemicolon::CompleteComma,898                BLOCK_EXPR if next_token != Some(T![;]) => CompleteSemicolon::CompleteSemi,899                _ => CompleteSemicolon::DoNotComplete,900            }901        } else {902            CompleteSemicolon::DoNotComplete903        };904905        let display_target = krate.to_display_target(db);906        let ctx = CompletionContext {907            sema,908            scope,909            db,910            config,911            position,912            trigger_character,913            original_token,914            token,915            krate,916            module,917            containing_function,918            is_nightly,919            edition,920            expected_name,921            expected_type,922            qualifier_ctx,923            locals,924            depth_from_crate_root,925            exclude_flyimport,926            exclude_traits,927            complete_semicolon,928            display_target,929        };930        Some((ctx, analysis))931    }932}933934const OP_TRAIT_LANG: &[hir::LangItem] = &[935    hir::LangItem::AddAssign,936    hir::LangItem::Add,937    hir::LangItem::BitAndAssign,938    hir::LangItem::BitAnd,939    hir::LangItem::BitOrAssign,940    hir::LangItem::BitOr,941    hir::LangItem::BitXorAssign,942    hir::LangItem::BitXor,943    hir::LangItem::DerefMut,944    hir::LangItem::Deref,945    hir::LangItem::DivAssign,946    hir::LangItem::Div,947    hir::LangItem::PartialEq,948    hir::LangItem::FnMut,949    hir::LangItem::FnOnce,950    hir::LangItem::Fn,951    hir::LangItem::IndexMut,952    hir::LangItem::Index,953    hir::LangItem::MulAssign,954    hir::LangItem::Mul,955    hir::LangItem::Neg,956    hir::LangItem::Not,957    hir::LangItem::PartialOrd,958    hir::LangItem::RemAssign,959    hir::LangItem::Rem,960    hir::LangItem::ShlAssign,961    hir::LangItem::Shl,962    hir::LangItem::ShrAssign,963    hir::LangItem::Shr,964    hir::LangItem::Sub,965];966967// FIXME: Find a way to keep this up to date somehow?968const INTERNAL_FEATURES_LIST: &[Symbol] = &[969    sym::abi_unadjusted,970    sym::allocator_internals,971    sym::allow_internal_unsafe,972    sym::allow_internal_unstable,973    sym::cfg_emscripten_wasm_eh,974    sym::cfg_target_has_reliable_f16_f128,975    sym::compiler_builtins,976    sym::custom_mir,977    sym::eii_internals,978    sym::field_representing_type_raw,979    sym::intrinsics,980    sym::core_intrinsics,981    sym::lang_items,982    sym::link_cfg,983    sym::more_maybe_bounds,984    sym::negative_bounds,985    sym::pattern_complexity_limit,986    sym::prelude_import,987    sym::profiler_runtime,988    sym::rustc_attrs,989    sym::staged_api,990    sym::test_unstable_lint,991    sym::builtin_syntax,992    sym::link_llvm_intrinsics,993    sym::needs_panic_runtime,994    sym::panic_runtime,995    sym::pattern_types,996    sym::rustdoc_internals,997    sym::contracts_internals,998    sym::freeze_impls,999    sym::unsized_fn_params,1000];10011002static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =1003    LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());10041005fn is_internal_feature(feature: &Symbol) -> bool {1006    if INTERNAL_FEATURES.contains(feature) {1007        return true;1008    }1009    // Libs features are internal if they end in `_internal` or `_internals`.1010    let feature = feature.as_str();1011    feature.ends_with("_internal") || feature.ends_with("_internals")1012}

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.