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