1 //! See `CompletionContext` structure.
9 use base_db::SourceDatabaseExt;
11 HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
14 base_db::{FilePosition, SourceDatabase},
15 famous_defs::FamousDefs,
16 FxHashMap, FxHashSet, RootDatabase,
19 ast::{self, AttrKind, NameOrNameRef},
21 SyntaxKind::{self, *},
22 SyntaxToken, TextRange, TextSize,
26 use crate::CompletionConfig;
28 const COMPLETION_MARKER: &str = "intellijRulezz";
30 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
31 pub(crate) enum PatternRefutability {
37 pub(crate) enum Visible {
43 /// Existing qualifiers for the thing we are currently completing.
44 #[derive(Debug, Default)]
45 pub(super) struct QualifierCtx {
46 pub(super) unsafe_tok: Option<SyntaxToken>,
47 pub(super) vis_node: Option<ast::Visibility>,
51 pub(super) fn none(&self) -> bool {
52 self.unsafe_tok.is_none() && self.vis_node.is_none()
56 /// The state of the path we are currently completing.
58 pub(crate) struct PathCompletionCtx {
59 /// If this is a call with () already there (or {} in case of record patterns)
60 pub(super) has_call_parens: bool,
61 /// If this has a macro call bang !
62 pub(super) has_macro_bang: bool,
63 /// The qualifier of the current path.
64 pub(super) qualified: Qualified,
65 /// The parent of the path we are completing.
66 pub(super) parent: Option<ast::Path>,
68 /// The path of which we are completing the segment
69 pub(super) path: ast::Path,
70 /// The path of which we are completing the segment in the original file
71 pub(crate) original_path: Option<ast::Path>,
72 pub(super) kind: PathKind,
73 /// Whether the path segment has type args or not.
74 pub(super) has_type_args: bool,
75 /// Whether the qualifier comes from a use tree parent or not
76 pub(crate) use_tree_parent: bool,
79 impl PathCompletionCtx {
80 pub(super) fn is_trivial_path(&self) -> bool {
84 has_call_parens: false,
85 has_macro_bang: false,
86 qualified: Qualified::No,
95 /// The kind of path we are completing right now.
96 #[derive(Debug, PartialEq, Eq)]
97 pub(super) enum PathKind {
102 location: TypeLocation,
108 existing_derives: ExistingDerives,
110 /// Path in item position, that is inside an (Assoc)ItemList
115 pat_ctx: PatternContext,
123 pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
125 #[derive(Debug, PartialEq, Eq)]
126 pub(crate) struct AttrCtx {
127 pub(crate) kind: AttrKind,
128 pub(crate) annotated_item_kind: Option<SyntaxKind>,
131 #[derive(Debug, PartialEq, Eq)]
132 pub(crate) struct ExprCtx {
133 pub(crate) in_block_expr: bool,
134 pub(crate) in_loop_body: bool,
135 pub(crate) after_if_expr: bool,
136 /// Whether this expression is the direct condition of an if or while expression
137 pub(crate) in_condition: bool,
138 pub(crate) incomplete_let: bool,
139 pub(crate) ref_expr_parent: Option<ast::RefExpr>,
140 /// The surrounding RecordExpression we are completing a functional update
141 pub(crate) is_func_update: Option<ast::RecordExpr>,
142 pub(crate) self_param: Option<hir::SelfParam>,
143 pub(crate) innermost_ret_ty: Option<hir::Type>,
144 pub(crate) impl_: Option<ast::Impl>,
145 /// Whether this expression occurs in match arm guard position: before the
147 pub(crate) in_match_guard: bool,
150 /// Original file ast nodes
151 #[derive(Clone, Debug, PartialEq, Eq)]
152 pub(crate) enum TypeLocation {
154 TypeAscription(TypeAscriptionTarget),
155 GenericArgList(Option<ast::GenericArgList>),
162 #[derive(Clone, Debug, PartialEq, Eq)]
163 pub(crate) enum TypeAscriptionTarget {
164 Let(Option<ast::Pat>),
165 FnParam(Option<ast::Pat>),
166 RetType(Option<ast::Expr>),
167 Const(Option<ast::Expr>),
170 /// The kind of item list a [`PathKind::Item`] belongs to.
171 #[derive(Debug, PartialEq, Eq)]
172 pub(super) enum ItemListKind {
176 TraitImpl(Option<ast::Impl>),
182 pub(super) enum Qualified {
186 resolution: Option<PathResolution>,
187 /// How many `super` segments are present in the path
189 /// This would be None, if path is not solely made of
190 /// `super` segments, e.g.
196 /// Otherwise it should be Some(count of `super`)
197 super_chain_len: Option<usize>,
201 ty: Option<hir::Type>,
202 trait_: Option<hir::Trait>,
204 /// Whether the path is an absolute path
208 /// The state of the pattern we are completing.
209 #[derive(Debug, Clone, PartialEq, Eq)]
210 pub(super) struct PatternContext {
211 pub(super) refutability: PatternRefutability,
212 pub(super) param_ctx: Option<ParamContext>,
213 pub(super) has_type_ascription: bool,
214 pub(super) parent_pat: Option<ast::Pat>,
215 pub(super) ref_token: Option<SyntaxToken>,
216 pub(super) mut_token: Option<SyntaxToken>,
217 /// The record pattern this name or ref is a field of
218 pub(super) record_pat: Option<ast::RecordPat>,
219 pub(super) impl_: Option<ast::Impl>,
222 #[derive(Debug, Clone, PartialEq, Eq)]
223 pub(super) struct ParamContext {
224 pub(super) param_list: ast::ParamList,
225 pub(super) param: ast::Param,
226 pub(super) kind: ParamKind,
229 /// The state of the lifetime we are completing.
231 pub(super) struct LifetimeContext {
232 pub(super) lifetime: Option<ast::Lifetime>,
233 pub(super) kind: LifetimeKind,
236 /// The kind of lifetime we are completing.
238 pub(super) enum LifetimeKind {
239 LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
245 /// The state of the name we are completing.
247 pub(super) struct NameContext {
249 pub(super) name: Option<ast::Name>,
250 pub(super) kind: NameKind,
253 /// The kind of the name we are completing.
256 pub(super) enum NameKind {
261 IdentPat(PatternContext),
278 /// The state of the NameRef we are completing.
280 pub(super) struct NameRefContext {
281 /// NameRef syntax in the original file
282 pub(super) nameref: Option<ast::NameRef>,
283 pub(super) kind: NameRefKind,
286 /// The kind of the NameRef we are completing.
288 pub(super) enum NameRefKind {
289 Path(PathCompletionCtx),
290 DotAccess(DotAccess),
291 /// Position where we are only interested in keyword completions
293 /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
296 expr: ast::RecordExpr,
298 Pattern(PatternContext),
301 /// The identifier we are currently completing.
303 pub(super) enum CompletionAnalysis {
305 NameRef(NameRefContext),
306 Lifetime(LifetimeContext),
307 /// The string the cursor is currently inside
310 original: ast::String,
312 expanded: Option<ast::String>,
314 /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
317 fake_attribute_under_caret: Option<ast::Attr>,
321 /// Information about the field or method access we are completing.
323 pub(super) struct DotAccess {
324 pub(super) receiver: Option<ast::Expr>,
325 pub(super) receiver_ty: Option<TypeInfo>,
326 pub(super) kind: DotAccessKind,
330 pub(super) enum DotAccessKind {
332 /// True if the receiver is an integer and there is no ident in the original file after it yet
334 receiver_is_ambiguous_float_literal: bool,
341 #[derive(Clone, Debug, PartialEq, Eq)]
342 pub(crate) enum ParamKind {
344 Closure(ast::ClosureExpr),
347 /// `CompletionContext` is created early during completion to figure out, where
348 /// exactly is the cursor, syntax-wise.
350 pub(crate) struct CompletionContext<'a> {
351 pub(super) sema: Semantics<'a, RootDatabase>,
352 pub(super) scope: SemanticsScope<'a>,
353 pub(super) db: &'a RootDatabase,
354 pub(super) config: &'a CompletionConfig,
355 pub(super) position: FilePosition,
357 /// The token before the cursor, in the original file.
358 pub(super) original_token: SyntaxToken,
359 /// The token before the cursor, in the macro-expanded file.
360 pub(super) token: SyntaxToken,
361 /// The crate of the current file.
362 pub(super) krate: hir::Crate,
363 /// The module of the `scope`.
364 pub(super) module: hir::Module,
366 /// The expected name of what we are completing.
367 /// This is usually the parameter name of the function argument we are completing.
368 pub(super) expected_name: Option<NameOrNameRef>,
369 /// The expected type of what we are completing.
370 pub(super) expected_type: Option<Type>,
372 pub(super) qualifier_ctx: QualifierCtx,
374 pub(super) locals: FxHashMap<Name, Local>,
376 /// The module depth of the current module of the cursor position.
380 /// Here depth will be 2
381 pub(super) depth_from_crate_root: usize,
384 impl<'a> CompletionContext<'a> {
385 /// The range of the identifier that is being completed.
386 pub(crate) fn source_range(&self) -> TextRange {
387 // check kind of macro-expanded token, but use range of original token
388 let kind = self.token.kind();
391 // assume we are completing a lifetime but the user has only typed the '
392 cov_mark::hit!(completes_if_lifetime_without_idents);
393 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
395 IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
396 _ if kind.is_keyword() => self.original_token.text_range(),
397 _ => TextRange::empty(self.position.offset),
401 pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
402 FamousDefs(&self.sema, self.krate)
405 /// Checks if an item is visible and not `doc(hidden)` at the completion site.
406 pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
408 ScopeDef::ModuleDef(def) => match def {
409 hir::ModuleDef::Module(it) => self.is_visible(it),
410 hir::ModuleDef::Function(it) => self.is_visible(it),
411 hir::ModuleDef::Adt(it) => self.is_visible(it),
412 hir::ModuleDef::Variant(it) => self.is_visible(it),
413 hir::ModuleDef::Const(it) => self.is_visible(it),
414 hir::ModuleDef::Static(it) => self.is_visible(it),
415 hir::ModuleDef::Trait(it) => self.is_visible(it),
416 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
417 hir::ModuleDef::Macro(it) => self.is_visible(it),
418 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
420 ScopeDef::GenericParam(_)
421 | ScopeDef::ImplSelfType(_)
422 | ScopeDef::AdtSelfType(_)
425 | ScopeDef::Unknown => Visible::Yes,
429 /// Checks if an item is visible and not `doc(hidden)` at the completion site.
430 pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
432 I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
434 let vis = item.visibility(self.db);
435 let attrs = item.attrs(self.db);
436 self.is_visible_impl(&vis, &attrs, item.krate(self.db))
439 /// Check if an item is `#[doc(hidden)]`.
440 pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
441 let attrs = item.attrs(self.db);
442 let krate = item.krate(self.db);
443 match (attrs, krate) {
444 (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
449 /// Whether the given trait is an operator trait or not.
450 pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
451 match trait_.attrs(self.db).lang() {
452 Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
457 /// Returns the traits in scope, with the [`Drop`] trait removed.
458 pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
459 let mut traits_in_scope = self.scope.visible_traits();
460 if let Some(drop) = self.famous_defs().core_ops_Drop() {
461 traits_in_scope.0.remove(&drop.into());
466 pub(crate) fn iterate_path_candidates(
469 mut cb: impl FnMut(hir::AssocItem),
471 let mut seen = FxHashSet::default();
472 ty.iterate_path_candidates(
475 &self.traits_in_scope(),
479 // We might iterate candidates of a trait multiple times here, so deduplicate
481 if seen.insert(item) {
489 /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
490 pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
491 let _p = profile::span("CompletionContext::process_all_names");
492 self.scope.process_all_names(&mut |name, def| {
493 if self.is_scope_def_hidden(def) {
501 pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
502 let _p = profile::span("CompletionContext::process_all_names_raw");
503 self.scope.process_all_names(&mut |name, def| f(name, def));
506 fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
507 if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
508 return self.is_doc_hidden(&attrs, krate);
516 vis: &hir::Visibility,
518 defining_crate: hir::Crate,
520 if !vis.is_visible_from(self.db, self.module.into()) {
521 if !self.config.enable_private_editable {
524 // If the definition location is editable, also show private items
525 let root_file = defining_crate.root_file(self.db);
526 let source_root_id = self.db.file_source_root(root_file);
527 let is_editable = !self.db.source_root(source_root_id).is_library;
528 return if is_editable { Visible::Editable } else { Visible::No };
531 if self.is_doc_hidden(attrs, defining_crate) {
538 fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
539 // `doc(hidden)` items are only completed within the defining crate.
540 self.krate != defining_crate && attrs.has_doc_hidden()
544 // CompletionContext construction
545 impl<'a> CompletionContext<'a> {
547 db: &'a RootDatabase,
548 position @ FilePosition { file_id, offset }: FilePosition,
549 config: &'a CompletionConfig,
550 ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
551 let _p = profile::span("CompletionContext::new");
552 let sema = Semantics::new(db);
554 let original_file = sema.parse(file_id);
556 // Insert a fake ident to get a valid parse tree. We will use this file
557 // to determine context, though the original_file will be used for
558 // actual completion.
559 let file_with_fake_ident = {
560 let parse = db.parse(file_id);
561 let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
562 parse.reparse(&edit).tree()
564 let fake_ident_token =
565 file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
567 let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
568 let token = sema.descend_into_macros_single(original_token.clone());
570 // adjust for macro input, this still fails if there is no token written yet
571 let scope_offset = if original_token == token { offset } else { token.text_range().end() };
572 let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?;
574 let krate = scope.krate();
575 let module = scope.module();
577 let mut locals = FxHashMap::default();
578 scope.process_all_names(&mut |name, scope| {
579 if let ScopeDef::Local(local) = scope {
580 locals.insert(name, local);
584 let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
586 let mut ctx = CompletionContext {
598 qualifier_ctx: Default::default(),
600 depth_from_crate_root,
602 let ident_ctx = ctx.expand_and_analyze(
603 original_file.syntax().clone(),
604 file_with_fake_ident.syntax().clone(),
608 Some((ctx, ident_ctx))
612 const OP_TRAIT_LANG_NAMES: &[&str] = &[