aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/base_db/src/fixture.rs10
-rw-r--r--crates/base_db/src/input.rs2
-rw-r--r--crates/hir/src/lib.rs18
-rw-r--r--crates/hir_def/src/data.rs2
-rw-r--r--crates/hir_def/src/item_tree.rs1
-rw-r--r--crates/hir_def/src/item_tree/lower.rs8
-rw-r--r--crates/hir_def/src/item_tree/pretty.rs1
-rw-r--r--crates/hir_def/src/nameres/collector.rs9
-rw-r--r--crates/hir_def/src/nameres/proc_macro.rs10
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs74
-rw-r--r--crates/hir_expand/src/builtin_macro.rs37
-rw-r--r--crates/hir_expand/src/eager.rs3
-rw-r--r--crates/hir_expand/src/lib.rs5
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/chalk_db.rs8
-rw-r--r--crates/hir_ty/src/infer.rs8
-rw-r--r--crates/hir_ty/src/tests.rs45
-rw-r--r--crates/hir_ty/src/tests/incremental.rs51
-rw-r--r--crates/hir_ty/src/tests/macros.rs18
-rw-r--r--crates/hir_ty/src/tests/traits.rs80
-rw-r--r--crates/ide/src/annotations.rs34
-rw-r--r--crates/ide/src/fixture.rs17
-rwxr-xr-xcrates/ide/src/folding_ranges.rs17
-rw-r--r--crates/ide_completion/Cargo.toml1
-rw-r--r--crates/ide_completion/src/completions/attribute.rs927
-rw-r--r--crates/ide_completion/src/completions/attribute/derive.rs147
-rw-r--r--crates/ide_completion/src/completions/attribute/lint.rs187
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs15
-rw-r--r--crates/ide_completion/src/completions/keyword.rs220
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs5
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs56
-rw-r--r--crates/ide_completion/src/completions/record.rs20
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs82
-rw-r--r--crates/ide_completion/src/context.rs197
-rw-r--r--crates/ide_completion/src/patterns.rs405
-rw-r--r--crates/ide_completion/src/render/macro_.rs6
-rw-r--r--crates/ide_completion/src/test_utils.rs18
-rw-r--r--crates/ide_db/src/call_info/tests.rs6
-rw-r--r--crates/ide_db/src/traits/tests.rs6
-rw-r--r--crates/mbe/src/expander/matcher.rs2
-rw-r--r--crates/rust-analyzer/src/bin/main.rs84
-rw-r--r--crates/rust-analyzer/src/caps.rs20
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/to_proto.rs1
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/node_ext.rs7
-rw-r--r--crates/test_utils/src/lib.rs15
49 files changed, 1968 insertions, 949 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 0132565e4..69ceba735 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -34,19 +34,13 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
34 34
35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) { 35 fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 36 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
37 let offset = match range_or_offset { 37 let offset = range_or_offset.expect_offset();
38 RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"),
39 RangeOrOffset::Offset(it) => it,
40 };
41 (db, FilePosition { file_id, offset }) 38 (db, FilePosition { file_id, offset })
42 } 39 }
43 40
44 fn with_range(ra_fixture: &str) -> (Self, FileRange) { 41 fn with_range(ra_fixture: &str) -> (Self, FileRange) {
45 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); 42 let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
46 let range = match range_or_offset { 43 let range = range_or_offset.expect_range();
47 RangeOrOffset::Range(it) => it,
48 RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"),
49 };
50 (db, FileRange { file_id, range }) 44 (db, FileRange { file_id, range })
51 } 45 }
52 46
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 64ccd11ee..23cb0c839 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -147,7 +147,7 @@ impl CrateDisplayName {
147#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 147#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
148pub struct ProcMacroId(pub u32); 148pub struct ProcMacroId(pub u32);
149 149
150#[derive(Copy, Clone, Eq, PartialEq, Debug)] 150#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
151pub enum ProcMacroKind { 151pub enum ProcMacroKind {
152 CustomDerive, 152 CustomDerive,
153 FuncLike, 153 FuncLike,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 01b2de515..975ae4869 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1282,10 +1282,16 @@ impl BuiltinType {
1282 1282
1283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1284pub enum MacroKind { 1284pub enum MacroKind {
1285 /// `macro_rules!` or Macros 2.0 macro.
1285 Declarative, 1286 Declarative,
1286 ProcMacro, 1287 /// A built-in or custom derive.
1287 Derive, 1288 Derive,
1289 /// A built-in function-like macro.
1288 BuiltIn, 1290 BuiltIn,
1291 /// A procedural attribute macro.
1292 Attr,
1293 /// A function-like procedural macro.
1294 ProcMacro,
1289} 1295}
1290 1296
1291#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -1315,11 +1321,13 @@ impl MacroDef {
1315 pub fn kind(&self) -> MacroKind { 1321 pub fn kind(&self) -> MacroKind {
1316 match self.id.kind { 1322 match self.id.kind {
1317 MacroDefKind::Declarative(_) => MacroKind::Declarative, 1323 MacroDefKind::Declarative(_) => MacroKind::Declarative,
1318 MacroDefKind::BuiltIn(_, _) => MacroKind::BuiltIn, 1324 MacroDefKind::BuiltIn(_, _) | MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn,
1319 MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive, 1325 MacroDefKind::BuiltInDerive(_, _) => MacroKind::Derive,
1320 MacroDefKind::BuiltInEager(_, _) => MacroKind::BuiltIn, 1326 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::CustomDerive, _) => {
1321 // FIXME might be a derive 1327 MacroKind::Derive
1322 MacroDefKind::ProcMacro(_, _) => MacroKind::ProcMacro, 1328 }
1329 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::Attr, _) => MacroKind::Attr,
1330 MacroDefKind::ProcMacro(_, base_db::ProcMacroKind::FuncLike, _) => MacroKind::ProcMacro,
1323 } 1331 }
1324 } 1332 }
1325} 1333}
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index a04f73352..d2bb381be 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -22,6 +22,7 @@ pub struct FunctionData {
22 pub name: Name, 22 pub name: Name,
23 pub params: Vec<Interned<TypeRef>>, 23 pub params: Vec<Interned<TypeRef>>,
24 pub ret_type: Interned<TypeRef>, 24 pub ret_type: Interned<TypeRef>,
25 pub async_ret_type: Option<Interned<TypeRef>>,
25 pub attrs: Attrs, 26 pub attrs: Attrs,
26 pub visibility: RawVisibility, 27 pub visibility: RawVisibility,
27 pub abi: Option<Interned<str>>, 28 pub abi: Option<Interned<str>>,
@@ -63,6 +64,7 @@ impl FunctionData {
63 }) 64 })
64 .collect(), 65 .collect(),
65 ret_type: func.ret_type.clone(), 66 ret_type: func.ret_type.clone(),
67 async_ret_type: func.async_ret_type.clone(),
66 attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()), 68 attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
67 visibility: item_tree[func.visibility].clone(), 69 visibility: item_tree[func.visibility].clone(),
68 abi: func.abi.clone(), 70 abi: func.abi.clone(),
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index c4d20c416..227337a8d 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -580,6 +580,7 @@ pub struct Function {
580 pub abi: Option<Interned<str>>, 580 pub abi: Option<Interned<str>>,
581 pub params: IdRange<Param>, 581 pub params: IdRange<Param>,
582 pub ret_type: Interned<TypeRef>, 582 pub ret_type: Interned<TypeRef>,
583 pub async_ret_type: Option<Interned<TypeRef>>,
583 pub ast_id: FileAstId<ast::Fn>, 584 pub ast_id: FileAstId<ast::Fn>,
584 pub(crate) flags: FnFlags, 585 pub(crate) flags: FnFlags,
585} 586}
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index b83adec46..6208facd5 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -356,12 +356,13 @@ impl<'a> Ctx<'a> {
356 _ => TypeRef::unit(), 356 _ => TypeRef::unit(),
357 }; 357 };
358 358
359 let ret_type = if func.async_token().is_some() { 359 let (ret_type, async_ret_type) = if func.async_token().is_some() {
360 let async_ret_type = ret_type.clone();
360 let future_impl = desugar_future_path(ret_type); 361 let future_impl = desugar_future_path(ret_type);
361 let ty_bound = Interned::new(TypeBound::Path(future_impl)); 362 let ty_bound = Interned::new(TypeBound::Path(future_impl));
362 TypeRef::ImplTrait(vec![ty_bound]) 363 (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
363 } else { 364 } else {
364 ret_type 365 (ret_type, None)
365 }; 366 };
366 367
367 let abi = func.abi().map(lower_abi); 368 let abi = func.abi().map(lower_abi);
@@ -395,6 +396,7 @@ impl<'a> Ctx<'a> {
395 abi, 396 abi,
396 params, 397 params,
397 ret_type: Interned::new(ret_type), 398 ret_type: Interned::new(ret_type),
399 async_ret_type: async_ret_type.map(Interned::new),
398 ast_id, 400 ast_id,
399 flags, 401 flags,
400 }; 402 };
diff --git a/crates/hir_def/src/item_tree/pretty.rs b/crates/hir_def/src/item_tree/pretty.rs
index d1ee697cb..cc9944a22 100644
--- a/crates/hir_def/src/item_tree/pretty.rs
+++ b/crates/hir_def/src/item_tree/pretty.rs
@@ -235,6 +235,7 @@ impl<'a> Printer<'a> {
235 abi, 235 abi,
236 params, 236 params,
237 ret_type, 237 ret_type,
238 async_ret_type: _,
238 ast_id: _, 239 ast_id: _,
239 flags, 240 flags,
240 } = &self.tree[it]; 241 } = &self.tree[it];
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 4296c6304..d9d6c91a8 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -477,16 +477,21 @@ impl DefCollector<'_> {
477 /// going out of sync with what the build system sees (since we resolve using VFS state, but 477 /// going out of sync with what the build system sees (since we resolve using VFS state, but
478 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that. 478 /// Cargo builds only on-disk files). We could and probably should add diagnostics for that.
479 fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) { 479 fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) {
480 let kind = def.kind.to_basedb_kind();
480 self.exports_proc_macros = true; 481 self.exports_proc_macros = true;
481 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) { 482 let macro_def = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
482 Some((_, expander)) => MacroDefId { 483 Some((_, expander)) => MacroDefId {
483 krate: self.def_map.krate, 484 krate: self.def_map.krate,
484 kind: MacroDefKind::ProcMacro(*expander, ast_id), 485 kind: MacroDefKind::ProcMacro(*expander, kind, ast_id),
485 local_inner: false, 486 local_inner: false,
486 }, 487 },
487 None => MacroDefId { 488 None => MacroDefId {
488 krate: self.def_map.krate, 489 krate: self.def_map.krate,
489 kind: MacroDefKind::ProcMacro(ProcMacroExpander::dummy(self.def_map.krate), ast_id), 490 kind: MacroDefKind::ProcMacro(
491 ProcMacroExpander::dummy(self.def_map.krate),
492 kind,
493 ast_id,
494 ),
490 local_inner: false, 495 local_inner: false,
491 }, 496 },
492 }; 497 };
diff --git a/crates/hir_def/src/nameres/proc_macro.rs b/crates/hir_def/src/nameres/proc_macro.rs
index 156598f19..3f095d623 100644
--- a/crates/hir_def/src/nameres/proc_macro.rs
+++ b/crates/hir_def/src/nameres/proc_macro.rs
@@ -18,6 +18,16 @@ pub(super) enum ProcMacroKind {
18 Attr, 18 Attr,
19} 19}
20 20
21impl ProcMacroKind {
22 pub(super) fn to_basedb_kind(&self) -> base_db::ProcMacroKind {
23 match self {
24 ProcMacroKind::CustomDerive { .. } => base_db::ProcMacroKind::CustomDerive,
25 ProcMacroKind::FnLike => base_db::ProcMacroKind::FuncLike,
26 ProcMacroKind::Attr => base_db::ProcMacroKind::Attr,
27 }
28 }
29}
30
21impl Attrs { 31impl Attrs {
22 #[rustfmt::skip] 32 #[rustfmt::skip]
23 pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> { 33 pub(super) fn parse_proc_macro_decl(&self, func_name: &Name) -> Option<ProcMacroDef> {
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index d884a6eb4..7bf152e26 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -1,6 +1,8 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use base_db::SourceDatabaseExt; 3use base_db::{salsa::SweepStrategy, SourceDatabaseExt};
4
5use crate::{AdtId, ModuleDefId};
4 6
5use super::*; 7use super::*;
6 8
@@ -163,3 +165,73 @@ m!(Z);
163 assert_eq!(n_reparsed_macros, 0); 165 assert_eq!(n_reparsed_macros, 0);
164 } 166 }
165} 167}
168
169#[test]
170fn item_tree_prevents_reparsing() {
171 // The `ItemTree` is used by both name resolution and the various queries in `adt.rs` and
172 // `data.rs`. After computing the `ItemTree` and deleting the parse tree, we should be able to
173 // run those other queries without triggering a reparse.
174
175 let (db, pos) = TestDB::with_position(
176 r#"
177pub struct S;
178pub union U {}
179pub enum E {
180 Variant,
181}
182pub fn f(_: S) { $0 }
183pub trait Tr {}
184impl Tr for () {}
185pub const C: u8 = 0;
186pub static ST: u8 = 0;
187pub type Ty = ();
188"#,
189 );
190 let krate = db.test_crate();
191 {
192 let events = db.log_executed(|| {
193 db.file_item_tree(pos.file_id.into());
194 });
195 let n_calculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
196 assert_eq!(n_calculated_item_trees, 1);
197 let n_parsed_files = events.iter().filter(|it| it.contains("parse(")).count();
198 assert_eq!(n_parsed_files, 1);
199 }
200
201 // Delete the parse tree.
202 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
203 base_db::ParseQuery.in_db(&db).sweep(sweep);
204
205 {
206 let events = db.log_executed(|| {
207 let crate_def_map = db.crate_def_map(krate);
208 let (_, module_data) = crate_def_map.modules.iter().last().unwrap();
209 assert_eq!(module_data.scope.resolutions().count(), 8);
210 assert_eq!(module_data.scope.impls().count(), 1);
211
212 for imp in module_data.scope.impls() {
213 db.impl_data(imp);
214 }
215
216 for (_, res) in module_data.scope.resolutions() {
217 match res.values.or(res.types).unwrap().0 {
218 ModuleDefId::FunctionId(f) => drop(db.function_data(f)),
219 ModuleDefId::AdtId(adt) => match adt {
220 AdtId::StructId(it) => drop(db.struct_data(it)),
221 AdtId::UnionId(it) => drop(db.union_data(it)),
222 AdtId::EnumId(it) => drop(db.enum_data(it)),
223 },
224 ModuleDefId::ConstId(it) => drop(db.const_data(it)),
225 ModuleDefId::StaticId(it) => drop(db.static_data(it)),
226 ModuleDefId::TraitId(it) => drop(db.trait_data(it)),
227 ModuleDefId::TypeAliasId(it) => drop(db.type_alias_data(it)),
228 ModuleDefId::EnumVariantId(_)
229 | ModuleDefId::ModuleId(_)
230 | ModuleDefId::BuiltinType(_) => unreachable!(),
231 }
232 }
233 });
234 let n_reparsed_files = events.iter().filter(|it| it.contains("parse(")).count();
235 assert_eq!(n_reparsed_files, 0);
236 }
237}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 94d7aecb6..0b310ba2f 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -8,7 +8,6 @@ use base_db::{AnchoredPath, Edition, FileId};
8use cfg::CfgExpr; 8use cfg::CfgExpr;
9use either::Either; 9use either::Either;
10use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; 10use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
11use parser::FragmentKind;
12use syntax::ast::{self, AstToken}; 11use syntax::ast::{self, AstToken};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
@@ -47,7 +46,7 @@ macro_rules! register_builtin {
47 let expander = match *self { 46 let expander = match *self {
48 $( EagerExpander::$e_kind => $e_expand, )* 47 $( EagerExpander::$e_kind => $e_expand, )*
49 }; 48 };
50 expander(db,arg_id,tt) 49 expander(db, arg_id, tt)
51 } 50 }
52 } 51 }
53 52
@@ -64,14 +63,13 @@ macro_rules! register_builtin {
64#[derive(Debug)] 63#[derive(Debug)]
65pub struct ExpandedEager { 64pub struct ExpandedEager {
66 pub(crate) subtree: tt::Subtree, 65 pub(crate) subtree: tt::Subtree,
67 pub(crate) fragment: FragmentKind,
68 /// The included file ID of the include macro. 66 /// The included file ID of the include macro.
69 pub(crate) included_file: Option<FileId>, 67 pub(crate) included_file: Option<FileId>,
70} 68}
71 69
72impl ExpandedEager { 70impl ExpandedEager {
73 fn new(subtree: tt::Subtree, fragment: FragmentKind) -> Self { 71 fn new(subtree: tt::Subtree) -> Self {
74 ExpandedEager { subtree, fragment, included_file: None } 72 ExpandedEager { subtree, included_file: None }
75 } 73 }
76} 74}
77 75
@@ -340,7 +338,7 @@ fn compile_error_expand(
340 _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()), 338 _ => mbe::ExpandError::BindingError("`compile_error!` argument must be a string".into()),
341 }; 339 };
342 340
343 ExpandResult { value: Some(ExpandedEager::new(quote! {}, FragmentKind::Items)), err: Some(err) } 341 ExpandResult { value: Some(ExpandedEager::new(quote! {})), err: Some(err) }
344} 342}
345 343
346fn concat_expand( 344fn concat_expand(
@@ -371,7 +369,7 @@ fn concat_expand(
371 } 369 }
372 } 370 }
373 } 371 }
374 ExpandResult { value: Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr)), err } 372 ExpandResult { value: Some(ExpandedEager::new(quote!(#text))), err }
375} 373}
376 374
377fn concat_idents_expand( 375fn concat_idents_expand(
@@ -393,7 +391,7 @@ fn concat_idents_expand(
393 } 391 }
394 } 392 }
395 let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() }; 393 let ident = tt::Ident { text: ident.into(), id: tt::TokenId::unspecified() };
396 ExpandResult { value: Some(ExpandedEager::new(quote!(#ident), FragmentKind::Expr)), err } 394 ExpandResult { value: Some(ExpandedEager::new(quote!(#ident))), err }
397} 395}
398 396
399fn relative_file( 397fn relative_file(
@@ -442,14 +440,7 @@ fn include_expand(
442 440
443 match res { 441 match res {
444 Ok((subtree, file_id)) => { 442 Ok((subtree, file_id)) => {
445 // FIXME: 443 ExpandResult::ok(Some(ExpandedEager { subtree, included_file: Some(file_id) }))
446 // Handle include as expression
447
448 ExpandResult::ok(Some(ExpandedEager {
449 subtree,
450 fragment: FragmentKind::Items,
451 included_file: Some(file_id),
452 }))
453 } 444 }
454 Err(e) => ExpandResult::only_err(e), 445 Err(e) => ExpandResult::only_err(e),
455 } 446 }
@@ -472,7 +463,7 @@ fn include_bytes_expand(
472 id: tt::TokenId::unspecified(), 463 id: tt::TokenId::unspecified(),
473 }))], 464 }))],
474 }; 465 };
475 ExpandResult::ok(Some(ExpandedEager::new(res, FragmentKind::Expr))) 466 ExpandResult::ok(Some(ExpandedEager::new(res)))
476} 467}
477 468
478fn include_str_expand( 469fn include_str_expand(
@@ -492,14 +483,14 @@ fn include_str_expand(
492 let file_id = match relative_file(db, arg_id.into(), &path, true) { 483 let file_id = match relative_file(db, arg_id.into(), &path, true) {
493 Ok(file_id) => file_id, 484 Ok(file_id) => file_id,
494 Err(_) => { 485 Err(_) => {
495 return ExpandResult::ok(Some(ExpandedEager::new(quote!(""), FragmentKind::Expr))); 486 return ExpandResult::ok(Some(ExpandedEager::new(quote!(""))));
496 } 487 }
497 }; 488 };
498 489
499 let text = db.file_text(file_id); 490 let text = db.file_text(file_id);
500 let text = &*text; 491 let text = &*text;
501 492
502 ExpandResult::ok(Some(ExpandedEager::new(quote!(#text), FragmentKind::Expr))) 493 ExpandResult::ok(Some(ExpandedEager::new(quote!(#text))))
503} 494}
504 495
505fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> { 496fn get_env_inner(db: &dyn AstDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
@@ -535,7 +526,7 @@ fn env_expand(
535 }); 526 });
536 let expanded = quote! { #s }; 527 let expanded = quote! { #s };
537 528
538 ExpandResult { value: Some(ExpandedEager::new(expanded, FragmentKind::Expr)), err } 529 ExpandResult { value: Some(ExpandedEager::new(expanded)), err }
539} 530}
540 531
541fn option_env_expand( 532fn option_env_expand(
@@ -553,7 +544,7 @@ fn option_env_expand(
553 Some(s) => quote! { std::option::Some(#s) }, 544 Some(s) => quote! { std::option::Some(#s) },
554 }; 545 };
555 546
556 ExpandResult::ok(Some(ExpandedEager::new(expanded, FragmentKind::Expr))) 547 ExpandResult::ok(Some(ExpandedEager::new(expanded)))
557} 548}
558 549
559#[cfg(test)] 550#[cfg(test)]
@@ -565,6 +556,7 @@ mod tests {
565 }; 556 };
566 use base_db::{fixture::WithFixture, SourceDatabase}; 557 use base_db::{fixture::WithFixture, SourceDatabase};
567 use expect_test::{expect, Expect}; 558 use expect_test::{expect, Expect};
559 use parser::FragmentKind;
568 use std::sync::Arc; 560 use std::sync::Arc;
569 use syntax::ast::NameOwner; 561 use syntax::ast::NameOwner;
570 562
@@ -617,6 +609,7 @@ mod tests {
617 local_inner: false, 609 local_inner: false,
618 }; 610 };
619 611
612 let fragment = crate::to_fragment_kind(&macro_call);
620 let args = macro_call.token_tree().unwrap(); 613 let args = macro_call.token_tree().unwrap();
621 let parsed_args = mbe::ast_to_token_tree(&args).0; 614 let parsed_args = mbe::ast_to_token_tree(&args).0;
622 let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)); 615 let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call));
@@ -639,7 +632,7 @@ mod tests {
639 arg_or_expansion: Arc::new(expanded.subtree), 632 arg_or_expansion: Arc::new(expanded.subtree),
640 included_file: expanded.included_file, 633 included_file: expanded.included_file,
641 }), 634 }),
642 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: expanded.fragment }, 635 kind: MacroCallKind::FnLike { ast_id: call_id, fragment },
643 }; 636 };
644 637
645 let id: MacroCallId = db.intern_macro(loc).into(); 638 let id: MacroCallId = db.intern_macro(loc).into();
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index 1464320ba..14af628a1 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -113,6 +113,7 @@ pub fn expand_eager_macro(
113 113
114 let ast_map = db.ast_id_map(macro_call.file_id); 114 let ast_map = db.ast_id_map(macro_call.file_id);
115 let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value)); 115 let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value));
116 let fragment = crate::to_fragment_kind(&macro_call.value);
116 117
117 // Note: 118 // Note:
118 // When `lazy_expand` is called, its *parent* file must be already exists. 119 // When `lazy_expand` is called, its *parent* file must be already exists.
@@ -152,7 +153,7 @@ pub fn expand_eager_macro(
152 arg_or_expansion: Arc::new(expanded.subtree), 153 arg_or_expansion: Arc::new(expanded.subtree),
153 included_file: expanded.included_file, 154 included_file: expanded.included_file,
154 }), 155 }),
155 kind: MacroCallKind::FnLike { ast_id: call_id, fragment: expanded.fragment }, 156 kind: MacroCallKind::FnLike { ast_id: call_id, fragment },
156 }; 157 };
157 158
158 Ok(db.intern_macro(loc)) 159 Ok(db.intern_macro(loc))
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 10d37234e..90d8ae240 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -15,6 +15,7 @@ pub mod quote;
15pub mod eager; 15pub mod eager;
16mod input; 16mod input;
17 17
18use base_db::ProcMacroKind;
18use either::Either; 19use either::Either;
19 20
20pub use mbe::{ExpandError, ExpandResult}; 21pub use mbe::{ExpandError, ExpandResult};
@@ -207,7 +208,7 @@ impl MacroDefId {
207 MacroDefKind::BuiltIn(_, id) => id, 208 MacroDefKind::BuiltIn(_, id) => id,
208 MacroDefKind::BuiltInDerive(_, id) => id, 209 MacroDefKind::BuiltInDerive(_, id) => id,
209 MacroDefKind::BuiltInEager(_, id) => id, 210 MacroDefKind::BuiltInEager(_, id) => id,
210 MacroDefKind::ProcMacro(_, id) => return Either::Right(*id), 211 MacroDefKind::ProcMacro(.., id) => return Either::Right(*id),
211 }; 212 };
212 Either::Left(*id) 213 Either::Left(*id)
213 } 214 }
@@ -224,7 +225,7 @@ pub enum MacroDefKind {
224 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 225 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
225 BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>), 226 BuiltInDerive(BuiltinDeriveExpander, AstId<ast::Macro>),
226 BuiltInEager(EagerExpander, AstId<ast::Macro>), 227 BuiltInEager(EagerExpander, AstId<ast::Macro>),
227 ProcMacro(ProcMacroExpander, AstId<ast::Fn>), 228 ProcMacro(ProcMacroExpander, ProcMacroKind, AstId<ast::Fn>),
228} 229}
229 230
230#[derive(Debug, Clone, PartialEq, Eq, Hash)] 231#[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index a9994082a..c3d02424d 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -18,9 +18,9 @@ ena = "0.14.0"
18log = "0.4.8" 18log = "0.4.8"
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20scoped-tls = "1" 20scoped-tls = "1"
21chalk-solve = { version = "0.67", default-features = false } 21chalk-solve = { version = "0.68", default-features = false }
22chalk-ir = "0.67" 22chalk-ir = "0.68"
23chalk-recursive = "0.67" 23chalk-recursive = "0.68"
24la-arena = { version = "0.2.0", path = "../../lib/arena" } 24la-arena = { version = "0.2.0", path = "../../lib/arena" }
25 25
26stdx = { path = "../stdx", version = "0.0.0" } 26stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/hir_ty/src/chalk_db.rs b/crates/hir_ty/src/chalk_db.rs
index b108fd559..4e042bf42 100644
--- a/crates/hir_ty/src/chalk_db.rs
+++ b/crates/hir_ty/src/chalk_db.rs
@@ -383,7 +383,7 @@ pub(crate) fn associated_ty_data_query(
383 // Lower bounds -- we could/should maybe move this to a separate query in `lower` 383 // Lower bounds -- we could/should maybe move this to a separate query in `lower`
384 let type_alias_data = db.type_alias_data(type_alias); 384 let type_alias_data = db.type_alias_data(type_alias);
385 let generic_params = generics(db.upcast(), type_alias.into()); 385 let generic_params = generics(db.upcast(), type_alias.into());
386 let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST); 386 // let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
387 let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast()); 387 let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
388 let ctx = crate::TyLoweringContext::new(db, &resolver) 388 let ctx = crate::TyLoweringContext::new(db, &resolver)
389 .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable); 389 .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
@@ -396,8 +396,10 @@ pub(crate) fn associated_ty_data_query(
396 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty)) 396 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
397 .collect(); 397 .collect();
398 398
399 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); 399 // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed.
400 let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses }; 400 // (rust-analyzer#9052)
401 // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
402 let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses: vec![] };
401 let datum = AssociatedTyDatum { 403 let datum = AssociatedTyDatum {
402 trait_id: to_chalk_trait_id(trait_), 404 trait_id: to_chalk_trait_id(trait_),
403 id, 405 id,
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 8cefd80f3..7a4268819 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -558,7 +558,13 @@ impl<'a> InferenceContext<'a> {
558 558
559 self.infer_pat(*pat, &ty, BindingMode::default()); 559 self.infer_pat(*pat, &ty, BindingMode::default());
560 } 560 }
561 let return_ty = self.make_ty_with_mode(&data.ret_type, ImplTraitLoweringMode::Disallowed); // FIXME implement RPIT 561 let error_ty = &TypeRef::Error;
562 let return_ty = if data.is_async() {
563 data.async_ret_type.as_deref().unwrap_or(error_ty)
564 } else {
565 &*data.ret_type
566 };
567 let return_ty = self.make_ty_with_mode(return_ty, ImplTraitLoweringMode::Disallowed); // FIXME implement RPIT
562 self.return_ty = return_ty; 568 self.return_ty = return_ty;
563 } 569 }
564 570
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index cc819373c..9d726b024 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -7,6 +7,7 @@ mod traits;
7mod method_resolution; 7mod method_resolution;
8mod macros; 8mod macros;
9mod display_source_code; 9mod display_source_code;
10mod incremental;
10 11
11use std::{env, sync::Arc}; 12use std::{env, sync::Arc};
12 13
@@ -317,50 +318,6 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
317 text 318 text
318} 319}
319 320
320#[test]
321fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
322 let (mut db, pos) = TestDB::with_position(
323 "
324 //- /lib.rs
325 fn foo() -> i32 {
326 $01 + 1
327 }
328 ",
329 );
330 {
331 let events = db.log_executed(|| {
332 let module = db.module_for_file(pos.file_id);
333 let crate_def_map = module.def_map(&db);
334 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
335 db.infer(def);
336 });
337 });
338 assert!(format!("{:?}", events).contains("infer"))
339 }
340
341 let new_text = "
342 fn foo() -> i32 {
343 1
344 +
345 1
346 }
347 "
348 .to_string();
349
350 db.set_file_text(pos.file_id, Arc::new(new_text));
351
352 {
353 let events = db.log_executed(|| {
354 let module = db.module_for_file(pos.file_id);
355 let crate_def_map = module.def_map(&db);
356 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
357 db.infer(def);
358 });
359 });
360 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
361 }
362}
363
364fn check_infer(ra_fixture: &str, expect: Expect) { 321fn check_infer(ra_fixture: &str, expect: Expect) {
365 let mut actual = infer(ra_fixture); 322 let mut actual = infer(ra_fixture);
366 actual.push('\n'); 323 actual.push('\n');
diff --git a/crates/hir_ty/src/tests/incremental.rs b/crates/hir_ty/src/tests/incremental.rs
new file mode 100644
index 000000000..3e08e83e8
--- /dev/null
+++ b/crates/hir_ty/src/tests/incremental.rs
@@ -0,0 +1,51 @@
1use std::sync::Arc;
2
3use base_db::{fixture::WithFixture, SourceDatabaseExt};
4
5use crate::{db::HirDatabase, test_db::TestDB};
6
7use super::visit_module;
8
9#[test]
10fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
11 let (mut db, pos) = TestDB::with_position(
12 "
13 //- /lib.rs
14 fn foo() -> i32 {
15 $01 + 1
16 }
17 ",
18 );
19 {
20 let events = db.log_executed(|| {
21 let module = db.module_for_file(pos.file_id);
22 let crate_def_map = module.def_map(&db);
23 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
24 db.infer(def);
25 });
26 });
27 assert!(format!("{:?}", events).contains("infer"))
28 }
29
30 let new_text = "
31 fn foo() -> i32 {
32 1
33 +
34 1
35 }
36 "
37 .to_string();
38
39 db.set_file_text(pos.file_id, Arc::new(new_text));
40
41 {
42 let events = db.log_executed(|| {
43 let module = db.module_for_file(pos.file_id);
44 let crate_def_map = module.def_map(&db);
45 visit_module(&db, &crate_def_map, module.local_id, &mut |def| {
46 db.infer(def);
47 });
48 });
49 assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
50 }
51}
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 6588aa46c..7647bb08b 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -752,6 +752,24 @@ fn bar() -> u32 {0}
752} 752}
753 753
754#[test] 754#[test]
755fn infer_builtin_macros_include_expression() {
756 check_types(
757 r#"
758//- /main.rs
759#[rustc_builtin_macro]
760macro_rules! include {() => {}}
761fn main() {
762 let i = include!("bla.rs");
763 i;
764 //^ i32
765}
766//- /bla.rs
7670
768 "#,
769 )
770}
771
772#[test]
755fn infer_builtin_macros_include_child_mod() { 773fn infer_builtin_macros_include_child_mod() {
756 check_types( 774 check_types(
757 r#" 775 r#"
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 6ad96bfe3..49add4ab9 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -161,7 +161,7 @@ mod result {
161} 161}
162 162
163#[test] 163#[test]
164fn infer_tryv2() { 164fn infer_try_trait_v2() {
165 check_types( 165 check_types(
166 r#" 166 r#"
167//- /main.rs crate:main deps:core 167//- /main.rs crate:main deps:core
@@ -172,26 +172,41 @@ fn test() {
172} //^ i32 172} //^ i32
173 173
174//- /core.rs crate:core 174//- /core.rs crate:core
175#[prelude_import] use ops::*;
176mod ops { 175mod ops {
177 trait Try { 176 mod try_trait {
178 type Output; 177 pub trait Try: FromResidual {
179 type Residual; 178 type Output;
179 type Residual;
180 }
181 pub trait FromResidual<R = <Self as Try>::Residual> {}
180 } 182 }
183
184 pub use self::try_trait::FromResidual;
185 pub use self::try_trait::Try;
186}
187
188mov convert {
189 pub trait From<T> {}
190 impl<T> From<T> for T {}
181} 191}
182 192
183#[prelude_import] use result::*; 193#[prelude_import] use result::*;
184mod result { 194mod result {
185 enum Infallible {} 195 use crate::convert::From;
186 enum Result<O, E> { 196 use crate::ops::{Try, FromResidual};
197
198 pub enum Infallible {}
199 pub enum Result<O, E> {
187 Ok(O), 200 Ok(O),
188 Err(E) 201 Err(E)
189 } 202 }
190 203
191 impl<O, E> crate::ops::Try for Result<O, E> { 204 impl<O, E> Try for Result<O, E> {
192 type Output = O; 205 type Output = O;
193 type Error = Result<Infallible, E>; 206 type Error = Result<Infallible, E>;
194 } 207 }
208
209 impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {}
195} 210}
196"#, 211"#,
197 ); 212 );
@@ -3660,3 +3675,52 @@ impl foo::Foo for u32 {
3660 "#]], 3675 "#]],
3661 ); 3676 );
3662} 3677}
3678
3679#[test]
3680fn infer_async_ret_type() {
3681 check_types(
3682 r#"
3683//- /main.rs crate:main deps:core
3684
3685enum Result<T, E> {
3686 Ok(T),
3687 Err(E),
3688}
3689
3690use Result::*;
3691
3692
3693struct Fooey;
3694
3695impl Fooey {
3696 fn collect<B: Convert>(self) -> B {
3697 B::new()
3698 }
3699}
3700
3701trait Convert {
3702 fn new() -> Self;
3703}
3704impl Convert for u32 {
3705 fn new() -> Self {
3706 0
3707 }
3708}
3709
3710async fn get_accounts() -> Result<u32, ()> {
3711 let ret = Fooey.collect();
3712 // ^ u32
3713 Ok(ret)
3714}
3715
3716//- /core.rs crate:core
3717#[prelude_import] use future::*;
3718mod future {
3719 #[lang = "future_trait"]
3720 trait Future {
3721 type Output;
3722 }
3723}
3724"#,
3725 );
3726}
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index b0c4ed60a..8d68dce05 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -58,7 +58,7 @@ pub(crate) fn annotations(
58 } 58 }
59 59
60 let action = runnable.action(); 60 let action = runnable.action();
61 let range = runnable.nav.full_range; 61 let range = runnable.nav.focus_or_full_range();
62 62
63 if config.run { 63 if config.run {
64 annotations.push(Annotation { 64 annotations.push(Annotation {
@@ -224,7 +224,7 @@ fn main() {
224 expect![[r#" 224 expect![[r#"
225 [ 225 [
226 Annotation { 226 Annotation {
227 range: 50..85, 227 range: 53..57,
228 kind: Runnable { 228 kind: Runnable {
229 debug: false, 229 debug: false,
230 runnable: Runnable { 230 runnable: Runnable {
@@ -243,7 +243,7 @@ fn main() {
243 }, 243 },
244 }, 244 },
245 Annotation { 245 Annotation {
246 range: 50..85, 246 range: 53..57,
247 kind: Runnable { 247 kind: Runnable {
248 debug: true, 248 debug: true,
249 runnable: Runnable { 249 runnable: Runnable {
@@ -328,7 +328,7 @@ fn main() {
328 expect![[r#" 328 expect![[r#"
329 [ 329 [
330 Annotation { 330 Annotation {
331 range: 14..48, 331 range: 17..21,
332 kind: Runnable { 332 kind: Runnable {
333 debug: false, 333 debug: false,
334 runnable: Runnable { 334 runnable: Runnable {
@@ -347,7 +347,7 @@ fn main() {
347 }, 347 },
348 }, 348 },
349 Annotation { 349 Annotation {
350 range: 14..48, 350 range: 17..21,
351 kind: Runnable { 351 kind: Runnable {
352 debug: true, 352 debug: true,
353 runnable: Runnable { 353 runnable: Runnable {
@@ -436,7 +436,7 @@ fn main() {
436 expect![[r#" 436 expect![[r#"
437 [ 437 [
438 Annotation { 438 Annotation {
439 range: 66..100, 439 range: 69..73,
440 kind: Runnable { 440 kind: Runnable {
441 debug: false, 441 debug: false,
442 runnable: Runnable { 442 runnable: Runnable {
@@ -455,7 +455,7 @@ fn main() {
455 }, 455 },
456 }, 456 },
457 Annotation { 457 Annotation {
458 range: 66..100, 458 range: 69..73,
459 kind: Runnable { 459 kind: Runnable {
460 debug: true, 460 debug: true,
461 runnable: Runnable { 461 runnable: Runnable {
@@ -597,7 +597,7 @@ fn main() {}
597 expect![[r#" 597 expect![[r#"
598 [ 598 [
599 Annotation { 599 Annotation {
600 range: 0..12, 600 range: 3..7,
601 kind: Runnable { 601 kind: Runnable {
602 debug: false, 602 debug: false,
603 runnable: Runnable { 603 runnable: Runnable {
@@ -616,7 +616,7 @@ fn main() {}
616 }, 616 },
617 }, 617 },
618 Annotation { 618 Annotation {
619 range: 0..12, 619 range: 3..7,
620 kind: Runnable { 620 kind: Runnable {
621 debug: true, 621 debug: true,
622 runnable: Runnable { 622 runnable: Runnable {
@@ -670,7 +670,7 @@ fn main() {
670 expect![[r#" 670 expect![[r#"
671 [ 671 [
672 Annotation { 672 Annotation {
673 range: 58..95, 673 range: 61..65,
674 kind: Runnable { 674 kind: Runnable {
675 debug: false, 675 debug: false,
676 runnable: Runnable { 676 runnable: Runnable {
@@ -689,7 +689,7 @@ fn main() {
689 }, 689 },
690 }, 690 },
691 Annotation { 691 Annotation {
692 range: 58..95, 692 range: 61..65,
693 kind: Runnable { 693 kind: Runnable {
694 debug: true, 694 debug: true,
695 runnable: Runnable { 695 runnable: Runnable {
@@ -812,7 +812,7 @@ mod tests {
812 expect![[r#" 812 expect![[r#"
813 [ 813 [
814 Annotation { 814 Annotation {
815 range: 0..12, 815 range: 3..7,
816 kind: Runnable { 816 kind: Runnable {
817 debug: false, 817 debug: false,
818 runnable: Runnable { 818 runnable: Runnable {
@@ -831,7 +831,7 @@ mod tests {
831 }, 831 },
832 }, 832 },
833 Annotation { 833 Annotation {
834 range: 0..12, 834 range: 3..7,
835 kind: Runnable { 835 kind: Runnable {
836 debug: true, 836 debug: true,
837 runnable: Runnable { 837 runnable: Runnable {
@@ -850,7 +850,7 @@ mod tests {
850 }, 850 },
851 }, 851 },
852 Annotation { 852 Annotation {
853 range: 14..64, 853 range: 18..23,
854 kind: Runnable { 854 kind: Runnable {
855 debug: false, 855 debug: false,
856 runnable: Runnable { 856 runnable: Runnable {
@@ -871,7 +871,7 @@ mod tests {
871 }, 871 },
872 }, 872 },
873 Annotation { 873 Annotation {
874 range: 14..64, 874 range: 18..23,
875 kind: Runnable { 875 kind: Runnable {
876 debug: true, 876 debug: true,
877 runnable: Runnable { 877 runnable: Runnable {
@@ -892,7 +892,7 @@ mod tests {
892 }, 892 },
893 }, 893 },
894 Annotation { 894 Annotation {
895 range: 30..62, 895 range: 45..57,
896 kind: Runnable { 896 kind: Runnable {
897 debug: false, 897 debug: false,
898 runnable: Runnable { 898 runnable: Runnable {
@@ -918,7 +918,7 @@ mod tests {
918 }, 918 },
919 }, 919 },
920 Annotation { 920 Annotation {
921 range: 30..62, 921 range: 45..57,
922 kind: Runnable { 922 kind: Runnable {
923 debug: true, 923 debug: true,
924 runnable: Runnable { 924 runnable: Runnable {
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs
index cc6641ba1..6780af617 100644
--- a/crates/ide/src/fixture.rs
+++ b/crates/ide/src/fixture.rs
@@ -1,7 +1,7 @@
1//! Utilities for creating `Analysis` instances for tests. 1//! Utilities for creating `Analysis` instances for tests.
2use ide_db::base_db::fixture::ChangeFixture; 2use ide_db::base_db::fixture::ChangeFixture;
3use syntax::{TextRange, TextSize}; 3use syntax::{TextRange, TextSize};
4use test_utils::{extract_annotations, RangeOrOffset}; 4use test_utils::extract_annotations;
5 5
6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; 6use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
7 7
@@ -27,10 +27,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
27 let change_fixture = ChangeFixture::parse(ra_fixture); 27 let change_fixture = ChangeFixture::parse(ra_fixture);
28 host.db.apply_change(change_fixture.change); 28 host.db.apply_change(change_fixture.change);
29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 29 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
30 let offset = match range_or_offset { 30 let offset = range_or_offset.expect_offset();
31 RangeOrOffset::Range(_) => panic!(),
32 RangeOrOffset::Offset(it) => it,
33 };
34 (host.analysis(), FilePosition { file_id, offset }) 31 (host.analysis(), FilePosition { file_id, offset })
35} 32}
36 33
@@ -40,10 +37,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
40 let change_fixture = ChangeFixture::parse(ra_fixture); 37 let change_fixture = ChangeFixture::parse(ra_fixture);
41 host.db.apply_change(change_fixture.change); 38 host.db.apply_change(change_fixture.change);
42 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 39 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
43 let range = match range_or_offset { 40 let range = range_or_offset.expect_range();
44 RangeOrOffset::Range(it) => it,
45 RangeOrOffset::Offset(_) => panic!(),
46 };
47 (host.analysis(), FileRange { file_id, range }) 41 (host.analysis(), FileRange { file_id, range })
48} 42}
49 43
@@ -53,10 +47,7 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
53 let change_fixture = ChangeFixture::parse(ra_fixture); 47 let change_fixture = ChangeFixture::parse(ra_fixture);
54 host.db.apply_change(change_fixture.change); 48 host.db.apply_change(change_fixture.change);
55 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 49 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
56 let offset = match range_or_offset { 50 let offset = range_or_offset.expect_offset();
57 RangeOrOffset::Range(_) => panic!(),
58 RangeOrOffset::Offset(it) => it,
59 };
60 51
61 let annotations = change_fixture 52 let annotations = change_fixture
62 .files 53 .files
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index b893c1c54..c5015a345 100755
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -19,6 +19,7 @@ pub enum FoldKind {
19 Statics, 19 Statics,
20 Array, 20 Array,
21 WhereClause, 21 WhereClause,
22 ReturnType,
22} 23}
23 24
24#[derive(Debug)] 25#[derive(Debug)]
@@ -131,6 +132,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
131 COMMENT => Some(FoldKind::Comment), 132 COMMENT => Some(FoldKind::Comment),
132 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList), 133 ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
133 ARRAY_EXPR => Some(FoldKind::Array), 134 ARRAY_EXPR => Some(FoldKind::Array),
135 RET_TYPE => Some(FoldKind::ReturnType),
134 ASSOC_ITEM_LIST 136 ASSOC_ITEM_LIST
135 | RECORD_FIELD_LIST 137 | RECORD_FIELD_LIST
136 | RECORD_PAT_FIELD_LIST 138 | RECORD_PAT_FIELD_LIST
@@ -300,6 +302,7 @@ mod tests {
300 FoldKind::Statics => "statics", 302 FoldKind::Statics => "statics",
301 FoldKind::Array => "array", 303 FoldKind::Array => "array",
302 FoldKind::WhereClause => "whereclause", 304 FoldKind::WhereClause => "whereclause",
305 FoldKind::ReturnType => "returntype",
303 }; 306 };
304 assert_eq!(kind, &attr.unwrap()); 307 assert_eq!(kind, &attr.unwrap());
305 } 308 }
@@ -560,4 +563,18 @@ where
560"#, 563"#,
561 ) 564 )
562 } 565 }
566
567 #[test]
568 fn fold_return_type() {
569 check(
570 r#"
571fn foo()<fold returntype>-> (
572 bool,
573 bool,
574)</fold> { (true, true) }
575
576fn bar() -> (bool, bool) { (true, true) }
577 "#,
578 )
579 }
563} 580}
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index 6bd8a5500..ba81c9e04 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -15,6 +15,7 @@ itertools = "0.10.0"
15log = "0.4.8" 15log = "0.4.8"
16rustc-hash = "1.1.0" 16rustc-hash = "1.1.0"
17either = "1.6.1" 17either = "1.6.1"
18once_cell = "1.7"
18 19
19stdx = { path = "../stdx", version = "0.0.0" } 20stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 21syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index b1505c74b..76d926157 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,9 +3,9 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use itertools::Itertools; 6use once_cell::sync::Lazy;
7use rustc_hash::FxHashSet; 7use rustc_hash::{FxHashMap, FxHashSet};
8use syntax::{ast, AstNode, T}; 8use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -14,33 +14,40 @@ use crate::{
14 Completions, 14 Completions,
15}; 15};
16 16
17pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 17mod derive;
18 if ctx.mod_declaration_under_caret.is_some() { 18mod lint;
19 return None; 19pub(crate) use self::lint::LintCompletion;
20 }
21 20
21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => { 24 (Some(path), Some(token_tree)) => match path.text().as_str() {
25 let path = path.syntax().text(); 25 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 if path == "derive" { 26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 complete_derive(acc, ctx, token_tree) 27 "allow" | "warn" | "deny" | "forbid" => {
28 } else if path == "feature" { 28 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, FEATURES) 29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
33 } 30 }
34 } 31 _ => (),
35 (_, Some(_token_tree)) => {} 32 },
36 _ => complete_attribute_start(acc, ctx, attribute), 33 (None, Some(_)) => (),
34 _ => complete_new_attribute(acc, ctx, attribute),
37 } 35 }
38 Some(()) 36 Some(())
39} 37}
40 38
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { 39fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
40 let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind());
41 let attributes = attribute_annotated_item_kind.and_then(|kind| {
42 if ast::Expr::can_cast(kind) {
43 Some(EXPR_ATTRIBUTES)
44 } else {
45 KIND_TO_ATTRIBUTES.get(&kind).copied()
46 }
47 });
42 let is_inner = attribute.kind() == ast::AttrKind::Inner; 48 let is_inner = attribute.kind() == ast::AttrKind::Inner;
43 for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { 49
50 let add_completion = |attr_completion: &AttrCompletion| {
44 let mut item = CompletionItem::new( 51 let mut item = CompletionItem::new(
45 CompletionKind::Attribute, 52 CompletionKind::Attribute,
46 ctx.source_range(), 53 ctx.source_range(),
@@ -56,9 +63,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
56 item.insert_snippet(cap, snippet); 63 item.insert_snippet(cap, snippet);
57 } 64 }
58 65
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 66 if is_inner || !attr_completion.prefer_inner {
60 acc.add(item.build()); 67 acc.add(item.build());
61 } 68 }
69 };
70
71 match attributes {
72 Some(applicable) => applicable
73 .iter()
74 .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
75 .flat_map(|idx| ATTRIBUTES.get(idx))
76 .for_each(add_completion),
77 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
78 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
62 } 79 }
63} 80}
64 81
@@ -70,6 +87,10 @@ struct AttrCompletion {
70} 87}
71 88
72impl AttrCompletion { 89impl AttrCompletion {
90 fn key(&self) -> &'static str {
91 self.lookup.unwrap_or(self.label)
92 }
93
73 const fn prefer_inner(self) -> AttrCompletion { 94 const fn prefer_inner(self) -> AttrCompletion {
74 AttrCompletion { prefer_inner: true, ..self } 95 AttrCompletion { prefer_inner: true, ..self }
75 } 96 }
@@ -83,30 +104,122 @@ const fn attr(
83 AttrCompletion { label, lookup, snippet, prefer_inner: false } 104 AttrCompletion { label, lookup, snippet, prefer_inner: false }
84} 105}
85 106
107macro_rules! attrs {
108 // attributes applicable to all items
109 [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
110 attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
111 };
112 // attributes applicable to all adts
113 [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
114 attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
115 };
116 // attributes applicable to all linkable things aka functions/statics
117 [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
118 attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
119 };
120 // error fallback for nicer error message
121 [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
122 compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
123 };
124 // general push down accumulation
125 [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
126 attrs!(@ { $($tt)* } { $($acc)*, $lit })
127 };
128 [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
129 compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
130 };
131 // final output construction
132 [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
133 // starting matcher
134 [$($tt:tt),*] => {
135 attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
136 };
137}
138
139#[rustfmt::skip]
140static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
141 use SyntaxKind::*;
142 std::array::IntoIter::new([
143 (
144 SOURCE_FILE,
145 attrs!(
146 item,
147 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
148 "recursion_limit", "type_length_limit", "windows_subsystem"
149 ),
150 ),
151 (MODULE, attrs!(item, "no_implicit_prelude", "path")),
152 (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
153 (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
154 (MACRO_DEF, attrs!(item)),
155 (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
156 (USE, attrs!(item)),
157 (TYPE_ALIAS, attrs!(item)),
158 (STRUCT, attrs!(item, adt, "non_exhaustive")),
159 (ENUM, attrs!(item, adt, "non_exhaustive")),
160 (UNION, attrs!(item, adt)),
161 (CONST, attrs!(item)),
162 (
163 FN,
164 attrs!(
165 item, linkable,
166 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
167 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
168 "test", "track_caller"
169 ),
170 ),
171 (STATIC, attrs!(item, linkable, "global_allocator", "used")),
172 (TRAIT, attrs!(item, "must_use")),
173 (IMPL, attrs!(item, "automatically_derived")),
174 (ASSOC_ITEM_LIST, attrs!(item)),
175 (EXTERN_BLOCK, attrs!(item, "link")),
176 (EXTERN_ITEM_LIST, attrs!(item, "link")),
177 (MACRO_CALL, attrs!()),
178 (SELF_PARAM, attrs!()),
179 (PARAM, attrs!()),
180 (RECORD_FIELD, attrs!()),
181 (VARIANT, attrs!("non_exhaustive")),
182 (TYPE_PARAM, attrs!()),
183 (CONST_PARAM, attrs!()),
184 (LIFETIME_PARAM, attrs!()),
185 (LET_STMT, attrs!()),
186 (EXPR_STMT, attrs!()),
187 (LITERAL, attrs!()),
188 (RECORD_EXPR_FIELD_LIST, attrs!()),
189 (RECORD_EXPR_FIELD, attrs!()),
190 (MATCH_ARM_LIST, attrs!()),
191 (MATCH_ARM, attrs!()),
192 (IDENT_PAT, attrs!()),
193 (RECORD_PAT_FIELD, attrs!()),
194 ])
195 .collect()
196});
197const EXPR_ATTRIBUTES: &[&str] = attrs!();
198
86/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 199/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
200// Keep these sorted for the binary search!
87const ATTRIBUTES: &[AttrCompletion] = &[ 201const ATTRIBUTES: &[AttrCompletion] = &[
88 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 202 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
89 attr("automatically_derived", None, None), 203 attr("automatically_derived", None, None),
90 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
91 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), 204 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
205 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
92 attr("cold", None, None), 206 attr("cold", None, None),
93 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 207 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
94 .prefer_inner(), 208 .prefer_inner(),
95 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 209 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
96 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), 210 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
97 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 211 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
212 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
213 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
214 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
98 attr( 215 attr(
99 r#"export_name = "…""#, 216 r#"export_name = "…""#,
100 Some("export_name"), 217 Some("export_name"),
101 Some(r#"export_name = "${0:exported_symbol_name}""#), 218 Some(r#"export_name = "${0:exported_symbol_name}""#),
102 ), 219 ),
103 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
104 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
105 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
106 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 220 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
107 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 221 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
108 // FIXME: resolve through macro resolution? 222 attr("global_allocator", None, None),
109 attr("global_allocator", None, None).prefer_inner(),
110 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 223 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
111 attr("inline", Some("inline"), Some("inline")), 224 attr("inline", Some("inline"), Some("inline")),
112 attr("link", None, None), 225 attr("link", None, None),
@@ -119,13 +232,13 @@ const ATTRIBUTES: &[AttrCompletion] = &[
119 attr("macro_export", None, None), 232 attr("macro_export", None, None),
120 attr("macro_use", None, None), 233 attr("macro_use", None, None),
121 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), 234 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
122 attr("no_link", None, None).prefer_inner(),
123 attr("no_implicit_prelude", None, None).prefer_inner(), 235 attr("no_implicit_prelude", None, None).prefer_inner(),
236 attr("no_link", None, None).prefer_inner(),
124 attr("no_main", None, None).prefer_inner(), 237 attr("no_main", None, None).prefer_inner(),
125 attr("no_mangle", None, None), 238 attr("no_mangle", None, None),
126 attr("no_std", None, None).prefer_inner(), 239 attr("no_std", None, None).prefer_inner(),
127 attr("non_exhaustive", None, None), 240 attr("non_exhaustive", None, None),
128 attr("panic_handler", None, None).prefer_inner(), 241 attr("panic_handler", None, None),
129 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)), 242 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
130 attr("proc_macro", None, None), 243 attr("proc_macro", None, None),
131 attr("proc_macro_attribute", None, None), 244 attr("proc_macro_attribute", None, None),
@@ -153,412 +266,518 @@ const ATTRIBUTES: &[AttrCompletion] = &[
153 .prefer_inner(), 266 .prefer_inner(),
154]; 267];
155 268
156fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 269fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
157 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 270 let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
158 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 271 let mut input_derives = FxHashSet::default();
159 .iter() 272 let mut tokens = derive_input
160 .filter(|completion| !existing_derives.contains(completion.label)) 273 .syntax()
161 { 274 .children_with_tokens()
162 let mut components = vec![derive_completion.label]; 275 .filter_map(NodeOrToken::into_token)
163 components.extend( 276 .skip_while(|token| token != &l_paren)
164 derive_completion 277 .skip(1)
165 .dependencies 278 .take_while(|token| token != &r_paren)
166 .iter() 279 .peekable();
167 .filter(|&&dependency| !existing_derives.contains(dependency)), 280 let mut input = String::new();
168 ); 281 while tokens.peek().is_some() {
169 let lookup = components.join(", "); 282 for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
170 let label = components.iter().rev().join(", "); 283 input.push_str(token.text());
171 let mut item =
172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 item.add_to(acc);
175 }
176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 let mut item = CompletionItem::new(
179 CompletionKind::Attribute,
180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
185 } 284 }
186 }
187}
188 285
189fn complete_lint( 286 if !input.is_empty() {
190 acc: &mut Completions, 287 input_derives.insert(input.trim().to_owned());
191 ctx: &CompletionContext,
192 derive_input: ast::TokenTree,
193 lints_completions: &[LintCompletion],
194) {
195 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
196 for lint_completion in lints_completions
197 .into_iter()
198 .filter(|completion| !existing_lints.contains(completion.label))
199 {
200 let mut item = CompletionItem::new(
201 CompletionKind::Attribute,
202 ctx.source_range(),
203 lint_completion.label,
204 );
205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
206 item.add_to(acc)
207 } 288 }
208 }
209}
210 289
211fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 290 input.clear();
212 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
213 (Some(left_paren), Some(right_paren))
214 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
215 {
216 let mut input_derives = FxHashSet::default();
217 let mut current_derive = String::new();
218 for token in derive_input
219 .syntax()
220 .children_with_tokens()
221 .filter_map(|token| token.into_token())
222 .skip_while(|token| token != &left_paren)
223 .skip(1)
224 .take_while(|token| token != &right_paren)
225 {
226 if T![,] == token.kind() {
227 if !current_derive.is_empty() {
228 input_derives.insert(current_derive);
229 current_derive = String::new();
230 }
231 } else {
232 current_derive.push_str(token.text().trim());
233 }
234 }
235
236 if !current_derive.is_empty() {
237 input_derives.insert(current_derive);
238 }
239 Ok(input_derives)
240 }
241 _ => Err(()),
242 } 291 }
243}
244
245fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
246 let mut result = FxHashSet::default();
247 ctx.scope.process_all_names(&mut |name, scope_def| {
248 if let hir::ScopeDef::MacroDef(mac) = scope_def {
249 // FIXME kind() doesn't check whether proc-macro is a derive
250 if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro {
251 result.insert(name.to_string());
252 }
253 }
254 });
255 result
256}
257
258struct DeriveCompletion {
259 label: &'static str,
260 dependencies: &'static [&'static str],
261}
262
263/// Standard Rust derives and the information about their dependencies
264/// (the dependencies are needed so that the main derive don't break the compilation when added)
265const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
266 DeriveCompletion { label: "Clone", dependencies: &[] },
267 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
268 DeriveCompletion { label: "Debug", dependencies: &[] },
269 DeriveCompletion { label: "Default", dependencies: &[] },
270 DeriveCompletion { label: "Hash", dependencies: &[] },
271 DeriveCompletion { label: "PartialEq", dependencies: &[] },
272 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
273 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
274 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
275];
276 292
277pub(crate) struct LintCompletion { 293 Some(input_derives)
278 pub(crate) label: &'static str,
279 pub(crate) description: &'static str,
280} 294}
281 295
282#[rustfmt::skip]
283const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
284 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
285 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
286 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
287 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
288 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
289 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
290 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
291 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
292 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
293 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
294 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
295 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
296 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
297 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
298 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
299 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
300 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
301 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
302 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
303 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
304 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
305 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
306 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
307 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
308 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
309 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
310 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
311 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
312 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
313 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
314 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
315 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
316 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
317 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
318 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
319 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
320 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
321 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
322 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
323 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
324 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
325 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
326 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
327 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
328 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
329 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
330 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
331 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
332 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
333 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
334 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
335 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
336 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
337 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
338 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
339 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
340 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
341 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
342 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
343 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
344 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
345 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
346 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
347 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
348 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
349 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
350 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
351 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
352 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
353 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
354 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
355 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
356 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
357 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
358 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
359 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
360 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
361 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
362 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
363 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
364 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
365 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
366 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
367 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
368 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
369 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
370 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
371 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
372 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
373 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
374 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
375 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
376 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
377 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
378 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
379 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
380 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
381 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
382 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
383 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
384 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
385 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
386 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
387 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
388 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
389 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
390 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
391 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
392 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
393 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
394 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
395 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
396 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
397 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
398 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
399];
400
401#[cfg(test)] 296#[cfg(test)]
402mod tests { 297mod tests {
298 use super::*;
299
403 use expect_test::{expect, Expect}; 300 use expect_test::{expect, Expect};
404 301
405 use crate::{test_utils::completion_list, CompletionKind}; 302 use crate::{test_utils::completion_list, CompletionKind};
406 303
304 #[test]
305 fn attributes_are_sorted() {
306 let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
307 let mut prev = attrs.next().unwrap();
308
309 attrs.for_each(|next| {
310 assert!(
311 prev < next,
312 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
313 prev,
314 next
315 );
316 prev = next;
317 });
318 }
319
407 fn check(ra_fixture: &str, expect: Expect) { 320 fn check(ra_fixture: &str, expect: Expect) {
408 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 321 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
409 expect.assert_eq(&actual); 322 expect.assert_eq(&actual);
410 } 323 }
411 324
412 #[test] 325 #[test]
413 fn empty_derive_completion() { 326 fn test_attribute_completion_inside_nested_attr() {
327 check(r#"#[cfg($0)]"#, expect![[]])
328 }
329
330 #[test]
331 fn test_attribute_completion_with_existing_attr() {
414 check( 332 check(
415 r#" 333 r#"#[no_mangle] #[$0] mcall!();"#,
416#[derive($0)]
417struct Test {}
418 "#,
419 expect![[r#" 334 expect![[r#"
420 at Clone 335 at allow(…)
421 at Clone, Copy 336 at cfg(…)
422 at Debug 337 at cfg_attr(…)
423 at Default 338 at deny(…)
424 at Hash 339 at forbid(…)
425 at PartialEq 340 at warn(…)
426 at PartialEq, Eq 341 "#]],
427 at PartialEq, PartialOrd 342 )
428 at PartialEq, Eq, PartialOrd, Ord 343 }
344
345 #[test]
346 fn complete_attribute_on_source_file() {
347 check(
348 r#"#![$0]"#,
349 expect![[r#"
350 at allow(…)
351 at cfg(…)
352 at cfg_attr(…)
353 at deny(…)
354 at forbid(…)
355 at warn(…)
356 at deprecated
357 at doc = "…"
358 at doc(hidden)
359 at doc(alias = "…")
360 at must_use
361 at no_mangle
362 at crate_name = ""
363 at feature(…)
364 at no_implicit_prelude
365 at no_main
366 at no_std
367 at recursion_limit = …
368 at type_length_limit = …
369 at windows_subsystem = "…"
429 "#]], 370 "#]],
430 ); 371 );
431 } 372 }
432 373
433 #[test] 374 #[test]
434 fn no_completion_for_incorrect_derive() { 375 fn complete_attribute_on_module() {
435 check( 376 check(
436 r#" 377 r#"#[$0] mod foo;"#,
437#[derive{$0)] 378 expect![[r#"
438struct Test {} 379 at allow(…)
439"#, 380 at cfg(…)
440 expect![[r#""#]], 381 at cfg_attr(…)
441 ) 382 at deny(…)
383 at forbid(…)
384 at warn(…)
385 at deprecated
386 at doc = "…"
387 at doc(hidden)
388 at doc(alias = "…")
389 at must_use
390 at no_mangle
391 at path = "…"
392 "#]],
393 );
394 check(
395 r#"mod foo {#![$0]}"#,
396 expect![[r#"
397 at allow(…)
398 at cfg(…)
399 at cfg_attr(…)
400 at deny(…)
401 at forbid(…)
402 at warn(…)
403 at deprecated
404 at doc = "…"
405 at doc(hidden)
406 at doc(alias = "…")
407 at must_use
408 at no_mangle
409 at no_implicit_prelude
410 "#]],
411 );
442 } 412 }
443 413
444 #[test] 414 #[test]
445 fn derive_with_input_completion() { 415 fn complete_attribute_on_macro_rules() {
446 check( 416 check(
447 r#" 417 r#"#[$0] macro_rules! foo {}"#,
448#[derive(serde::Serialize, PartialEq, $0)]
449struct Test {}
450"#,
451 expect![[r#" 418 expect![[r#"
452 at Clone 419 at allow(…)
453 at Clone, Copy 420 at cfg(…)
454 at Debug 421 at cfg_attr(…)
455 at Default 422 at deny(…)
456 at Hash 423 at forbid(…)
457 at Eq 424 at warn(…)
458 at PartialOrd 425 at deprecated
459 at Eq, PartialOrd, Ord 426 at doc = "…"
427 at doc(hidden)
428 at doc(alias = "…")
429 at must_use
430 at no_mangle
431 at macro_export
432 at macro_use
460 "#]], 433 "#]],
461 ) 434 );
462 } 435 }
463 436
464 #[test] 437 #[test]
465 fn test_attribute_completion() { 438 fn complete_attribute_on_macro_def() {
466 check( 439 check(
467 r#"#[$0]"#, 440 r#"#[$0] macro foo {}"#,
468 expect![[r#" 441 expect![[r#"
469 at allow(…) 442 at allow(…)
470 at automatically_derived 443 at cfg(…)
471 at cfg_attr(…) 444 at cfg_attr(…)
445 at deny(…)
446 at forbid(…)
447 at warn(…)
448 at deprecated
449 at doc = "…"
450 at doc(hidden)
451 at doc(alias = "…")
452 at must_use
453 at no_mangle
454 "#]],
455 );
456 }
457
458 #[test]
459 fn complete_attribute_on_extern_crate() {
460 check(
461 r#"#[$0] extern crate foo;"#,
462 expect![[r#"
463 at allow(…)
472 at cfg(…) 464 at cfg(…)
473 at cold 465 at cfg_attr(…)
474 at deny(…) 466 at deny(…)
467 at forbid(…)
468 at warn(…)
475 at deprecated 469 at deprecated
476 at derive(…) 470 at doc = "…"
477 at export_name = "…" 471 at doc(hidden)
478 at doc(alias = "…") 472 at doc(alias = "…")
473 at must_use
474 at no_mangle
475 at macro_use
476 "#]],
477 );
478 }
479
480 #[test]
481 fn complete_attribute_on_use() {
482 check(
483 r#"#[$0] use foo;"#,
484 expect![[r#"
485 at allow(…)
486 at cfg(…)
487 at cfg_attr(…)
488 at deny(…)
489 at forbid(…)
490 at warn(…)
491 at deprecated
479 at doc = "…" 492 at doc = "…"
480 at doc(hidden) 493 at doc(hidden)
494 at doc(alias = "…")
495 at must_use
496 at no_mangle
497 "#]],
498 );
499 }
500
501 #[test]
502 fn complete_attribute_on_type_alias() {
503 check(
504 r#"#[$0] type foo = ();"#,
505 expect![[r#"
506 at allow(…)
507 at cfg(…)
508 at cfg_attr(…)
509 at deny(…)
481 at forbid(…) 510 at forbid(…)
482 at ignore = "…" 511 at warn(…)
483 at inline 512 at deprecated
484 at link 513 at doc = "…"
485 at link_name = "…" 514 at doc(hidden)
486 at link_section = "…" 515 at doc(alias = "…")
487 at macro_export 516 at must_use
488 at macro_use 517 at no_mangle
518 "#]],
519 );
520 }
521
522 #[test]
523 fn complete_attribute_on_struct() {
524 check(
525 r#"#[$0] struct Foo;"#,
526 expect![[r#"
527 at allow(…)
528 at cfg(…)
529 at cfg_attr(…)
530 at deny(…)
531 at forbid(…)
532 at warn(…)
533 at deprecated
534 at doc = "…"
535 at doc(hidden)
536 at doc(alias = "…")
489 at must_use 537 at must_use
490 at no_mangle 538 at no_mangle
539 at derive(…)
540 at repr(…)
491 at non_exhaustive 541 at non_exhaustive
492 at path = "…" 542 "#]],
493 at proc_macro 543 );
494 at proc_macro_attribute 544 }
495 at proc_macro_derive(…) 545
546 #[test]
547 fn complete_attribute_on_enum() {
548 check(
549 r#"#[$0] enum Foo {}"#,
550 expect![[r#"
551 at allow(…)
552 at cfg(…)
553 at cfg_attr(…)
554 at deny(…)
555 at forbid(…)
556 at warn(…)
557 at deprecated
558 at doc = "…"
559 at doc(hidden)
560 at doc(alias = "…")
561 at must_use
562 at no_mangle
563 at derive(…)
496 at repr(…) 564 at repr(…)
497 at should_panic 565 at non_exhaustive
498 at target_feature = "…" 566 "#]],
499 at test 567 );
500 at track_caller 568 }
501 at used 569
570 #[test]
571 fn complete_attribute_on_const() {
572 check(
573 r#"#[$0] const FOO: () = ();"#,
574 expect![[r#"
575 at allow(…)
576 at cfg(…)
577 at cfg_attr(…)
578 at deny(…)
579 at forbid(…)
502 at warn(…) 580 at warn(…)
581 at deprecated
582 at doc = "…"
583 at doc(hidden)
584 at doc(alias = "…")
585 at must_use
586 at no_mangle
503 "#]], 587 "#]],
504 ) 588 );
505 } 589 }
506 590
507 #[test] 591 #[test]
508 fn test_attribute_completion_inside_nested_attr() { 592 fn complete_attribute_on_static() {
509 check(r#"#[cfg($0)]"#, expect![[]]) 593 check(
594 r#"#[$0] static FOO: () = ()"#,
595 expect![[r#"
596 at allow(…)
597 at cfg(…)
598 at cfg_attr(…)
599 at deny(…)
600 at forbid(…)
601 at warn(…)
602 at deprecated
603 at doc = "…"
604 at doc(hidden)
605 at doc(alias = "…")
606 at must_use
607 at no_mangle
608 at export_name = "…"
609 at link_name = "…"
610 at link_section = "…"
611 at global_allocator
612 at used
613 "#]],
614 );
510 } 615 }
511 616
512 #[test] 617 #[test]
513 fn test_inner_attribute_completion() { 618 fn complete_attribute_on_trait() {
514 check( 619 check(
515 r"#![$0]", 620 r#"#[$0] trait Foo {}"#,
516 expect![[r#" 621 expect![[r#"
517 at allow(…) 622 at allow(…)
518 at automatically_derived 623 at cfg(…)
519 at cfg_attr(…) 624 at cfg_attr(…)
625 at deny(…)
626 at forbid(…)
627 at warn(…)
628 at deprecated
629 at doc = "…"
630 at doc(hidden)
631 at doc(alias = "…")
632 at must_use
633 at no_mangle
634 at must_use
635 "#]],
636 );
637 }
638
639 #[test]
640 fn complete_attribute_on_impl() {
641 check(
642 r#"#[$0] impl () {}"#,
643 expect![[r#"
644 at allow(…)
520 at cfg(…) 645 at cfg(…)
521 at cold 646 at cfg_attr(…)
522 at crate_name = ""
523 at deny(…) 647 at deny(…)
648 at forbid(…)
649 at warn(…)
524 at deprecated 650 at deprecated
525 at derive(…) 651 at doc = "…"
526 at export_name = "…" 652 at doc(hidden)
653 at doc(alias = "…")
654 at must_use
655 at no_mangle
656 at automatically_derived
657 "#]],
658 );
659 check(
660 r#"impl () {#![$0]}"#,
661 expect![[r#"
662 at allow(…)
663 at cfg(…)
664 at cfg_attr(…)
665 at deny(…)
666 at forbid(…)
667 at warn(…)
668 at deprecated
669 at doc = "…"
670 at doc(hidden)
527 at doc(alias = "…") 671 at doc(alias = "…")
672 at must_use
673 at no_mangle
674 "#]],
675 );
676 }
677
678 #[test]
679 fn complete_attribute_on_extern_block() {
680 check(
681 r#"#[$0] extern {}"#,
682 expect![[r#"
683 at allow(…)
684 at cfg(…)
685 at cfg_attr(…)
686 at deny(…)
687 at forbid(…)
688 at warn(…)
689 at deprecated
528 at doc = "…" 690 at doc = "…"
529 at doc(hidden) 691 at doc(hidden)
530 at feature(…) 692 at doc(alias = "…")
693 at must_use
694 at no_mangle
695 at link
696 "#]],
697 );
698 check(
699 r#"extern {#![$0]}"#,
700 expect![[r#"
701 at allow(…)
702 at cfg(…)
703 at cfg_attr(…)
704 at deny(…)
531 at forbid(…) 705 at forbid(…)
532 at global_allocator 706 at warn(…)
533 at ignore = "…" 707 at deprecated
534 at inline 708 at doc = "…"
709 at doc(hidden)
710 at doc(alias = "…")
711 at must_use
712 at no_mangle
535 at link 713 at link
714 "#]],
715 );
716 }
717
718 #[test]
719 fn complete_attribute_on_variant() {
720 check(
721 r#"enum Foo { #[$0] Bar }"#,
722 expect![[r#"
723 at allow(…)
724 at cfg(…)
725 at cfg_attr(…)
726 at deny(…)
727 at forbid(…)
728 at warn(…)
729 at non_exhaustive
730 "#]],
731 );
732 }
733
734 #[test]
735 fn complete_attribute_on_fn() {
736 check(
737 r#"#[$0] fn main() {}"#,
738 expect![[r#"
739 at allow(…)
740 at cfg(…)
741 at cfg_attr(…)
742 at deny(…)
743 at forbid(…)
744 at warn(…)
745 at deprecated
746 at doc = "…"
747 at doc(hidden)
748 at doc(alias = "…")
749 at must_use
750 at no_mangle
751 at export_name = "…"
536 at link_name = "…" 752 at link_name = "…"
537 at link_section = "…" 753 at link_section = "…"
538 at macro_export 754 at cold
539 at macro_use 755 at ignore = "…"
756 at inline
540 at must_use 757 at must_use
541 at no_link
542 at no_implicit_prelude
543 at no_main
544 at no_mangle
545 at no_std
546 at non_exhaustive
547 at panic_handler 758 at panic_handler
548 at path = "…"
549 at proc_macro 759 at proc_macro
550 at proc_macro_attribute
551 at proc_macro_derive(…) 760 at proc_macro_derive(…)
552 at recursion_limit = … 761 at proc_macro_attribute
553 at repr(…)
554 at should_panic 762 at should_panic
555 at target_feature = "…" 763 at target_feature = "…"
556 at test 764 at test
557 at track_caller 765 at track_caller
558 at type_length_limit = … 766 "#]],
559 at used 767 );
768 }
769
770 #[test]
771 fn complete_attribute_on_expr() {
772 check(
773 r#"fn main() { #[$0] foo() }"#,
774 expect![[r#"
775 at allow(…)
776 at cfg(…)
777 at cfg_attr(…)
778 at deny(…)
779 at forbid(…)
560 at warn(…) 780 at warn(…)
561 at windows_subsystem = "…"
562 "#]], 781 "#]],
563 ); 782 );
564 } 783 }
diff --git a/crates/ide_completion/src/completions/attribute/derive.rs b/crates/ide_completion/src/completions/attribute/derive.rs
new file mode 100644
index 000000000..0bc3eab98
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/derive.rs
@@ -0,0 +1,147 @@
1//! Completion for derives
2use itertools::Itertools;
3use rustc_hash::FxHashSet;
4use syntax::ast;
5
6use crate::{
7 context::CompletionContext,
8 item::{CompletionItem, CompletionItemKind, CompletionKind},
9 Completions,
10};
11
12pub(super) fn complete_derive(
13 acc: &mut Completions,
14 ctx: &CompletionContext,
15 derive_input: ast::TokenTree,
16) {
17 if let Some(existing_derives) = super::parse_comma_sep_input(derive_input) {
18 for derive_completion in DEFAULT_DERIVE_COMPLETIONS
19 .iter()
20 .filter(|completion| !existing_derives.contains(completion.label))
21 {
22 let mut components = vec![derive_completion.label];
23 components.extend(
24 derive_completion
25 .dependencies
26 .iter()
27 .filter(|&&dependency| !existing_derives.contains(dependency)),
28 );
29 let lookup = components.join(", ");
30 let label = components.iter().rev().join(", ");
31 let mut item =
32 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
33 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
34 item.add_to(acc);
35 }
36
37 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
38 let mut item = CompletionItem::new(
39 CompletionKind::Attribute,
40 ctx.source_range(),
41 custom_derive_name,
42 );
43 item.kind(CompletionItemKind::Attribute);
44 item.add_to(acc);
45 }
46 }
47}
48
49fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
50 let mut result = FxHashSet::default();
51 ctx.scope.process_all_names(&mut |name, scope_def| {
52 if let hir::ScopeDef::MacroDef(mac) = scope_def {
53 if mac.kind() == hir::MacroKind::Derive {
54 result.insert(name.to_string());
55 }
56 }
57 });
58 result
59}
60
61struct DeriveCompletion {
62 label: &'static str,
63 dependencies: &'static [&'static str],
64}
65
66/// Standard Rust derives and the information about their dependencies
67/// (the dependencies are needed so that the main derive don't break the compilation when added)
68const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
69 DeriveCompletion { label: "Clone", dependencies: &[] },
70 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
71 DeriveCompletion { label: "Debug", dependencies: &[] },
72 DeriveCompletion { label: "Default", dependencies: &[] },
73 DeriveCompletion { label: "Hash", dependencies: &[] },
74 DeriveCompletion { label: "PartialEq", dependencies: &[] },
75 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
76 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
77 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
78];
79
80#[cfg(test)]
81mod tests {
82 use expect_test::{expect, Expect};
83
84 use crate::{test_utils::completion_list, CompletionKind};
85
86 fn check(ra_fixture: &str, expect: Expect) {
87 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
88 expect.assert_eq(&actual);
89 }
90
91 #[test]
92 fn no_completion_for_incorrect_derive() {
93 check(r#"#[derive{$0)] struct Test;"#, expect![[]])
94 }
95
96 #[test]
97 fn empty_derive() {
98 check(
99 r#"#[derive($0)] struct Test;"#,
100 expect![[r#"
101 at Clone
102 at Clone, Copy
103 at Debug
104 at Default
105 at Hash
106 at PartialEq
107 at PartialEq, Eq
108 at PartialEq, PartialOrd
109 at PartialEq, Eq, PartialOrd, Ord
110 "#]],
111 );
112 }
113
114 #[test]
115 fn derive_with_input() {
116 check(
117 r#"#[derive(serde::Serialize, PartialEq, $0)] struct Test;"#,
118 expect![[r#"
119 at Clone
120 at Clone, Copy
121 at Debug
122 at Default
123 at Hash
124 at Eq
125 at PartialOrd
126 at Eq, PartialOrd, Ord
127 "#]],
128 )
129 }
130
131 #[test]
132 fn derive_with_input2() {
133 check(
134 r#"#[derive($0 serde::Serialize, PartialEq)] struct Test;"#,
135 expect![[r#"
136 at Clone
137 at Clone, Copy
138 at Debug
139 at Default
140 at Hash
141 at Eq
142 at PartialOrd
143 at Eq, PartialOrd, Ord
144 "#]],
145 )
146 }
147}
diff --git a/crates/ide_completion/src/completions/attribute/lint.rs b/crates/ide_completion/src/completions/attribute/lint.rs
new file mode 100644
index 000000000..403630dce
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/lint.rs
@@ -0,0 +1,187 @@
1//! Completion for lints
2use syntax::ast;
3
4use crate::{
5 context::CompletionContext,
6 item::{CompletionItem, CompletionItemKind, CompletionKind},
7 Completions,
8};
9
10pub(super) fn complete_lint(
11 acc: &mut Completions,
12 ctx: &CompletionContext,
13 derive_input: ast::TokenTree,
14 lints_completions: &[LintCompletion],
15) {
16 if let Some(existing_lints) = super::parse_comma_sep_input(derive_input) {
17 for lint_completion in lints_completions
18 .into_iter()
19 .filter(|completion| !existing_lints.contains(completion.label))
20 {
21 let mut item = CompletionItem::new(
22 CompletionKind::Attribute,
23 ctx.source_range(),
24 lint_completion.label,
25 );
26 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
27 item.add_to(acc)
28 }
29 }
30}
31
32pub(crate) struct LintCompletion {
33 pub(crate) label: &'static str,
34 pub(crate) description: &'static str,
35}
36
37#[rustfmt::skip]
38pub(super) const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
39 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
40 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
41 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
42 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
43 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
44 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
45 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
46 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
47 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
48 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
49 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
50 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
51 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
52 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
53 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
54 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
55 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
56 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
57 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
58 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
59 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
60 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
61 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
62 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
63 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
64 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
65 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
66 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
67 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
68 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
69 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
70 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
71 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
72 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
73 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
74 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
75 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
76 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
77 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
78 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
79 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
80 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
81 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
82 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
83 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
84 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
85 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
86 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
87 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
88 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
89 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
90 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
91 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
92 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
93 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
94 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
95 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
96 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
97 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
98 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
99 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
100 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
101 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
102 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
103 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
104 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
105 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
106 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
107 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
108 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
109 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
110 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
111 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
112 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
113 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
114 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
115 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
116 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
117 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
118 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
119 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
120 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
121 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
122 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
123 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
124 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
125 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
126 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
127 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
128 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
129 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
130 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
131 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
132 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
133 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
134 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
135 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
136 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
137 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
138 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
139 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
140 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
141 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
142 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
143 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
144 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
145 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
146 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
147 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
148 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
149 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
150 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
151 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
152 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
153 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
154];
155
156#[cfg(test)]
157mod tests {
158
159 use crate::test_utils::check_edit;
160
161 #[test]
162 fn check_empty() {
163 check_edit(
164 "deprecated",
165 r#"#[allow($0)] struct Test;"#,
166 r#"#[allow(deprecated)] struct Test;"#,
167 )
168 }
169
170 #[test]
171 fn check_with_existing() {
172 check_edit(
173 "deprecated",
174 r#"#[allow(keyword_idents, $0)] struct Test;"#,
175 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
176 )
177 }
178
179 #[test]
180 fn check_qualified() {
181 check_edit(
182 "deprecated",
183 r#"#[allow(keyword_idents, $0)] struct Test;"#,
184 r#"#[allow(keyword_idents, deprecated)] struct Test;"#,
185 )
186 }
187}
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index be9cfbded..df27e7a84 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -110,7 +110,11 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
110 if !ctx.config.enable_imports_on_the_fly { 110 if !ctx.config.enable_imports_on_the_fly {
111 return None; 111 return None;
112 } 112 }
113 if ctx.use_item_syntax.is_some() || ctx.is_path_disallowed() { 113 if ctx.use_item_syntax.is_some()
114 || ctx.is_path_disallowed()
115 || ctx.expects_item()
116 || ctx.expects_assoc_item()
117 {
114 return None; 118 return None;
115 } 119 }
116 let potential_import_name = { 120 let potential_import_name = {
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 0ea558489..cb90e8a3e 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -128,4 +128,19 @@ fn outer(text: String) {
128 "#]], 128 "#]],
129 ) 129 )
130 } 130 }
131
132 #[test]
133 fn completes_non_ident_pat_param() {
134 check(
135 r#"
136struct Bar { bar: u32 }
137
138fn foo(Bar { bar }: Bar) {}
139fn foo2($0) {}
140"#,
141 expect![[r#"
142 bn Bar { bar }: Bar
143 "#]],
144 )
145 }
131} 146}
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 58e35bad9..0d035c611 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -4,7 +4,10 @@ use std::iter;
4 4
5use syntax::{SyntaxKind, T}; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; 7use crate::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
9 CompletionKind, Completions,
10};
8 11
9pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
10 // complete keyword "crate" in use stmt 13 // complete keyword "crate" in use stmt
@@ -44,96 +47,95 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
44 cov_mark::hit!(no_keyword_completion_in_comments); 47 cov_mark::hit!(no_keyword_completion_in_comments);
45 return; 48 return;
46 } 49 }
47 if ctx.record_lit_syntax.is_some() { 50 if matches!(ctx.completion_location, Some(ImmediateLocation::RecordExpr(_))) {
48 cov_mark::hit!(no_keyword_completion_in_record_lit); 51 cov_mark::hit!(no_keyword_completion_in_record_lit);
49 return; 52 return;
50 } 53 }
54 let mut add_keyword = |kw, snippet| add_keyword(ctx, acc, kw, snippet);
51 55
52 let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent(); 56 let expects_assoc_item = ctx.expects_assoc_item();
53 let has_block_expr_parent = ctx.has_block_expr_parent(); 57 let has_block_expr_parent = ctx.has_block_expr_parent();
54 let has_item_list_parent = ctx.has_item_list_parent(); 58 let expects_item = ctx.expects_item();
59
55 if ctx.has_impl_or_trait_prev_sibling() { 60 if ctx.has_impl_or_trait_prev_sibling() {
56 add_keyword(ctx, acc, "where", "where "); 61 add_keyword("where", "where ");
57 return; 62 return;
58 } 63 }
59 if ctx.previous_token_is(T![unsafe]) { 64 if ctx.previous_token_is(T![unsafe]) {
60 if has_item_list_parent || has_block_expr_parent { 65 if expects_item || expects_assoc_item || has_block_expr_parent {
61 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}") 66 add_keyword("fn", "fn $1($2) {\n $0\n}")
62 } 67 }
63 68
64 if has_item_list_parent || has_block_expr_parent { 69 if expects_item || has_block_expr_parent {
65 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}"); 70 add_keyword("trait", "trait $1 {\n $0\n}");
66 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}"); 71 add_keyword("impl", "impl $1 {\n $0\n}");
67 } 72 }
68 73
69 return; 74 return;
70 } 75 }
71 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent {
72 add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}");
73 }
74 if has_item_list_parent || has_block_expr_parent {
75 add_keyword(ctx, acc, "use", "use ");
76 add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
77 add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
78 }
79 76
80 if has_item_list_parent { 77 if expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_record_field() {
81 add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}"); 78 add_keyword("pub(crate)", "pub(crate) ");
82 add_keyword(ctx, acc, "struct", "struct $0"); 79 add_keyword("pub", "pub ");
83 add_keyword(ctx, acc, "union", "union $1 {\n $0\n}");
84 } 80 }
85 81
86 if ctx.is_expr { 82 if expects_item || expects_assoc_item || has_block_expr_parent {
87 add_keyword(ctx, acc, "match", "match $1 {\n $0\n}"); 83 add_keyword("unsafe", "unsafe ");
88 add_keyword(ctx, acc, "while", "while $1 {\n $0\n}"); 84 add_keyword("fn", "fn $1($2) {\n $0\n}");
89 add_keyword(ctx, acc, "while let", "while let $1 = $2 {\n $0\n}"); 85 add_keyword("const", "const $0");
90 add_keyword(ctx, acc, "loop", "loop {\n $0\n}"); 86 add_keyword("type", "type $0");
91 add_keyword(ctx, acc, "if", "if $1 {\n $0\n}");
92 add_keyword(ctx, acc, "if let", "if let $1 = $2 {\n $0\n}");
93 add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}");
94 } 87 }
95 88
96 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent { 89 if expects_item || has_block_expr_parent {
97 add_keyword(ctx, acc, "let", "let "); 90 add_keyword("use", "use $0");
91 add_keyword("impl", "impl $1 {\n $0\n}");
92 add_keyword("trait", "trait $1 {\n $0\n}");
93 add_keyword("static", "static $0");
94 add_keyword("extern", "extern $0");
95 add_keyword("mod", "mod $0");
98 } 96 }
99 97
100 if ctx.after_if { 98 if expects_item {
101 add_keyword(ctx, acc, "else", "else {\n $0\n}"); 99 add_keyword("enum", "enum $1 {\n $0\n}");
102 add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}"); 100 add_keyword("struct", "struct $0");
103 } 101 add_keyword("union", "union $1 {\n $0\n}");
104 if has_item_list_parent || has_block_expr_parent {
105 add_keyword(ctx, acc, "mod", "mod $0");
106 } 102 }
107 if ctx.has_ident_or_ref_pat_parent() { 103
108 add_keyword(ctx, acc, "mut", "mut "); 104 if ctx.expects_expression() {
105 if !has_block_expr_parent {
106 add_keyword("unsafe", "unsafe {\n $0\n}");
107 }
108 add_keyword("match", "match $1 {\n $0\n}");
109 add_keyword("while", "while $1 {\n $0\n}");
110 add_keyword("while let", "while let $1 = $2 {\n $0\n}");
111 add_keyword("loop", "loop {\n $0\n}");
112 add_keyword("if", "if $1 {\n $0\n}");
113 add_keyword("if let", "if let $1 = $2 {\n $0\n}");
114 add_keyword("for", "for $1 in $2 {\n $0\n}");
109 } 115 }
110 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent { 116
111 add_keyword(ctx, acc, "const", "const "); 117 if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
112 add_keyword(ctx, acc, "type", "type "); 118 add_keyword("let", "let ");
113 } 119 }
114 if has_item_list_parent || has_block_expr_parent { 120
115 add_keyword(ctx, acc, "static", "static "); 121 if ctx.after_if() {
116 }; 122 add_keyword("else", "else {\n $0\n}");
117 if has_item_list_parent || has_block_expr_parent { 123 add_keyword("else if", "else if $1 {\n $0\n}");
118 add_keyword(ctx, acc, "extern", "extern ");
119 } 124 }
120 if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm 125
121 { 126 if ctx.expects_ident_pat_or_ref_expr() {
122 add_keyword(ctx, acc, "unsafe", "unsafe "); 127 add_keyword("mut", "mut ");
123 } 128 }
129
124 if ctx.in_loop_body { 130 if ctx.in_loop_body {
125 if ctx.can_be_stmt { 131 if ctx.can_be_stmt {
126 add_keyword(ctx, acc, "continue", "continue;"); 132 add_keyword("continue", "continue;");
127 add_keyword(ctx, acc, "break", "break;"); 133 add_keyword("break", "break;");
128 } else { 134 } else {
129 add_keyword(ctx, acc, "continue", "continue"); 135 add_keyword("continue", "continue");
130 add_keyword(ctx, acc, "break", "break"); 136 add_keyword("break", "break");
131 } 137 }
132 } 138 }
133 if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() {
134 add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
135 add_keyword(ctx, acc, "pub", "pub ");
136 }
137 139
138 if !ctx.is_trivial_path { 140 if !ctx.is_trivial_path {
139 return; 141 return;
@@ -144,8 +146,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
144 }; 146 };
145 147
146 add_keyword( 148 add_keyword(
147 ctx,
148 acc,
149 "return", 149 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 151 (true, true) => "return $0;",
@@ -162,15 +162,12 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
162 162
163 match ctx.config.snippet_cap { 163 match ctx.config.snippet_cap {
164 Some(cap) => { 164 Some(cap) => {
165 let tmp; 165 if snippet.ends_with('}') && ctx.incomplete_let {
166 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
167 cov_mark::hit!(let_semi); 166 cov_mark::hit!(let_semi);
168 tmp = format!("{};", snippet); 167 item.insert_snippet(cap, format!("{};", snippet));
169 &tmp
170 } else { 168 } else {
171 snippet 169 item.insert_snippet(cap, snippet);
172 }; 170 }
173 item.insert_snippet(cap, snippet);
174 } 171 }
175 None => { 172 None => {
176 item.insert_text(if snippet.contains('$') { kw } else { snippet }); 173 item.insert_text(if snippet.contains('$') { kw } else { snippet });
@@ -233,21 +230,21 @@ mod tests {
233 check( 230 check(
234 r"m$0", 231 r"m$0",
235 expect![[r#" 232 expect![[r#"
233 kw pub(crate)
234 kw pub
235 kw unsafe
236 kw fn 236 kw fn
237 kw const
238 kw type
237 kw use 239 kw use
238 kw impl 240 kw impl
239 kw trait 241 kw trait
242 kw static
243 kw extern
244 kw mod
240 kw enum 245 kw enum
241 kw struct 246 kw struct
242 kw union 247 kw union
243 kw mod
244 kw const
245 kw type
246 kw static
247 kw extern
248 kw unsafe
249 kw pub(crate)
250 kw pub
251 "#]], 248 "#]],
252 ); 249 );
253 } 250 }
@@ -257,10 +254,16 @@ mod tests {
257 check( 254 check(
258 r"fn quux() { $0 }", 255 r"fn quux() { $0 }",
259 expect![[r#" 256 expect![[r#"
257 kw unsafe
260 kw fn 258 kw fn
259 kw const
260 kw type
261 kw use 261 kw use
262 kw impl 262 kw impl
263 kw trait 263 kw trait
264 kw static
265 kw extern
266 kw mod
264 kw match 267 kw match
265 kw while 268 kw while
266 kw while let 269 kw while let
@@ -269,12 +272,6 @@ mod tests {
269 kw if let 272 kw if let
270 kw for 273 kw for
271 kw let 274 kw let
272 kw mod
273 kw const
274 kw type
275 kw static
276 kw extern
277 kw unsafe
278 kw return 275 kw return
279 "#]], 276 "#]],
280 ); 277 );
@@ -285,10 +282,16 @@ mod tests {
285 check( 282 check(
286 r"fn quux() { if true { $0 } }", 283 r"fn quux() { if true { $0 } }",
287 expect![[r#" 284 expect![[r#"
285 kw unsafe
288 kw fn 286 kw fn
287 kw const
288 kw type
289 kw use 289 kw use
290 kw impl 290 kw impl
291 kw trait 291 kw trait
292 kw static
293 kw extern
294 kw mod
292 kw match 295 kw match
293 kw while 296 kw while
294 kw while let 297 kw while let
@@ -297,12 +300,6 @@ mod tests {
297 kw if let 300 kw if let
298 kw for 301 kw for
299 kw let 302 kw let
300 kw mod
301 kw const
302 kw type
303 kw static
304 kw extern
305 kw unsafe
306 kw return 303 kw return
307 "#]], 304 "#]],
308 ); 305 );
@@ -313,10 +310,16 @@ mod tests {
313 check( 310 check(
314 r#"fn quux() { if true { () } $0 }"#, 311 r#"fn quux() { if true { () } $0 }"#,
315 expect![[r#" 312 expect![[r#"
313 kw unsafe
316 kw fn 314 kw fn
315 kw const
316 kw type
317 kw use 317 kw use
318 kw impl 318 kw impl
319 kw trait 319 kw trait
320 kw static
321 kw extern
322 kw mod
320 kw match 323 kw match
321 kw while 324 kw while
322 kw while let 325 kw while let
@@ -327,12 +330,6 @@ mod tests {
327 kw let 330 kw let
328 kw else 331 kw else
329 kw else if 332 kw else if
330 kw mod
331 kw const
332 kw type
333 kw static
334 kw extern
335 kw unsafe
336 kw return 333 kw return
337 "#]], 334 "#]],
338 ); 335 );
@@ -354,6 +351,7 @@ fn quux() -> i32 {
354} 351}
355"#, 352"#,
356 expect![[r#" 353 expect![[r#"
354 kw unsafe
357 kw match 355 kw match
358 kw while 356 kw while
359 kw while let 357 kw while let
@@ -361,7 +359,6 @@ fn quux() -> i32 {
361 kw if 359 kw if
362 kw if let 360 kw if let
363 kw for 361 kw for
364 kw unsafe
365 kw return 362 kw return
366 "#]], 363 "#]],
367 ); 364 );
@@ -372,10 +369,10 @@ fn quux() -> i32 {
372 check( 369 check(
373 r"trait My { $0 }", 370 r"trait My { $0 }",
374 expect![[r#" 371 expect![[r#"
372 kw unsafe
375 kw fn 373 kw fn
376 kw const 374 kw const
377 kw type 375 kw type
378 kw unsafe
379 "#]], 376 "#]],
380 ); 377 );
381 } 378 }
@@ -385,12 +382,27 @@ fn quux() -> i32 {
385 check( 382 check(
386 r"impl My { $0 }", 383 r"impl My { $0 }",
387 expect![[r#" 384 expect![[r#"
385 kw pub(crate)
386 kw pub
387 kw unsafe
388 kw fn 388 kw fn
389 kw const 389 kw const
390 kw type 390 kw type
391 kw unsafe 391 "#]],
392 );
393 }
394
395 #[test]
396 fn test_keywords_in_impl_def_with_attr() {
397 check(
398 r"impl My { #[foo] $0 }",
399 expect![[r#"
392 kw pub(crate) 400 kw pub(crate)
393 kw pub 401 kw pub
402 kw unsafe
403 kw fn
404 kw const
405 kw type
394 "#]], 406 "#]],
395 ); 407 );
396 } 408 }
@@ -400,10 +412,16 @@ fn quux() -> i32 {
400 check( 412 check(
401 r"fn my() { loop { $0 } }", 413 r"fn my() { loop { $0 } }",
402 expect![[r#" 414 expect![[r#"
415 kw unsafe
403 kw fn 416 kw fn
417 kw const
418 kw type
404 kw use 419 kw use
405 kw impl 420 kw impl
406 kw trait 421 kw trait
422 kw static
423 kw extern
424 kw mod
407 kw match 425 kw match
408 kw while 426 kw while
409 kw while let 427 kw while let
@@ -412,12 +430,6 @@ fn quux() -> i32 {
412 kw if let 430 kw if let
413 kw for 431 kw for
414 kw let 432 kw let
415 kw mod
416 kw const
417 kw type
418 kw static
419 kw extern
420 kw unsafe
421 kw continue 433 kw continue
422 kw break 434 kw break
423 kw return 435 kw return
@@ -564,6 +576,7 @@ pub mod future {
564 check( 576 check(
565 r#"fn main() { let _ = $0 }"#, 577 r#"fn main() { let _ = $0 }"#,
566 expect![[r#" 578 expect![[r#"
579 kw unsafe
567 kw match 580 kw match
568 kw while 581 kw while
569 kw while let 582 kw while let
@@ -624,6 +637,7 @@ fn foo() {
624} 637}
625"#, 638"#,
626 expect![[r#" 639 expect![[r#"
640 kw unsafe
627 kw match 641 kw match
628 kw while 642 kw while
629 kw while let 643 kw while let
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
index c5e377500..ec57aee30 100644
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ b/crates/ide_completion/src/completions/macro_in_item_position.rs
@@ -2,6 +2,7 @@
2 2
3use crate::{CompletionContext, Completions}; 3use crate::{CompletionContext, Completions};
4 4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
5pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 7 // Show only macros in top level.
7 if !ctx.is_new_item { 8 if !ctx.is_new_item {
@@ -12,6 +13,10 @@ pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &Compl
12 if let hir::ScopeDef::MacroDef(mac) = res { 13 if let hir::ScopeDef::MacroDef(mac) = res {
13 acc.add_macro(ctx, Some(name.to_string()), mac); 14 acc.add_macro(ctx, Some(name.to_string()), mac);
14 } 15 }
16 // FIXME: This should be done in qualified_path/unqualified_path instead?
17 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
18 acc.add_resolution(ctx, name.to_string(), &res);
19 }
15 }) 20 })
16} 21}
17 22
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 4f9415736..6a5746fb9 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -9,14 +9,14 @@ use ide_db::{
9}; 9};
10use rustc_hash::FxHashSet; 10use rustc_hash::FxHashSet;
11 11
12use crate::CompletionItem; 12use crate::{patterns::ImmediateLocation, CompletionItem};
13 13
14use crate::{context::CompletionContext, item::CompletionKind, Completions}; 14use crate::{context::CompletionContext, item::CompletionKind, Completions};
15 15
16/// Complete mod declaration, i.e. `mod $0 ;` 16/// Complete mod declaration, i.e. `mod $0 ;`
17pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 17pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
18 let mod_under_caret = match &ctx.mod_declaration_under_caret { 18 let mod_under_caret = match &ctx.completion_location {
19 Some(mod_under_caret) if mod_under_caret.item_list().is_none() => mod_under_caret, 19 Some(ImmediateLocation::ModDeclaration(mod_under_caret)) => mod_under_caret,
20 _ => return None, 20 _ => return None,
21 }; 21 };
22 22
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index ed48f61af..7a0e1ead3 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,7 +7,7 @@ use syntax::AstNode;
7use crate::{CompletionContext, Completions}; 7use crate::{CompletionContext, Completions};
8 8
9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) { 9pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionContext) {
10 if ctx.is_path_disallowed() { 10 if ctx.is_path_disallowed() || ctx.expects_item() {
11 return; 11 return;
12 } 12 }
13 let path = match &ctx.path_qual { 13 let path = match &ctx.path_qual {
@@ -20,6 +20,20 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
20 None => return, 20 None => return,
21 }; 21 };
22 let context_module = ctx.scope.module(); 22 let context_module = ctx.scope.module();
23 if ctx.expects_assoc_item() {
24 if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope {
27 if let ScopeDef::MacroDef(macro_def) = def {
28 acc.add_macro(ctx, Some(name.to_string()), macro_def);
29 }
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
31 acc.add_resolution(ctx, name.to_string(), &def);
32 }
33 }
34 }
35 return;
36 }
23 37
24 // Add associated types on type parameters and `Self`. 38 // Add associated types on type parameters and `Self`.
25 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| { 39 resolution.assoc_type_shorthand_candidates(ctx.db, |_, alias| {
@@ -594,7 +608,7 @@ fn main() { T::$0; }
594macro_rules! foo { () => {} } 608macro_rules! foo { () => {} }
595 609
596fn main() { let _ = crate::$0 } 610fn main() { let _ = crate::$0 }
597 "#, 611"#,
598 expect![[r##" 612 expect![[r##"
599 fn main() fn() 613 fn main() fn()
600 ma foo!(…) #[macro_export] macro_rules! foo 614 ma foo!(…) #[macro_export] macro_rules! foo
@@ -603,6 +617,44 @@ fn main() { let _ = crate::$0 }
603 } 617 }
604 618
605 #[test] 619 #[test]
620 fn completes_in_assoc_item_list() {
621 check(
622 r#"
623#[macro_export]
624macro_rules! foo { () => {} }
625mod bar {}
626
627struct MyStruct {}
628impl MyStruct {
629 crate::$0
630}
631"#,
632 expect![[r##"
633 md bar
634 ma foo! #[macro_export] macro_rules! foo
635 "##]],
636 );
637 }
638
639 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() {
642 check(
643 r#"
644struct MyStruct {}
645macro_rules! foo {}
646mod bar {}
647
648crate::$0
649"#,
650 expect![[r#"
651 md bar
652 ma foo! macro_rules! foo
653 "#]],
654 )
655 }
656
657 #[test]
606 fn test_super_super_completion() { 658 fn test_super_super_completion() {
607 check( 659 check(
608 r#" 660 r#"
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index e1526b70b..227c08d01 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -2,21 +2,21 @@
2use ide_db::{helpers::FamousDefs, SymbolKind}; 2use ide_db::{helpers::FamousDefs, SymbolKind};
3use syntax::ast::Expr; 3use syntax::ast::Expr;
4 4
5use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions}; 5use crate::{
6 item::CompletionKind, patterns::ImmediateLocation, CompletionContext, CompletionItem,
7 Completions,
8};
6 9
7pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 10pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
8 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) { 11 let missing_fields = match &ctx.completion_location {
9 (None, None) => return None, 12 Some(ImmediateLocation::RecordExpr(record_expr)) => {
10 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), 13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
11 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
12 (_, Some(record_lit)) => {
13 let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
14 let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default(); 14 let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
15 let impl_default_trait = default_trait 15 let impl_default_trait = default_trait
16 .zip(ty) 16 .zip(ty)
17 .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[])); 17 .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[]));
18 18
19 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); 19 let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
20 if impl_default_trait && !missing_fields.is_empty() { 20 if impl_default_trait && !missing_fields.is_empty() {
21 let completion_text = "..Default::default()"; 21 let completion_text = "..Default::default()";
22 let mut item = CompletionItem::new( 22 let mut item = CompletionItem::new(
@@ -32,6 +32,10 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
32 32
33 missing_fields 33 missing_fields
34 } 34 }
35 Some(ImmediateLocation::RecordPat(record_pat)) => {
36 ctx.sema.record_pattern_missing_fields(record_pat)
37 }
38 _ => return None,
35 }; 39 };
36 40
37 for (field, ty) in missing_fields { 41 for (field, ty) in missing_fields {
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 046a393ae..ede07f605 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -1,7 +1,6 @@
1//! Completion of names from the current scope, e.g. locals and imported items. 1//! Completion of names from the current scope, e.g. locals and imported items.
2 2
3use hir::ScopeDef; 3use hir::ScopeDef;
4use syntax::AstNode;
5 4
6use crate::{CompletionContext, Completions}; 5use crate::{CompletionContext, Completions};
7 6
@@ -9,10 +8,30 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
9 if !ctx.is_trivial_path { 8 if !ctx.is_trivial_path {
10 return; 9 return;
11 } 10 }
12 if ctx.is_path_disallowed() { 11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return;
13 }
14 if ctx.expects_assoc_item() {
15 ctx.scope.process_all_names(&mut |name, def| {
16 if let ScopeDef::MacroDef(macro_def) = def {
17 acc.add_macro(ctx, Some(name.to_string()), macro_def);
18 }
19 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
20 acc.add_resolution(ctx, name.to_string(), &def);
21 }
22 });
13 return; 23 return;
14 } 24 }
15 25
26 if ctx.expects_use_tree() {
27 cov_mark::hit!(only_completes_modules_in_import);
28 ctx.scope.process_all_names(&mut |name, res| {
29 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
30 acc.add_resolution(ctx, name.to_string(), &res);
31 }
32 });
33 return;
34 }
16 if let Some(hir::Adt::Enum(e)) = 35 if let Some(hir::Adt::Enum(e)) =
17 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 36 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
18 { 37 {
@@ -26,14 +45,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
26 cov_mark::hit!(skip_lifetime_completion); 45 cov_mark::hit!(skip_lifetime_completion);
27 return; 46 return;
28 } 47 }
29 if ctx.use_item_syntax.is_some() {
30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
31 if name_ref.syntax().text() == name.to_string().as_str() {
32 cov_mark::hit!(self_fulfilling_completion);
33 return;
34 }
35 }
36 }
37 acc.add_resolution(ctx, name.to_string(), &res); 48 acc.add_resolution(ctx, name.to_string(), &res);
38 }); 49 });
39} 50}
@@ -57,15 +68,17 @@ mod tests {
57 } 68 }
58 69
59 #[test] 70 #[test]
60 fn self_fulfilling_completion() { 71 fn only_completes_modules_in_import() {
61 cov_mark::check!(self_fulfilling_completion); 72 cov_mark::check!(only_completes_modules_in_import);
62 check( 73 check(
63 r#" 74 r#"
64use foo$0 75use f$0
65use std::collections; 76
77struct Foo;
78mod foo {}
66"#, 79"#,
67 expect![[r#" 80 expect![[r#"
68 ?? collections 81 md foo
69 "#]], 82 "#]],
70 ); 83 );
71 } 84 }
@@ -647,7 +660,7 @@ fn f() {}
647 } 660 }
648 661
649 #[test] 662 #[test]
650 fn completes_type_or_trait_in_impl_block() { 663 fn completes_target_type_or_trait_in_impl_block() {
651 check( 664 check(
652 r#" 665 r#"
653trait MyTrait {} 666trait MyTrait {}
@@ -662,4 +675,41 @@ impl My$0
662 "#]], 675 "#]],
663 ) 676 )
664 } 677 }
678
679 #[test]
680 fn completes_in_assoc_item_list() {
681 check(
682 r#"
683macro_rules! foo {}
684mod bar {}
685
686struct MyStruct {}
687impl MyStruct {
688 $0
689}
690"#,
691 expect![[r#"
692 md bar
693 ma foo! macro_rules! foo
694 "#]],
695 )
696 }
697
698 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
699 #[test]
700 fn completes_in_item_list() {
701 check(
702 r#"
703struct MyStruct {}
704macro_rules! foo {}
705mod bar {}
706
707$0
708"#,
709 expect![[r#"
710 md bar
711 ma foo!(…) macro_rules! foo
712 "#]],
713 )
714 }
665} 715}
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 66577df94..7c46c815d 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -17,9 +17,8 @@ use text_edit::Indel;
17 17
18use crate::{ 18use crate::{
19 patterns::{ 19 patterns::{
20 for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, 20 determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
21 has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, 21 is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
22 has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
23 }, 22 },
24 CompletionConfig, 23 CompletionConfig,
25}; 24};
@@ -30,24 +29,6 @@ pub(crate) enum PatternRefutability {
30 Irrefutable, 29 Irrefutable,
31} 30}
32 31
33/// Direct parent container of the cursor position
34#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub(crate) enum ImmediateLocation {
36 Impl,
37 Trait,
38 RecordFieldList,
39 RefPatOrExpr,
40 IdentPat,
41 BlockExpr,
42 ItemList,
43}
44
45#[derive(Copy, Clone, Debug, PartialEq, Eq)]
46pub(crate) enum PrevSibling {
47 Trait,
48 Impl,
49}
50
51/// `CompletionContext` is created early during completion to figure out, where 32/// `CompletionContext` is created early during completion to figure out, where
52/// exactly is the cursor, syntax-wise. 33/// exactly is the cursor, syntax-wise.
53#[derive(Debug)] 34#[derive(Debug)]
@@ -73,11 +54,6 @@ pub(crate) struct CompletionContext<'a> {
73 /// The parent impl of the cursor position if it exists. 54 /// The parent impl of the cursor position if it exists.
74 pub(super) impl_def: Option<ast::Impl>, 55 pub(super) impl_def: Option<ast::Impl>,
75 56
76 /// RecordExpr the token is a field of
77 pub(super) record_lit_syntax: Option<ast::RecordExpr>,
78 /// RecordPat the token is a field of
79 pub(super) record_pat_syntax: Option<ast::RecordPat>,
80
81 // potentially set if we are completing a lifetime 57 // potentially set if we are completing a lifetime
82 pub(super) lifetime_syntax: Option<ast::Lifetime>, 58 pub(super) lifetime_syntax: Option<ast::Lifetime>,
83 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, 59 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
@@ -89,6 +65,8 @@ pub(crate) struct CompletionContext<'a> {
89 pub(super) is_param: bool, 65 pub(super) is_param: bool,
90 66
91 pub(super) completion_location: Option<ImmediateLocation>, 67 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>,
92 70
93 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
94 pub(super) active_parameter: Option<ActiveParameter>, 72 pub(super) active_parameter: Option<ActiveParameter>,
@@ -96,7 +74,6 @@ pub(crate) struct CompletionContext<'a> {
96 pub(super) is_trivial_path: bool, 74 pub(super) is_trivial_path: bool,
97 /// If not a trivial path, the prefix (qualifier). 75 /// If not a trivial path, the prefix (qualifier).
98 pub(super) path_qual: Option<ast::Path>, 76 pub(super) path_qual: Option<ast::Path>,
99 pub(super) after_if: bool,
100 /// `true` if we are a statement or a last expr in the block. 77 /// `true` if we are a statement or a last expr in the block.
101 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
102 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
@@ -114,19 +91,15 @@ pub(crate) struct CompletionContext<'a> {
114 pub(super) is_macro_call: bool, 91 pub(super) is_macro_call: bool,
115 pub(super) is_path_type: bool, 92 pub(super) is_path_type: bool,
116 pub(super) has_type_args: bool, 93 pub(super) has_type_args: bool,
117 pub(super) attribute_under_caret: Option<ast::Attr>,
118 pub(super) mod_declaration_under_caret: Option<ast::Module>,
119 pub(super) locals: Vec<(String, Local)>, 94 pub(super) locals: Vec<(String, Local)>,
120 95
121 // keyword patterns
122 pub(super) previous_token: Option<SyntaxToken>, 96 pub(super) previous_token: Option<SyntaxToken>,
123 pub(super) prev_sibling: Option<PrevSibling>,
124 pub(super) in_loop_body: bool, 97 pub(super) in_loop_body: bool,
125 pub(super) is_match_arm: bool,
126 pub(super) incomplete_let: bool, 98 pub(super) incomplete_let: bool,
127 99
128 no_completion_required: bool, 100 no_completion_required: bool,
129} 101}
102
130impl<'a> CompletionContext<'a> { 103impl<'a> CompletionContext<'a> {
131 pub(super) fn new( 104 pub(super) fn new(
132 db: &'a RootDatabase, 105 db: &'a RootDatabase,
@@ -176,8 +149,6 @@ impl<'a> CompletionContext<'a> {
176 lifetime_param_syntax: None, 149 lifetime_param_syntax: None,
177 function_def: None, 150 function_def: None,
178 use_item_syntax: None, 151 use_item_syntax: None,
179 record_lit_syntax: None,
180 record_pat_syntax: None,
181 impl_def: None, 152 impl_def: None,
182 active_parameter: ActiveParameter::at(db, position), 153 active_parameter: ActiveParameter::at(db, position),
183 is_label_ref: false, 154 is_label_ref: false,
@@ -185,7 +156,6 @@ impl<'a> CompletionContext<'a> {
185 is_pat_or_const: None, 156 is_pat_or_const: None,
186 is_trivial_path: false, 157 is_trivial_path: false,
187 path_qual: None, 158 path_qual: None,
188 after_if: false,
189 can_be_stmt: false, 159 can_be_stmt: false,
190 is_expr: false, 160 is_expr: false,
191 is_new_item: false, 161 is_new_item: false,
@@ -196,15 +166,13 @@ impl<'a> CompletionContext<'a> {
196 is_macro_call: false, 166 is_macro_call: false,
197 is_path_type: false, 167 is_path_type: false,
198 has_type_args: false, 168 has_type_args: false,
199 attribute_under_caret: None,
200 mod_declaration_under_caret: None,
201 previous_token: None, 169 previous_token: None,
202 in_loop_body: false, 170 in_loop_body: false,
203 completion_location: None, 171 completion_location: None,
204 prev_sibling: None, 172 prev_sibling: None,
205 is_match_arm: false,
206 no_completion_required: false, 173 no_completion_required: false,
207 incomplete_let: false, 174 incomplete_let: false,
175 attribute_under_caret: None,
208 locals, 176 locals,
209 }; 177 };
210 178
@@ -247,7 +215,6 @@ impl<'a> CompletionContext<'a> {
247 break; 215 break;
248 } 216 }
249 } 217 }
250 ctx.fill_keyword_patterns(&speculative_file, offset);
251 ctx.fill(&original_file, speculative_file, offset); 218 ctx.fill(&original_file, speculative_file, offset);
252 Some(ctx) 219 Some(ctx)
253 } 220 }
@@ -281,88 +248,63 @@ impl<'a> CompletionContext<'a> {
281 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) 248 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
282 } 249 }
283 250
284 pub(crate) fn has_impl_or_trait_parent(&self) -> bool { 251 pub(crate) fn expects_assoc_item(&self) -> bool {
285 matches!( 252 matches!(
286 self.completion_location, 253 self.completion_location,
287 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) 254 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
288 ) 255 )
289 } 256 }
290 257
291 pub(crate) fn has_block_expr_parent(&self) -> bool { 258 pub(crate) fn expects_use_tree(&self) -> bool {
292 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 259 matches!(self.completion_location, Some(ImmediateLocation::Use))
293 } 260 }
294 261
295 pub(crate) fn has_item_list_parent(&self) -> bool { 262 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
263 matches!(self.completion_location, Some(ImmediateLocation::Impl))
264 }
265
266 pub(crate) fn expects_item(&self) -> bool {
296 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 267 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
297 } 268 }
298 269
299 pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { 270 pub(crate) fn expects_expression(&self) -> bool {
271 self.is_expr
272 }
273
274 pub(crate) fn has_block_expr_parent(&self) -> bool {
275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
276 }
277
278 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
300 matches!( 279 matches!(
301 self.completion_location, 280 self.completion_location,
302 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) 281 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
303 ) 282 )
304 } 283 }
305 284
306 pub(crate) fn has_impl_parent(&self) -> bool { 285 pub(crate) fn expect_record_field(&self) -> bool {
307 matches!(self.completion_location, Some(ImmediateLocation::Impl)) 286 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
308 }
309
310 pub(crate) fn has_field_list_parent(&self) -> bool {
311 matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
312 } 287 }
313 288
314 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { 289 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
315 self.prev_sibling.is_some() 290 matches!(
291 self.prev_sibling,
292 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
293 )
316 } 294 }
317 295
318 pub(crate) fn is_path_disallowed(&self) -> bool { 296 pub(crate) fn after_if(&self) -> bool {
319 self.record_lit_syntax.is_some() 297 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
320 || self.record_pat_syntax.is_some()
321 || self.attribute_under_caret.is_some()
322 || self.mod_declaration_under_caret.is_some()
323 || self.has_impl_or_trait_parent()
324 } 298 }
325 299
326 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 300 pub(crate) fn is_path_disallowed(&self) -> bool {
327 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 301 matches!(
328 let syntax_element = NodeOrToken::Token(fake_ident_token); 302 self.completion_location,
329 self.previous_token = previous_token(syntax_element.clone()); 303 Some(ImmediateLocation::Attribute(_))
330 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 304 | Some(ImmediateLocation::ModDeclaration(_))
331 self.is_match_arm = is_match_arm(syntax_element.clone()); 305 | Some(ImmediateLocation::RecordPat(_))
332 if has_prev_sibling(syntax_element.clone(), IMPL) { 306 | Some(ImmediateLocation::RecordExpr(_))
333 self.prev_sibling = Some(PrevSibling::Impl) 307 ) || self.attribute_under_caret.is_some()
334 } else if has_prev_sibling(syntax_element.clone(), TRAIT) {
335 self.prev_sibling = Some(PrevSibling::Trait)
336 }
337
338 if has_block_expr_parent(syntax_element.clone()) {
339 self.completion_location = Some(ImmediateLocation::BlockExpr);
340 } else if has_bind_pat_parent(syntax_element.clone()) {
341 self.completion_location = Some(ImmediateLocation::IdentPat);
342 } else if has_ref_parent(syntax_element.clone()) {
343 self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
344 } else if has_impl_parent(syntax_element.clone()) {
345 self.completion_location = Some(ImmediateLocation::Impl);
346 } else if has_field_list_parent(syntax_element.clone()) {
347 self.completion_location = Some(ImmediateLocation::RecordFieldList);
348 } else if has_trait_parent(syntax_element.clone()) {
349 self.completion_location = Some(ImmediateLocation::Trait);
350 } else if has_item_list_or_source_file_parent(syntax_element.clone()) {
351 self.completion_location = Some(ImmediateLocation::ItemList);
352 }
353
354 self.mod_declaration_under_caret =
355 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
356 .filter(|module| module.item_list().is_none());
357 self.incomplete_let =
358 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
359 it.syntax().text_range().end() == syntax_element.text_range().end()
360 });
361
362 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
363 let fn_is_prev = self.previous_token_is(T![fn]);
364 let for_is_prev2 = for_is_prev2(syntax_element.clone());
365 self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
366 } 308 }
367 309
368 fn fill_impl_def(&mut self) { 310 fn fill_impl_def(&mut self) {
@@ -480,23 +422,43 @@ impl<'a> CompletionContext<'a> {
480 file_with_fake_ident: SyntaxNode, 422 file_with_fake_ident: SyntaxNode,
481 offset: TextSize, 423 offset: TextSize,
482 ) { 424 ) {
425 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
426 let syntax_element = NodeOrToken::Token(fake_ident_token);
427 self.previous_token = previous_token(syntax_element.clone());
428 self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
429 self.no_completion_required = {
430 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
431 let fn_is_prev = self.previous_token_is(T![fn]);
432 let for_is_prev2 = for_is_prev2(syntax_element.clone());
433 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
434 };
435 self.in_loop_body = is_in_loop_body(syntax_element.clone());
436
437 self.incomplete_let =
438 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
439 it.syntax().text_range().end() == syntax_element.text_range().end()
440 });
441
483 let (expected_type, expected_name) = self.expected_type_and_name(); 442 let (expected_type, expected_name) = self.expected_type_and_name();
484 self.expected_type = expected_type; 443 self.expected_type = expected_type;
485 self.expected_name = expected_name; 444 self.expected_name = expected_name;
486 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 445
487 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { 446 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
488 Some(it) => it, 447 Some(it) => it,
489 None => return, 448 None => return,
490 }; 449 };
450 self.completion_location =
451 determine_location(&self.sema, original_file, offset, &name_like);
452 self.prev_sibling = determine_prev_sibling(&name_like);
491 match name_like { 453 match name_like {
492 ast::NameLike::Lifetime(lifetime) => { 454 ast::NameLike::Lifetime(lifetime) => {
493 self.classify_lifetime(original_file, lifetime, offset); 455 self.classify_lifetime(original_file, lifetime, offset);
494 } 456 }
495 ast::NameLike::NameRef(name_ref) => { 457 ast::NameLike::NameRef(name_ref) => {
496 self.classify_name_ref(original_file, name_ref, offset); 458 self.classify_name_ref(original_file, name_ref);
497 } 459 }
498 ast::NameLike::Name(name) => { 460 ast::NameLike::Name(name) => {
499 self.classify_name(original_file, name, offset); 461 self.classify_name(name);
500 } 462 }
501 } 463 }
502 } 464 }
@@ -530,7 +492,7 @@ impl<'a> CompletionContext<'a> {
530 } 492 }
531 } 493 }
532 494
533 fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { 495 fn classify_name(&mut self, name: ast::Name) {
534 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { 496 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
535 self.is_pat_or_const = Some(PatternRefutability::Refutable); 497 self.is_pat_or_const = Some(PatternRefutability::Refutable);
536 // if any of these is here our bind pat can't be a const pat anymore 498 // if any of these is here our bind pat can't be a const pat anymore
@@ -568,28 +530,12 @@ impl<'a> CompletionContext<'a> {
568 530
569 self.fill_impl_def(); 531 self.fill_impl_def();
570 } 532 }
533
571 self.is_param |= is_node::<ast::Param>(name.syntax()); 534 self.is_param |= is_node::<ast::Param>(name.syntax());
572 if ast::RecordPatField::for_field_name(&name).is_some() {
573 self.record_pat_syntax =
574 self.sema.find_node_at_offset_with_macros(&original_file, offset);
575 }
576 } 535 }
577 536
578 fn classify_name_ref( 537 fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
579 &mut self,
580 original_file: &SyntaxNode,
581 name_ref: ast::NameRef,
582 offset: TextSize,
583 ) {
584 self.fill_impl_def(); 538 self.fill_impl_def();
585 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
586 self.record_lit_syntax =
587 self.sema.find_node_at_offset_with_macros(original_file, offset);
588 }
589 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
590 self.record_pat_syntax =
591 self.sema.find_node_at_offset_with_macros(&original_file, offset);
592 }
593 539
594 self.name_ref_syntax = 540 self.name_ref_syntax =
595 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 541 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
@@ -676,17 +622,6 @@ impl<'a> CompletionContext<'a> {
676 }) 622 })
677 .unwrap_or(false); 623 .unwrap_or(false);
678 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
679
680 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
681 if let Some(if_expr) =
682 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
683 {
684 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
685 {
686 self.after_if = true;
687 }
688 }
689 }
690 } 625 }
691 626
692 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 04f2c532b..26516046b 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -1,38 +1,197 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context 1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2 2
3use hir::Semantics;
4use ide_db::RootDatabase;
3use syntax::{ 5use syntax::{
4 algo::non_trivia_sibling, 6 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner}, 7 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
7 SyntaxKind::{self, *}, 9 SyntaxKind::*,
8 SyntaxNode, SyntaxToken, T, 10 SyntaxNode, SyntaxToken, TextSize, T,
9}; 11};
10 12
11#[cfg(test)] 13#[cfg(test)]
12use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable}; 14use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
13 15
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { 16/// Direct parent container of the cursor position
15 not_same_range_ancestor(element) 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 18pub(crate) enum ImmediatePrevSibling {
17 .and_then(|it| it.parent()) 19 IfExpr,
18 .filter(|it| it.kind() == TRAIT) 20 TraitDefName,
19 .is_some() 21 ImplDefType,
20} 22}
21#[test] 23
22fn test_has_trait_parent() { 24/// Direct parent container of the cursor position
23 check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent); 25#[derive(Clone, Debug, PartialEq, Eq)]
26pub(crate) enum ImmediateLocation {
27 Use,
28 Impl,
29 Trait,
30 RecordField,
31 RefExpr,
32 IdentPat,
33 BlockExpr,
34 ItemList,
35 // Fake file ast node
36 Attribute(ast::Attr),
37 // Fake file ast node
38 ModDeclaration(ast::Module),
39 // Original file ast node
40 /// The record expr of the field name we are completing
41 RecordExpr(ast::RecordExpr),
42 // Original file ast node
43 /// The record pat of the field name we are completing
44 RecordPat(ast::RecordPat),
24} 45}
25 46
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { 47pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<ImmediatePrevSibling> {
27 not_same_range_ancestor(element) 48 let node = match name_like {
28 .filter(|it| it.kind() == ASSOC_ITEM_LIST) 49 ast::NameLike::NameRef(name_ref) => maximize_name_ref(name_ref),
29 .and_then(|it| it.parent()) 50 ast::NameLike::Name(n) => n.syntax().clone(),
30 .filter(|it| it.kind() == IMPL) 51 ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
31 .is_some() 52 };
53 let node = match node.parent().and_then(ast::MacroCall::cast) {
54 // When a path is being typed after the name of a trait/type of an impl it is being
55 // parsed as a macro, so when the trait/impl has a block following it an we are between the
56 // name and block the macro will attach the block to itself so maximizing fails to take
57 // that into account
58 // FIXME path expr and statement have a similar problem with attrs
59 Some(call)
60 if call.excl_token().is_none()
61 && call.token_tree().map_or(false, |t| t.l_curly_token().is_some())
62 && call.semicolon_token().is_none() =>
63 {
64 call.syntax().clone()
65 }
66 _ => node,
67 };
68 let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
69 let res = match_ast! {
70 match prev_sibling {
71 ast::ExprStmt(it) => {
72 let node = it.expr().filter(|_| it.semicolon_token().is_none())?.syntax().clone();
73 match_ast! {
74 match node {
75 ast::IfExpr(_it) => ImmediatePrevSibling::IfExpr,
76 _ => return None,
77 }
78 }
79 },
80 ast::Trait(it) => if it.assoc_item_list().is_none() {
81 ImmediatePrevSibling::TraitDefName
82 } else {
83 return None
84 },
85 ast::Impl(it) => if it.assoc_item_list().is_none()
86 && (it.for_token().is_none() || it.self_ty().is_some()) {
87 ImmediatePrevSibling::ImplDefType
88 } else {
89 return None
90 },
91 _ => return None,
92 }
93 };
94 Some(res)
32} 95}
33#[test] 96
34fn test_has_impl_parent() { 97pub(crate) fn determine_location(
35 check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent); 98 sema: &Semantics<RootDatabase>,
99 original_file: &SyntaxNode,
100 offset: TextSize,
101 name_like: &ast::NameLike,
102) -> Option<ImmediateLocation> {
103 let node = match name_like {
104 ast::NameLike::NameRef(name_ref) => {
105 if ast::RecordExprField::for_field_name(&name_ref).is_some() {
106 return sema
107 .find_node_at_offset_with_macros(original_file, offset)
108 .map(ImmediateLocation::RecordExpr);
109 }
110 if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() {
111 return sema
112 .find_node_at_offset_with_macros(original_file, offset)
113 .map(ImmediateLocation::RecordPat);
114 }
115 maximize_name_ref(name_ref)
116 }
117 ast::NameLike::Name(name) => {
118 if ast::RecordPatField::for_field_name(&name).is_some() {
119 return sema
120 .find_node_at_offset_with_macros(original_file, offset)
121 .map(ImmediateLocation::RecordPat);
122 }
123 name.syntax().clone()
124 }
125 ast::NameLike::Lifetime(lt) => lt.syntax().clone(),
126 };
127
128 let parent = match node.parent() {
129 Some(parent) => match ast::MacroCall::cast(parent.clone()) {
130 // When a path is being typed in an (Assoc)ItemList the parser will always emit a macro_call.
131 // This is usually fine as the node expansion code above already accounts for that with
132 // the ancestors call, but there is one exception to this which is that when an attribute
133 // precedes it the code above will not walk the Path to the parent MacroCall as their ranges differ.
134 // FIXME path expr and statement have a similar problem
135 Some(call)
136 if call.excl_token().is_none()
137 && call.token_tree().is_none()
138 && call.semicolon_token().is_none() =>
139 {
140 call.syntax().parent()?
141 }
142 _ => parent,
143 },
144 // SourceFile
145 None => {
146 return match node.kind() {
147 MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
148 _ => None,
149 }
150 }
151 };
152
153 let res = match_ast! {
154 match parent {
155 ast::IdentPat(_it) => ImmediateLocation::IdentPat,
156 ast::Use(_it) => ImmediateLocation::Use,
157 ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
158 ast::SourceFile(_it) => ImmediateLocation::ItemList,
159 ast::ItemList(_it) => ImmediateLocation::ItemList,
160 ast::RefExpr(_it) => ImmediateLocation::RefExpr,
161 ast::RecordField(_it) => ImmediateLocation::RecordField,
162 ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
163 Some(IMPL) => ImmediateLocation::Impl,
164 Some(TRAIT) => ImmediateLocation::Trait,
165 _ => return None,
166 },
167 ast::Module(it) => if it.item_list().is_none() {
168 ImmediateLocation::ModDeclaration(it)
169 } else {
170 return None
171 },
172 ast::Attr(it) => ImmediateLocation::Attribute(it),
173 _ => return None,
174 }
175 };
176 Some(res)
177}
178
179fn maximize_name_ref(name_ref: &ast::NameRef) -> SyntaxNode {
180 // Maximize a nameref to its enclosing path if its the last segment of said path
181 if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
182 let p = segment.parent_path();
183 if p.parent_path().is_none() {
184 if let Some(it) = p
185 .syntax()
186 .ancestors()
187 .take_while(|it| it.text_range() == p.syntax().text_range())
188 .last()
189 {
190 return it;
191 }
192 }
193 }
194 name_ref.syntax().clone()
36} 195}
37 196
38pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool { 197pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
@@ -53,68 +212,6 @@ fn test_inside_impl_trait_block() {
53 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block); 212 check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
54} 213}
55 214
56pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57 not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
58}
59#[test]
60fn test_has_field_list_parent() {
61 check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent);
62 check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent);
63}
64
65pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67}
68#[test]
69fn test_has_block_expr_parent() {
70 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent);
71}
72
73pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74 element.ancestors().any(|it| it.kind() == IDENT_PAT)
75}
76
77#[test]
78fn test_has_bind_pat_parent() {
79 check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent);
80 check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
81}
82
83pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
84 not_same_range_ancestor(element)
85 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
86 .is_some()
87}
88#[test]
89fn test_has_ref_parent() {
90 check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent);
91 check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent);
92}
93
94pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
95 match not_same_range_ancestor(element) {
96 Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST,
97 None => true,
98 }
99}
100#[test]
101fn test_has_item_list_or_source_file_parent() {
102 check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent);
103 check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent);
104}
105
106pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
107 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
108 && previous_sibling_or_ancestor_sibling(element)
109 .and_then(|it| it.into_token())
110 .filter(|it| it.kind() == FAT_ARROW)
111 .is_some()
112}
113#[test]
114fn test_is_match_arm() {
115 check_pattern_is_applicable(r"fn my_fn() { match () { () => m$0 } }", is_match_arm);
116}
117
118pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> { 215pub(crate) fn previous_token(element: SyntaxElement) -> Option<SyntaxToken> {
119 element.into_token().and_then(|it| previous_non_trivia_token(it)) 216 element.into_token().and_then(|it| previous_non_trivia_token(it))
120} 217}
@@ -134,14 +231,6 @@ fn test_for_is_prev2() {
134 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 231 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
135} 232}
136 233
137pub(crate) fn has_prev_sibling(element: SyntaxElement, kind: SyntaxKind) -> bool {
138 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == kind).is_some()
139}
140#[test]
141fn test_has_impl_as_prev_sibling() {
142 check_pattern_is_applicable(r"impl A w$0 {}", |it| has_prev_sibling(it, IMPL));
143}
144
145pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 234pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
146 element 235 element
147 .ancestors() 236 .ancestors()
@@ -160,14 +249,6 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
160 .is_some() 249 .is_some()
161} 250}
162 251
163fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
164 element
165 .ancestors()
166 .take_while(|it| it.text_range() == element.text_range())
167 .last()
168 .and_then(|it| it.parent())
169}
170
171fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> { 252fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
172 let mut token = token.prev_token(); 253 let mut token = token.prev_token();
173 while let Some(inner) = token.clone() { 254 while let Some(inner) = token.clone() {
@@ -180,17 +261,119 @@ fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
180 None 261 None
181} 262}
182 263
183fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> { 264#[cfg(test)]
184 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); 265mod tests {
185 if let Some(sibling) = token_sibling { 266 use syntax::algo::find_node_at_offset;
186 Some(sibling) 267
187 } else { 268 use crate::test_utils::position;
188 // if not trying to find first ancestor which has such a sibling 269
189 let range = element.text_range(); 270 use super::*;
190 let top_node = element.ancestors().take_while(|it| it.text_range() == range).last()?; 271
191 let prev_sibling_node = top_node.ancestors().find(|it| { 272 fn check_location(code: &str, loc: impl Into<Option<ImmediateLocation>>) {
192 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() 273 let (db, pos) = position(code);
193 })?; 274
194 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) 275 let sema = Semantics::new(&db);
276 let original_file = sema.parse(pos.file_id);
277
278 let name_like = find_node_at_offset(original_file.syntax(), pos.offset).unwrap();
279 assert_eq!(
280 determine_location(&sema, original_file.syntax(), pos.offset, &name_like),
281 loc.into()
282 );
283 }
284
285 fn check_prev_sibling(code: &str, sibling: impl Into<Option<ImmediatePrevSibling>>) {
286 check_pattern_is_applicable(code, |e| {
287 let name = &e.parent().and_then(ast::NameLike::cast).expect("Expected a namelike");
288 assert_eq!(determine_prev_sibling(name), sibling.into());
289 true
290 });
291 }
292
293 #[test]
294 fn test_trait_loc() {
295 check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
296 check_location(r"trait A { #[attr] f$0 }", ImmediateLocation::Trait);
297 check_location(r"trait A { f$0 fn f() {} }", ImmediateLocation::Trait);
298 check_location(r"trait A { fn f() {} f$0 }", ImmediateLocation::Trait);
299 check_location(r"trait A$0 {}", None);
300 check_location(r"trait A { fn f$0 }", None);
301 }
302
303 #[test]
304 fn test_impl_loc() {
305 check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
306 check_location(r"impl A { #[attr] f$0 }", ImmediateLocation::Impl);
307 check_location(r"impl A { f$0 fn f() {} }", ImmediateLocation::Impl);
308 check_location(r"impl A { fn f() {} f$0 }", ImmediateLocation::Impl);
309 check_location(r"impl A$0 {}", None);
310 check_location(r"impl A { fn f$0 }", None);
311 }
312
313 #[test]
314 fn test_use_loc() {
315 check_location(r"use f$0", ImmediateLocation::Use);
316 check_location(r"use f$0;", ImmediateLocation::Use);
317 check_location(r"use f::{f$0}", None);
318 check_location(r"use {f$0}", None);
319 }
320
321 #[test]
322 fn test_record_field_loc() {
323 check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
324 check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
325 check_location(r"struct Foo { pub f: i32, f$0 }", ImmediateLocation::RecordField);
326 }
327
328 #[test]
329 fn test_block_expr_loc() {
330 check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
331 check_location(r"fn my_fn() { f$0 f }", ImmediateLocation::BlockExpr);
332 }
333
334 #[test]
335 fn test_ident_pat_loc() {
336 check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
337 check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
338 check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
339 check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
340 }
341
342 #[test]
343 fn test_ref_expr_loc() {
344 check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
345 }
346
347 #[test]
348 fn test_item_list_loc() {
349 check_location(r"i$0", ImmediateLocation::ItemList);
350 check_location(r"#[attr] i$0", ImmediateLocation::ItemList);
351 check_location(r"fn f() {} i$0", ImmediateLocation::ItemList);
352 check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
353 check_location(r"mod foo { #[attr] f$0 }", ImmediateLocation::ItemList);
354 check_location(r"mod foo { fn f() {} f$0 }", ImmediateLocation::ItemList);
355 check_location(r"mod foo$0 {}", None);
356 }
357
358 #[test]
359 fn test_impl_prev_sibling() {
360 check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType);
361 check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType);
362 check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType);
363 check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType);
364 check_prev_sibling(r"impl A for w$0 {}", None);
365 check_prev_sibling(r"impl A for w$0", None);
366 }
367
368 #[test]
369 fn test_trait_prev_sibling() {
370 check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName);
371 check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName);
372 }
373
374 #[test]
375 fn test_if_expr_prev_sibling() {
376 check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);
377 check_prev_sibling(r"fn foo() { if true {}; w$0", None);
195 } 378 }
196} 379}
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 7578ad50b..b90fd3890 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -74,7 +74,11 @@ impl<'a> MacroRender<'a> {
74 if self.needs_bang() && self.ctx.snippet_cap().is_some() { 74 if self.needs_bang() && self.ctx.snippet_cap().is_some() {
75 format!("{}!{}…{}", self.name, self.bra, self.ket) 75 format!("{}!{}…{}", self.name, self.bra, self.ket)
76 } else { 76 } else {
77 self.banged_name() 77 if self.macro_.kind() == hir::MacroKind::Derive {
78 self.name.to_string()
79 } else {
80 self.banged_name()
81 }
78 } 82 }
79 } 83 }
80 84
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index 37be575e5..93c7c872c 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -12,7 +12,7 @@ use ide_db::{
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::{format_to, trim_indent}; 13use stdx::{format_to, trim_indent};
14use syntax::{AstNode, NodeOrToken, SyntaxElement}; 14use syntax::{AstNode, NodeOrToken, SyntaxElement};
15use test_utils::{assert_eq_text, RangeOrOffset}; 15use test_utils::assert_eq_text;
16 16
17use crate::{item::CompletionKind, CompletionConfig, CompletionItem}; 17use crate::{item::CompletionKind, CompletionConfig, CompletionItem};
18 18
@@ -36,10 +36,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
36 let mut database = RootDatabase::default(); 36 let mut database = RootDatabase::default();
37 database.apply_change(change_fixture.change); 37 database.apply_change(change_fixture.change);
38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 38 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
39 let offset = match range_or_offset { 39 let offset = range_or_offset.expect_offset();
40 RangeOrOffset::Range(_) => panic!(),
41 RangeOrOffset::Offset(it) => it,
42 };
43 (database, FilePosition { file_id, offset }) 40 (database, FilePosition { file_id, offset })
44} 41}
45 42
@@ -52,10 +49,11 @@ pub(crate) fn do_completion_with_config(
52 code: &str, 49 code: &str,
53 kind: CompletionKind, 50 kind: CompletionKind,
54) -> Vec<CompletionItem> { 51) -> Vec<CompletionItem> {
55 let mut kind_completions: Vec<CompletionItem> = 52 get_all_items(config, code)
56 get_all_items(config, code).into_iter().filter(|c| c.completion_kind == kind).collect(); 53 .into_iter()
57 kind_completions.sort_by(|l, r| l.label().cmp(r.label())); 54 .filter(|c| c.completion_kind == kind)
58 kind_completions 55 .sorted_by(|l, r| l.label().cmp(r.label()))
56 .collect()
59} 57}
60 58
61pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { 59pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
@@ -132,7 +130,7 @@ pub(crate) fn check_edit_with_config(
132 assert_eq_text!(&ra_fixture_after, &actual) 130 assert_eq_text!(&ra_fixture_after, &actual)
133} 131}
134 132
135pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { 133pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
136 let (db, pos) = position(code); 134 let (db, pos) = position(code);
137 135
138 let sema = Semantics::new(&db); 136 let sema = Semantics::new(&db);
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs
index 1aeda08e5..b585085f3 100644
--- a/crates/ide_db/src/call_info/tests.rs
+++ b/crates/ide_db/src/call_info/tests.rs
@@ -1,6 +1,5 @@
1use base_db::{fixture::ChangeFixture, FilePosition}; 1use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use test_utils::RangeOrOffset;
4 3
5use crate::RootDatabase; 4use crate::RootDatabase;
6 5
@@ -10,10 +9,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
10 let mut database = RootDatabase::default(); 9 let mut database = RootDatabase::default();
11 database.apply_change(change_fixture.change); 10 database.apply_change(change_fixture.change);
12 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 11 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
13 let offset = match range_or_offset { 12 let offset = range_or_offset.expect_offset();
14 RangeOrOffset::Range(_) => panic!(),
15 RangeOrOffset::Offset(it) => it,
16 };
17 (database, FilePosition { file_id, offset }) 13 (database, FilePosition { file_id, offset })
18} 14}
19 15
diff --git a/crates/ide_db/src/traits/tests.rs b/crates/ide_db/src/traits/tests.rs
index 2a5482024..de994407c 100644
--- a/crates/ide_db/src/traits/tests.rs
+++ b/crates/ide_db/src/traits/tests.rs
@@ -2,7 +2,6 @@ use base_db::{fixture::ChangeFixture, FilePosition};
2use expect_test::{expect, Expect}; 2use expect_test::{expect, Expect};
3use hir::Semantics; 3use hir::Semantics;
4use syntax::ast::{self, AstNode}; 4use syntax::ast::{self, AstNode};
5use test_utils::RangeOrOffset;
6 5
7use crate::RootDatabase; 6use crate::RootDatabase;
8 7
@@ -12,10 +11,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
12 let mut database = RootDatabase::default(); 11 let mut database = RootDatabase::default();
13 database.apply_change(change_fixture.change); 12 database.apply_change(change_fixture.change);
14 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)"); 13 let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
15 let offset = match range_or_offset { 14 let offset = range_or_offset.expect_offset();
16 RangeOrOffset::Range(_) => panic!(),
17 RangeOrOffset::Offset(it) => it,
18 };
19 (database, FilePosition { file_id, offset }) 15 (database, FilePosition { file_id, offset })
20} 16}
21 17
diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs
index 75d2f2eed..84ca3ff87 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -701,7 +701,7 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
701 "path" => Path, 701 "path" => Path,
702 "expr" => Expr, 702 "expr" => Expr,
703 "ty" => Type, 703 "ty" => Type,
704 "pat" => Pattern, 704 "pat" | "pat_param" => Pattern, // FIXME: edition2021
705 "stmt" => Statement, 705 "stmt" => Statement,
706 "block" => Block, 706 "block" => Block,
707 "meta" => MetaItem, 707 "meta" => MetaItem,
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 6c883dd58..2b842d393 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -158,7 +158,24 @@ fn run_server() -> Result<()> {
158 let initialize_params = 158 let initialize_params =
159 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?; 159 from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;
160 160
161 let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities); 161 let root_path = match initialize_params
162 .root_uri
163 .and_then(|it| it.to_file_path().ok())
164 .and_then(|it| AbsPathBuf::try_from(it).ok())
165 {
166 Some(it) => it,
167 None => {
168 let cwd = env::current_dir()?;
169 AbsPathBuf::assert(cwd)
170 }
171 };
172
173 let mut config = Config::new(root_path, initialize_params.capabilities);
174 if let Some(json) = initialize_params.initialization_options {
175 config.update(json);
176 }
177
178 let server_capabilities = rust_analyzer::server_capabilities(&config);
162 179
163 let initialize_result = lsp_types::InitializeResult { 180 let initialize_result = lsp_types::InitializeResult {
164 capabilities: server_capabilities, 181 capabilities: server_capabilities,
@@ -166,11 +183,7 @@ fn run_server() -> Result<()> {
166 name: String::from("rust-analyzer"), 183 name: String::from("rust-analyzer"),
167 version: Some(String::from(env!("REV"))), 184 version: Some(String::from(env!("REV"))),
168 }), 185 }),
169 offset_encoding: if supports_utf8(&initialize_params.capabilities) { 186 offset_encoding: if supports_utf8(&config.caps) { Some("utf-8".to_string()) } else { None },
170 Some("utf-8".to_string())
171 } else {
172 None
173 },
174 }; 187 };
175 188
176 let initialize_result = serde_json::to_value(initialize_result).unwrap(); 189 let initialize_result = serde_json::to_value(initialize_result).unwrap();
@@ -181,47 +194,26 @@ fn run_server() -> Result<()> {
181 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 194 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
182 } 195 }
183 196
184 let config = { 197 if config.linked_projects().is_empty() && config.detached_files().is_empty() {
185 let root_path = match initialize_params 198 let workspace_roots = initialize_params
186 .root_uri 199 .workspace_folders
187 .and_then(|it| it.to_file_path().ok()) 200 .map(|workspaces| {
188 .and_then(|it| AbsPathBuf::try_from(it).ok()) 201 workspaces
189 { 202 .into_iter()
190 Some(it) => it, 203 .filter_map(|it| it.uri.to_file_path().ok())
191 None => { 204 .filter_map(|it| AbsPathBuf::try_from(it).ok())
192 let cwd = env::current_dir()?; 205 .collect::<Vec<_>>()
193 AbsPathBuf::assert(cwd) 206 })
194 } 207 .filter(|workspaces| !workspaces.is_empty())
195 }; 208 .unwrap_or_else(|| vec![config.root_path.clone()]);
196 209
197 let mut config = Config::new(root_path, initialize_params.capabilities); 210 let discovered = ProjectManifest::discover_all(&workspace_roots);
198 if let Some(json) = initialize_params.initialization_options { 211 log::info!("discovered projects: {:?}", discovered);
199 config.update(json); 212 if discovered.is_empty() {
200 } 213 log::error!("failed to find any projects in {:?}", workspace_roots);
201
202 if config.linked_projects().is_empty() && config.detached_files().is_empty() {
203 let workspace_roots = initialize_params
204 .workspace_folders
205 .map(|workspaces| {
206 workspaces
207 .into_iter()
208 .filter_map(|it| it.uri.to_file_path().ok())
209 .filter_map(|it| AbsPathBuf::try_from(it).ok())
210 .collect::<Vec<_>>()
211 })
212 .filter(|workspaces| !workspaces.is_empty())
213 .unwrap_or_else(|| vec![config.root_path.clone()]);
214
215 let discovered = ProjectManifest::discover_all(&workspace_roots);
216 log::info!("discovered projects: {:?}", discovered);
217 if discovered.is_empty() {
218 log::error!("failed to find any projects in {:?}", workspace_roots);
219 }
220 config.discovered_projects = Some(discovered);
221 } 214 }
222 215 config.discovered_projects = Some(discovered);
223 config 216 }
224 };
225 217
226 rust_analyzer::main_loop(config, connection)?; 218 rust_analyzer::main_loop(config, connection)?;
227 219
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 4d88932ca..fe5255240 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -1,6 +1,4 @@
1//! Advertises the capabilities of the LSP Server. 1//! Advertises the capabilities of the LSP Server.
2use std::env;
3
4use lsp_types::{ 2use lsp_types::{
5 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions, 3 CallHierarchyServerCapability, ClientCapabilities, CodeActionKind, CodeActionOptions,
6 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
@@ -15,24 +13,21 @@ use lsp_types::{
15}; 13};
16use serde_json::json; 14use serde_json::json;
17 15
16use crate::config::{Config, RustfmtConfig};
18use crate::semantic_tokens; 17use crate::semantic_tokens;
19 18
20pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabilities { 19pub fn server_capabilities(config: &Config) -> ServerCapabilities {
21 ServerCapabilities { 20 ServerCapabilities {
22 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions { 21 text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
23 open_close: Some(true), 22 open_close: Some(true),
24 change: Some(if env::var("RA_NO_INCREMENTAL_SYNC").is_ok() { 23 change: Some(TextDocumentSyncKind::Incremental),
25 TextDocumentSyncKind::Full
26 } else {
27 TextDocumentSyncKind::Incremental
28 }),
29 will_save: None, 24 will_save: None,
30 will_save_wait_until: None, 25 will_save_wait_until: None,
31 save: Some(SaveOptions::default().into()), 26 save: Some(SaveOptions::default().into()),
32 })), 27 })),
33 hover_provider: Some(HoverProviderCapability::Simple(true)), 28 hover_provider: Some(HoverProviderCapability::Simple(true)),
34 completion_provider: Some(CompletionOptions { 29 completion_provider: Some(CompletionOptions {
35 resolve_provider: completions_resolve_provider(client_caps), 30 resolve_provider: completions_resolve_provider(&config.caps),
36 trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]), 31 trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]),
37 all_commit_characters: None, 32 all_commit_characters: None,
38 completion_item: None, 33 completion_item: None,
@@ -51,10 +46,13 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
51 document_highlight_provider: Some(OneOf::Left(true)), 46 document_highlight_provider: Some(OneOf::Left(true)),
52 document_symbol_provider: Some(OneOf::Left(true)), 47 document_symbol_provider: Some(OneOf::Left(true)),
53 workspace_symbol_provider: Some(OneOf::Left(true)), 48 workspace_symbol_provider: Some(OneOf::Left(true)),
54 code_action_provider: Some(code_action_capabilities(client_caps)), 49 code_action_provider: Some(code_action_capabilities(&config.caps)),
55 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }), 50 code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(true) }),
56 document_formatting_provider: Some(OneOf::Left(true)), 51 document_formatting_provider: Some(OneOf::Left(true)),
57 document_range_formatting_provider: Some(OneOf::Left(true)), 52 document_range_formatting_provider: match config.rustfmt() {
53 RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } => Some(OneOf::Left(true)),
54 _ => Some(OneOf::Left(false)),
55 },
58 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 56 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
59 first_trigger_character: "=".to_string(), 57 first_trigger_character: "=".to_string(),
60 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]), 58 more_trigger_character: Some(vec![".".to_string(), ">".to_string(), "{".to_string()]),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 7620a2fe1..a67b0bb25 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -111,15 +111,15 @@ config_data! {
111 /// Map of prefixes to be substituted when parsing diagnostic file paths. 111 /// Map of prefixes to be substituted when parsing diagnostic file paths.
112 /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. 112 /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`.
113 diagnostics_remapPrefix: FxHashMap<String, String> = "{}", 113 diagnostics_remapPrefix: FxHashMap<String, String> = "{}",
114 /// List of warnings that should be displayed with info severity.
115 ///
116 /// The warnings will be indicated by a blue squiggly underline in code
117 /// and a blue icon in the `Problems Panel`.
118 diagnostics_warningsAsHint: Vec<String> = "[]",
119 /// List of warnings that should be displayed with hint severity. 114 /// List of warnings that should be displayed with hint severity.
120 /// 115 ///
121 /// The warnings will be indicated by faded text or three dots in code 116 /// The warnings will be indicated by faded text or three dots in code
122 /// and will not show up in the `Problems Panel`. 117 /// and will not show up in the `Problems Panel`.
118 diagnostics_warningsAsHint: Vec<String> = "[]",
119 /// List of warnings that should be displayed with info severity.
120 ///
121 /// The warnings will be indicated by a blue squiggly underline in code
122 /// and a blue icon in the `Problems Panel`.
123 diagnostics_warningsAsInfo: Vec<String> = "[]", 123 diagnostics_warningsAsInfo: Vec<String> = "[]",
124 124
125 /// Controls file watching implementation. 125 /// Controls file watching implementation.
@@ -238,7 +238,7 @@ impl Default for ConfigData {
238 238
239#[derive(Debug, Clone)] 239#[derive(Debug, Clone)]
240pub struct Config { 240pub struct Config {
241 caps: lsp_types::ClientCapabilities, 241 pub caps: lsp_types::ClientCapabilities,
242 data: ConfigData, 242 data: ConfigData,
243 detached_files: Vec<AbsPathBuf>, 243 detached_files: Vec<AbsPathBuf>,
244 pub discovered_projects: Option<Vec<ProjectManifest>>, 244 pub discovered_projects: Option<Vec<ProjectManifest>>,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 6d18d0ffc..f5c8535a2 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -534,6 +534,7 @@ pub(crate) fn folding_range(
534 | FoldKind::Consts 534 | FoldKind::Consts
535 | FoldKind::Statics 535 | FoldKind::Statics
536 | FoldKind::WhereClause 536 | FoldKind::WhereClause
537 | FoldKind::ReturnType
537 | FoldKind::Array => None, 538 | FoldKind::Array => None,
538 }; 539 };
539 540
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index a6c294245..2106732cd 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
14cov-mark = { version = "1.1", features = ["thread-local"] } 14cov-mark = { version = "1.1", features = ["thread-local"] }
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "=0.13.0-pre.6" 16rowan = "=0.13.0-pre.6"
17rustc_lexer = { version = "720.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "721.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.7" 19arrayvec = "0.7"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index df8f98b5b..884fe0739 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -243,6 +243,13 @@ impl ast::Path {
243 } 243 }
244 } 244 }
245 245
246 pub fn as_single_name_ref(&self) -> Option<ast::NameRef> {
247 match self.qualifier() {
248 Some(_) => None,
249 None => self.segment()?.name_ref(),
250 }
251 }
252
246 pub fn first_qualifier_or_self(&self) -> ast::Path { 253 pub fn first_qualifier_or_self(&self) -> ast::Path {
247 successors(Some(self.clone()), ast::Path::qualifier).last().unwrap() 254 successors(Some(self.clone()), ast::Path::qualifier).last().unwrap()
248 } 255 }
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index fce4fd6bf..bd017567c 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -96,6 +96,21 @@ pub enum RangeOrOffset {
96 Offset(TextSize), 96 Offset(TextSize),
97} 97}
98 98
99impl RangeOrOffset {
100 pub fn expect_offset(self) -> TextSize {
101 match self {
102 RangeOrOffset::Offset(it) => it,
103 RangeOrOffset::Range(_) => panic!("expected an offset but got a range instead"),
104 }
105 }
106 pub fn expect_range(self) -> TextRange {
107 match self {
108 RangeOrOffset::Range(it) => it,
109 RangeOrOffset::Offset(_) => panic!("expected a range but got an offset"),
110 }
111 }
112}
113
99impl From<RangeOrOffset> for TextRange { 114impl From<RangeOrOffset> for TextRange {
100 fn from(selection: RangeOrOffset) -> Self { 115 fn from(selection: RangeOrOffset) -> Self {
101 match selection { 116 match selection {