]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/context.rs
New upstream version 1.68.2+dfsg1
[rustc.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, T,
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 // try to skip completions on path with invalid colons
573 // this approach works in normal path and inside token tree
574 match original_token.kind() {
575 T![:] => {
576 // return if no prev token before colon
577 let prev_token = original_token.prev_token()?;
578
579 // only has a single colon
580 if prev_token.kind() != T![:] {
581 return None;
582 }
583
584 // has 3 colon or 2 coloncolon in a row
585 // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205
586 // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751
587 if prev_token
588 .prev_token()
589 .map(|t| t.kind() == T![:] || t.kind() == T![::])
590 .unwrap_or(false)
591 {
592 return None;
593 }
594 }
595 _ => {}
596 }
597
598 let AnalysisResult {
599 analysis,
600 expected: (expected_type, expected_name),
601 qualifier_ctx,
602 token,
603 offset,
604 } = expand_and_analyze(
605 &sema,
606 original_file.syntax().clone(),
607 file_with_fake_ident.syntax().clone(),
608 offset,
609 &original_token,
610 )?;
611
612 // adjust for macro input, this still fails if there is no token written yet
613 let scope = sema.scope_at_offset(&token.parent()?, offset)?;
614
615 let krate = scope.krate();
616 let module = scope.module();
617
618 let mut locals = FxHashMap::default();
619 scope.process_all_names(&mut |name, scope| {
620 if let ScopeDef::Local(local) = scope {
621 locals.insert(name, local);
622 }
623 });
624
625 let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
626
627 let ctx = CompletionContext {
628 sema,
629 scope,
630 db,
631 config,
632 position,
633 original_token,
634 token,
635 krate,
636 module,
637 expected_name,
638 expected_type,
639 qualifier_ctx,
640 locals,
641 depth_from_crate_root,
642 };
643 Some((ctx, analysis))
644 }
645 }
646
647 const OP_TRAIT_LANG_NAMES: &[&str] = &[
648 "add_assign",
649 "add",
650 "bitand_assign",
651 "bitand",
652 "bitor_assign",
653 "bitor",
654 "bitxor_assign",
655 "bitxor",
656 "deref_mut",
657 "deref",
658 "div_assign",
659 "div",
660 "eq",
661 "fn_mut",
662 "fn_once",
663 "fn",
664 "index_mut",
665 "index",
666 "mul_assign",
667 "mul",
668 "neg",
669 "not",
670 "partial_ord",
671 "rem_assign",
672 "rem",
673 "shl_assign",
674 "shl",
675 "shr_assign",
676 "shr",
677 "sub",
678 ];