]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/context.rs
Auto merge of #99505 - joboet:futex_once, r=thomcc
[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     #[allow(dead_code)]
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,
77 }
78
79 impl PathCompletionCtx {
80     pub(super) fn is_trivial_path(&self) -> bool {
81         matches!(
82             self,
83             PathCompletionCtx {
84                 has_call_parens: false,
85                 has_macro_bang: false,
86                 qualified: Qualified::No,
87                 parent: None,
88                 has_type_args: false,
89                 ..
90             }
91         )
92     }
93 }
94
95 /// The kind of path we are completing right now.
96 #[derive(Debug, PartialEq, Eq)]
97 pub(super) enum PathKind {
98     Expr {
99         expr_ctx: ExprCtx,
100     },
101     Type {
102         location: TypeLocation,
103     },
104     Attr {
105         attr_ctx: AttrCtx,
106     },
107     Derive {
108         existing_derives: ExistingDerives,
109     },
110     /// Path in item position, that is inside an (Assoc)ItemList
111     Item {
112         kind: ItemListKind,
113     },
114     Pat {
115         pat_ctx: PatternContext,
116     },
117     Vis {
118         has_in_token: bool,
119     },
120     Use,
121 }
122
123 pub(crate) type ExistingDerives = FxHashSet<hir::Macro>;
124
125 #[derive(Debug, PartialEq, Eq)]
126 pub(crate) struct AttrCtx {
127     pub(crate) kind: AttrKind,
128     pub(crate) annotated_item_kind: Option<SyntaxKind>,
129 }
130
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
146     /// fat arrow token
147     pub(crate) in_match_guard: bool,
148 }
149
150 /// Original file ast nodes
151 #[derive(Clone, Debug, PartialEq, Eq)]
152 pub(crate) enum TypeLocation {
153     TupleField,
154     TypeAscription(TypeAscriptionTarget),
155     GenericArgList(Option<ast::GenericArgList>),
156     TypeBound,
157     ImplTarget,
158     ImplTrait,
159     Other,
160 }
161
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>),
168 }
169
170 /// The kind of item list a [`PathKind::Item`] belongs to.
171 #[derive(Debug, PartialEq, Eq)]
172 pub(super) enum ItemListKind {
173     SourceFile,
174     Module,
175     Impl,
176     TraitImpl(Option<ast::Impl>),
177     Trait,
178     ExternBlock,
179 }
180
181 #[derive(Debug)]
182 pub(super) enum Qualified {
183     No,
184     With {
185         path: ast::Path,
186         resolution: Option<PathResolution>,
187         /// How many `super` segments are present in the path
188         ///
189         /// This would be None, if path is not solely made of
190         /// `super` segments, e.g.
191         ///
192         /// ```rust
193         ///   use super::foo;
194         /// ```
195         ///
196         /// Otherwise it should be Some(count of `super`)
197         super_chain_len: Option<usize>,
198     },
199     /// <_>::
200     TypeAnchor {
201         ty: Option<hir::Type>,
202         trait_: Option<hir::Trait>,
203     },
204     /// Whether the path is an absolute path
205     Absolute,
206 }
207
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>,
220 }
221
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,
227 }
228
229 /// The state of the lifetime we are completing.
230 #[derive(Debug)]
231 pub(super) struct LifetimeContext {
232     pub(super) lifetime: Option<ast::Lifetime>,
233     pub(super) kind: LifetimeKind,
234 }
235
236 /// The kind of lifetime we are completing.
237 #[derive(Debug)]
238 pub(super) enum LifetimeKind {
239     LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
240     Lifetime,
241     LabelRef,
242     LabelDef,
243 }
244
245 /// The state of the name we are completing.
246 #[derive(Debug)]
247 pub(super) struct NameContext {
248     #[allow(dead_code)]
249     pub(super) name: Option<ast::Name>,
250     pub(super) kind: NameKind,
251 }
252
253 /// The kind of the name we are completing.
254 #[derive(Debug)]
255 #[allow(dead_code)]
256 pub(super) enum NameKind {
257     Const,
258     ConstParam,
259     Enum,
260     Function,
261     IdentPat(PatternContext),
262     MacroDef,
263     MacroRules,
264     /// Fake node
265     Module(ast::Module),
266     RecordField,
267     Rename,
268     SelfParam,
269     Static,
270     Struct,
271     Trait,
272     TypeAlias,
273     TypeParam,
274     Union,
275     Variant,
276 }
277
278 /// The state of the NameRef we are completing.
279 #[derive(Debug)]
280 pub(super) struct NameRefContext {
281     /// NameRef syntax in the original file
282     pub(super) nameref: Option<ast::NameRef>,
283     pub(super) kind: NameRefKind,
284 }
285
286 /// The kind of the NameRef we are completing.
287 #[derive(Debug)]
288 pub(super) enum NameRefKind {
289     Path(PathCompletionCtx),
290     DotAccess(DotAccess),
291     /// Position where we are only interested in keyword completions
292     Keyword(ast::Item),
293     /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
294     RecordExpr {
295         dot_prefix: bool,
296         expr: ast::RecordExpr,
297     },
298     Pattern(PatternContext),
299 }
300
301 /// The identifier we are currently completing.
302 #[derive(Debug)]
303 pub(super) enum CompletionAnalysis {
304     Name(NameContext),
305     NameRef(NameRefContext),
306     Lifetime(LifetimeContext),
307     /// The string the cursor is currently inside
308     String {
309         /// original token
310         original: ast::String,
311         /// fake token
312         expanded: Option<ast::String>,
313     },
314     /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
315     UnexpandedAttrTT {
316         colon_prefix: bool,
317         fake_attribute_under_caret: Option<ast::Attr>,
318     },
319 }
320
321 /// Information about the field or method access we are completing.
322 #[derive(Debug)]
323 pub(super) struct DotAccess {
324     pub(super) receiver: Option<ast::Expr>,
325     pub(super) receiver_ty: Option<TypeInfo>,
326     pub(super) kind: DotAccessKind,
327 }
328
329 #[derive(Debug)]
330 pub(super) enum DotAccessKind {
331     Field {
332         /// True if the receiver is an integer and there is no ident in the original file after it yet
333         /// like `0.$0`
334         receiver_is_ambiguous_float_literal: bool,
335     },
336     Method {
337         has_parens: bool,
338     },
339 }
340
341 #[derive(Clone, Debug, PartialEq, Eq)]
342 pub(crate) enum ParamKind {
343     Function(ast::Fn),
344     Closure(ast::ClosureExpr),
345 }
346
347 /// `CompletionContext` is created early during completion to figure out, where
348 /// exactly is the cursor, syntax-wise.
349 #[derive(Debug)]
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,
356
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,
365
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>,
371
372     pub(super) qualifier_ctx: QualifierCtx,
373
374     pub(super) locals: FxHashMap<Name, Local>,
375
376     /// The module depth of the current module of the cursor position.
377     /// - crate-root
378     ///  - mod foo
379     ///   - mod bar
380     /// Here depth will be 2
381     pub(super) depth_from_crate_root: usize,
382 }
383
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();
389         match kind {
390             CHAR => {
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))
394             }
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),
398         }
399     }
400
401     pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
402         FamousDefs(&self.sema, self.krate)
403     }
404
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 {
407         match item {
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,
419             },
420             ScopeDef::GenericParam(_)
421             | ScopeDef::ImplSelfType(_)
422             | ScopeDef::AdtSelfType(_)
423             | ScopeDef::Local(_)
424             | ScopeDef::Label(_)
425             | ScopeDef::Unknown => Visible::Yes,
426         }
427     }
428
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
431     where
432         I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
433     {
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))
437     }
438
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),
445             _ => false,
446         }
447     }
448
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()),
453             None => false,
454         }
455     }
456
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());
462         }
463         traits_in_scope
464     }
465
466     pub(crate) fn iterate_path_candidates(
467         &self,
468         ty: &hir::Type,
469         mut cb: impl FnMut(hir::AssocItem),
470     ) {
471         let mut seen = FxHashSet::default();
472         ty.iterate_path_candidates(
473             self.db,
474             &self.scope,
475             &self.traits_in_scope(),
476             Some(self.module),
477             None,
478             |item| {
479                 // We might iterate candidates of a trait multiple times here, so deduplicate
480                 // them.
481                 if seen.insert(item) {
482                     cb(item)
483                 }
484                 None::<()>
485             },
486         );
487     }
488
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) {
494                 return;
495             }
496
497             f(name, def);
498         });
499     }
500
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));
504     }
505
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);
509         }
510
511         false
512     }
513
514     fn is_visible_impl(
515         &self,
516         vis: &hir::Visibility,
517         attrs: &hir::Attrs,
518         defining_crate: hir::Crate,
519     ) -> Visible {
520         if !vis.is_visible_from(self.db, self.module.into()) {
521             if !self.config.enable_private_editable {
522                 return Visible::No;
523             }
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 };
529         }
530
531         if self.is_doc_hidden(attrs, defining_crate) {
532             Visible::No
533         } else {
534             Visible::Yes
535         }
536     }
537
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()
541     }
542 }
543
544 // CompletionContext construction
545 impl<'a> CompletionContext<'a> {
546     pub(super) fn new(
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);
553
554         let original_file = sema.parse(file_id);
555
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()
563         };
564         let fake_ident_token =
565             file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
566
567         let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
568         let token = sema.descend_into_macros_single(original_token.clone());
569
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)?;
573
574         let krate = scope.krate();
575         let module = scope.module();
576
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);
581             }
582         });
583
584         let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
585
586         let mut ctx = CompletionContext {
587             sema,
588             scope,
589             db,
590             config,
591             position,
592             original_token,
593             token,
594             krate,
595             module,
596             expected_name: None,
597             expected_type: None,
598             qualifier_ctx: Default::default(),
599             locals,
600             depth_from_crate_root,
601         };
602         let ident_ctx = ctx.expand_and_analyze(
603             original_file.syntax().clone(),
604             file_with_fake_ident.syntax().clone(),
605             offset,
606             fake_ident_token,
607         )?;
608         Some((ctx, ident_ctx))
609     }
610 }
611
612 const OP_TRAIT_LANG_NAMES: &[&str] = &[
613     "add_assign",
614     "add",
615     "bitand_assign",
616     "bitand",
617     "bitor_assign",
618     "bitor",
619     "bitxor_assign",
620     "bitxor",
621     "deref_mut",
622     "deref",
623     "div_assign",
624     "div",
625     "eq",
626     "fn_mut",
627     "fn_once",
628     "fn",
629     "index_mut",
630     "index",
631     "mul_assign",
632     "mul",
633     "neg",
634     "not",
635     "partial_ord",
636     "rem_assign",
637     "rem",
638     "shl_assign",
639     "shl",
640     "shr_assign",
641     "shr",
642     "sub",
643 ];