1 //! See [`CompletionContext`] structure.
9 use base_db
::SourceDatabaseExt
;
11 HasAttrs
, Local
, Name
, PathResolution
, ScopeDef
, Semantics
, SemanticsScope
, Type
, TypeInfo
,
14 base_db
::{FilePosition, SourceDatabase}
,
15 famous_defs
::FamousDefs
,
16 FxHashMap
, FxHashSet
, RootDatabase
,
19 ast
::{self, AttrKind, NameOrNameRef}
,
21 SyntaxKind
::{self, *}
,
22 SyntaxToken
, TextRange
, TextSize
, T
,
27 context
::analysis
::{expand_and_analyze, AnalysisResult}
,
31 const COMPLETION_MARKER
: &str = "intellijRulezz";
33 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
34 pub(crate) enum PatternRefutability
{
40 pub(crate) enum Visible
{
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
>,
54 pub(super) fn none(&self) -> bool
{
55 self.unsafe_tok
.is_none() && self.vis_node
.is_none()
59 /// The state of the path we are currently completing.
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
>,
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
,
82 impl PathCompletionCtx
{
83 pub(super) fn is_trivial_path(&self) -> bool
{
87 has_call_parens
: false,
88 has_macro_bang
: false,
89 qualified
: Qualified
::No
,
98 /// The kind of path we are completing right now.
99 #[derive(Debug, PartialEq, Eq)]
100 pub(super) enum PathKind
{
105 location
: TypeLocation
,
111 existing_derives
: ExistingDerives
,
113 /// Path in item position, that is inside an (Assoc)ItemList
118 pat_ctx
: PatternContext
,
126 pub(crate) type ExistingDerives
= FxHashSet
<hir
::Macro
>;
128 #[derive(Debug, PartialEq, Eq)]
129 pub(crate) struct AttrCtx
{
130 pub(crate) kind
: AttrKind
,
131 pub(crate) annotated_item_kind
: Option
<SyntaxKind
>,
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
150 pub(crate) in_match_guard
: bool
,
153 /// Original file ast nodes
154 #[derive(Clone, Debug, PartialEq, Eq)]
155 pub(crate) enum TypeLocation
{
157 TypeAscription(TypeAscriptionTarget
),
158 GenericArgList(Option
<ast
::GenericArgList
>),
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
>),
173 /// The kind of item list a [`PathKind::Item`] belongs to.
174 #[derive(Debug, PartialEq, Eq)]
175 pub(super) enum ItemListKind
{
179 TraitImpl(Option
<ast
::Impl
>),
185 pub(super) enum Qualified
{
189 resolution
: Option
<PathResolution
>,
190 /// How many `super` segments are present in the path
192 /// This would be None, if path is not solely made of
193 /// `super` segments, e.g.
199 /// Otherwise it should be Some(count of `super`)
200 super_chain_len
: Option
<usize>,
204 ty
: Option
<hir
::Type
>,
205 trait_
: Option
<hir
::Trait
>,
207 /// Whether the path is an absolute path
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
>,
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
,
232 /// The state of the lifetime we are completing.
234 pub(super) struct LifetimeContext
{
235 pub(super) lifetime
: Option
<ast
::Lifetime
>,
236 pub(super) kind
: LifetimeKind
,
239 /// The kind of lifetime we are completing.
241 pub(super) enum LifetimeKind
{
242 LifetimeParam { is_decl: bool, param: ast::LifetimeParam }
,
248 /// The state of the name we are completing.
250 pub(super) struct NameContext
{
252 pub(super) name
: Option
<ast
::Name
>,
253 pub(super) kind
: NameKind
,
256 /// The kind of the name we are completing.
259 pub(super) enum NameKind
{
264 IdentPat(PatternContext
),
281 /// The state of the NameRef we are completing.
283 pub(super) struct NameRefContext
{
284 /// NameRef syntax in the original file
285 pub(super) nameref
: Option
<ast
::NameRef
>,
286 pub(super) kind
: NameRefKind
,
289 /// The kind of the NameRef we are completing.
291 pub(super) enum NameRefKind
{
292 Path(PathCompletionCtx
),
293 DotAccess(DotAccess
),
294 /// Position where we are only interested in keyword completions
296 /// The record expression this nameref is a field of and whether a dot precedes the completion identifier.
299 expr
: ast
::RecordExpr
,
301 Pattern(PatternContext
),
304 /// The identifier we are currently completing.
306 pub(super) enum CompletionAnalysis
{
308 NameRef(NameRefContext
),
309 Lifetime(LifetimeContext
),
310 /// The string the cursor is currently inside
313 original
: ast
::String
,
315 expanded
: Option
<ast
::String
>,
317 /// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
320 fake_attribute_under_caret
: Option
<ast
::Attr
>,
324 /// Information about the field or method access we are completing.
326 pub(super) struct DotAccess
{
327 pub(super) receiver
: Option
<ast
::Expr
>,
328 pub(super) receiver_ty
: Option
<TypeInfo
>,
329 pub(super) kind
: DotAccessKind
,
333 pub(super) enum DotAccessKind
{
335 /// True if the receiver is an integer and there is no ident in the original file after it yet
337 receiver_is_ambiguous_float_literal
: bool
,
344 #[derive(Clone, Debug, PartialEq, Eq)]
345 pub(crate) enum ParamKind
{
347 Closure(ast
::ClosureExpr
),
350 /// `CompletionContext` is created early during completion to figure out, where
351 /// exactly is the cursor, syntax-wise.
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
,
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
,
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
>,
375 pub(super) qualifier_ctx
: QualifierCtx
,
377 pub(super) locals
: FxHashMap
<Name
, Local
>,
379 /// The module depth of the current module of the cursor position.
383 /// Here depth will be 2
384 pub(super) depth_from_crate_root
: usize,
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();
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))
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
),
404 pub(crate) fn famous_defs(&self) -> FamousDefs
<'_
, '_
> {
405 FamousDefs(&self.sema
, self.krate
)
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
{
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
,
423 ScopeDef
::GenericParam(_
)
424 | ScopeDef
::ImplSelfType(_
)
425 | ScopeDef
::AdtSelfType(_
)
428 | ScopeDef
::Unknown
=> Visible
::Yes
,
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
435 I
: hir
::HasVisibility
+ hir
::HasAttrs
+ hir
::HasCrate
+ Copy
,
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
))
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
),
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()),
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());
469 pub(crate) fn iterate_path_candidates(
472 mut cb
: impl FnMut(hir
::AssocItem
),
474 let mut seen
= FxHashSet
::default();
475 ty
.iterate_path_candidates(
478 &self.traits_in_scope(),
482 // We might iterate candidates of a trait multiple times here, so deduplicate
484 if seen
.insert(item
) {
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
) {
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
));
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
);
519 vis
: &hir
::Visibility
,
521 defining_crate
: hir
::Crate
,
523 if !vis
.is_visible_from(self.db
, self.module
.into()) {
524 if !self.config
.enable_private_editable
{
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 }
;
534 if self.is_doc_hidden(attrs
, defining_crate
) {
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()
547 // CompletionContext construction
548 impl<'a
> CompletionContext
<'a
> {
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
);
557 let original_file
= sema
.parse(file_id
);
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()
568 // always pick the token to the immediate left of the cursor, as that is what we are actually
570 let original_token
= original_file
.syntax().token_at_offset(offset
).left_biased()?
;
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() {
576 // return if no prev token before colon
577 let prev_token
= original_token
.prev_token()?
;
579 // only has a single colon
580 if prev_token
.kind() != T
![:] {
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
589 .map(|t
| t
.kind() == T
![:] || t
.kind() == T
![::])
600 expected
: (expected_type
, expected_name
),
604 } = expand_and_analyze(
606 original_file
.syntax().clone(),
607 file_with_fake_ident
.syntax().clone(),
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
)?
;
615 let krate
= scope
.krate();
616 let module
= scope
.module();
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
);
625 let depth_from_crate_root
= iter
::successors(module
.parent(db
), |m
| m
.parent(db
)).count();
627 let ctx
= CompletionContext
{
641 depth_from_crate_root
,
643 Some((ctx
, analysis
))
647 const OP_TRAIT_LANG_NAMES
: &[&str] = &[