]> git.lizzy.rs Git - rust.git/blob - crates/ide-completion/src/context.rs
Auto merge of #12891 - brennanvincent:expander_stack, r=lnicola
[rust.git] / 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     Infer,
197     /// Whether the path is an absolute path
198     Absolute,
199 }
200
201 /// The state of the pattern we are completing.
202 #[derive(Debug, Clone, PartialEq, Eq)]
203 pub(super) struct PatternContext {
204     pub(super) refutability: PatternRefutability,
205     pub(super) param_ctx: Option<ParamContext>,
206     pub(super) has_type_ascription: bool,
207     pub(super) parent_pat: Option<ast::Pat>,
208     pub(super) ref_token: Option<SyntaxToken>,
209     pub(super) mut_token: Option<SyntaxToken>,
210     /// The record pattern this name or ref is a field of
211     pub(super) record_pat: Option<ast::RecordPat>,
212     pub(super) impl_: Option<ast::Impl>,
213 }
214
215 #[derive(Debug, Clone, PartialEq, Eq)]
216 pub(super) struct ParamContext {
217     pub(super) param_list: ast::ParamList,
218     pub(super) param: ast::Param,
219     pub(super) kind: ParamKind,
220 }
221
222 /// The state of the lifetime we are completing.
223 #[derive(Debug)]
224 pub(super) struct LifetimeContext {
225     pub(super) lifetime: Option<ast::Lifetime>,
226     pub(super) kind: LifetimeKind,
227 }
228
229 /// The kind of lifetime we are completing.
230 #[derive(Debug)]
231 pub(super) enum LifetimeKind {
232     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
233     Lifetime,
234     LabelRef,
235     LabelDef,
236 }
237
238 /// The state of the name we are completing.
239 #[derive(Debug)]
240 pub(super) struct NameContext {
241     #[allow(dead_code)]
242     pub(super) name: Option<ast::Name>,
243     pub(super) kind: NameKind,
244 }
245
246 /// The kind of the name we are completing.
247 #[derive(Debug)]
248 #[allow(dead_code)]
249 pub(super) enum NameKind {
250     Const,
251     ConstParam,
252     Enum,
253     Function,
254     IdentPat(PatternContext),
255     MacroDef,
256     MacroRules,
257     /// Fake node
258     Module(ast::Module),
259     RecordField,
260     Rename,
261     SelfParam,
262     Static,
263     Struct,
264     Trait,
265     TypeAlias,
266     TypeParam,
267     Union,
268     Variant,
269 }
270
271 /// The state of the NameRef we are completing.
272 #[derive(Debug)]
273 pub(super) struct NameRefContext {
274     /// NameRef syntax in the original file
275     pub(super) nameref: Option<ast::NameRef>,
276     pub(super) kind: NameRefKind,
277 }
278
279 /// The kind of the NameRef we are completing.
280 #[derive(Debug)]
281 pub(super) enum NameRefKind {
282     Path(PathCompletionCtx),
283     DotAccess(DotAccess),
284     /// Position where we are only interested in keyword completions
285     Keyword(ast::Item),
286     /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
287     RecordExpr {
288         dot_prefix: bool,
289         expr: ast::RecordExpr,
290     },
291     Pattern(PatternContext),
292 }
293
294 /// The identifier we are currently completing.
295 #[derive(Debug)]
296 pub(super) enum CompletionAnalysis {
297     Name(NameContext),
298     NameRef(NameRefContext),
299     Lifetime(LifetimeContext),
300     /// The string the cursor is currently inside
301     String {
302         /// original token
303         original: ast::String,
304         /// fake token
305         expanded: Option<ast::String>,
306     },
307     /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
308     UnexpandedAttrTT {
309         colon_prefix: bool,
310         fake_attribute_under_caret: Option<ast::Attr>,
311     },
312 }
313
314 /// Information about the field or method access we are completing.
315 #[derive(Debug)]
316 pub(super) struct DotAccess {
317     pub(super) receiver: Option<ast::Expr>,
318     pub(super) receiver_ty: Option<TypeInfo>,
319     pub(super) kind: DotAccessKind,
320 }
321
322 #[derive(Debug)]
323 pub(super) enum DotAccessKind {
324     Field {
325         /// True if the receiver is an integer and there is no ident in the original file after it yet
326         /// like `0.$0`
327         receiver_is_ambiguous_float_literal: bool,
328     },
329     Method {
330         has_parens: bool,
331     },
332 }
333
334 #[derive(Clone, Debug, PartialEq, Eq)]
335 pub(crate) enum ParamKind {
336     Function(ast::Fn),
337     Closure(ast::ClosureExpr),
338 }
339
340 /// `CompletionContext` is created early during completion to figure out, where
341 /// exactly is the cursor, syntax-wise.
342 #[derive(Debug)]
343 pub(crate) struct CompletionContext<'a> {
344     pub(super) sema: Semantics<'a, RootDatabase>,
345     pub(super) scope: SemanticsScope<'a>,
346     pub(super) db: &'a RootDatabase,
347     pub(super) config: &'a CompletionConfig,
348     pub(super) position: FilePosition,
349
350     /// The token before the cursor, in the original file.
351     pub(super) original_token: SyntaxToken,
352     /// The token before the cursor, in the macro-expanded file.
353     pub(super) token: SyntaxToken,
354     /// The crate of the current file.
355     pub(super) krate: hir::Crate,
356     /// The module of the `scope`.
357     pub(super) module: hir::Module,
358
359     /// The expected name of what we are completing.
360     /// This is usually the parameter name of the function argument we are completing.
361     pub(super) expected_name: Option<NameOrNameRef>,
362     /// The expected type of what we are completing.
363     pub(super) expected_type: Option<Type>,
364
365     pub(super) qualifier_ctx: QualifierCtx,
366
367     pub(super) locals: FxHashMap<Name, Local>,
368
369     /// The module depth of the current module of the cursor position.
370     /// - crate-root
371     ///  - mod foo
372     ///   - mod bar
373     /// Here depth will be 2
374     pub(super) depth_from_crate_root: usize,
375 }
376
377 impl<'a> CompletionContext<'a> {
378     /// The range of the identifier that is being completed.
379     pub(crate) fn source_range(&self) -> TextRange {
380         // check kind of macro-expanded token, but use range of original token
381         let kind = self.token.kind();
382         match kind {
383             CHAR => {
384                 // assume we are completing a lifetime but the user has only typed the '
385                 cov_mark::hit!(completes_if_lifetime_without_idents);
386                 TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
387             }
388             IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
389             _ if kind.is_keyword() => self.original_token.text_range(),
390             _ => TextRange::empty(self.position.offset),
391         }
392     }
393
394     pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
395         FamousDefs(&self.sema, self.krate)
396     }
397
398     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
399     pub(crate) fn def_is_visible(&self, item: &ScopeDef) -> Visible {
400         match item {
401             ScopeDef::ModuleDef(def) => match def {
402                 hir::ModuleDef::Module(it) => self.is_visible(it),
403                 hir::ModuleDef::Function(it) => self.is_visible(it),
404                 hir::ModuleDef::Adt(it) => self.is_visible(it),
405                 hir::ModuleDef::Variant(it) => self.is_visible(it),
406                 hir::ModuleDef::Const(it) => self.is_visible(it),
407                 hir::ModuleDef::Static(it) => self.is_visible(it),
408                 hir::ModuleDef::Trait(it) => self.is_visible(it),
409                 hir::ModuleDef::TypeAlias(it) => self.is_visible(it),
410                 hir::ModuleDef::Macro(it) => self.is_visible(it),
411                 hir::ModuleDef::BuiltinType(_) => Visible::Yes,
412             },
413             ScopeDef::GenericParam(_)
414             | ScopeDef::ImplSelfType(_)
415             | ScopeDef::AdtSelfType(_)
416             | ScopeDef::Local(_)
417             | ScopeDef::Label(_)
418             | ScopeDef::Unknown => Visible::Yes,
419         }
420     }
421
422     /// Checks if an item is visible and not `doc(hidden)` at the completion site.
423     pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
424     where
425         I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
426     {
427         let vis = item.visibility(self.db);
428         let attrs = item.attrs(self.db);
429         self.is_visible_impl(&vis, &attrs, item.krate(self.db))
430     }
431
432     /// Check if an item is `#[doc(hidden)]`.
433     pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
434         let attrs = item.attrs(self.db);
435         let krate = item.krate(self.db);
436         match (attrs, krate) {
437             (Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
438             _ => false,
439         }
440     }
441
442     /// Whether the given trait is an operator trait or not.
443     pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
444         match trait_.attrs(self.db).lang() {
445             Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
446             None => false,
447         }
448     }
449
450     /// Returns the traits in scope, with the [`Drop`] trait removed.
451     pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
452         let mut traits_in_scope = self.scope.visible_traits();
453         if let Some(drop) = self.famous_defs().core_ops_Drop() {
454             traits_in_scope.0.remove(&drop.into());
455         }
456         traits_in_scope
457     }
458
459     pub(crate) fn iterate_path_candidates(
460         &self,
461         ty: &hir::Type,
462         mut cb: impl FnMut(hir::AssocItem),
463     ) {
464         let mut seen = FxHashSet::default();
465         ty.iterate_path_candidates(
466             self.db,
467             &self.scope,
468             &self.traits_in_scope(),
469             Some(self.module),
470             None,
471             |item| {
472                 // We might iterate candidates of a trait multiple times here, so deduplicate
473                 // them.
474                 if seen.insert(item) {
475                     cb(item)
476                 }
477                 None::<()>
478             },
479         );
480     }
481
482     /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
483     pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
484         let _p = profile::span("CompletionContext::process_all_names");
485         self.scope.process_all_names(&mut |name, def| {
486             if self.is_scope_def_hidden(def) {
487                 return;
488             }
489
490             f(name, def);
491         });
492     }
493
494     pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
495         let _p = profile::span("CompletionContext::process_all_names_raw");
496         self.scope.process_all_names(&mut |name, def| f(name, def));
497     }
498
499     fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
500         if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
501             return self.is_doc_hidden(&attrs, krate);
502         }
503
504         false
505     }
506
507     fn is_visible_impl(
508         &self,
509         vis: &hir::Visibility,
510         attrs: &hir::Attrs,
511         defining_crate: hir::Crate,
512     ) -> Visible {
513         if !vis.is_visible_from(self.db, self.module.into()) {
514             if !self.config.enable_private_editable {
515                 return Visible::No;
516             }
517             // If the definition location is editable, also show private items
518             let root_file = defining_crate.root_file(self.db);
519             let source_root_id = self.db.file_source_root(root_file);
520             let is_editable = !self.db.source_root(source_root_id).is_library;
521             return if is_editable { Visible::Editable } else { Visible::No };
522         }
523
524         if self.is_doc_hidden(attrs, defining_crate) {
525             Visible::No
526         } else {
527             Visible::Yes
528         }
529     }
530
531     fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
532         // `doc(hidden)` items are only completed within the defining crate.
533         self.krate != defining_crate && attrs.has_doc_hidden()
534     }
535 }
536
537 // CompletionContext construction
538 impl<'a> CompletionContext<'a> {
539     pub(super) fn new(
540         db: &'a RootDatabase,
541         position @ FilePosition { file_id, offset }: FilePosition,
542         config: &'a CompletionConfig,
543     ) -> Option<(CompletionContext<'a>, CompletionAnalysis)> {
544         let _p = profile::span("CompletionContext::new");
545         let sema = Semantics::new(db);
546
547         let original_file = sema.parse(file_id);
548
549         // Insert a fake ident to get a valid parse tree. We will use this file
550         // to determine context, though the original_file will be used for
551         // actual completion.
552         let file_with_fake_ident = {
553             let parse = db.parse(file_id);
554             let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
555             parse.reparse(&edit).tree()
556         };
557         let fake_ident_token =
558             file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
559
560         let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
561         let token = sema.descend_into_macros_single(original_token.clone());
562
563         // adjust for macro input, this still fails if there is no token written yet
564         let scope_offset = if original_token == token { offset } else { token.text_range().end() };
565         let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?;
566
567         let krate = scope.krate();
568         let module = scope.module();
569
570         let mut locals = FxHashMap::default();
571         scope.process_all_names(&mut |name, scope| {
572             if let ScopeDef::Local(local) = scope {
573                 locals.insert(name, local);
574             }
575         });
576
577         let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
578
579         let mut ctx = CompletionContext {
580             sema,
581             scope,
582             db,
583             config,
584             position,
585             original_token,
586             token,
587             krate,
588             module,
589             expected_name: None,
590             expected_type: None,
591             qualifier_ctx: Default::default(),
592             locals,
593             depth_from_crate_root,
594         };
595         let ident_ctx = ctx.expand_and_analyze(
596             original_file.syntax().clone(),
597             file_with_fake_ident.syntax().clone(),
598             offset,
599             fake_ident_token,
600         )?;
601         Some((ctx, ident_ctx))
602     }
603 }
604
605 const OP_TRAIT_LANG_NAMES: &[&str] = &[
606     "add_assign",
607     "add",
608     "bitand_assign",
609     "bitand",
610     "bitor_assign",
611     "bitor",
612     "bitxor_assign",
613     "bitxor",
614     "deref_mut",
615     "deref",
616     "div_assign",
617     "div",
618     "eq",
619     "fn_mut",
620     "fn_once",
621     "fn",
622     "index_mut",
623     "index",
624     "mul_assign",
625     "mul",
626     "neg",
627     "not",
628     "partial_ord",
629     "rem_assign",
630     "rem",
631     "shl_assign",
632     "shl",
633     "shr_assign",
634     "shr",
635     "sub",
636 ];