2020-10-18 10:09:00 +00:00
|
|
|
//! See `CompletionContext` structure.
|
2019-09-30 08:58:53 +00:00
|
|
|
|
2022-01-05 20:12:36 +00:00
|
|
|
use std::iter;
|
|
|
|
|
2021-07-23 17:57:16 +00:00
|
|
|
use base_db::SourceDatabaseExt;
|
2022-02-02 12:35:46 +00:00
|
|
|
use hir::{
|
|
|
|
HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
|
|
|
|
};
|
2021-05-26 19:09:27 +00:00
|
|
|
use ide_db::{
|
2021-11-22 12:19:46 +00:00
|
|
|
active_parameter::ActiveParameter,
|
2021-05-26 19:09:27 +00:00
|
|
|
base_db::{FilePosition, SourceDatabase},
|
2022-03-06 18:01:30 +00:00
|
|
|
famous_defs::FamousDefs,
|
2022-04-25 16:51:59 +00:00
|
|
|
FxHashMap, FxHashSet, RootDatabase,
|
2021-05-26 19:09:27 +00:00
|
|
|
};
|
2020-08-12 16:26:51 +00:00
|
|
|
use syntax::{
|
2022-02-02 12:35:46 +00:00
|
|
|
algo::{find_node_at_offset, non_trivia_sibling},
|
2022-05-07 11:46:43 +00:00
|
|
|
ast::{self, AttrKind, HasArgList, HasName, NameOrNameRef},
|
2022-06-03 14:25:37 +00:00
|
|
|
match_ast, AstNode, AstToken, Direction, NodeOrToken,
|
2021-05-26 19:09:27 +00:00
|
|
|
SyntaxKind::{self, *},
|
|
|
|
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
|
2019-01-08 19:33:36 +00:00
|
|
|
};
|
2020-08-12 15:03:06 +00:00
|
|
|
use text_edit::Indel;
|
2019-01-08 19:33:36 +00:00
|
|
|
|
2020-08-13 16:06:14 +00:00
|
|
|
use crate::{
|
2020-10-18 10:09:00 +00:00
|
|
|
patterns::{
|
2022-06-03 14:25:37 +00:00
|
|
|
determine_location, is_in_loop_body, is_in_token_of_for_loop, previous_token,
|
|
|
|
ImmediateLocation,
|
2020-08-13 16:06:14 +00:00
|
|
|
},
|
2020-10-18 10:09:00 +00:00
|
|
|
CompletionConfig,
|
2020-06-11 12:16:35 +00:00
|
|
|
};
|
2019-04-22 13:04:56 +00:00
|
|
|
|
2022-01-31 10:56:42 +00:00
|
|
|
const COMPLETION_MARKER: &str = "intellijRulezz";
|
|
|
|
|
2021-05-26 21:46:00 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub(crate) enum PatternRefutability {
|
2021-05-26 20:39:47 +00:00
|
|
|
Refutable,
|
|
|
|
Irrefutable,
|
|
|
|
}
|
2022-02-02 15:01:46 +00:00
|
|
|
|
2022-02-02 11:05:21 +00:00
|
|
|
pub(crate) enum Visible {
|
2022-02-02 01:05:49 +00:00
|
|
|
Yes,
|
|
|
|
Editable,
|
|
|
|
No,
|
|
|
|
}
|
2021-05-26 20:39:47 +00:00
|
|
|
|
2022-06-03 18:49:25 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2021-06-08 14:50:10 +00:00
|
|
|
pub(super) enum PathKind {
|
2022-05-06 13:44:41 +00:00
|
|
|
Expr {
|
|
|
|
in_block_expr: bool,
|
|
|
|
in_loop_body: bool,
|
2022-06-03 14:25:37 +00:00
|
|
|
after_if_expr: bool,
|
2022-06-03 18:49:25 +00:00
|
|
|
ref_expr_parent: Option<ast::RefExpr>,
|
2022-06-17 08:45:19 +00:00
|
|
|
is_func_update: Option<ast::RecordExpr>,
|
2022-05-06 13:44:41 +00:00
|
|
|
},
|
2022-06-03 13:41:51 +00:00
|
|
|
Type {
|
|
|
|
in_tuple_struct: bool,
|
|
|
|
},
|
2022-05-05 08:34:57 +00:00
|
|
|
Attr {
|
|
|
|
kind: AttrKind,
|
|
|
|
annotated_item_kind: Option<SyntaxKind>,
|
|
|
|
},
|
2022-03-10 19:53:50 +00:00
|
|
|
Derive,
|
2022-05-05 08:34:57 +00:00
|
|
|
/// Path in item position, that is inside an (Assoc)ItemList
|
2022-05-06 13:44:41 +00:00
|
|
|
Item {
|
|
|
|
kind: ItemListKind,
|
|
|
|
},
|
2021-12-06 14:51:33 +00:00
|
|
|
Pat,
|
2022-05-05 08:34:57 +00:00
|
|
|
Vis {
|
|
|
|
has_in_token: bool,
|
|
|
|
},
|
2021-12-06 14:51:33 +00:00
|
|
|
Use,
|
2021-06-08 14:50:10 +00:00
|
|
|
}
|
|
|
|
|
2022-05-06 13:44:41 +00:00
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub(super) enum ItemListKind {
|
|
|
|
SourceFile,
|
|
|
|
Module,
|
|
|
|
Impl,
|
2022-06-03 13:15:21 +00:00
|
|
|
TraitImpl,
|
2022-05-06 13:44:41 +00:00
|
|
|
Trait,
|
|
|
|
ExternBlock,
|
|
|
|
}
|
|
|
|
|
2022-05-30 14:01:17 +00:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub(super) struct QualifierCtx {
|
|
|
|
pub(super) unsafe_tok: Option<SyntaxToken>,
|
|
|
|
pub(super) vis_node: Option<ast::Visibility>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl QualifierCtx {
|
|
|
|
pub(super) fn none(&self) -> bool {
|
|
|
|
self.unsafe_tok.is_none() && self.vis_node.is_none()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-06 18:02:26 +00:00
|
|
|
#[derive(Debug)]
|
2022-02-02 15:01:46 +00:00
|
|
|
pub(crate) struct PathCompletionCtx {
|
2022-03-16 12:41:35 +00:00
|
|
|
/// If this is a call with () already there (or {} in case of record patterns)
|
2022-03-12 15:10:30 +00:00
|
|
|
pub(super) has_call_parens: bool,
|
2022-05-05 08:34:57 +00:00
|
|
|
/// If this has a macro call bang !
|
|
|
|
pub(super) has_macro_bang: bool,
|
2022-02-02 15:01:46 +00:00
|
|
|
/// Whether this path stars with a `::`.
|
|
|
|
pub(super) is_absolute_path: bool,
|
|
|
|
/// The qualifier of the current path if it exists.
|
|
|
|
pub(super) qualifier: Option<PathQualifierCtx>,
|
2022-05-05 13:49:03 +00:00
|
|
|
/// The parent of the path we are completing.
|
|
|
|
pub(super) parent: Option<ast::Path>,
|
2022-05-06 10:04:41 +00:00
|
|
|
pub(super) kind: PathKind,
|
2021-06-08 14:50:10 +00:00
|
|
|
/// Whether the path segment has type args or not.
|
2021-06-07 10:29:46 +00:00
|
|
|
pub(super) has_type_args: bool,
|
2021-06-06 18:02:26 +00:00
|
|
|
}
|
|
|
|
|
2022-06-03 17:48:55 +00:00
|
|
|
impl PathCompletionCtx {
|
2022-06-03 17:51:08 +00:00
|
|
|
pub(super) fn is_trivial_path(&self) -> bool {
|
2022-06-03 17:48:55 +00:00
|
|
|
matches!(
|
|
|
|
self,
|
|
|
|
PathCompletionCtx {
|
|
|
|
has_call_parens: false,
|
|
|
|
has_macro_bang: false,
|
|
|
|
is_absolute_path: false,
|
|
|
|
qualifier: None,
|
|
|
|
parent: None,
|
|
|
|
has_type_args: false,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 15:01:46 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(crate) struct PathQualifierCtx {
|
2022-02-02 17:18:08 +00:00
|
|
|
pub(crate) path: ast::Path,
|
|
|
|
pub(crate) resolution: Option<PathResolution>,
|
2022-02-02 15:01:46 +00:00
|
|
|
/// Whether this path consists solely of `super` segments
|
2022-02-02 17:18:08 +00:00
|
|
|
pub(crate) is_super_chain: bool,
|
2022-02-02 15:01:46 +00:00
|
|
|
/// Whether the qualifier comes from a use tree parent or not
|
2022-02-02 17:18:08 +00:00
|
|
|
pub(crate) use_tree_parent: bool,
|
2022-05-05 20:21:42 +00:00
|
|
|
/// <_>
|
|
|
|
pub(crate) is_infer_qualifier: bool,
|
2022-02-02 15:01:46 +00:00
|
|
|
}
|
|
|
|
|
2021-08-14 17:06:35 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct PatternContext {
|
|
|
|
pub(super) refutability: PatternRefutability,
|
2022-01-31 10:56:42 +00:00
|
|
|
pub(super) param_ctx: Option<(ast::ParamList, ast::Param, ParamKind)>,
|
2021-11-05 17:39:36 +00:00
|
|
|
pub(super) has_type_ascription: bool,
|
2022-02-02 17:09:30 +00:00
|
|
|
pub(super) parent_pat: Option<ast::Pat>,
|
|
|
|
pub(super) ref_token: Option<SyntaxToken>,
|
|
|
|
pub(super) mut_token: Option<SyntaxToken>,
|
2022-05-24 11:24:36 +00:00
|
|
|
/// The record pattern this name or ref is a field of
|
|
|
|
pub(super) record_pat: Option<ast::RecordPat>,
|
2021-08-14 17:06:35 +00:00
|
|
|
}
|
|
|
|
|
2021-10-16 21:56:43 +00:00
|
|
|
#[derive(Debug)]
|
2022-05-07 12:16:03 +00:00
|
|
|
pub(super) struct LifetimeContext {
|
|
|
|
pub(super) lifetime: Option<ast::Lifetime>,
|
|
|
|
pub(super) kind: LifetimeKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2022-05-07 13:08:33 +00:00
|
|
|
pub(super) enum LifetimeKind {
|
2022-03-12 00:59:01 +00:00
|
|
|
LifetimeParam { is_decl: bool, param: ast::LifetimeParam },
|
2021-10-16 21:56:43 +00:00
|
|
|
Lifetime,
|
|
|
|
LabelRef,
|
|
|
|
LabelDef,
|
|
|
|
}
|
|
|
|
|
2022-05-07 12:16:03 +00:00
|
|
|
#[derive(Debug)]
|
2022-05-07 13:08:33 +00:00
|
|
|
pub(super) struct NameContext {
|
2022-05-07 12:16:03 +00:00
|
|
|
#[allow(dead_code)]
|
|
|
|
pub(super) name: Option<ast::Name>,
|
|
|
|
pub(super) kind: NameKind,
|
|
|
|
}
|
|
|
|
|
2022-04-17 19:53:58 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
2022-05-07 12:16:03 +00:00
|
|
|
pub(super) enum NameKind {
|
2022-04-17 19:53:58 +00:00
|
|
|
Const,
|
|
|
|
ConstParam,
|
|
|
|
Enum,
|
|
|
|
Function,
|
|
|
|
IdentPat,
|
|
|
|
MacroDef,
|
|
|
|
MacroRules,
|
|
|
|
/// Fake node
|
|
|
|
Module(ast::Module),
|
|
|
|
RecordField,
|
|
|
|
Rename,
|
|
|
|
SelfParam,
|
|
|
|
Static,
|
|
|
|
Struct,
|
|
|
|
Trait,
|
|
|
|
TypeAlias,
|
|
|
|
TypeParam,
|
|
|
|
Union,
|
|
|
|
Variant,
|
|
|
|
}
|
|
|
|
|
2022-05-07 11:46:43 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct NameRefContext {
|
2022-05-07 12:16:03 +00:00
|
|
|
/// NameRef syntax in the original file
|
|
|
|
pub(super) nameref: Option<ast::NameRef>,
|
2022-06-17 08:45:19 +00:00
|
|
|
pub(super) kind: Option<NameRefKind>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) enum NameRefKind {
|
|
|
|
Path(PathCompletionCtx),
|
|
|
|
DotAccess(DotAccess),
|
2022-06-03 14:11:26 +00:00
|
|
|
/// Position where we are only interested in keyword completions
|
2022-06-17 08:45:19 +00:00
|
|
|
Keyword(ast::Item),
|
2022-05-24 11:24:36 +00:00
|
|
|
/// The record expression this nameref is a field of
|
2022-06-17 08:45:19 +00:00
|
|
|
RecordExpr(ast::RecordExpr),
|
2022-05-07 11:46:43 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 13:05:43 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) enum IdentContext {
|
|
|
|
Name(NameContext),
|
|
|
|
NameRef(NameRefContext),
|
|
|
|
Lifetime(LifetimeContext),
|
|
|
|
/// Original token, fake token
|
|
|
|
String {
|
|
|
|
original: ast::String,
|
|
|
|
expanded: Option<ast::String>,
|
|
|
|
},
|
|
|
|
UnexpandedAttrTT {
|
|
|
|
fake_attribute_under_caret: Option<ast::Attr>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-05-07 11:46:43 +00:00
|
|
|
#[derive(Debug)]
|
2022-05-23 15:40:41 +00:00
|
|
|
pub(super) struct DotAccess {
|
|
|
|
pub(super) receiver: Option<ast::Expr>,
|
|
|
|
pub(super) receiver_ty: Option<TypeInfo>,
|
|
|
|
pub(super) kind: DotAccessKind,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) enum DotAccessKind {
|
2022-05-07 11:46:43 +00:00
|
|
|
Field {
|
|
|
|
/// True if the receiver is an integer and there is no ident in the original file after it yet
|
|
|
|
/// like `0.$0`
|
|
|
|
receiver_is_ambiguous_float_literal: bool,
|
|
|
|
},
|
|
|
|
Method {
|
|
|
|
has_parens: bool,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-01-31 10:56:42 +00:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2021-08-14 16:18:18 +00:00
|
|
|
pub(crate) enum ParamKind {
|
2022-01-31 10:56:42 +00:00
|
|
|
Function(ast::Fn),
|
|
|
|
Closure(ast::ClosureExpr),
|
2021-08-14 16:18:18 +00:00
|
|
|
}
|
2022-01-06 12:31:36 +00:00
|
|
|
|
2019-01-08 19:33:36 +00:00
|
|
|
/// `CompletionContext` is created early during completion to figure out, where
|
|
|
|
/// exactly is the cursor, syntax-wise.
|
|
|
|
#[derive(Debug)]
|
2019-01-19 14:02:50 +00:00
|
|
|
pub(crate) struct CompletionContext<'a> {
|
2020-02-18 17:35:10 +00:00
|
|
|
pub(super) sema: Semantics<'a, RootDatabase>,
|
2020-07-10 23:26:24 +00:00
|
|
|
pub(super) scope: SemanticsScope<'a>,
|
2020-02-06 11:52:32 +00:00
|
|
|
pub(super) db: &'a RootDatabase,
|
2020-03-31 14:02:55 +00:00
|
|
|
pub(super) config: &'a CompletionConfig,
|
2020-08-11 06:54:33 +00:00
|
|
|
pub(super) position: FilePosition,
|
2022-03-12 00:59:01 +00:00
|
|
|
|
2020-03-07 14:27:03 +00:00
|
|
|
/// The token before the cursor, in the original file.
|
|
|
|
pub(super) original_token: SyntaxToken,
|
|
|
|
/// The token before the cursor, in the macro-expanded file.
|
2019-07-19 09:56:47 +00:00
|
|
|
pub(super) token: SyntaxToken,
|
2021-10-11 19:49:39 +00:00
|
|
|
/// The crate of the current file.
|
2022-03-31 09:12:08 +00:00
|
|
|
pub(super) krate: hir::Crate,
|
2022-02-02 11:05:21 +00:00
|
|
|
/// The module of the `scope`.
|
2022-03-31 09:12:08 +00:00
|
|
|
pub(super) module: hir::Module,
|
2022-04-18 14:54:13 +00:00
|
|
|
|
|
|
|
/// The expected name of what we are completing.
|
|
|
|
/// This is usually the parameter name of the function argument we are completing.
|
2021-03-20 22:22:09 +00:00
|
|
|
pub(super) expected_name: Option<NameOrNameRef>,
|
2022-04-18 14:54:13 +00:00
|
|
|
/// The expected type of what we are completing.
|
2020-04-26 08:54:08 +00:00
|
|
|
pub(super) expected_type: Option<Type>,
|
2021-05-27 00:54:49 +00:00
|
|
|
|
|
|
|
/// The parent function of the cursor position if it exists.
|
|
|
|
pub(super) function_def: Option<ast::Fn>,
|
2021-05-26 20:39:47 +00:00
|
|
|
/// The parent impl of the cursor position if it exists.
|
2020-07-30 16:28:28 +00:00
|
|
|
pub(super) impl_def: Option<ast::Impl>,
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Are we completing inside a let statement with a missing semicolon?
|
2021-10-17 08:59:06 +00:00
|
|
|
pub(super) incomplete_let: bool,
|
2021-05-26 20:39:47 +00:00
|
|
|
|
2021-05-27 00:54:49 +00:00
|
|
|
pub(super) completion_location: Option<ImmediateLocation>,
|
2021-06-07 17:06:03 +00:00
|
|
|
pub(super) previous_token: Option<SyntaxToken>,
|
2021-05-27 00:54:49 +00:00
|
|
|
|
2022-05-07 13:05:43 +00:00
|
|
|
pub(super) ident_ctx: IdentContext,
|
2022-05-07 12:16:03 +00:00
|
|
|
|
2021-08-14 17:06:35 +00:00
|
|
|
pub(super) pattern_ctx: Option<PatternContext>,
|
2022-05-30 14:01:17 +00:00
|
|
|
pub(super) qualifier_ctx: QualifierCtx,
|
2022-01-06 12:31:36 +00:00
|
|
|
|
2022-03-10 20:22:13 +00:00
|
|
|
pub(super) existing_derives: FxHashSet<hir::Macro>,
|
|
|
|
|
2022-03-14 19:36:35 +00:00
|
|
|
pub(super) locals: FxHashMap<Name, Local>,
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
2021-05-27 02:34:21 +00:00
|
|
|
|
2019-01-08 19:33:36 +00:00
|
|
|
impl<'a> CompletionContext<'a> {
|
2020-10-03 01:00:09 +00:00
|
|
|
/// The range of the identifier that is being completed.
|
2019-01-20 04:02:00 +00:00
|
|
|
pub(crate) fn source_range(&self) -> TextRange {
|
2020-03-07 14:27:03 +00:00
|
|
|
// check kind of macro-expanded token, but use range of original token
|
2020-10-03 01:00:09 +00:00
|
|
|
let kind = self.token.kind();
|
2022-03-12 00:59:01 +00:00
|
|
|
match kind {
|
|
|
|
CHAR => {
|
|
|
|
// assume we are completing a lifetime but the user has only typed the '
|
|
|
|
cov_mark::hit!(completes_if_lifetime_without_idents);
|
|
|
|
TextRange::at(self.original_token.text_range().start(), TextSize::from(1))
|
|
|
|
}
|
|
|
|
IDENT | LIFETIME_IDENT | UNDERSCORE => self.original_token.text_range(),
|
|
|
|
_ if kind.is_keyword() => self.original_token.text_range(),
|
|
|
|
_ => TextRange::empty(self.position.offset),
|
2019-01-20 05:34:16 +00:00
|
|
|
}
|
2019-01-19 14:02:50 +00:00
|
|
|
}
|
|
|
|
|
2021-05-26 19:09:27 +00:00
|
|
|
pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
|
|
|
|
self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
|
|
|
|
}
|
|
|
|
|
2021-12-22 01:25:38 +00:00
|
|
|
pub(crate) fn famous_defs(&self) -> FamousDefs {
|
|
|
|
FamousDefs(&self.sema, self.krate)
|
|
|
|
}
|
|
|
|
|
2022-05-07 13:05:43 +00:00
|
|
|
pub(super) fn nameref_ctx(&self) -> Option<&NameRefContext> {
|
|
|
|
match &self.ident_ctx {
|
|
|
|
IdentContext::NameRef(it) => Some(it),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn name_ctx(&self) -> Option<&NameContext> {
|
|
|
|
match &self.ident_ctx {
|
|
|
|
IdentContext::Name(it) => Some(it),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn lifetime_ctx(&self) -> Option<&LifetimeContext> {
|
|
|
|
match &self.ident_ctx {
|
|
|
|
IdentContext::Lifetime(it) => Some(it),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-16 21:56:43 +00:00
|
|
|
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
|
2022-05-07 13:05:43 +00:00
|
|
|
match self.nameref_ctx() {
|
2022-06-17 08:45:19 +00:00
|
|
|
Some(NameRefContext {
|
|
|
|
kind: Some(NameRefKind::DotAccess(DotAccess { receiver, .. })),
|
|
|
|
..
|
|
|
|
}) => receiver.as_ref(),
|
2021-10-16 21:56:43 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
2021-05-27 00:54:49 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 13:21:18 +00:00
|
|
|
pub(crate) fn has_dot_receiver(&self) -> bool {
|
2022-05-07 11:46:43 +00:00
|
|
|
self.dot_receiver().is_some()
|
2021-06-02 13:21:18 +00:00
|
|
|
}
|
|
|
|
|
2022-05-10 12:31:28 +00:00
|
|
|
// FIXME: This shouldn't exist
|
2021-06-16 13:08:44 +00:00
|
|
|
pub(crate) fn expects_generic_arg(&self) -> bool {
|
|
|
|
matches!(self.completion_location, Some(ImmediateLocation::GenericArgList(_)))
|
|
|
|
}
|
|
|
|
|
2022-05-07 11:46:43 +00:00
|
|
|
pub(crate) fn path_context(&self) -> Option<&PathCompletionCtx> {
|
2022-06-17 08:45:19 +00:00
|
|
|
self.nameref_ctx().and_then(|ctx| match &ctx.kind {
|
|
|
|
Some(NameRefKind::Path(path)) => Some(path),
|
|
|
|
_ => None,
|
|
|
|
})
|
2022-05-07 11:46:43 +00:00
|
|
|
}
|
|
|
|
|
2021-06-07 10:29:46 +00:00
|
|
|
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
|
2022-05-07 11:46:43 +00:00
|
|
|
self.path_context().and_then(|it| it.qualifier.as_ref().map(|it| &it.path))
|
2021-06-07 10:29:46 +00:00
|
|
|
}
|
|
|
|
|
2021-07-23 13:36:43 +00:00
|
|
|
/// Checks if an item is visible and not `doc(hidden)` at the completion site.
|
2022-02-02 01:05:49 +00:00
|
|
|
pub(crate) fn is_visible<I>(&self, item: &I) -> Visible
|
2021-07-23 13:36:43 +00:00
|
|
|
where
|
|
|
|
I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy,
|
|
|
|
{
|
|
|
|
self.is_visible_impl(&item.visibility(self.db), &item.attrs(self.db), item.krate(self.db))
|
|
|
|
}
|
|
|
|
|
2021-12-21 13:07:48 +00:00
|
|
|
pub(crate) fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
|
2021-07-28 13:59:02 +00:00
|
|
|
if let (Some(attrs), Some(krate)) = (scope_def.attrs(self.db), scope_def.krate(self.db)) {
|
|
|
|
return self.is_doc_hidden(&attrs, krate);
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2021-10-11 19:49:39 +00:00
|
|
|
/// Check if an item is `#[doc(hidden)]`.
|
2021-07-28 17:22:59 +00:00
|
|
|
pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
|
|
|
|
let attrs = item.attrs(self.db);
|
|
|
|
let krate = item.krate(self.db);
|
|
|
|
match (attrs, krate) {
|
|
|
|
(Some(attrs), Some(krate)) => self.is_doc_hidden(&attrs, krate),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2022-01-06 12:31:36 +00:00
|
|
|
/// Whether the given trait is an operator trait or not.
|
|
|
|
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
|
2022-01-11 09:07:16 +00:00
|
|
|
match trait_.attrs(self.db).lang() {
|
|
|
|
Some(lang) => OP_TRAIT_LANG_NAMES.contains(&lang.as_str()),
|
|
|
|
None => false,
|
|
|
|
}
|
2022-01-06 12:31:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 20:21:42 +00:00
|
|
|
/// Returns the traits in scope, with the [`Drop`] trait removed.
|
|
|
|
pub(crate) fn traits_in_scope(&self) -> hir::VisibleTraits {
|
|
|
|
let mut traits_in_scope = self.scope.visible_traits();
|
|
|
|
if let Some(drop) = self.famous_defs().core_ops_Drop() {
|
|
|
|
traits_in_scope.0.remove(&drop.into());
|
|
|
|
}
|
|
|
|
traits_in_scope
|
|
|
|
}
|
|
|
|
|
2021-07-28 13:59:02 +00:00
|
|
|
/// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
|
|
|
|
pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
|
2021-12-21 17:25:50 +00:00
|
|
|
let _p = profile::span("CompletionContext::process_all_names");
|
2021-07-28 13:59:02 +00:00
|
|
|
self.scope.process_all_names(&mut |name, def| {
|
2021-12-21 13:07:48 +00:00
|
|
|
if self.is_scope_def_hidden(def) {
|
2021-07-28 13:59:02 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
f(name, def);
|
2022-03-14 19:36:35 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
|
|
|
|
let _p = profile::span("CompletionContext::process_all_names_raw");
|
|
|
|
self.scope.process_all_names(&mut |name, def| f(name, def));
|
2021-07-28 13:59:02 +00:00
|
|
|
}
|
|
|
|
|
2021-07-23 13:36:43 +00:00
|
|
|
fn is_visible_impl(
|
|
|
|
&self,
|
|
|
|
vis: &hir::Visibility,
|
|
|
|
attrs: &hir::Attrs,
|
|
|
|
defining_crate: hir::Crate,
|
2022-02-02 01:05:49 +00:00
|
|
|
) -> Visible {
|
2022-03-31 09:12:08 +00:00
|
|
|
if !vis.is_visible_from(self.db, self.module.into()) {
|
2022-02-23 15:02:54 +00:00
|
|
|
if !self.config.enable_private_editable {
|
|
|
|
return Visible::No;
|
|
|
|
}
|
2021-07-23 17:57:16 +00:00
|
|
|
// If the definition location is editable, also show private items
|
|
|
|
let root_file = defining_crate.root_file(self.db);
|
|
|
|
let source_root_id = self.db.file_source_root(root_file);
|
|
|
|
let is_editable = !self.db.source_root(source_root_id).is_library;
|
2022-02-02 01:05:49 +00:00
|
|
|
return if is_editable { Visible::Editable } else { Visible::No };
|
2021-07-23 13:36:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-02 01:05:49 +00:00
|
|
|
if self.is_doc_hidden(attrs, defining_crate) {
|
|
|
|
Visible::No
|
|
|
|
} else {
|
|
|
|
Visible::Yes
|
|
|
|
}
|
2021-07-28 13:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool {
|
2022-03-31 09:12:08 +00:00
|
|
|
// `doc(hidden)` items are only completed within the defining crate.
|
|
|
|
self.krate != defining_crate && attrs.has_doc_hidden()
|
2021-07-23 13:36:43 +00:00
|
|
|
}
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CompletionContext construction
|
|
|
|
impl<'a> CompletionContext<'a> {
|
|
|
|
pub(super) fn new(
|
|
|
|
db: &'a RootDatabase,
|
2021-11-10 16:33:35 +00:00
|
|
|
position @ FilePosition { file_id, offset }: FilePosition,
|
2021-10-17 09:15:56 +00:00
|
|
|
config: &'a CompletionConfig,
|
|
|
|
) -> Option<CompletionContext<'a>> {
|
2021-11-08 18:41:16 +00:00
|
|
|
let _p = profile::span("CompletionContext::new");
|
2021-10-17 09:15:56 +00:00
|
|
|
let sema = Semantics::new(db);
|
|
|
|
|
2021-11-10 16:33:35 +00:00
|
|
|
let original_file = sema.parse(file_id);
|
2021-10-17 09:15:56 +00:00
|
|
|
|
|
|
|
// Insert a fake ident to get a valid parse tree. We will use this file
|
|
|
|
// to determine context, though the original_file will be used for
|
|
|
|
// actual completion.
|
|
|
|
let file_with_fake_ident = {
|
2021-11-10 16:33:35 +00:00
|
|
|
let parse = db.parse(file_id);
|
2022-01-31 10:56:42 +00:00
|
|
|
let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
|
2021-10-17 09:15:56 +00:00
|
|
|
parse.reparse(&edit).tree()
|
|
|
|
};
|
|
|
|
let fake_ident_token =
|
2021-12-21 13:07:48 +00:00
|
|
|
file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
|
2021-10-17 09:15:56 +00:00
|
|
|
|
2021-11-10 16:33:35 +00:00
|
|
|
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
|
2021-10-17 09:15:56 +00:00
|
|
|
let token = sema.descend_into_macros_single(original_token.clone());
|
2022-03-31 09:12:08 +00:00
|
|
|
let scope = sema.scope_at_offset(&token.parent()?, offset)?;
|
2021-10-17 09:15:56 +00:00
|
|
|
let krate = scope.krate();
|
2022-02-01 22:29:40 +00:00
|
|
|
let module = scope.module();
|
2022-04-18 14:54:13 +00:00
|
|
|
|
2022-03-14 19:36:35 +00:00
|
|
|
let mut locals = FxHashMap::default();
|
2021-10-17 09:15:56 +00:00
|
|
|
scope.process_all_names(&mut |name, scope| {
|
|
|
|
if let ScopeDef::Local(local) = scope {
|
2022-03-14 19:36:35 +00:00
|
|
|
locals.insert(name, local);
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
});
|
2022-01-06 12:31:36 +00:00
|
|
|
|
2021-10-17 09:15:56 +00:00
|
|
|
let mut ctx = CompletionContext {
|
|
|
|
sema,
|
|
|
|
scope,
|
|
|
|
db,
|
|
|
|
config,
|
|
|
|
position,
|
|
|
|
original_token,
|
|
|
|
token,
|
|
|
|
krate,
|
2022-02-01 22:29:40 +00:00
|
|
|
module,
|
2021-10-17 09:15:56 +00:00
|
|
|
expected_name: None,
|
|
|
|
expected_type: None,
|
|
|
|
function_def: None,
|
|
|
|
impl_def: None,
|
2022-05-07 11:46:43 +00:00
|
|
|
incomplete_let: false,
|
2021-10-17 09:15:56 +00:00
|
|
|
completion_location: None,
|
|
|
|
previous_token: None,
|
2022-05-07 13:05:43 +00:00
|
|
|
// dummy value, will be overwritten
|
|
|
|
ident_ctx: IdentContext::UnexpandedAttrTT { fake_attribute_under_caret: None },
|
2022-05-07 11:46:43 +00:00
|
|
|
pattern_ctx: None,
|
2022-05-30 14:01:17 +00:00
|
|
|
qualifier_ctx: Default::default(),
|
2022-03-10 20:22:13 +00:00
|
|
|
existing_derives: Default::default(),
|
2022-05-07 11:46:43 +00:00
|
|
|
locals,
|
2021-10-17 09:15:56 +00:00
|
|
|
};
|
|
|
|
ctx.expand_and_fill(
|
|
|
|
original_file.syntax().clone(),
|
|
|
|
file_with_fake_ident.syntax().clone(),
|
2021-11-10 16:33:35 +00:00
|
|
|
offset,
|
2021-10-17 09:15:56 +00:00
|
|
|
fake_ident_token,
|
2022-05-07 13:05:43 +00:00
|
|
|
)?;
|
2021-10-17 09:15:56 +00:00
|
|
|
Some(ctx)
|
|
|
|
}
|
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Expand attributes and macro calls at the current cursor position for both the original file
|
|
|
|
/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
|
|
|
|
/// and speculative states stay in sync.
|
2021-10-17 09:15:56 +00:00
|
|
|
fn expand_and_fill(
|
|
|
|
&mut self,
|
|
|
|
mut original_file: SyntaxNode,
|
|
|
|
mut speculative_file: SyntaxNode,
|
|
|
|
mut offset: TextSize,
|
|
|
|
mut fake_ident_token: SyntaxToken,
|
2022-05-07 13:05:43 +00:00
|
|
|
) -> Option<()> {
|
2021-12-21 17:25:50 +00:00
|
|
|
let _p = profile::span("CompletionContext::expand_and_fill");
|
2022-03-10 19:53:50 +00:00
|
|
|
let mut derive_ctx = None;
|
|
|
|
|
2022-01-05 20:12:36 +00:00
|
|
|
'expansion: loop {
|
|
|
|
let parent_item =
|
|
|
|
|item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
|
|
|
|
let ancestor_items = iter::successors(
|
|
|
|
Option::zip(
|
|
|
|
find_node_at_offset::<ast::Item>(&original_file, offset),
|
|
|
|
find_node_at_offset::<ast::Item>(&speculative_file, offset),
|
|
|
|
),
|
|
|
|
|(a, b)| parent_item(a).zip(parent_item(b)),
|
|
|
|
);
|
2022-04-18 14:54:13 +00:00
|
|
|
|
|
|
|
// first try to expand attributes as these are always the outermost macro calls
|
|
|
|
'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
|
2021-10-17 09:15:56 +00:00
|
|
|
match (
|
|
|
|
self.sema.expand_attr_macro(&actual_item),
|
|
|
|
self.sema.speculative_expand_attr_macro(
|
|
|
|
&actual_item,
|
|
|
|
&item_with_fake_ident,
|
|
|
|
fake_ident_token.clone(),
|
|
|
|
),
|
|
|
|
) {
|
2022-04-18 14:54:13 +00:00
|
|
|
// maybe parent items have attributes, so continue walking the ancestors
|
|
|
|
(None, None) => continue 'ancestors,
|
2022-01-05 20:12:36 +00:00
|
|
|
// successful expansions
|
|
|
|
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
|
|
|
|
let new_offset = fake_mapped_token.text_range().start();
|
2021-10-17 09:15:56 +00:00
|
|
|
if new_offset > actual_expansion.text_range().end() {
|
2022-04-18 14:54:13 +00:00
|
|
|
// offset outside of bounds from the original expansion,
|
|
|
|
// stop here to prevent problems from happening
|
2022-01-05 20:12:36 +00:00
|
|
|
break 'expansion;
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
original_file = actual_expansion;
|
2022-01-05 20:12:36 +00:00
|
|
|
speculative_file = fake_expansion;
|
|
|
|
fake_ident_token = fake_mapped_token;
|
2021-10-17 09:15:56 +00:00
|
|
|
offset = new_offset;
|
2022-01-05 20:12:36 +00:00
|
|
|
continue 'expansion;
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2022-01-05 20:12:36 +00:00
|
|
|
// exactly one expansion failed, inconsistent state so stop expanding completely
|
|
|
|
_ => break 'expansion,
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-18 14:54:13 +00:00
|
|
|
|
|
|
|
// No attributes have been expanded, so look for macro_call! token trees or derive token trees
|
2022-03-10 19:53:50 +00:00
|
|
|
let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
|
|
|
|
Some(it) => it,
|
2022-04-18 14:54:13 +00:00
|
|
|
None => break 'expansion,
|
2022-03-10 19:53:50 +00:00
|
|
|
};
|
|
|
|
let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
|
|
|
|
Some(it) => it,
|
2022-04-18 14:54:13 +00:00
|
|
|
None => break 'expansion,
|
2022-03-10 19:53:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Expand pseudo-derive expansion
|
|
|
|
if let (Some(orig_attr), Some(spec_attr)) = (
|
|
|
|
orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
|
|
|
|
spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
|
|
|
|
) {
|
2022-04-18 14:54:13 +00:00
|
|
|
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
|
2022-03-10 19:53:50 +00:00
|
|
|
self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
|
|
|
|
self.sema.speculative_expand_derive_as_pseudo_attr_macro(
|
|
|
|
&orig_attr,
|
|
|
|
&spec_attr,
|
|
|
|
fake_ident_token.clone(),
|
|
|
|
),
|
|
|
|
) {
|
2022-04-18 14:54:13 +00:00
|
|
|
derive_ctx = Some((
|
|
|
|
actual_expansion,
|
|
|
|
fake_expansion,
|
|
|
|
fake_mapped_token.text_range().start(),
|
|
|
|
orig_attr,
|
|
|
|
));
|
2022-03-10 19:53:50 +00:00
|
|
|
}
|
2022-04-18 14:54:13 +00:00
|
|
|
// at this point we won't have any more successful expansions, so stop
|
|
|
|
break 'expansion;
|
2022-03-10 19:53:50 +00:00
|
|
|
}
|
2021-10-17 09:15:56 +00:00
|
|
|
|
|
|
|
// Expand fn-like macro calls
|
|
|
|
if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
|
2022-03-10 19:53:50 +00:00
|
|
|
orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
|
|
|
|
spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
|
2021-10-17 09:15:56 +00:00
|
|
|
) {
|
|
|
|
let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
|
|
|
|
let mac_call_path1 =
|
|
|
|
macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
|
2022-04-18 14:54:13 +00:00
|
|
|
|
|
|
|
// inconsistent state, stop expanding
|
2021-10-17 09:15:56 +00:00
|
|
|
if mac_call_path0 != mac_call_path1 {
|
2022-04-18 14:54:13 +00:00
|
|
|
break 'expansion;
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
let speculative_args = match macro_call_with_fake_ident.token_tree() {
|
|
|
|
Some(tt) => tt,
|
2022-04-18 14:54:13 +00:00
|
|
|
None => break 'expansion,
|
2021-10-17 09:15:56 +00:00
|
|
|
};
|
|
|
|
|
2022-01-05 20:12:36 +00:00
|
|
|
match (
|
2021-10-17 09:15:56 +00:00
|
|
|
self.sema.expand(&actual_macro_call),
|
|
|
|
self.sema.speculative_expand(
|
|
|
|
&actual_macro_call,
|
|
|
|
&speculative_args,
|
2022-01-05 20:12:36 +00:00
|
|
|
fake_ident_token.clone(),
|
2021-10-17 09:15:56 +00:00
|
|
|
),
|
|
|
|
) {
|
2022-01-05 20:12:36 +00:00
|
|
|
// successful expansions
|
|
|
|
(Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
|
|
|
|
let new_offset = fake_mapped_token.text_range().start();
|
|
|
|
if new_offset > actual_expansion.text_range().end() {
|
2022-04-18 14:54:13 +00:00
|
|
|
// offset outside of bounds from the original expansion,
|
|
|
|
// stop here to prevent problems from happening
|
|
|
|
break 'expansion;
|
2022-01-05 20:12:36 +00:00
|
|
|
}
|
|
|
|
original_file = actual_expansion;
|
|
|
|
speculative_file = fake_expansion;
|
|
|
|
fake_ident_token = fake_mapped_token;
|
|
|
|
offset = new_offset;
|
2022-04-18 14:54:13 +00:00
|
|
|
continue 'expansion;
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2022-04-18 14:54:13 +00:00
|
|
|
// at least on expansion failed, we won't have anything to expand from this point
|
|
|
|
// onwards so break out
|
|
|
|
_ => break 'expansion,
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-05 20:12:36 +00:00
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
// none of our states have changed so stop the loop
|
|
|
|
break 'expansion;
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 13:05:43 +00:00
|
|
|
self.fill(&original_file, speculative_file, offset, derive_ctx)
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2021-07-23 13:36:43 +00:00
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Calculate the expected type and name of the cursor position.
|
2021-05-03 19:34:34 +00:00
|
|
|
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
|
|
|
|
let mut node = match self.token.parent() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return (None, None),
|
|
|
|
};
|
|
|
|
loop {
|
|
|
|
break match_ast! {
|
|
|
|
match node {
|
|
|
|
ast::LetStmt(it) => {
|
|
|
|
cov_mark::hit!(expected_type_let_with_leading_char);
|
|
|
|
cov_mark::hit!(expected_type_let_without_leading_char);
|
|
|
|
let ty = it.pat()
|
2021-05-08 21:14:08 +00:00
|
|
|
.and_then(|pat| self.sema.type_of_pat(&pat))
|
2021-08-02 18:42:25 +00:00
|
|
|
.or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
|
2021-08-03 15:28:51 +00:00
|
|
|
.map(TypeInfo::original);
|
2021-10-03 12:53:01 +00:00
|
|
|
let name = match it.pat() {
|
|
|
|
Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
|
|
|
|
Some(_) | None => None,
|
2021-05-03 19:34:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
(ty, name)
|
|
|
|
},
|
2022-01-23 22:37:59 +00:00
|
|
|
ast::LetExpr(it) => {
|
|
|
|
cov_mark::hit!(expected_type_if_let_without_leading_char);
|
|
|
|
let ty = it.pat()
|
|
|
|
.and_then(|pat| self.sema.type_of_pat(&pat))
|
|
|
|
.or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
|
|
|
|
.map(TypeInfo::original);
|
|
|
|
(ty, None)
|
|
|
|
},
|
2021-12-14 11:44:31 +00:00
|
|
|
ast::ArgList(_) => {
|
2021-06-20 16:32:45 +00:00
|
|
|
cov_mark::hit!(expected_type_fn_param);
|
2021-05-03 19:34:34 +00:00
|
|
|
ActiveParameter::at_token(
|
|
|
|
&self.sema,
|
|
|
|
self.token.clone(),
|
|
|
|
).map(|ap| {
|
|
|
|
let name = ap.ident().map(NameOrNameRef::Name);
|
2021-06-20 16:32:45 +00:00
|
|
|
let ty = if has_ref(&self.token) {
|
|
|
|
cov_mark::hit!(expected_type_fn_param_ref);
|
|
|
|
ap.ty.remove_ref()
|
|
|
|
} else {
|
|
|
|
Some(ap.ty)
|
|
|
|
};
|
|
|
|
(ty, name)
|
2021-05-03 19:34:34 +00:00
|
|
|
})
|
|
|
|
.unwrap_or((None, None))
|
|
|
|
},
|
2021-08-11 16:05:39 +00:00
|
|
|
ast::RecordExprFieldList(it) => {
|
2021-05-23 16:10:40 +00:00
|
|
|
// wouldn't try {} be nice...
|
|
|
|
(|| {
|
2021-08-11 16:05:39 +00:00
|
|
|
if self.token.kind() == T![..]
|
|
|
|
|| self.token.prev_token().map(|t| t.kind()) == Some(T![..])
|
|
|
|
{
|
|
|
|
cov_mark::hit!(expected_type_struct_func_update);
|
|
|
|
let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
|
|
|
|
let ty = self.sema.type_of_expr(&record_expr.into())?;
|
|
|
|
Some((
|
|
|
|
Some(ty.original),
|
|
|
|
None
|
|
|
|
))
|
|
|
|
} else {
|
|
|
|
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
|
|
|
let expr_field = self.token.prev_sibling_or_token()?
|
|
|
|
.into_node()
|
|
|
|
.and_then(ast::RecordExprField::cast)?;
|
|
|
|
let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
|
|
|
|
Some((
|
|
|
|
Some(ty),
|
|
|
|
expr_field.field_name().map(NameOrNameRef::NameRef),
|
|
|
|
))
|
|
|
|
}
|
2021-05-23 16:10:40 +00:00
|
|
|
})().unwrap_or((None, None))
|
2021-05-03 19:34:34 +00:00
|
|
|
},
|
|
|
|
ast::RecordExprField(it) => {
|
2021-12-11 17:47:21 +00:00
|
|
|
if let Some(expr) = it.expr() {
|
|
|
|
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
|
|
|
(
|
|
|
|
self.sema.type_of_expr(&expr).map(TypeInfo::original),
|
|
|
|
it.field_name().map(NameOrNameRef::NameRef),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
cov_mark::hit!(expected_type_struct_field_followed_by_comma);
|
|
|
|
let ty = self.sema.resolve_record_field(&it)
|
|
|
|
.map(|(_, _, ty)| ty);
|
|
|
|
(
|
|
|
|
ty,
|
|
|
|
it.field_name().map(NameOrNameRef::NameRef),
|
|
|
|
)
|
|
|
|
}
|
2021-05-03 19:34:34 +00:00
|
|
|
},
|
2022-05-19 10:27:43 +00:00
|
|
|
// match foo { $0 }
|
|
|
|
// match foo { ..., pat => $0 }
|
2021-05-03 19:34:34 +00:00
|
|
|
ast::MatchExpr(it) => {
|
2022-05-19 10:27:43 +00:00
|
|
|
let ty = if self.previous_token_is(T![=>]) {
|
|
|
|
// match foo { ..., pat => $0 }
|
|
|
|
cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
|
|
|
|
cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
|
|
|
|
self.sema.type_of_expr(&it.into())
|
|
|
|
} else {
|
|
|
|
// match foo { $0 }
|
|
|
|
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
|
|
|
it.expr().and_then(|e| self.sema.type_of_expr(&e))
|
|
|
|
}.map(TypeInfo::original);
|
2021-05-03 19:34:34 +00:00
|
|
|
(ty, None)
|
|
|
|
},
|
|
|
|
ast::IfExpr(it) => {
|
|
|
|
let ty = it.condition()
|
2021-08-02 18:42:25 +00:00
|
|
|
.and_then(|e| self.sema.type_of_expr(&e))
|
2021-08-03 15:28:51 +00:00
|
|
|
.map(TypeInfo::original);
|
2021-05-03 19:34:34 +00:00
|
|
|
(ty, None)
|
|
|
|
},
|
|
|
|
ast::IdentPat(it) => {
|
|
|
|
cov_mark::hit!(expected_type_if_let_with_leading_char);
|
|
|
|
cov_mark::hit!(expected_type_match_arm_with_leading_char);
|
2021-08-03 15:28:51 +00:00
|
|
|
let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
|
2021-05-03 19:34:34 +00:00
|
|
|
(ty, None)
|
|
|
|
},
|
|
|
|
ast::Fn(it) => {
|
|
|
|
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
|
|
|
|
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
|
|
|
|
let def = self.sema.to_def(&it);
|
|
|
|
(def.map(|def| def.ret_type(self.db)), None)
|
|
|
|
},
|
2021-05-23 16:23:03 +00:00
|
|
|
ast::ClosureExpr(it) => {
|
|
|
|
let ty = self.sema.type_of_expr(&it.into());
|
2021-08-03 15:28:51 +00:00
|
|
|
ty.and_then(|ty| ty.original.as_callable(self.db))
|
2021-05-23 16:23:03 +00:00
|
|
|
.map(|c| (Some(c.return_type()), None))
|
|
|
|
.unwrap_or((None, None))
|
|
|
|
},
|
2021-12-14 11:44:31 +00:00
|
|
|
ast::ParamList(_) => (None, None),
|
|
|
|
ast::Stmt(_) => (None, None),
|
|
|
|
ast::Item(_) => (None, None),
|
2021-05-03 19:34:34 +00:00
|
|
|
_ => {
|
|
|
|
match node.parent() {
|
|
|
|
Some(n) => {
|
|
|
|
node = n;
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
None => (None, None),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Fill the completion context, this is what does semantic reasoning about the surrounding context
|
|
|
|
/// of the completion location.
|
2020-02-18 17:35:10 +00:00
|
|
|
fn fill(
|
|
|
|
&mut self,
|
2020-03-07 14:27:03 +00:00
|
|
|
original_file: &SyntaxNode,
|
|
|
|
file_with_fake_ident: SyntaxNode,
|
2020-04-24 21:40:41 +00:00
|
|
|
offset: TextSize,
|
2022-04-18 13:34:36 +00:00
|
|
|
derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
|
2022-05-07 13:05:43 +00:00
|
|
|
) -> Option<()> {
|
2022-05-30 14:01:17 +00:00
|
|
|
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
|
2021-05-30 19:23:42 +00:00
|
|
|
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
2022-03-15 17:06:34 +00:00
|
|
|
if is_in_token_of_for_loop(syntax_element.clone()) {
|
2022-03-12 00:59:01 +00:00
|
|
|
// for pat $0
|
|
|
|
// there is nothing to complete here except `in` keyword
|
|
|
|
// don't bother populating the context
|
|
|
|
// FIXME: the completion calculations should end up good enough
|
|
|
|
// such that this special case becomes unnecessary
|
2022-05-07 13:05:43 +00:00
|
|
|
return None;
|
2022-03-12 00:59:01 +00:00
|
|
|
}
|
2021-05-30 19:23:42 +00:00
|
|
|
|
2022-03-12 00:59:01 +00:00
|
|
|
self.previous_token = previous_token(syntax_element.clone());
|
2021-12-28 13:47:21 +00:00
|
|
|
|
2021-05-30 19:23:42 +00:00
|
|
|
self.incomplete_let =
|
|
|
|
syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
|
|
|
|
it.syntax().text_range().end() == syntax_element.text_range().end()
|
|
|
|
});
|
|
|
|
|
2022-03-12 00:59:01 +00:00
|
|
|
(self.expected_type, self.expected_name) = self.expected_type_and_name();
|
2021-05-30 19:23:42 +00:00
|
|
|
|
2022-03-10 19:53:50 +00:00
|
|
|
// Overwrite the path kind for derives
|
2022-04-18 13:34:36 +00:00
|
|
|
if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
|
|
|
|
self.existing_derives = self
|
2022-03-10 20:22:13 +00:00
|
|
|
.sema
|
2022-04-18 13:34:36 +00:00
|
|
|
.resolve_derive_macro(&origin_attr)
|
|
|
|
.into_iter()
|
|
|
|
.flatten()
|
|
|
|
.flatten()
|
|
|
|
.collect();
|
2022-03-10 20:22:13 +00:00
|
|
|
|
2022-03-10 19:53:50 +00:00
|
|
|
if let Some(ast::NameLike::NameRef(name_ref)) =
|
|
|
|
find_node_at_offset(&file_with_fake_ident, offset)
|
|
|
|
{
|
2022-05-07 13:05:43 +00:00
|
|
|
let parent = name_ref.syntax().parent()?;
|
2022-06-03 17:48:55 +00:00
|
|
|
let (mut nameref_ctx, _, _) =
|
2022-05-07 13:05:43 +00:00
|
|
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent);
|
2022-06-17 08:45:19 +00:00
|
|
|
if let Some(NameRefKind::Path(path_ctx)) = &mut nameref_ctx.kind {
|
2022-05-07 13:05:43 +00:00
|
|
|
path_ctx.kind = PathKind::Derive;
|
2022-03-10 19:53:50 +00:00
|
|
|
}
|
2022-05-07 13:05:43 +00:00
|
|
|
self.ident_ctx = IdentContext::NameRef(nameref_ctx);
|
|
|
|
return Some(());
|
2022-03-10 19:53:50 +00:00
|
|
|
}
|
2022-05-07 13:05:43 +00:00
|
|
|
return None;
|
2022-03-10 19:53:50 +00:00
|
|
|
}
|
|
|
|
|
2021-06-13 03:54:16 +00:00
|
|
|
let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
|
2021-05-26 20:39:47 +00:00
|
|
|
Some(it) => it,
|
2022-05-07 13:05:43 +00:00
|
|
|
None => {
|
|
|
|
if let Some(original) = ast::String::cast(self.original_token.clone()) {
|
|
|
|
self.ident_ctx = IdentContext::String {
|
|
|
|
original,
|
|
|
|
expanded: ast::String::cast(self.token.clone()),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
// Fix up trailing whitespace problem
|
|
|
|
// #[attr(foo = $0
|
|
|
|
let token = if self.token.kind() == SyntaxKind::WHITESPACE {
|
|
|
|
self.previous_token.as_ref()?
|
|
|
|
} else {
|
|
|
|
&self.token
|
|
|
|
};
|
|
|
|
let p = token.parent()?;
|
|
|
|
if p.kind() == SyntaxKind::TOKEN_TREE
|
|
|
|
&& p.ancestors().any(|it| it.kind() == SyntaxKind::META)
|
|
|
|
{
|
|
|
|
self.ident_ctx = IdentContext::UnexpandedAttrTT {
|
|
|
|
fake_attribute_under_caret: syntax_element
|
|
|
|
.ancestors()
|
|
|
|
.find_map(ast::Attr::cast),
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Some(());
|
|
|
|
}
|
2021-05-26 20:39:47 +00:00
|
|
|
};
|
2021-05-30 19:23:42 +00:00
|
|
|
self.completion_location =
|
|
|
|
determine_location(&self.sema, original_file, offset, &name_like);
|
2021-10-17 08:59:06 +00:00
|
|
|
self.impl_def = self
|
|
|
|
.sema
|
|
|
|
.token_ancestors_with_macros(self.token.clone())
|
2022-06-03 17:48:55 +00:00
|
|
|
.take_while(|it| it.kind() != SOURCE_FILE)
|
|
|
|
.filter_map(ast::Item::cast)
|
|
|
|
.take(2)
|
|
|
|
.find_map(|it| match it {
|
|
|
|
ast::Item::Impl(impl_) => Some(impl_),
|
|
|
|
_ => None,
|
|
|
|
});
|
2021-10-17 08:59:06 +00:00
|
|
|
self.function_def = self
|
|
|
|
.sema
|
|
|
|
.token_ancestors_with_macros(self.token.clone())
|
|
|
|
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
|
2022-06-03 17:48:55 +00:00
|
|
|
.filter_map(ast::Item::cast)
|
|
|
|
.take(2)
|
|
|
|
.find_map(|it| match it {
|
|
|
|
ast::Item::Fn(fn_) => Some(fn_),
|
|
|
|
_ => None,
|
|
|
|
});
|
2022-03-10 19:53:50 +00:00
|
|
|
|
2021-05-26 20:39:47 +00:00
|
|
|
match name_like {
|
|
|
|
ast::NameLike::Lifetime(lifetime) => {
|
2022-05-07 13:05:43 +00:00
|
|
|
self.ident_ctx = IdentContext::Lifetime(Self::classify_lifetime(
|
|
|
|
&self.sema,
|
|
|
|
original_file,
|
|
|
|
lifetime,
|
|
|
|
)?);
|
2019-02-24 20:49:47 +00:00
|
|
|
}
|
2021-05-26 20:39:47 +00:00
|
|
|
ast::NameLike::NameRef(name_ref) => {
|
2022-05-07 13:05:43 +00:00
|
|
|
let parent = name_ref.syntax().parent()?;
|
2022-06-03 17:48:55 +00:00
|
|
|
let (nameref_ctx, pat_ctx, qualifier_ctx) =
|
2022-05-30 14:01:17 +00:00
|
|
|
Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone());
|
|
|
|
|
2022-06-03 17:48:55 +00:00
|
|
|
self.qualifier_ctx = qualifier_ctx;
|
2022-05-07 13:05:43 +00:00
|
|
|
self.ident_ctx = IdentContext::NameRef(nameref_ctx);
|
|
|
|
self.pattern_ctx = pat_ctx;
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
2021-05-26 20:39:47 +00:00
|
|
|
ast::NameLike::Name(name) => {
|
2022-05-07 13:05:43 +00:00
|
|
|
let (name_ctx, pat_ctx) = Self::classify_name(&self.sema, original_file, name)?;
|
|
|
|
self.pattern_ctx = pat_ctx;
|
|
|
|
self.ident_ctx = IdentContext::Name(name_ctx);
|
2019-07-21 11:11:45 +00:00
|
|
|
}
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
2022-05-07 13:05:43 +00:00
|
|
|
Some(())
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
2019-02-24 20:49:47 +00:00
|
|
|
|
2021-03-20 21:43:42 +00:00
|
|
|
fn classify_lifetime(
|
2022-03-12 00:59:01 +00:00
|
|
|
_sema: &Semantics<RootDatabase>,
|
2022-05-07 12:16:03 +00:00
|
|
|
original_file: &SyntaxNode,
|
2021-03-20 21:43:42 +00:00
|
|
|
lifetime: ast::Lifetime,
|
2021-10-17 09:15:56 +00:00
|
|
|
) -> Option<LifetimeContext> {
|
|
|
|
let parent = lifetime.syntax().parent()?;
|
|
|
|
if parent.kind() == ERROR {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2022-05-07 12:16:03 +00:00
|
|
|
let kind = match_ast! {
|
2021-10-17 09:15:56 +00:00
|
|
|
match parent {
|
2022-05-07 12:16:03 +00:00
|
|
|
ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
|
2022-03-12 00:59:01 +00:00
|
|
|
is_decl: param.lifetime().as_ref() == Some(&lifetime),
|
|
|
|
param
|
|
|
|
},
|
2022-05-07 12:16:03 +00:00
|
|
|
ast::BreakExpr(_) => LifetimeKind::LabelRef,
|
|
|
|
ast::ContinueExpr(_) => LifetimeKind::LabelRef,
|
|
|
|
ast::Label(_) => LifetimeKind::LabelDef,
|
|
|
|
_ => LifetimeKind::Lifetime,
|
2021-03-21 00:00:09 +00:00
|
|
|
}
|
2022-05-07 12:16:03 +00:00
|
|
|
};
|
|
|
|
let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start());
|
|
|
|
|
|
|
|
Some(LifetimeContext { lifetime, kind })
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2021-03-21 00:00:09 +00:00
|
|
|
|
2022-01-31 10:56:42 +00:00
|
|
|
fn classify_name(
|
|
|
|
_sema: &Semantics<RootDatabase>,
|
|
|
|
original_file: &SyntaxNode,
|
|
|
|
name: ast::Name,
|
2022-04-17 19:53:58 +00:00
|
|
|
) -> Option<(NameContext, Option<PatternContext>)> {
|
|
|
|
let parent = name.syntax().parent()?;
|
|
|
|
let mut pat_ctx = None;
|
2022-05-07 12:16:03 +00:00
|
|
|
let kind = match_ast! {
|
2022-04-17 19:53:58 +00:00
|
|
|
match parent {
|
2022-05-07 12:16:03 +00:00
|
|
|
ast::Const(_) => NameKind::Const,
|
|
|
|
ast::ConstParam(_) => NameKind::ConstParam,
|
|
|
|
ast::Enum(_) => NameKind::Enum,
|
|
|
|
ast::Fn(_) => NameKind::Function,
|
2022-04-17 19:53:58 +00:00
|
|
|
ast::IdentPat(bind_pat) => {
|
2022-05-24 11:24:36 +00:00
|
|
|
pat_ctx = Some({
|
|
|
|
let mut pat_ctx = pattern_context_for(original_file, bind_pat.into());
|
|
|
|
if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
|
|
|
|
pat_ctx.record_pat = find_node_in_file_compensated(original_file, &record_field.parent_record_pat());
|
|
|
|
}
|
|
|
|
pat_ctx
|
|
|
|
});
|
2022-04-17 19:53:58 +00:00
|
|
|
|
2022-05-07 12:16:03 +00:00
|
|
|
NameKind::IdentPat
|
2022-04-17 19:53:58 +00:00
|
|
|
},
|
2022-05-07 12:16:03 +00:00
|
|
|
ast::MacroDef(_) => NameKind::MacroDef,
|
|
|
|
ast::MacroRules(_) => NameKind::MacroRules,
|
|
|
|
ast::Module(module) => NameKind::Module(module),
|
|
|
|
ast::RecordField(_) => NameKind::RecordField,
|
|
|
|
ast::Rename(_) => NameKind::Rename,
|
|
|
|
ast::SelfParam(_) => NameKind::SelfParam,
|
|
|
|
ast::Static(_) => NameKind::Static,
|
|
|
|
ast::Struct(_) => NameKind::Struct,
|
|
|
|
ast::Trait(_) => NameKind::Trait,
|
|
|
|
ast::TypeAlias(_) => NameKind::TypeAlias,
|
|
|
|
ast::TypeParam(_) => NameKind::TypeParam,
|
|
|
|
ast::Union(_) => NameKind::Union,
|
|
|
|
ast::Variant(_) => NameKind::Variant,
|
2022-04-17 19:53:58 +00:00
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
};
|
2022-05-07 12:16:03 +00:00
|
|
|
let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
|
|
|
|
Some((NameContext { name, kind }, pat_ctx))
|
2021-03-20 21:43:42 +00:00
|
|
|
}
|
|
|
|
|
2021-10-17 09:15:56 +00:00
|
|
|
fn classify_name_ref(
|
2022-02-02 12:35:46 +00:00
|
|
|
sema: &Semantics<RootDatabase>,
|
2021-10-17 09:15:56 +00:00
|
|
|
original_file: &SyntaxNode,
|
|
|
|
name_ref: ast::NameRef,
|
2022-05-07 12:16:03 +00:00
|
|
|
parent: SyntaxNode,
|
2022-06-03 17:48:55 +00:00
|
|
|
) -> (NameRefContext, Option<PatternContext>, QualifierCtx) {
|
2022-05-07 12:16:03 +00:00
|
|
|
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
|
|
|
|
2022-06-17 08:45:19 +00:00
|
|
|
let mut res = (NameRefContext { nameref, kind: None }, None, QualifierCtx::default());
|
2022-06-03 17:48:55 +00:00
|
|
|
let (nameref_ctx, pattern_ctx, qualifier_ctx) = &mut res;
|
2022-05-24 11:24:36 +00:00
|
|
|
|
|
|
|
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind =
|
2022-05-24 11:24:36 +00:00
|
|
|
find_node_in_file_compensated(original_file, &record_field.parent_record_lit())
|
2022-06-17 08:45:19 +00:00
|
|
|
.map(NameRefKind::RecordExpr);
|
2022-06-03 17:48:55 +00:00
|
|
|
return res;
|
2022-05-24 11:24:36 +00:00
|
|
|
}
|
|
|
|
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
|
2022-06-03 17:48:55 +00:00
|
|
|
*pattern_ctx = Some(PatternContext {
|
|
|
|
param_ctx: None,
|
|
|
|
has_type_ascription: false,
|
|
|
|
ref_token: None,
|
|
|
|
mut_token: None,
|
|
|
|
record_pat: find_node_in_file_compensated(
|
|
|
|
original_file,
|
|
|
|
&record_field.parent_record_pat(),
|
|
|
|
),
|
|
|
|
..pattern_context_for(
|
|
|
|
original_file,
|
|
|
|
record_field.parent_record_pat().clone().into(),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
return res;
|
2022-05-24 11:24:36 +00:00
|
|
|
}
|
2022-05-07 12:16:03 +00:00
|
|
|
|
2022-05-07 11:46:43 +00:00
|
|
|
let segment = match_ast! {
|
|
|
|
match parent {
|
|
|
|
ast::PathSegment(segment) => segment,
|
|
|
|
ast::FieldExpr(field) => {
|
|
|
|
let receiver = find_in_original_file(field.expr(), original_file);
|
|
|
|
let receiver_is_ambiguous_float_literal = match &receiver {
|
|
|
|
Some(ast::Expr::Literal(l)) => matches! {
|
|
|
|
l.kind(),
|
2022-05-13 13:27:17 +00:00
|
|
|
ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.'))
|
2022-05-07 11:46:43 +00:00
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
};
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
|
2022-05-23 15:40:41 +00:00
|
|
|
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
|
|
|
|
kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
|
|
|
|
receiver
|
2022-06-17 08:45:19 +00:00
|
|
|
}));
|
2022-06-03 17:48:55 +00:00
|
|
|
return res;
|
2022-05-07 11:46:43 +00:00
|
|
|
},
|
|
|
|
ast::MethodCallExpr(method) => {
|
2022-05-23 15:40:41 +00:00
|
|
|
let receiver = find_in_original_file(method.receiver(), original_file);
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind = Some(NameRefKind::DotAccess(DotAccess {
|
2022-05-23 15:40:41 +00:00
|
|
|
receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
|
|
|
|
kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
|
|
|
|
receiver
|
2022-06-17 08:45:19 +00:00
|
|
|
}));
|
2022-06-03 17:48:55 +00:00
|
|
|
return res;
|
2022-05-07 11:46:43 +00:00
|
|
|
},
|
2022-06-03 17:48:55 +00:00
|
|
|
_ => return res,
|
2022-05-07 11:46:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let path = segment.parent_path();
|
2022-02-02 15:01:46 +00:00
|
|
|
let mut path_ctx = PathCompletionCtx {
|
2021-12-06 14:51:33 +00:00
|
|
|
has_call_parens: false,
|
2022-05-05 13:49:03 +00:00
|
|
|
has_macro_bang: false,
|
2022-02-02 15:01:46 +00:00
|
|
|
is_absolute_path: false,
|
2021-10-17 09:15:56 +00:00
|
|
|
qualifier: None,
|
2022-05-05 13:49:03 +00:00
|
|
|
parent: path.parent_path(),
|
2022-05-06 13:44:41 +00:00
|
|
|
kind: PathKind::Item { kind: ItemListKind::SourceFile },
|
2021-10-17 09:15:56 +00:00
|
|
|
has_type_args: false,
|
|
|
|
};
|
2022-05-06 13:44:41 +00:00
|
|
|
|
|
|
|
let is_in_block = |it: &SyntaxNode| {
|
|
|
|
it.parent()
|
|
|
|
.map(|node| {
|
|
|
|
ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
|
|
|
|
})
|
|
|
|
.unwrap_or(false)
|
|
|
|
};
|
2022-06-17 08:45:19 +00:00
|
|
|
let func_update_record = |syn: &SyntaxNode| {
|
2022-05-24 11:24:36 +00:00
|
|
|
if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
|
2022-06-17 08:45:19 +00:00
|
|
|
find_node_in_file_compensated(original_file, &record_expr)
|
|
|
|
} else {
|
|
|
|
None
|
2022-05-24 11:24:36 +00:00
|
|
|
}
|
2022-05-10 13:00:58 +00:00
|
|
|
};
|
2022-06-03 14:25:37 +00:00
|
|
|
let after_if_expr = |node: SyntaxNode| {
|
|
|
|
let prev_expr = (|| {
|
|
|
|
let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
|
|
|
|
ast::ExprStmt::cast(prev_sibling)?.expr()
|
|
|
|
})();
|
|
|
|
matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
|
|
|
|
};
|
2021-05-26 20:39:47 +00:00
|
|
|
|
2022-05-30 14:01:17 +00:00
|
|
|
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
|
|
|
|
// ex. trait Foo $0 {}
|
|
|
|
// in these cases parser recovery usually kicks in for our inserted identifier, causing it
|
|
|
|
// to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block
|
|
|
|
// expression or an item list.
|
|
|
|
// The following code checks if the body is missing, if it is we either cut off the body
|
|
|
|
// from the item or it was missing in the first place
|
|
|
|
let inbetween_body_and_decl_check = |node: SyntaxNode| {
|
|
|
|
if let Some(NodeOrToken::Node(n)) =
|
|
|
|
syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev)
|
|
|
|
{
|
|
|
|
if let Some(item) = ast::Item::cast(n) {
|
2022-06-03 14:11:26 +00:00
|
|
|
let is_inbetween = match &item {
|
2022-05-30 14:01:17 +00:00
|
|
|
ast::Item::Const(it) => it.body().is_none(),
|
|
|
|
ast::Item::Enum(it) => it.variant_list().is_none(),
|
|
|
|
ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
|
|
|
|
ast::Item::Fn(it) => it.body().is_none(),
|
|
|
|
ast::Item::Impl(it) => it.assoc_item_list().is_none(),
|
|
|
|
ast::Item::Module(it) => it.item_list().is_none(),
|
|
|
|
ast::Item::Static(it) => it.body().is_none(),
|
|
|
|
ast::Item::Struct(it) => it.field_list().is_none(),
|
|
|
|
ast::Item::Trait(it) => it.assoc_item_list().is_none(),
|
|
|
|
ast::Item::TypeAlias(it) => it.ty().is_none(),
|
|
|
|
ast::Item::Union(it) => it.record_field_list().is_none(),
|
|
|
|
_ => false,
|
2022-06-03 14:11:26 +00:00
|
|
|
};
|
|
|
|
if is_inbetween {
|
|
|
|
return Some(item);
|
2022-05-30 14:01:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-03 14:11:26 +00:00
|
|
|
None
|
2022-05-30 14:01:17 +00:00
|
|
|
};
|
|
|
|
|
2022-06-03 17:48:55 +00:00
|
|
|
// Infer the path kind
|
|
|
|
let kind = path.syntax().parent().and_then(|it| {
|
|
|
|
match_ast! {
|
2021-12-06 14:51:33 +00:00
|
|
|
match it {
|
2022-06-03 13:41:51 +00:00
|
|
|
ast::PathType(it) => Some(PathKind::Type {
|
|
|
|
in_tuple_struct: it.syntax().parent().map_or(false, |it| ast::TupleField::can_cast(it.kind()))
|
|
|
|
}),
|
2021-12-06 14:51:33 +00:00
|
|
|
ast::PathExpr(it) => {
|
2022-05-30 14:01:17 +00:00
|
|
|
if let Some(p) = it.syntax().parent() {
|
|
|
|
if ast::ExprStmt::can_cast(p.kind()) {
|
2022-06-03 14:11:26 +00:00
|
|
|
if let Some(kind) = inbetween_body_and_decl_check(p) {
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
|
2022-06-03 17:48:55 +00:00
|
|
|
return None;
|
2022-05-30 14:01:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-17 08:45:19 +00:00
|
|
|
path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
|
2022-05-06 13:44:41 +00:00
|
|
|
let in_block_expr = is_in_block(it.syntax());
|
|
|
|
let in_loop_body = is_in_loop_body(it.syntax());
|
2022-06-03 14:25:37 +00:00
|
|
|
let after_if_expr = after_if_expr(it.syntax().clone());
|
2022-06-03 18:49:25 +00:00
|
|
|
let ref_expr_parent = path.as_single_name_ref()
|
|
|
|
.and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
|
2022-06-17 08:45:19 +00:00
|
|
|
let is_func_update = func_update_record(it.syntax());
|
2022-06-03 14:25:37 +00:00
|
|
|
|
2022-06-17 08:45:19 +00:00
|
|
|
Some(PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update })
|
2021-12-06 14:51:33 +00:00
|
|
|
},
|
|
|
|
ast::TupleStructPat(it) => {
|
|
|
|
path_ctx.has_call_parens = true;
|
2022-06-03 17:48:55 +00:00
|
|
|
*pattern_ctx = Some(pattern_context_for(original_file, it.into()));
|
2021-12-06 14:51:33 +00:00
|
|
|
Some(PathKind::Pat)
|
|
|
|
},
|
|
|
|
ast::RecordPat(it) => {
|
2022-03-16 12:41:35 +00:00
|
|
|
path_ctx.has_call_parens = true;
|
2022-06-03 17:48:55 +00:00
|
|
|
*pattern_ctx = Some(pattern_context_for(original_file, it.into()));
|
2021-12-06 14:51:33 +00:00
|
|
|
Some(PathKind::Pat)
|
|
|
|
},
|
|
|
|
ast::PathPat(it) => {
|
2022-06-03 17:48:55 +00:00
|
|
|
*pattern_ctx = Some(pattern_context_for(original_file, it.into()));
|
2021-12-06 14:51:33 +00:00
|
|
|
Some(PathKind::Pat)
|
|
|
|
},
|
2022-05-05 08:34:57 +00:00
|
|
|
ast::MacroCall(it) => {
|
2022-06-03 14:11:26 +00:00
|
|
|
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind = Some(NameRefKind::Keyword(kind));
|
2022-06-03 17:48:55 +00:00
|
|
|
return None;
|
2022-05-30 14:01:17 +00:00
|
|
|
}
|
|
|
|
|
2022-05-05 08:34:57 +00:00
|
|
|
path_ctx.has_macro_bang = it.excl_token().is_some();
|
2022-05-06 13:44:41 +00:00
|
|
|
let parent = it.syntax().parent();
|
|
|
|
match parent.as_ref().map(|it| it.kind()) {
|
2022-05-05 08:34:57 +00:00
|
|
|
Some(SyntaxKind::MACRO_PAT) => Some(PathKind::Pat),
|
2022-06-03 13:41:51 +00:00
|
|
|
Some(SyntaxKind::MACRO_TYPE) => Some(PathKind::Type { in_tuple_struct: false }),
|
2022-05-06 13:44:41 +00:00
|
|
|
Some(SyntaxKind::ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::Module }),
|
2022-06-03 13:15:21 +00:00
|
|
|
Some(SyntaxKind::ASSOC_ITEM_LIST) => Some(PathKind::Item { kind: match parent.and_then(|it| it.parent()) {
|
|
|
|
Some(it) => match_ast! {
|
|
|
|
match it {
|
|
|
|
ast::Trait(_) => ItemListKind::Trait,
|
|
|
|
ast::Impl(it) => if it.trait_().is_some() {
|
|
|
|
ItemListKind::TraitImpl
|
|
|
|
} else {
|
|
|
|
ItemListKind::Impl
|
|
|
|
},
|
2022-06-03 17:48:55 +00:00
|
|
|
_ => return None
|
2022-06-03 13:15:21 +00:00
|
|
|
}
|
|
|
|
},
|
2022-06-03 17:48:55 +00:00
|
|
|
None => return None,
|
2022-05-06 13:44:41 +00:00
|
|
|
} }),
|
|
|
|
Some(SyntaxKind::EXTERN_ITEM_LIST) => Some(PathKind::Item { kind: ItemListKind::ExternBlock }),
|
|
|
|
Some(SyntaxKind::SOURCE_FILE) => Some(PathKind::Item { kind: ItemListKind::SourceFile }),
|
|
|
|
_ => {
|
2022-06-03 17:48:55 +00:00
|
|
|
return parent.and_then(ast::MacroExpr::cast).map(|it| {
|
2022-05-06 13:44:41 +00:00
|
|
|
let in_loop_body = is_in_loop_body(it.syntax());
|
|
|
|
let in_block_expr = is_in_block(it.syntax());
|
2022-06-03 14:25:37 +00:00
|
|
|
let after_if_expr = after_if_expr(it.syntax().clone());
|
2022-06-03 18:49:25 +00:00
|
|
|
let ref_expr_parent = path.as_single_name_ref()
|
|
|
|
.and_then(|_| it.syntax().parent()).and_then(ast::RefExpr::cast);
|
2022-06-17 08:45:19 +00:00
|
|
|
let is_func_update = func_update_record(it.syntax());
|
|
|
|
PathKind::Expr { in_block_expr, in_loop_body, after_if_expr, ref_expr_parent, is_func_update }
|
2022-06-03 17:48:55 +00:00
|
|
|
});
|
2022-05-06 13:44:41 +00:00
|
|
|
},
|
2022-05-05 08:34:57 +00:00
|
|
|
}
|
|
|
|
},
|
2022-02-02 12:35:46 +00:00
|
|
|
ast::Meta(meta) => (|| {
|
|
|
|
let attr = meta.parent_attr()?;
|
|
|
|
let kind = attr.kind();
|
|
|
|
let attached = attr.syntax().parent()?;
|
|
|
|
let is_trailing_outer_attr = kind != AttrKind::Inner
|
|
|
|
&& non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
|
|
|
|
let annotated_item_kind = if is_trailing_outer_attr {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(attached.kind())
|
|
|
|
};
|
|
|
|
Some(PathKind::Attr {
|
|
|
|
kind,
|
|
|
|
annotated_item_kind,
|
|
|
|
})
|
|
|
|
})(),
|
2021-12-06 14:51:33 +00:00
|
|
|
ast::Visibility(it) => Some(PathKind::Vis { has_in_token: it.in_token().is_some() }),
|
2021-12-14 11:44:31 +00:00
|
|
|
ast::UseTree(_) => Some(PathKind::Use),
|
2022-02-02 12:35:46 +00:00
|
|
|
_ => return None,
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2022-06-03 17:48:55 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-05-07 12:16:03 +00:00
|
|
|
match kind {
|
|
|
|
Some(kind) => path_ctx.kind = kind,
|
2022-06-03 17:48:55 +00:00
|
|
|
None => return res,
|
2022-05-07 12:16:03 +00:00
|
|
|
}
|
2021-10-17 09:15:56 +00:00
|
|
|
path_ctx.has_type_args = segment.generic_arg_list().is_some();
|
|
|
|
|
|
|
|
if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
|
2022-02-02 15:01:46 +00:00
|
|
|
if !use_tree_parent {
|
|
|
|
path_ctx.is_absolute_path =
|
|
|
|
path.top_path().segment().map_or(false, |it| it.coloncolon_token().is_some());
|
|
|
|
}
|
|
|
|
|
2022-02-02 12:35:46 +00:00
|
|
|
let path = path
|
2021-10-17 09:15:56 +00:00
|
|
|
.segment()
|
2022-01-31 10:56:42 +00:00
|
|
|
.and_then(|it| find_node_in_file(original_file, &it))
|
2021-10-17 09:15:56 +00:00
|
|
|
.map(|it| it.parent_path());
|
2022-02-02 12:35:46 +00:00
|
|
|
path_ctx.qualifier = path.map(|path| {
|
|
|
|
let res = sema.resolve_path(&path);
|
2022-02-02 15:01:46 +00:00
|
|
|
let is_super_chain = iter::successors(Some(path.clone()), |p| p.qualifier())
|
|
|
|
.all(|p| p.segment().and_then(|s| s.super_token()).is_some());
|
2022-05-05 20:21:42 +00:00
|
|
|
|
|
|
|
// `<_>::$0`
|
|
|
|
let is_infer_qualifier = path.qualifier().is_none()
|
|
|
|
&& matches!(
|
|
|
|
path.segment().and_then(|it| it.kind()),
|
|
|
|
Some(ast::PathSegmentKind::Type {
|
|
|
|
type_ref: Some(ast::Type::InferType(_)),
|
|
|
|
trait_ref: None,
|
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
PathQualifierCtx {
|
|
|
|
path,
|
|
|
|
resolution: res,
|
|
|
|
is_super_chain,
|
|
|
|
use_tree_parent,
|
|
|
|
is_infer_qualifier,
|
|
|
|
}
|
2022-02-02 12:35:46 +00:00
|
|
|
});
|
2022-05-07 11:46:43 +00:00
|
|
|
} else if let Some(segment) = path.segment() {
|
2021-10-17 09:15:56 +00:00
|
|
|
if segment.coloncolon_token().is_some() {
|
2022-02-02 15:01:46 +00:00
|
|
|
path_ctx.is_absolute_path = true;
|
2021-06-06 18:02:26 +00:00
|
|
|
}
|
2021-10-17 09:15:56 +00:00
|
|
|
}
|
2022-06-03 17:48:55 +00:00
|
|
|
|
|
|
|
if path_ctx.is_trivial_path() {
|
|
|
|
// fetch the full expression that may have qualifiers attached to it
|
|
|
|
let top_node = match path_ctx.kind {
|
|
|
|
PathKind::Expr { in_block_expr: true, .. } => {
|
|
|
|
parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
|
|
|
|
let parent = p.parent()?;
|
|
|
|
if ast::StmtList::can_cast(parent.kind()) {
|
|
|
|
Some(p)
|
|
|
|
} else if ast::ExprStmt::can_cast(parent.kind()) {
|
|
|
|
Some(parent)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
PathKind::Item { .. } => {
|
|
|
|
parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
|
|
|
|
}
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
if let Some(top) = top_node {
|
|
|
|
if let Some(NodeOrToken::Node(error_node)) =
|
|
|
|
syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
|
|
|
|
{
|
|
|
|
if error_node.kind() == SyntaxKind::ERROR {
|
|
|
|
qualifier_ctx.unsafe_tok = error_node
|
|
|
|
.children_with_tokens()
|
|
|
|
.filter_map(NodeOrToken::into_token)
|
|
|
|
.find(|it| it.kind() == T![unsafe]);
|
|
|
|
qualifier_ctx.vis_node =
|
|
|
|
error_node.children().find_map(ast::Visibility::cast);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-03 18:49:25 +00:00
|
|
|
if let PathKind::Item { .. } = path_ctx.kind {
|
2022-06-03 17:48:55 +00:00
|
|
|
if qualifier_ctx.none() {
|
|
|
|
if let Some(t) = top.first_token() {
|
|
|
|
if let Some(prev) = t
|
|
|
|
.prev_token()
|
|
|
|
.and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
|
|
|
|
{
|
|
|
|
if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
|
|
|
|
// This was inferred to be an item position path, but it seems
|
|
|
|
// to be part of some other broken node which leaked into an item
|
|
|
|
// list, so return without setting the path context
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-17 08:45:19 +00:00
|
|
|
nameref_ctx.kind = Some(NameRefKind::Path(path_ctx));
|
2022-06-03 17:48:55 +00:00
|
|
|
res
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-31 10:56:42 +00:00
|
|
|
fn pattern_context_for(original_file: &SyntaxNode, pat: ast::Pat) -> PatternContext {
|
2021-12-06 14:51:33 +00:00
|
|
|
let mut is_param = None;
|
|
|
|
let (refutability, has_type_ascription) =
|
|
|
|
pat
|
|
|
|
.syntax()
|
|
|
|
.ancestors()
|
|
|
|
.skip_while(|it| ast::Pat::can_cast(it.kind()))
|
|
|
|
.next()
|
|
|
|
.map_or((PatternRefutability::Irrefutable, false), |node| {
|
|
|
|
let refutability = match_ast! {
|
|
|
|
match node {
|
|
|
|
ast::LetStmt(let_) => return (PatternRefutability::Irrefutable, let_.ty().is_some()),
|
|
|
|
ast::Param(param) => {
|
2022-01-31 10:56:42 +00:00
|
|
|
let has_type_ascription = param.ty().is_some();
|
|
|
|
is_param = (|| {
|
|
|
|
let fake_param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
|
|
|
|
let param_list = find_node_in_file_compensated(original_file, &fake_param_list)?;
|
|
|
|
let param_list_owner = param_list.syntax().parent()?;
|
|
|
|
let kind = match_ast! {
|
|
|
|
match param_list_owner {
|
|
|
|
ast::ClosureExpr(closure) => ParamKind::Closure(closure),
|
|
|
|
ast::Fn(fn_) => ParamKind::Function(fn_),
|
|
|
|
_ => return None,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Some((param_list, param, kind))
|
|
|
|
})();
|
|
|
|
return (PatternRefutability::Irrefutable, has_type_ascription)
|
2021-12-06 14:51:33 +00:00
|
|
|
},
|
2021-12-14 11:44:31 +00:00
|
|
|
ast::MatchArm(_) => PatternRefutability::Refutable,
|
2022-01-23 22:37:59 +00:00
|
|
|
ast::LetExpr(_) => PatternRefutability::Refutable,
|
2021-12-14 11:44:31 +00:00
|
|
|
ast::ForExpr(_) => PatternRefutability::Irrefutable,
|
2021-12-06 14:51:33 +00:00
|
|
|
_ => PatternRefutability::Irrefutable,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
(refutability, false)
|
|
|
|
});
|
2022-02-02 17:09:30 +00:00
|
|
|
let (ref_token, mut_token) = match &pat {
|
|
|
|
ast::Pat::IdentPat(it) => (it.ref_token(), it.mut_token()),
|
|
|
|
_ => (None, None),
|
|
|
|
};
|
|
|
|
PatternContext {
|
|
|
|
refutability,
|
|
|
|
param_ctx: is_param,
|
|
|
|
has_type_ascription,
|
|
|
|
parent_pat: pat.syntax().parent().and_then(ast::Pat::cast),
|
|
|
|
mut_token,
|
|
|
|
ref_token,
|
2022-05-24 11:24:36 +00:00
|
|
|
record_pat: None,
|
2022-02-02 17:09:30 +00:00
|
|
|
}
|
2022-01-31 10:56:42 +00:00
|
|
|
}
|
|
|
|
|
2022-05-07 12:16:03 +00:00
|
|
|
fn find_in_original_file<N: AstNode>(x: Option<N>, original_file: &SyntaxNode) -> Option<N> {
|
|
|
|
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
|
|
|
let range = syntax.text_range().intersect(range)?;
|
|
|
|
syntax.covering_element(range).ancestors().find_map(N::cast)
|
|
|
|
}
|
|
|
|
x.map(|e| e.syntax().text_range()).and_then(|r| find_node_with_range(original_file, r))
|
|
|
|
}
|
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Attempts to find `node` inside `syntax` via `node`'s text range.
|
2022-01-31 10:56:42 +00:00
|
|
|
fn find_node_in_file<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
|
|
|
let syntax_range = syntax.text_range();
|
|
|
|
let range = node.syntax().text_range();
|
|
|
|
let intersection = range.intersect(syntax_range)?;
|
|
|
|
syntax.covering_element(intersection).ancestors().find_map(N::cast)
|
2021-12-06 14:51:33 +00:00
|
|
|
}
|
2022-01-06 12:31:36 +00:00
|
|
|
|
2022-04-18 14:54:13 +00:00
|
|
|
/// Attempts to find `node` inside `syntax` via `node`'s text range while compensating
|
|
|
|
/// for the offset introduced by the fake ident.
|
2022-01-31 10:56:42 +00:00
|
|
|
/// This is wrong if `node` comes before the insertion point! Use `find_node_in_file` instead.
|
|
|
|
fn find_node_in_file_compensated<N: AstNode>(syntax: &SyntaxNode, node: &N) -> Option<N> {
|
|
|
|
let syntax_range = syntax.text_range();
|
|
|
|
let range = node.syntax().text_range();
|
|
|
|
let end = range.end().checked_sub(TextSize::try_from(COMPLETION_MARKER.len()).ok()?)?;
|
|
|
|
if end < range.start() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let range = TextRange::new(range.start(), end);
|
|
|
|
// our inserted ident could cause `range` to be go outside of the original syntax, so cap it
|
|
|
|
let intersection = range.intersect(syntax_range)?;
|
|
|
|
syntax.covering_element(intersection).ancestors().find_map(N::cast)
|
2019-01-08 19:33:36 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 11:56:55 +00:00
|
|
|
fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> {
|
2020-08-13 20:41:55 +00:00
|
|
|
if let Some(qual) = path.qualifier() {
|
2021-06-17 11:56:55 +00:00
|
|
|
return Some((qual, false));
|
2020-08-13 20:41:55 +00:00
|
|
|
}
|
|
|
|
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
|
|
|
|
let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
|
2022-02-02 15:01:46 +00:00
|
|
|
Some((use_tree.path()?, true))
|
2020-08-13 20:41:55 +00:00
|
|
|
}
|
2021-03-14 04:30:14 +00:00
|
|
|
|
2021-06-20 16:32:45 +00:00
|
|
|
fn has_ref(token: &SyntaxToken) -> bool {
|
|
|
|
let mut token = token.clone();
|
2022-01-06 19:45:09 +00:00
|
|
|
for skip in [IDENT, WHITESPACE, T![mut]] {
|
2021-06-20 16:32:45 +00:00
|
|
|
if token.kind() == skip {
|
|
|
|
token = match token.prev_token() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
token.kind() == T![&]
|
|
|
|
}
|
|
|
|
|
2022-01-11 09:07:16 +00:00
|
|
|
const OP_TRAIT_LANG_NAMES: &[&str] = &[
|
|
|
|
"add_assign",
|
|
|
|
"add",
|
|
|
|
"bitand_assign",
|
|
|
|
"bitand",
|
|
|
|
"bitor_assign",
|
|
|
|
"bitor",
|
|
|
|
"bitxor_assign",
|
|
|
|
"bitxor",
|
|
|
|
"deref_mut",
|
|
|
|
"deref",
|
|
|
|
"div_assign",
|
|
|
|
"div",
|
2022-01-15 09:54:09 +00:00
|
|
|
"eq",
|
2022-01-11 09:07:16 +00:00
|
|
|
"fn_mut",
|
|
|
|
"fn_once",
|
|
|
|
"fn",
|
|
|
|
"index_mut",
|
|
|
|
"index",
|
|
|
|
"mul_assign",
|
|
|
|
"mul",
|
|
|
|
"neg",
|
|
|
|
"not",
|
2022-01-15 09:54:09 +00:00
|
|
|
"partial_ord",
|
2022-01-11 09:07:16 +00:00
|
|
|
"rem_assign",
|
|
|
|
"rem",
|
|
|
|
"shl_assign",
|
|
|
|
"shl",
|
|
|
|
"shr_assign",
|
|
|
|
"shr",
|
|
|
|
"sub",
|
2022-01-06 12:31:36 +00:00
|
|
|
];
|
2022-04-18 14:54:13 +00:00
|
|
|
|
2021-03-14 04:30:14 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use expect_test::{expect, Expect};
|
|
|
|
use hir::HirDisplay;
|
|
|
|
|
2021-06-16 19:45:02 +00:00
|
|
|
use crate::tests::{position, TEST_CONFIG};
|
2021-03-14 04:30:14 +00:00
|
|
|
|
|
|
|
use super::CompletionContext;
|
|
|
|
|
|
|
|
fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
|
|
|
|
let (db, pos) = position(ra_fixture);
|
2021-10-04 15:49:21 +00:00
|
|
|
let config = TEST_CONFIG;
|
|
|
|
let completion_context = CompletionContext::new(&db, pos, &config).unwrap();
|
2021-03-14 04:30:14 +00:00
|
|
|
|
|
|
|
let ty = completion_context
|
|
|
|
.expected_type
|
|
|
|
.map(|t| t.display_test(&db).to_string())
|
|
|
|
.unwrap_or("?".to_owned());
|
|
|
|
|
2021-03-20 22:22:09 +00:00
|
|
|
let name = completion_context
|
|
|
|
.expected_name
|
|
|
|
.map_or_else(|| "?".to_owned(), |name| name.to_string());
|
2021-03-14 04:30:14 +00:00
|
|
|
|
|
|
|
expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_let_without_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_let_without_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
let x: u32 = $0;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_let_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_let_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
let x: u32 = c$0;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-05-08 21:14:08 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_let_pat() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
let x$0 = 0u32;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
let $0 = 0u32;
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-14 04:30:14 +00:00
|
|
|
#[test]
|
2021-06-20 16:32:45 +00:00
|
|
|
fn expected_type_fn_param() {
|
|
|
|
cov_mark::check!(expected_type_fn_param);
|
2021-03-14 04:30:14 +00:00
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
2021-06-20 16:32:45 +00:00
|
|
|
fn foo() { bar($0); }
|
|
|
|
fn bar(x: u32) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() { bar(c$0); }
|
2021-03-14 04:30:14 +00:00
|
|
|
fn bar(x: u32) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-06-20 16:32:45 +00:00
|
|
|
fn expected_type_fn_param_ref() {
|
|
|
|
cov_mark::check!(expected_type_fn_param_ref);
|
2021-03-14 04:30:14 +00:00
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
2021-06-20 16:32:45 +00:00
|
|
|
fn foo() { bar(&$0); }
|
|
|
|
fn bar(x: &u32) {}
|
2021-03-14 04:30:14 +00:00
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
2021-06-20 16:32:45 +00:00
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() { bar(&mut $0); }
|
|
|
|
fn bar(x: &mut u32) {}
|
2022-01-06 19:45:09 +00:00
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() { bar(& c$0); }
|
|
|
|
fn bar(x: &u32) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() { bar(&mut c$0); }
|
|
|
|
fn bar(x: &mut u32) {}
|
2021-06-20 16:32:45 +00:00
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() { bar(&c$0); }
|
|
|
|
fn bar(x: &u32) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: x"#]],
|
|
|
|
);
|
2021-03-14 04:30:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_struct_field_without_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_struct_field_without_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { a: u32 }
|
|
|
|
fn foo() {
|
|
|
|
Foo { a: $0 };
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: a"#]],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-12-11 17:47:21 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_struct_field_followed_by_comma() {
|
|
|
|
cov_mark::check!(expected_type_struct_field_followed_by_comma);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { a: u32 }
|
|
|
|
fn foo() {
|
|
|
|
Foo { a: $0, };
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: a"#]],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-05-23 14:59:23 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_generic_struct_field() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo<T> { a: T }
|
|
|
|
fn foo() -> Foo<u32> {
|
|
|
|
Foo { a: $0 }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: a"#]],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-03-14 04:30:14 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_struct_field_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_struct_field_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { a: u32 }
|
|
|
|
fn foo() {
|
|
|
|
Foo { a: c$0 };
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: a"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_match_arm_without_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_match_arm_without_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
enum E { X }
|
|
|
|
fn foo() {
|
|
|
|
match E::X { $0 }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: E, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_match_arm_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_match_arm_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
enum E { X }
|
|
|
|
fn foo() {
|
|
|
|
match E::X { c$0 }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: E, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-05-19 10:27:43 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_match_arm_body_without_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_match_arm_body_without_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo;
|
|
|
|
enum E { X }
|
|
|
|
fn foo() -> Foo {
|
|
|
|
match E::X { E::X => $0 }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_match_body_arm_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_match_arm_body_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo;
|
|
|
|
enum E { X }
|
|
|
|
fn foo() -> Foo {
|
|
|
|
match E::X { E::X => c$0 }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-14 04:30:14 +00:00
|
|
|
#[test]
|
|
|
|
fn expected_type_if_let_without_leading_char() {
|
2021-05-03 19:34:34 +00:00
|
|
|
cov_mark::check!(expected_type_if_let_without_leading_char);
|
2021-03-14 04:30:14 +00:00
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
enum Foo { Bar, Baz, Quux }
|
|
|
|
|
|
|
|
fn foo() {
|
|
|
|
let f = Foo::Quux;
|
|
|
|
if let $0 = f { }
|
|
|
|
}
|
|
|
|
"#,
|
2021-05-03 19:34:34 +00:00
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
)
|
2021-03-14 04:30:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_if_let_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_if_let_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
enum Foo { Bar, Baz, Quux }
|
|
|
|
|
|
|
|
fn foo() {
|
|
|
|
let f = Foo::Quux;
|
|
|
|
if let c$0 = f { }
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_fn_ret_without_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_fn_ret_without_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() -> u32 {
|
|
|
|
$0
|
|
|
|
}
|
|
|
|
"#,
|
2021-05-03 19:34:34 +00:00
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
)
|
2021-03-14 04:30:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_fn_ret_with_leading_char() {
|
|
|
|
cov_mark::check!(expected_type_fn_ret_with_leading_char);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() -> u32 {
|
|
|
|
c$0
|
|
|
|
}
|
2021-05-03 19:34:34 +00:00
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_fn_ret_fn_ref_fully_typed() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() -> u32 {
|
|
|
|
foo$0
|
|
|
|
}
|
2021-03-14 04:30:14 +00:00
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
)
|
|
|
|
}
|
2021-05-23 14:59:23 +00:00
|
|
|
|
|
|
|
#[test]
|
2021-05-23 16:10:40 +00:00
|
|
|
fn expected_type_closure_param_return() {
|
2021-05-23 16:23:03 +00:00
|
|
|
// FIXME: make this work with `|| $0`
|
2021-05-23 14:59:23 +00:00
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
2021-06-18 19:14:39 +00:00
|
|
|
//- minicore: fn
|
2021-05-23 14:59:23 +00:00
|
|
|
fn foo() {
|
2021-05-23 16:23:03 +00:00
|
|
|
bar(|| a$0);
|
2021-05-23 14:59:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn bar(f: impl FnOnce() -> u32) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_generic_function() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
bar::<u32>($0);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn bar<T>(t: T) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: t"#]],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_generic_method() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
fn foo() {
|
|
|
|
S(1u32).bar($0);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct S<T>(T);
|
|
|
|
impl<T> S<T> {
|
|
|
|
fn bar(self, t: T) {}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: u32, name: t"#]],
|
|
|
|
);
|
|
|
|
}
|
2021-08-11 16:05:39 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_functional_update() {
|
|
|
|
cov_mark::check!(expected_type_struct_func_update);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { field: u32 }
|
|
|
|
fn foo() {
|
|
|
|
Foo {
|
|
|
|
..$0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
2021-11-08 13:10:26 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn expected_type_param_pat() {
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { field: u32 }
|
|
|
|
fn foo(a$0: Foo) {}
|
|
|
|
"#,
|
|
|
|
expect![[r#"ty: Foo, name: ?"#]],
|
|
|
|
);
|
|
|
|
check_expected_type_and_name(
|
|
|
|
r#"
|
|
|
|
struct Foo { field: u32 }
|
|
|
|
fn foo($0: Foo) {}
|
|
|
|
"#,
|
|
|
|
// FIXME make this work, currently fails due to pattern recovery eating the `:`
|
|
|
|
expect![[r#"ty: ?, name: ?"#]],
|
|
|
|
);
|
|
|
|
}
|
2021-03-14 04:30:14 +00:00
|
|
|
}
|