]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/context.rs
e35f79d2b6951e05a57c943af8eba44bb60e7f57
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / context.rs
1 //! See `CompletionContext` structure.
2
3 mod analysis;
4 #[cfg(test)]
5 mod tests;
6
7 use std::iter;
8
9 use base_db::SourceDatabaseExt;
10 use hir::{
11     HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
12 };
13 use ide_db::{
14     base_db::{FilePosition, SourceDatabase},
15     famous_defs::FamousDefs,
16     FxHashMap, FxHashSet, RootDatabase,
17 };
18 use syntax::{
19     ast::{self, AttrKind, NameOrNameRef},
20     AstNode,
21     SyntaxKind::{self, *},
22     SyntaxToken, TextRange, TextSize,
23 };
24 use text_edit::Indel;
25
26 use crate::CompletionConfig;
27
28 const COMPLETION_MARKER: &str = "intellijRulezz";
29
30 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
31 pub(crate) enum PatternRefutability {
32     Refutable,
33     Irrefutable,
34 }
35
36 #[derive(Debug)]
37 pub(crate) enum Visible {
38     Yes,
39     Editable,
40     No,
41 }
42
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>,
48 }
49
50 impl QualifierCtx {
51     pub(super) fn none(&self) -> bool {
52         self.unsafe_tok.is_none() && self.vis_node.is_none()
53     }
54 }
55
56 /// The state of the path we are currently completing.
57 #[derive(Debug)]
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>,
67     /// The path of which we are completing the segment
68     pub(super) path: ast::Path,
69     pub(super) kind: PathKind,
70     /// Whether the path segment has type args or not.
71     pub(super) has_type_args: bool,
72     /// Whether the qualifier comes from a use tree parent or not
73     pub(crate) use_tree_parent: bool,
74 }
75
76 impl PathCompletionCtx {
77     pub(super) fn is_trivial_path(&self) -> bool {
78         matches!(
79             self,
80             PathCompletionCtx {
81                 has_call_parens: false,
82                 has_macro_bang: false,
83                 qualified: Qualified::No,
84                 parent: None,
85                 has_type_args: false,
86                 ..
87             }
88         )
89     }
90 }
91
92 /// The kind of path we are completing right now.
93 #[derive(Debug, PartialEq, Eq)]
94 pub(super) enum PathKind {
95     Expr {
96         expr_ctx: ExprCtx,
97     },
98     Type {
99         location: TypeLocation,
100     },
101     Attr {
102         attr_ctx: AttrCtx,
103     },
104     Derive {
105         existing_derives: ExistingDerives,
106     },
107     /// Path in item position, that is inside an (Assoc)ItemList
108     Item {
109         kind: ItemListKind,
110     },
111     Pat {
112         pat_ctx: PatternContext,
113     },
114     Vis {
115         has_in_token: bool,
116     },
117     Use,
118 }
119
120 pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
121
122 #[derive(Debug, PartialEq, Eq)]
123 pub(crate) struct AttrCtx {
124     pub(crate) kind: AttrKind,
125     pub(crate) annotated_item_kind: Option<SyntaxKind>,
126 }
127
128 #[derive(Debug, PartialEq, Eq)]
129 pub(crate) struct ExprCtx {
130     pub(crate) in_block_expr: bool,
131     pub(crate) in_loop_body: bool,
132     pub(crate) after_if_expr: bool,
133     /// Whether this expression is the direct condition of an if or while expression
134     pub(crate) in_condition: bool,
135     pub(crate) incomplete_let: bool,
136     pub(crate) ref_expr_parent: Option<ast::RefExpr>,
137     pub(crate) is_func_update: Option<ast::RecordExpr>,
138     pub(crate) self_param: Option<hir::SelfParam>,
139     pub(crate) innermost_ret_ty: Option<hir::Type>,
140     pub(crate) impl_: Option<ast::Impl>,
141     /// Whether this expression occurs in match arm guard position: before the
142     /// fat arrow token
143     pub(crate) in_match_guard: bool,
144 }
145
146 /// Original file ast nodes
147 #[derive(Clone, Debug, PartialEq, Eq)]
148 pub(crate) enum TypeLocation {
149     TupleField,
150     TypeAscription(TypeAscriptionTarget),
151     GenericArgList(Option<ast::GenericArgList>),
152     TypeBound,
153     ImplTarget,
154     ImplTrait,
155     Other,
156 }
157
158 #[derive(Clone, Debug, PartialEq, Eq)]
159 pub(crate) enum TypeAscriptionTarget {
160     Let(Option<ast::Pat>),
161     FnParam(Option<ast::Pat>),
162     RetType(Option<ast::Expr>),
163     Const(Option<ast::Expr>),
164 }
165
166 /// The kind of item list a [`PathKind::Item`] belongs to.
167 #[derive(Debug, PartialEq, Eq)]
168 pub(super) enum ItemListKind {
169     SourceFile,
170     Module,
171     Impl,
172     TraitImpl(Option<ast::Impl>),
173     Trait,
174     ExternBlock,
175 }
176
177 #[derive(Debug)]
178 pub(super) enum Qualified {
179     No,
180     With {
181         path: ast::Path,
182         resolution: Option<PathResolution>,
183         /// How many `super` segments are present in the path
184         ///
185         /// This would be None, if path is not solely made of
186         /// `super` segments, e.g.
187         ///
188         /// ```rust
189         ///   use super::foo;
190         /// ```
191         ///
192         /// Otherwise it should be Some(count of `super`)
193         super_chain_len: Option<usize>,
194     },
195     /// <_>::
196     TypeAnchor {
197         ty: Option<hir::Type>,
198         trait_: Option<hir::Trait>,
199     },
200     /// Whether the path is an absolute path
201     Absolute,
202 }
203
204 /// The state of the pattern we are completing.
205 #[derive(Debug, Clone, PartialEq, Eq)]
206 pub(super) struct PatternContext {
207     pub(super) refutability: PatternRefutability,
208     pub(super) param_ctx: Option<ParamContext>,
209     pub(super) has_type_ascription: bool,
210     pub(super) parent_pat: Option<ast::Pat>,
211     pub(super) ref_token: Option<SyntaxToken>,
212     pub(super) mut_token: Option<SyntaxToken>,
213     /// The record pattern this name or ref is a field of
214     pub(super) record_pat: Option<ast::RecordPat>,
215     pub(super) impl_: Option<ast::Impl>,
216 }
217
218 #[derive(Debug, Clone, PartialEq, Eq)]
219 pub(super) struct ParamContext {
220     pub(super) param_list: ast::ParamList,
221     pub(super) param: ast::Param,
222     pub(super) kind: ParamKind,
223 }
224
225 /// The state of the lifetime we are completing.
226 #[derive(Debug)]
227 pub(super) struct LifetimeContext {
228     pub(super) lifetime: Option<ast::Lifetime>,
229     pub(super) kind: LifetimeKind,
230 }
231
232 /// The kind of lifetime we are completing.
233 #[derive(Debug)]
234 pub(super) enum LifetimeKind {
235     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
236     Lifetime,
237     LabelRef,
238     LabelDef,
239 }
240
241 /// The state of the name we are completing.
242 #[derive(Debug)]
243 pub(super) struct NameContext {
244     #[allow(dead_code)]
245     pub(super) name: Option<ast::Name>,
246     pub(super) kind: NameKind,
247 }
248
249 /// The kind of the name we are completing.
250 #[derive(Debug)]
251 #[allow(dead_code)]
252 pub(super) enum NameKind {
253     Const,
254     ConstParam,
255     Enum,
256     Function,
257     IdentPat(PatternContext),
258     MacroDef,
259     MacroRules,
260     /// Fake node
261     Module(ast::Module),
262     RecordField,
263     Rename,
264     SelfParam,
265     Static,
266     Struct,
267     Trait,
268     TypeAlias,
269     TypeParam,
270     Union,
271     Variant,
272 }
273
274 /// The state of the NameRef we are completing.
275 #[derive(Debug)]
276 pub(super) struct NameRefContext {
277     /// NameRef syntax in the original file
278     pub(super) nameref: Option<ast::NameRef>,
279     pub(super) kind: NameRefKind,
280 }
281
282 /// The kind of the NameRef we are completing.
283 #[derive(Debug)]
284 pub(super) enum NameRefKind {
285     Path(PathCompletionCtx),
286     DotAccess(DotAccess),
287     /// Position where we are only interested in keyword completions
288     Keyword(ast::Item),
289     /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
290     RecordExpr {
291         dot_prefix: bool,
292         expr: ast::RecordExpr,
293     },
294     Pattern(PatternContext),
295 }
296
297 /// The identifier we are currently completing.
298 #[derive(Debug)]
299 pub(super) enum CompletionAnalysis {
300     Name(NameContext),
301     NameRef(NameRefContext),
302     Lifetime(LifetimeContext),
303     /// The string the cursor is currently inside
304     String {
305         /// original token
306         original: ast::String,
307         /// fake token
308         expanded: Option<ast::String>,
309     },
310     /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
311     UnexpandedAttrTT {
312         colon_prefix: bool,
313         fake_attribute_under_caret: Option<ast::Attr>,
314     },
315 }
316
317 /// Information about the field or method access we are completing.
318 #[derive(Debug)]
319 pub(super) struct DotAccess {
320     pub(super) receiver: Option<ast::Expr>,
321     pub(super) receiver_ty: Option<TypeInfo>,
322     pub(super) kind: DotAccessKind,
323 }
324
325 #[derive(Debug)]
326 pub(super) enum DotAccessKind {
327     Field {
328         /// True if the receiver is an integer and there is no ident in the original file after it yet
329         /// like `0.$0`
330         receiver_is_ambiguous_float_literal: bool,
331     },
332     Method {
333         has_parens: bool,
334     },
335 }
336
337 #[derive(Clone, Debug, PartialEq, Eq)]
338 pub(crate) enum ParamKind {
339     Function(ast::Fn),
340     Closure(ast::ClosureExpr),
341 }
342
343 /// `CompletionContext` is created early during completion to figure out, where
344 /// exactly is the cursor, syntax-wise.
345 #[derive(Debug)]
346 pub(crate) struct CompletionContext<'a> {
347     pub(super) sema: Semantics<'a, RootDatabase>,
348     pub(super) scope: SemanticsScope<'a>,
349     pub(super) db: &'a RootDatabase,
350     pub(super) config: &'a CompletionConfig,
351     pub(super) position: FilePosition,
352
353     /// The token before the cursor, in the original file.
354     pub(super) original_token: SyntaxToken,
355     /// The token before the cursor, in the macro-expanded file.
356     pub(super) token: SyntaxToken,
357     /// The crate of the current file.
358     pub(super) krate: hir::Crate,
359     /// The module of the `scope`.
360     pub(super) module: hir::Module,
361
362     /// The expected name of what we are completing.
363     /// This is usually the parameter name of the function argument we are completing.
364     pub(super) expected_name: Option<NameOrNameRef>,
365     /// The expected type of what we are completing.
366     pub(super) expected_type: Option<Type>,
367
368     pub(super) qualifier_ctx: QualifierCtx,
369
370     pub(super) locals: FxHashMap<Name, Local>,
371
372     /// The module depth of the current module of the cursor position.
373     /// - crate-root
374     ///  - mod foo
375     ///   - mod bar
376     /// Here depth will be 2
377     pub(super) depth_from_crate_root: usize,
378 }
379
380 impl<'a> CompletionContext<'a> {
381     /// The range of the identifier that is being completed.
382     pub(crate) fn source_range(&self) -> TextRange {
383         // check kind of macro-expanded token, but use range of original token
384         let kind = self.token.kind();
385         match kind {
386             CHAR => {
387                 // assume we are completing a lifetime but the user has only typed the '
388                 cov_mark::hit!(completes_if_lifetime_without_idents);
389                 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
390             }
391             IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
392             _ if kind.is_keyword() => self.original_token.text_range(),
393             _ => TextRange::empty(self.position.offset),
394         }
395     }
396
397     pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
398         FamousDefs(&self.sema, self.krate)
399     }
400
401     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
402     pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
403         match item {
404             ScopeDef::ModuleDef(def) => match def {
405                 hir::ModuleDef::Module(it) => self.is_visible(it),
406                 hir::ModuleDef::Function(it) => self.is_visible(it),
407                 hir::ModuleDef::Adt(it) => self.is_visible(it),
408                 hir::ModuleDef::Variant(it) => self.is_visible(it),
409                 hir::ModuleDef::Const(it) => self.is_visible(it),
410                 hir::ModuleDef::Static(it) => self.is_visible(it),
411                 hir::ModuleDef::Trait(it) => self.is_visible(it),
412                 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
413                 hir::ModuleDef::Macro(it) => self.is_visible(it),
414                 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
415             },
416             ScopeDef::GenericParam(_)
417             | ScopeDef::ImplSelfType(_)
418             | ScopeDef::AdtSelfType(_)
419             | ScopeDef::Local(_)
420             | ScopeDef::Label(_)
421             | ScopeDef::Unknown => Visible::Yes,
422         }
423     }
424
425     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
426     pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
427     where
428         I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
429     {
430         let vis = item.visibility(self.db);
431         let attrs = item.attrs(self.db);
432         self.is_visible_impl(&vis, &attrs, item.krate(self.db))
433     }
434
435     /// Check if an item is `#[doc(hidden)]`.
436     pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
437         let attrs = item.attrs(self.db);
438         let krate = item.krate(self.db);
439         match (attrs, krate) {
440             (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
441             _ => false,
442         }
443     }
444
445     /// Whether the given trait is an operator trait or not.
446     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
447         match trait_.attrs(self.db).lang() {
448             Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
449             None => false,
450         }
451     }
452
453     /// Returns the traits in scope, with the [`Drop`] trait removed.
454     pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
455         let mut traits_in_scope = self.scope.visible_traits();
456         if let Some(drop) = self.famous_defs().core_ops_Drop() {
457             traits_in_scope.0.remove(&drop.into());
458         }
459         traits_in_scope
460     }
461
462     pub(crate) fn iterate_path_candidates(
463         &self,
464         ty: &hir::Type,
465         mut cb: impl FnMut(hir::AssocItem),
466     ) {
467         let mut seen = FxHashSet::default();
468         ty.iterate_path_candidates(
469             self.db,
470             &self.scope,
471             &self.traits_in_scope(),
472             Some(self.module),
473             None,
474             |item| {
475                 // We might iterate candidates of a trait multiple times here, so deduplicate
476                 // them.
477                 if seen.insert(item) {
478                     cb(item)
479                 }
480                 None::<()>
481             },
482         );
483     }
484
485     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
486     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
487         let _p = profile::span("CompletionContext::process_all_names");
488         self.scope.process_all_names(&mut |name, def| {
489             if self.is_scope_def_hidden(def) {
490                 return;
491             }
492
493             f(name, def);
494         });
495     }
496
497     pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
498         let _p = profile::span("CompletionContext::process_all_names_raw");
499         self.scope.process_all_names(&mut |name, def| f(name, def));
500     }
501
502     fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
503         if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
504             return self.is_doc_hidden(&attrs, krate);
505         }
506
507         false
508     }
509
510     fn is_visible_impl(
511         &self,
512         vis: &hir::Visibility,
513         attrs: &hir::Attrs,
514         defining_crate: hir::Crate,
515     ) -> Visible {
516         if !vis.is_visible_from(self.db, self.module.into()) {
517             if !self.config.enable_private_editable {
518                 return Visible::No;
519             }
520             // If the definition location is editable, also show private items
521             let root_file = defining_crate.root_file(self.db);
522             let source_root_id = self.db.file_source_root(root_file);
523             let is_editable = !self.db.source_root(source_root_id).is_library;
524             return if is_editable { Visible::Editable } else { Visible::No };
525         }
526
527         if self.is_doc_hidden(attrs, defining_crate) {
528             Visible::No
529         } else {
530             Visible::Yes
531         }
532     }
533
534     fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
535         // `doc(hidden)` items are only completed within the defining crate.
536         self.krate != defining_crate && attrs.has_doc_hidden()
537     }
538 }
539
540 // CompletionContext construction
541 impl<'a> CompletionContext<'a> {
542     pub(super) fn new(
543         db: &'a RootDatabase,
544         position @ FilePosition { file_id, offset }: FilePosition,
545         config: &'a CompletionConfig,
546     ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
547         let _p = profile::span("CompletionContext::new");
548         let sema = Semantics::new(db);
549
550         let original_file = sema.parse(file_id);
551
552         // Insert a fake ident to get a valid parse tree. We will use this file
553         // to determine context, though the original_file will be used for
554         // actual completion.
555         let file_with_fake_ident = {
556             let parse = db.parse(file_id);
557             let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
558             parse.reparse(&edit).tree()
559         };
560         let fake_ident_token =
561             file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
562
563         let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
564         let token = sema.descend_into_macros_single(original_token.clone());
565
566         // adjust for macro input, this still fails if there is no token written yet
567         let scope_offset = if original_token == token { offset } else { token.text_range().end() };
568         let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?;
569
570         let krate = scope.krate();
571         let module = scope.module();
572
573         let mut locals = FxHashMap::default();
574         scope.process_all_names(&mut |name, scope| {
575             if let ScopeDef::Local(local) = scope {
576                 locals.insert(name, local);
577             }
578         });
579
580         let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
581
582         let mut ctx = CompletionContext {
583             sema,
584             scope,
585             db,
586             config,
587             position,
588             original_token,
589             token,
590             krate,
591             module,
592             expected_name: None,
593             expected_type: None,
594             qualifier_ctx: Default::default(),
595             locals,
596             depth_from_crate_root,
597         };
598         let ident_ctx = ctx.expand_and_analyze(
599             original_file.syntax().clone(),
600             file_with_fake_ident.syntax().clone(),
601             offset,
602             fake_ident_token,
603         )?;
604         Some((ctx, ident_ctx))
605     }
606 }
607
608 const OP_TRAIT_LANG_NAMES: &[&str] = &[
609     "add_assign",
610     "add",
611     "bitand_assign",
612     "bitand",
613     "bitor_assign",
614     "bitor",
615     "bitxor_assign",
616     "bitxor",
617     "deref_mut",
618     "deref",
619     "div_assign",
620     "div",
621     "eq",
622     "fn_mut",
623     "fn_once",
624     "fn",
625     "index_mut",
626     "index",
627     "mul_assign",
628     "mul",
629     "neg",
630     "not",
631     "partial_ord",
632     "rem_assign",
633     "rem",
634     "shl_assign",
635     "shl",
636     "shr_assign",
637     "shr",
638     "sub",
639 ];