aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs9
-rw-r--r--crates/hir/src/semantics.rs92
-rw-r--r--crates/hir/src/semantics/source_to_def.rs15
-rw-r--r--crates/hir_def/src/attr.rs4
-rw-r--r--crates/hir_def/src/child_by_source.rs4
-rw-r--r--crates/hir_def/src/item_scope.rs17
-rw-r--r--crates/hir_def/src/keys.rs3
-rw-r--r--crates/hir_def/src/nameres/collector.rs5
-rw-r--r--crates/hir_expand/src/input.rs6
-rw-r--r--crates/ide/src/expand_macro.rs34
-rw-r--r--crates/ide/src/goto_definition.rs89
-rw-r--r--crates/ide/src/syntax_highlighting.rs30
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs352
-rw-r--r--crates/ide_completion/src/completions.rs1
-rw-r--r--crates/ide_completion/src/completions/dot.rs6
-rw-r--r--crates/ide_completion/src/completions/flyimport.rs6
-rw-r--r--crates/ide_completion/src/completions/keyword.rs25
-rw-r--r--crates/ide_completion/src/completions/macro_in_item_position.rs48
-rw-r--r--crates/ide_completion/src/completions/postfix.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs14
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs18
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs22
-rw-r--r--crates/ide_completion/src/context.rs152
-rw-r--r--crates/ide_completion/src/lib.rs1
-rw-r--r--crates/ide_completion/src/patterns.rs11
-rw-r--r--crates/ide_completion/src/render.rs8
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs12
-rw-r--r--crates/ide_completion/src/render/macro_.rs4
-rw-r--r--crates/ide_db/src/call_info.rs3
-rw-r--r--crates/rust-analyzer/src/handlers.rs2
-rw-r--r--docs/dev/README.md16
-rw-r--r--xtask/src/codegen/gen_lint_completions.rs2
33 files changed, 775 insertions, 253 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b43d61d0e..c2b68a853 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -50,7 +50,6 @@ use hir_def::{
50 per_ns::PerNs, 50 per_ns::PerNs,
51 resolver::{HasResolver, Resolver}, 51 resolver::{HasResolver, Resolver},
52 src::HasSource as _, 52 src::HasSource as _,
53 type_ref::TraitRef,
54 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, 53 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
55 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, 54 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
56 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 55 LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
@@ -1797,9 +1796,11 @@ impl Impl {
1797 } 1796 }
1798 1797
1799 // FIXME: the return type is wrong. This should be a hir version of 1798 // FIXME: the return type is wrong. This should be a hir version of
1800 // `TraitRef` (ie, resolved `TypeRef`). 1799 // `TraitRef` (to account for parameters and qualifiers)
1801 pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> { 1800 pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
1802 db.impl_data(self.id).target_trait.as_deref().cloned() 1801 let trait_ref = db.impl_trait(self.id)?.skip_binders().clone();
1802 let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id);
1803 Some(Trait { id })
1803 } 1804 }
1804 1805
1805 pub fn self_ty(self, db: &dyn HirDatabase) -> Type { 1806 pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index c7f2c02e4..2d08a7704 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -117,6 +117,16 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
117 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> { 117 pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
118 self.imp.expand(macro_call) 118 self.imp.expand(macro_call)
119 } 119 }
120
121 /// If `item` has an attribute macro attached to it, expands it.
122 pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
123 self.imp.expand_attr_macro(item)
124 }
125
126 pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
127 self.imp.is_attr_macro_call(item)
128 }
129
120 pub fn speculative_expand( 130 pub fn speculative_expand(
121 &self, 131 &self,
122 actual_macro_call: &ast::MacroCall, 132 actual_macro_call: &ast::MacroCall,
@@ -332,6 +342,22 @@ impl<'db> SemanticsImpl<'db> {
332 Some(node) 342 Some(node)
333 } 343 }
334 344
345 fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
346 let sa = self.analyze(item.syntax());
347 let src = InFile::new(sa.file_id, item.clone());
348 let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
349 let file_id = macro_call_id.as_file();
350 let node = self.db.parse_or_expand(file_id)?;
351 self.cache(node.clone(), file_id);
352 Some(node)
353 }
354
355 fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
356 let sa = self.analyze(item.syntax());
357 let src = InFile::new(sa.file_id, item.clone());
358 self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
359 }
360
335 fn speculative_expand( 361 fn speculative_expand(
336 &self, 362 &self,
337 actual_macro_call: &ast::MacroCall, 363 actual_macro_call: &ast::MacroCall,
@@ -362,25 +388,57 @@ impl<'db> SemanticsImpl<'db> {
362 388
363 let token = successors(Some(InFile::new(sa.file_id, token)), |token| { 389 let token = successors(Some(InFile::new(sa.file_id, token)), |token| {
364 self.db.unwind_if_cancelled(); 390 self.db.unwind_if_cancelled();
365 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?; 391
366 let tt = macro_call.token_tree()?; 392 for node in token.value.ancestors() {
367 if !tt.syntax().text_range().contains_range(token.value.text_range()) { 393 match_ast! {
368 return None; 394 match node {
369 } 395 ast::MacroCall(macro_call) => {
370 let file_id = sa.expand(self.db, token.with_value(&macro_call))?; 396 let tt = macro_call.token_tree()?;
371 let token = self 397 if !tt.syntax().text_range().contains_range(token.value.text_range()) {
372 .expansion_info_cache 398 return None;
373 .borrow_mut() 399 }
374 .entry(file_id) 400 let file_id = sa.expand(self.db, token.with_value(&macro_call))?;
375 .or_insert_with(|| file_id.expansion_info(self.db.upcast())) 401 let token = self
376 .as_ref()? 402 .expansion_info_cache
377 .map_token_down(token.as_ref())?; 403 .borrow_mut()
378 404 .entry(file_id)
379 if let Some(parent) = token.value.parent() { 405 .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
380 self.cache(find_root(&parent), token.file_id); 406 .as_ref()?
407 .map_token_down(token.as_ref())?;
408
409 if let Some(parent) = token.value.parent() {
410 self.cache(find_root(&parent), token.file_id);
411 }
412
413 return Some(token);
414 },
415 ast::Item(item) => {
416 match self.with_ctx(|ctx| ctx.item_to_macro_call(token.with_value(item))) {
417 Some(call_id) => {
418 let file_id = call_id.as_file();
419 let token = self
420 .expansion_info_cache
421 .borrow_mut()
422 .entry(file_id)
423 .or_insert_with(|| file_id.expansion_info(self.db.upcast()))
424 .as_ref()?
425 .map_token_down(token.as_ref())?;
426
427 if let Some(parent) = token.value.parent() {
428 self.cache(find_root(&parent), token.file_id);
429 }
430
431 return Some(token);
432 }
433 None => {}
434 }
435 },
436 _ => {}
437 }
438 }
381 } 439 }
382 440
383 Some(token) 441 None
384 }) 442 })
385 .last() 443 .last()
386 .unwrap(); 444 .unwrap();
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 9a5a2255f..22e196196 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -10,7 +10,7 @@ use hir_def::{
10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
11 UnionId, VariantId, 11 UnionId, VariantId,
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroCallId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
15use smallvec::SmallVec; 15use smallvec::SmallVec;
16use stdx::impl_from; 16use stdx::impl_from;
@@ -145,16 +145,25 @@ impl SourceToDefCtx<'_, '_> {
145 Some((container, label_id)) 145 Some((container, label_id))
146 } 146 }
147 147
148 pub(super) fn item_to_macro_call(&mut self, src: InFile<ast::Item>) -> Option<MacroCallId> {
149 let map = self.dyn_map(src.as_ref())?;
150 map[keys::ATTR_MACRO].get(&src).copied()
151 }
152
148 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>( 153 fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
149 &mut self, 154 &mut self,
150 src: InFile<Ast>, 155 src: InFile<Ast>,
151 key: Key<Ast, ID>, 156 key: Key<Ast, ID>,
152 ) -> Option<ID> { 157 ) -> Option<ID> {
153 let container = self.find_container(src.as_ref().map(|it| it.syntax()))?; 158 self.dyn_map(src.as_ref())?[key].get(&src).copied()
159 }
160
161 fn dyn_map<Ast: AstNode + 'static>(&mut self, src: InFile<&Ast>) -> Option<&DynMap> {
162 let container = self.find_container(src.map(|it| it.syntax()))?;
154 let db = self.db; 163 let db = self.db;
155 let dyn_map = 164 let dyn_map =
156 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); 165 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
157 dyn_map[key].get(&src).copied() 166 Some(dyn_map)
158 } 167 }
159 168
160 pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { 169 pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b7e72b790..3886b6c04 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -36,8 +36,8 @@ use crate::{
36pub struct Documentation(String); 36pub struct Documentation(String);
37 37
38impl Documentation { 38impl Documentation {
39 pub fn new(s: impl Into<String>) -> Self { 39 pub fn new(s: String) -> Self {
40 Documentation(s.into()) 40 Documentation(s)
41 } 41 }
42 42
43 pub fn as_str(&self) -> &str { 43 pub fn as_str(&self) -> &str {
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index f2e809ca9..f22383e22 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -85,6 +85,10 @@ impl ChildBySource for ItemScope {
85 res[keys::CONST].insert(src, konst); 85 res[keys::CONST].insert(src, konst);
86 }); 86 });
87 self.impls().for_each(|imp| add_impl(db, res, imp)); 87 self.impls().for_each(|imp| add_impl(db, res, imp));
88 self.attr_macro_invocs().for_each(|(ast_id, call_id)| {
89 let item = ast_id.with_value(ast_id.to_node(db.upcast()));
90 res[keys::ATTR_MACRO].insert(item, call_id);
91 });
88 92
89 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 93 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
90 match item { 94 match item {
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 9014468ea..0f74f050d 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -4,11 +4,11 @@
4use std::collections::hash_map::Entry; 4use std::collections::hash_map::Entry;
5 5
6use base_db::CrateId; 6use base_db::CrateId;
7use hir_expand::name::Name; 7use hir_expand::{name::Name, AstId, MacroCallId, MacroDefKind};
8use hir_expand::MacroDefKind;
9use once_cell::sync::Lazy; 8use once_cell::sync::Lazy;
10use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
11use stdx::format_to; 10use stdx::format_to;
11use syntax::ast;
12 12
13use crate::{ 13use crate::{
14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId, 14 db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
@@ -53,6 +53,7 @@ pub struct ItemScope {
53 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will 53 // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will
54 // be all resolved to the last one defined if shadowing happens. 54 // be all resolved to the last one defined if shadowing happens.
55 legacy_macros: FxHashMap<Name, MacroDefId>, 55 legacy_macros: FxHashMap<Name, MacroDefId>,
56 attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
56} 57}
57 58
58pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { 59pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
@@ -169,6 +170,16 @@ impl ItemScope {
169 self.legacy_macros.insert(name, mac); 170 self.legacy_macros.insert(name, mac);
170 } 171 }
171 172
173 pub(crate) fn add_attr_macro_invoc(&mut self, item: AstId<ast::Item>, call: MacroCallId) {
174 self.attr_macros.insert(item, call);
175 }
176
177 pub(crate) fn attr_macro_invocs(
178 &self,
179 ) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
180 self.attr_macros.iter().map(|(k, v)| (*k, *v))
181 }
182
172 pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { 183 pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
173 self.unnamed_trait_imports.get(&tr).copied() 184 self.unnamed_trait_imports.get(&tr).copied()
174 } 185 }
@@ -307,6 +318,7 @@ impl ItemScope {
307 unnamed_consts, 318 unnamed_consts,
308 unnamed_trait_imports, 319 unnamed_trait_imports,
309 legacy_macros, 320 legacy_macros,
321 attr_macros,
310 } = self; 322 } = self;
311 types.shrink_to_fit(); 323 types.shrink_to_fit();
312 values.shrink_to_fit(); 324 values.shrink_to_fit();
@@ -317,6 +329,7 @@ impl ItemScope {
317 unnamed_consts.shrink_to_fit(); 329 unnamed_consts.shrink_to_fit();
318 unnamed_trait_imports.shrink_to_fit(); 330 unnamed_trait_imports.shrink_to_fit();
319 legacy_macros.shrink_to_fit(); 331 legacy_macros.shrink_to_fit();
332 attr_macros.shrink_to_fit();
320 } 333 }
321} 334}
322 335
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 89b3ed868..688cd9fcf 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -2,7 +2,7 @@
2 2
3use std::marker::PhantomData; 3use std::marker::PhantomData;
4 4
5use hir_expand::{InFile, MacroDefId}; 5use hir_expand::{InFile, MacroCallId, MacroDefId};
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7use syntax::{ast, AstNode, AstPtr}; 7use syntax::{ast, AstNode, AstPtr};
8 8
@@ -32,6 +32,7 @@ pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new(); 32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
33 33
34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); 34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
35pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
35 36
36/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are 37/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
37/// equal if they point to exactly the same object. 38/// equal if they point to exactly the same object.
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d0b1db5d1..d019ba3a9 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -1112,6 +1112,11 @@ impl DefCollector<'_> {
1112 return false; 1112 return false;
1113 } 1113 }
1114 } 1114 }
1115
1116 self.def_map.modules[directive.module_id]
1117 .scope
1118 .add_attr_macro_invoc(ast_id.ast_id, call_id);
1119
1115 resolved.push((directive.module_id, call_id, directive.depth)); 1120 resolved.push((directive.module_id, call_id, directive.depth));
1116 res = ReachedFixedPoint::No; 1121 res = ReachedFixedPoint::No;
1117 return false; 1122 return false;
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
index 40116a479..82dc7f326 100644
--- a/crates/hir_expand/src/input.rs
+++ b/crates/hir_expand/src/input.rs
@@ -1,7 +1,7 @@
1//! Macro input conditioning. 1//! Macro input conditioning.
2 2
3use syntax::{ 3use syntax::{
4 ast::{self, AttrsOwner}, 4 ast::{self, make, AttrsOwner},
5 AstNode, SyntaxNode, 5 AstNode, SyntaxNode,
6}; 6};
7 7
@@ -61,7 +61,9 @@ fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item {
61 .attrs() 61 .attrs()
62 .nth(attr_index) 62 .nth(attr_index)
63 .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index)); 63 .unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index));
64 attr.syntax().detach(); 64 let syntax_index = attr.syntax().index();
65 let ws = make::tokens::whitespace(&" ".repeat(u32::from(attr.syntax().text().len()) as usize));
66 item.syntax().splice_children(syntax_index..syntax_index + 1, vec![ws.into()]);
65 item 67 item
66} 68}
67 69
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index e0d01fa96..3f38e2145 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -3,8 +3,7 @@ use std::iter;
3use hir::Semantics; 3use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::find_node_at_offset, ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, 6 ast, match_ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T,
7 SyntaxNode, WalkEvent, T,
8}; 7};
9 8
10use crate::FilePosition; 9use crate::FilePosition;
@@ -28,16 +27,37 @@ pub struct ExpandedMacro {
28pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { 27pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
29 let sema = Semantics::new(db); 28 let sema = Semantics::new(db);
30 let file = sema.parse(position.file_id); 29 let file = sema.parse(position.file_id);
31 let mac = find_node_at_offset::<ast::MacroCall>(file.syntax(), position.offset)?;
32 let name = mac.path()?.segment()?.name_ref()?;
33 30
34 let expanded = expand_macro_recur(&sema, &mac)?; 31 let tok = file.syntax().token_at_offset(position.offset).left_biased()?;
32 let mut expanded = None;
33 let mut name = None;
34 for node in tok.ancestors() {
35 match_ast! {
36 match node {
37 ast::MacroCall(mac) => {
38 name = Some(mac.path()?.segment()?.name_ref()?.to_string());
39 expanded = expand_macro_recur(&sema, &mac);
40 break;
41 },
42 ast::Item(item) => {
43 // FIXME: add the macro name
44 // FIXME: make this recursive too
45 name = Some("?".to_string());
46 expanded = sema.expand_attr_macro(&item);
47 if expanded.is_some() {
48 break;
49 }
50 },
51 _ => {}
52 }
53 }
54 }
35 55
36 // FIXME: 56 // FIXME:
37 // macro expansion may lose all white space information 57 // macro expansion may lose all white space information
38 // But we hope someday we can use ra_fmt for that 58 // But we hope someday we can use ra_fmt for that
39 let expansion = insert_whitespaces(expanded); 59 let expansion = insert_whitespaces(expanded?);
40 Some(ExpandedMacro { name: name.to_string(), expansion }) 60 Some(ExpandedMacro { name: name?, expansion })
41} 61}
42 62
43fn expand_macro_recur( 63fn expand_macro_recur(
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index b0bfd646e..2d36c34e9 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,10 +1,10 @@
1use std::convert::TryInto; 1use std::convert::TryInto;
2 2
3use either::Either; 3use either::Either;
4use hir::{InFile, Semantics}; 4use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
5use ide_db::{ 5use ide_db::{
6 base_db::{AnchoredPath, FileId, FileLoader}, 6 base_db::{AnchoredPath, FileId, FileLoader},
7 defs::{NameClass, NameRefClass}, 7 defs::{Definition, NameClass, NameRefClass},
8 RootDatabase, 8 RootDatabase,
9}; 9};
10use syntax::{ 10use syntax::{
@@ -57,7 +57,8 @@ pub(crate) fn goto_definition(
57 }, 57 },
58 ast::Name(name) => { 58 ast::Name(name) => {
59 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 59 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
60 def.try_to_nav(sema.db) 60 try_find_trait_item_definition(&sema.db, &def)
61 .or_else(|| def.try_to_nav(sema.db))
61 }, 62 },
62 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) { 63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
63 let def = name_class.referenced_or_defined(sema.db); 64 let def = name_class.referenced_or_defined(sema.db);
@@ -99,6 +100,34 @@ fn try_lookup_include_path(
99 }) 100 })
100} 101}
101 102
103/// finds the trait definition of an impl'd item
104/// e.g.
105/// ```rust
106/// trait A { fn a(); }
107/// struct S;
108/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
109/// ```
110fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> {
111 let name = def.name(db)?;
112 let assoc = match def {
113 Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db),
114 Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db),
115 Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db),
116 _ => None,
117 }?;
118
119 let imp = match assoc.container(db) {
120 hir::AssocItemContainer::Impl(imp) => imp,
121 _ => return None,
122 };
123
124 let trait_ = imp.trait_(db)?;
125 trait_
126 .items(db)
127 .iter()
128 .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
129}
130
102fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 131fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
103 return tokens.max_by_key(priority); 132 return tokens.max_by_key(priority);
104 fn priority(n: &SyntaxToken) -> usize { 133 fn priority(n: &SyntaxToken) -> usize {
@@ -1262,4 +1291,58 @@ fn main() {
1262"#, 1291"#,
1263 ); 1292 );
1264 } 1293 }
1294
1295 #[test]
1296 fn goto_def_of_trait_impl_fn() {
1297 check(
1298 r#"
1299trait Twait {
1300 fn a();
1301 // ^
1302}
1303
1304struct Stwuct;
1305
1306impl Twait for Stwuct {
1307 fn a$0();
1308}
1309"#,
1310 );
1311 }
1312
1313 #[test]
1314 fn goto_def_of_trait_impl_const() {
1315 check(
1316 r#"
1317trait Twait {
1318 const NOMS: bool;
1319 // ^^^^
1320}
1321
1322struct Stwuct;
1323
1324impl Twait for Stwuct {
1325 const NOMS$0: bool = true;
1326}
1327"#,
1328 );
1329 }
1330
1331 #[test]
1332 fn goto_def_of_trait_impl_type_alias() {
1333 check(
1334 r#"
1335trait Twait {
1336 type IsBad;
1337 // ^^^^^
1338}
1339
1340struct Stwuct;
1341
1342impl Twait for Stwuct {
1343 type IsBad$0 = !;
1344}
1345"#,
1346 );
1347 }
1265} 1348}
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 79c2f4a1e..b03f1c71f 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -192,6 +192,7 @@ fn traverse(
192 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default(); 192 let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
193 193
194 let mut current_macro_call: Option<ast::MacroCall> = None; 194 let mut current_macro_call: Option<ast::MacroCall> = None;
195 let mut current_attr_macro_call = None;
195 let mut current_macro: Option<ast::Macro> = None; 196 let mut current_macro: Option<ast::Macro> = None;
196 let mut macro_highlighter = MacroHighlighter::default(); 197 let mut macro_highlighter = MacroHighlighter::default();
197 let mut inside_attribute = false; 198 let mut inside_attribute = false;
@@ -227,6 +228,19 @@ fn traverse(
227 } 228 }
228 _ => (), 229 _ => (),
229 } 230 }
231 match event.clone().map(|it| it.into_node().and_then(ast::Item::cast)) {
232 WalkEvent::Enter(Some(item)) => {
233 if sema.is_attr_macro_call(&item) {
234 current_attr_macro_call = Some(item);
235 }
236 }
237 WalkEvent::Leave(Some(item)) => {
238 if current_attr_macro_call == Some(item) {
239 current_attr_macro_call = None;
240 }
241 }
242 _ => (),
243 }
230 244
231 match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) { 245 match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
232 WalkEvent::Enter(Some(mac)) => { 246 WalkEvent::Enter(Some(mac)) => {
@@ -286,6 +300,22 @@ fn traverse(
286 } 300 }
287 None => token.into(), 301 None => token.into(),
288 } 302 }
303 } else if current_attr_macro_call.is_some() {
304 let token = match element.clone().into_token() {
305 Some(it) => it,
306 _ => continue,
307 };
308 let token = sema.descend_into_macros(token.clone());
309 match token.parent() {
310 Some(parent) => {
311 // We only care Name and Name_ref
312 match (token.kind(), parent.kind()) {
313 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
314 _ => token.into(),
315 }
316 }
317 None => token.into(),
318 }
289 } else { 319 } else {
290 element.clone() 320 element.clone()
291 }; 321 };
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 70949ca35..fc5a17f05 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use ide_db::defs::{Definition, NameRefClass}; 2use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ 3use syntax::{
3 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, 4 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind,
8 9
9// Assist: convert_tuple_struct_to_named_struct 10// Assist: convert_tuple_struct_to_named_struct
10// 11//
11// Converts tuple struct to struct with named fields. 12// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
12// 13//
13// ``` 14// ```
14// struct Point$0(f32, f32); 15// struct Point$0(f32, f32);
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
49 acc: &mut Assists, 50 acc: &mut Assists,
50 ctx: &AssistContext, 51 ctx: &AssistContext,
51) -> Option<()> { 52) -> Option<()> {
52 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 53 let strukt = ctx
53 let tuple_fields = match strukt.field_list()? { 54 .find_node_at_offset::<ast::Struct>()
55 .map(Either::Left)
56 .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
57 let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
58 let tuple_fields = match field_list {
54 ast::FieldList::TupleFieldList(it) => it, 59 ast::FieldList::TupleFieldList(it) => it,
55 ast::FieldList::RecordFieldList(_) => return None, 60 ast::FieldList::RecordFieldList(_) => return None,
56 }; 61 };
57 let strukt_def = ctx.sema.to_def(&strukt)?; 62 let strukt_def = match &strukt {
63 Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
64 Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
65 };
66 let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
58 67
59 let target = strukt.syntax().text_range();
60 acc.add( 68 acc.add(
61 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), 69 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
62 "Convert to named struct", 70 "Convert to named struct",
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
73fn edit_struct_def( 81fn edit_struct_def(
74 ctx: &AssistContext, 82 ctx: &AssistContext,
75 edit: &mut AssistBuilder, 83 edit: &mut AssistBuilder,
76 strukt: &ast::Struct, 84 strukt: &Either<ast::Struct, ast::Variant>,
77 tuple_fields: ast::TupleFieldList, 85 tuple_fields: ast::TupleFieldList,
78 names: Vec<ast::Name>, 86 names: Vec<ast::Name>,
79) { 87) {
@@ -86,27 +94,40 @@ fn edit_struct_def(
86 94
87 edit.edit_file(ctx.frange.file_id); 95 edit.edit_file(ctx.frange.file_id);
88 96
89 if let Some(w) = strukt.where_clause() { 97 if let Either::Left(strukt) = strukt {
90 edit.delete(w.syntax().text_range()); 98 if let Some(w) = strukt.where_clause() {
91 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 99 edit.delete(w.syntax().text_range());
92 edit.insert(tuple_fields_text_range.start(), w.syntax().text()); 100 edit.insert(
93 edit.insert(tuple_fields_text_range.start(), ","); 101 tuple_fields_text_range.start(),
94 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 102 ast::make::tokens::single_newline().text(),
103 );
104 edit.insert(tuple_fields_text_range.start(), w.syntax().text());
105 edit.insert(tuple_fields_text_range.start(), ",");
106 edit.insert(
107 tuple_fields_text_range.start(),
108 ast::make::tokens::single_newline().text(),
109 );
110 } else {
111 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
112 }
113 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
95 } else { 114 } else {
96 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); 115 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97 } 116 }
98 117
99 edit.replace(tuple_fields_text_range, record_fields.to_string()); 118 edit.replace(tuple_fields_text_range, record_fields.to_string());
100 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101} 119}
102 120
103fn edit_struct_references( 121fn edit_struct_references(
104 ctx: &AssistContext, 122 ctx: &AssistContext,
105 edit: &mut AssistBuilder, 123 edit: &mut AssistBuilder,
106 strukt: hir::Struct, 124 strukt: Either<hir::Struct, hir::Variant>,
107 names: &[ast::Name], 125 names: &[ast::Name],
108) { 126) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 127 let strukt_def = match strukt {
128 Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))),
129 Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)),
130 };
110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); 131 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 132
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 133 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
@@ -513,4 +534,305 @@ where
513"#, 534"#,
514 ); 535 );
515 } 536 }
537 #[test]
538 fn not_applicable_other_than_tuple_variant() {
539 check_assist_not_applicable(
540 convert_tuple_struct_to_named_struct,
541 r#"enum Enum { Variant$0 { value: usize } };"#,
542 );
543 check_assist_not_applicable(
544 convert_tuple_struct_to_named_struct,
545 r#"enum Enum { Variant$0 }"#,
546 );
547 }
548
549 #[test]
550 fn convert_simple_variant() {
551 check_assist(
552 convert_tuple_struct_to_named_struct,
553 r#"
554enum A {
555 $0Variant(usize),
556}
557
558impl A {
559 fn new(value: usize) -> A {
560 A::Variant(value)
561 }
562
563 fn new_with_default() -> A {
564 A::new(Default::default())
565 }
566
567 fn value(self) -> usize {
568 match self {
569 A::Variant(value) => value,
570 }
571 }
572}"#,
573 r#"
574enum A {
575 Variant { field1: usize },
576}
577
578impl A {
579 fn new(value: usize) -> A {
580 A::Variant { field1: value }
581 }
582
583 fn new_with_default() -> A {
584 A::new(Default::default())
585 }
586
587 fn value(self) -> usize {
588 match self {
589 A::Variant { field1: value } => value,
590 }
591 }
592}"#,
593 );
594 }
595
596 #[test]
597 fn convert_variant_referenced_via_self_kw() {
598 check_assist(
599 convert_tuple_struct_to_named_struct,
600 r#"
601enum A {
602 $0Variant(usize),
603}
604
605impl A {
606 fn new(value: usize) -> A {
607 Self::Variant(value)
608 }
609
610 fn new_with_default() -> A {
611 Self::new(Default::default())
612 }
613
614 fn value(self) -> usize {
615 match self {
616 Self::Variant(value) => value,
617 }
618 }
619}"#,
620 r#"
621enum A {
622 Variant { field1: usize },
623}
624
625impl A {
626 fn new(value: usize) -> A {
627 Self::Variant { field1: value }
628 }
629
630 fn new_with_default() -> A {
631 Self::new(Default::default())
632 }
633
634 fn value(self) -> usize {
635 match self {
636 Self::Variant { field1: value } => value,
637 }
638 }
639}"#,
640 );
641 }
642
643 #[test]
644 fn convert_destructured_variant() {
645 check_assist(
646 convert_tuple_struct_to_named_struct,
647 r#"
648enum A {
649 $0Variant(usize),
650}
651
652impl A {
653 fn into_inner(self) -> usize {
654 let A::Variant(first) = self;
655 first
656 }
657
658 fn into_inner_via_self(self) -> usize {
659 let Self::Variant(first) = self;
660 first
661 }
662}"#,
663 r#"
664enum A {
665 Variant { field1: usize },
666}
667
668impl A {
669 fn into_inner(self) -> usize {
670 let A::Variant { field1: first } = self;
671 first
672 }
673
674 fn into_inner_via_self(self) -> usize {
675 let Self::Variant { field1: first } = self;
676 first
677 }
678}"#,
679 );
680 }
681
682 #[test]
683 fn convert_variant_with_wrapped_references() {
684 check_assist(
685 convert_tuple_struct_to_named_struct,
686 r#"
687enum Inner {
688 $0Variant(usize),
689}
690enum Outer {
691 Variant(Inner),
692}
693
694impl Outer {
695 fn new() -> Self {
696 Self::Variant(Inner::Variant(42))
697 }
698
699 fn into_inner_destructed(self) -> u32 {
700 let Outer::Variant(Inner::Variant(x)) = self;
701 x
702 }
703}"#,
704 r#"
705enum Inner {
706 Variant { field1: usize },
707}
708enum Outer {
709 Variant(Inner),
710}
711
712impl Outer {
713 fn new() -> Self {
714 Self::Variant(Inner::Variant { field1: 42 })
715 }
716
717 fn into_inner_destructed(self) -> u32 {
718 let Outer::Variant(Inner::Variant { field1: x }) = self;
719 x
720 }
721}"#,
722 );
723
724 check_assist(
725 convert_tuple_struct_to_named_struct,
726 r#"
727enum Inner {
728 Variant(usize),
729}
730enum Outer {
731 $0Variant(Inner),
732}
733
734impl Outer {
735 fn new() -> Self {
736 Self::Variant(Inner::Variant(42))
737 }
738
739 fn into_inner_destructed(self) -> u32 {
740 let Outer::Variant(Inner::Variant(x)) = self;
741 x
742 }
743}"#,
744 r#"
745enum Inner {
746 Variant(usize),
747}
748enum Outer {
749 Variant { field1: Inner },
750}
751
752impl Outer {
753 fn new() -> Self {
754 Self::Variant { field1: Inner::Variant(42) }
755 }
756
757 fn into_inner_destructed(self) -> u32 {
758 let Outer::Variant { field1: Inner::Variant(x) } = self;
759 x
760 }
761}"#,
762 );
763 }
764
765 #[test]
766 fn convert_variant_with_multi_file_references() {
767 check_assist(
768 convert_tuple_struct_to_named_struct,
769 r#"
770//- /main.rs
771struct Inner;
772enum A {
773 $0Variant(Inner),
774}
775
776mod foo;
777
778//- /foo.rs
779use crate::{A, Inner};
780fn f() {
781 let a = A::Variant(Inner);
782}
783"#,
784 r#"
785//- /main.rs
786struct Inner;
787enum A {
788 Variant { field1: Inner },
789}
790
791mod foo;
792
793//- /foo.rs
794use crate::{A, Inner};
795fn f() {
796 let a = A::Variant { field1: Inner };
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn convert_directly_used_variant() {
804 check_assist(
805 convert_tuple_struct_to_named_struct,
806 r#"
807//- /main.rs
808struct Inner;
809enum A {
810 $0Variant(Inner),
811}
812
813mod foo;
814
815//- /foo.rs
816use crate::{A::Variant, Inner};
817fn f() {
818 let a = Variant(Inner);
819}
820"#,
821 r#"
822//- /main.rs
823struct Inner;
824enum A {
825 Variant { field1: Inner },
826}
827
828mod foo;
829
830//- /foo.rs
831use crate::{A::Variant, Inner};
832fn f() {
833 let a = Variant { field1: Inner };
834}
835"#,
836 );
837 }
516} 838}
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index ffdcdc930..7a4d71e91 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -6,7 +6,6 @@ pub(crate) mod flyimport;
6pub(crate) mod fn_param; 6pub(crate) mod fn_param;
7pub(crate) mod keyword; 7pub(crate) mod keyword;
8pub(crate) mod lifetime; 8pub(crate) mod lifetime;
9pub(crate) mod macro_in_item_position;
10pub(crate) mod mod_; 9pub(crate) mod mod_;
11pub(crate) mod pattern; 10pub(crate) mod pattern;
12pub(crate) mod postfix; 11pub(crate) mod postfix;
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index e0a7021fd..8ad57a069 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -4,7 +4,7 @@ use either::Either;
4use hir::{HasVisibility, ScopeDef}; 4use hir::{HasVisibility, ScopeDef};
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6 6
7use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
8 8
9/// Complete dot accesses, i.e. fields or methods. 9/// Complete dot accesses, i.e. fields or methods.
10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -18,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
18 _ => return, 18 _ => return,
19 }; 19 };
20 20
21 if ctx.is_call { 21 if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
22 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
23 } else { 23 } else {
24 complete_fields(ctx, &receiver_ty, |field, ty| match field { 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.config.enable_self_on_the_fly { 33 if !ctx.config.enable_self_on_the_fly {
34 return; 34 return;
35 } 35 }
36 if !ctx.is_trivial_path || ctx.is_path_disallowed() { 36 if !ctx.is_trivial_path() || ctx.is_path_disallowed() {
37 return; 37 return;
38 } 38 }
39 ctx.scope.process_all_names(&mut |name, def| { 39 ctx.scope.process_all_names(&mut |name, def| {
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs
index d72bf13d3..7bf47bf75 100644
--- a/crates/ide_completion/src/completions/flyimport.rs
+++ b/crates/ide_completion/src/completions/flyimport.rs
@@ -161,13 +161,13 @@ pub(crate) fn position_for_import<'a>(
161) -> Option<&'a SyntaxNode> { 161) -> Option<&'a SyntaxNode> {
162 Some(match import_candidate { 162 Some(match import_candidate {
163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(), 163 Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(), 164 Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(), 165 Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166 None => ctx 166 None => ctx
167 .name_ref_syntax 167 .name_ref_syntax
168 .as_ref() 168 .as_ref()
169 .map(|name_ref| name_ref.syntax()) 169 .map(|name_ref| name_ref.syntax())
170 .or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax())) 170 .or_else(|| ctx.path_qual().map(|path| path.syntax()))
171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?, 171 .or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172 }) 172 })
173} 173}
@@ -190,7 +190,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
190 }; 190 };
191 let assets_for_path = ImportAssets::for_fuzzy_path( 191 let assets_for_path = ImportAssets::for_fuzzy_path(
192 current_module, 192 current_module,
193 ctx.path_qual.clone(), 193 ctx.path_qual().cloned(),
194 fuzzy_name, 194 fuzzy_name,
195 &ctx.sema, 195 &ctx.sema,
196 approximate_node, 196 approximate_node,
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1a7a484a4..ba13d3707 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -5,8 +5,8 @@ use std::iter;
5use syntax::{SyntaxKind, T}; 5use syntax::{SyntaxKind, T};
6 6
7use crate::{ 7use crate::{
8 patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, 8 context::PathCompletionContext, patterns::ImmediateLocation, CompletionContext, CompletionItem,
9 CompletionKind, Completions, 9 CompletionItemKind, CompletionKind, Completions,
10}; 10};
11 11
12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) { 12pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
19 }; 19 };
20 20
21 if ctx.use_item_syntax.is_some() { 21 if ctx.use_item_syntax.is_some() {
22 if ctx.path_qual.is_none() { 22 let qual = ctx.path_qual();
23 if qual.is_none() {
23 kw_completion("crate::").add_to(acc); 24 kw_completion("crate::").add_to(acc);
24 } 25 }
25 kw_completion("self").add_to(acc); 26 kw_completion("self").add_to(acc);
26 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 27 if iter::successors(qual.cloned(), |p| p.qualifier())
27 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 28 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
28 { 29 {
29 kw_completion("super::").add_to(acc); 30 kw_completion("super::").add_to(acc);
@@ -127,8 +128,15 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
127 add_keyword("mut", "mut "); 128 add_keyword("mut", "mut ");
128 } 129 }
129 130
130 if ctx.in_loop_body { 131 let (can_be_stmt, in_loop_body) = match ctx.path_context {
131 if ctx.can_be_stmt { 132 Some(PathCompletionContext {
133 is_trivial_path: true, can_be_stmt, in_loop_body, ..
134 }) => (can_be_stmt, in_loop_body),
135 _ => return,
136 };
137
138 if in_loop_body {
139 if can_be_stmt {
132 add_keyword("continue", "continue;"); 140 add_keyword("continue", "continue;");
133 add_keyword("break", "break;"); 141 add_keyword("break", "break;");
134 } else { 142 } else {
@@ -137,9 +145,6 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137 } 145 }
138 } 146 }
139 147
140 if !ctx.is_trivial_path {
141 return;
142 }
143 let fn_def = match &ctx.function_def { 148 let fn_def = match &ctx.function_def {
144 Some(it) => it, 149 Some(it) => it,
145 None => return, 150 None => return,
@@ -147,7 +152,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147 152
148 add_keyword( 153 add_keyword(
149 "return", 154 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 155 match (can_be_stmt, fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 156 (true, true) => "return $0;",
152 (true, false) => "return;", 157 (true, false) => "return;",
153 (false, true) => "return $0", 158 (false, true) => "return $0",
diff --git a/crates/ide_completion/src/completions/macro_in_item_position.rs b/crates/ide_completion/src/completions/macro_in_item_position.rs
deleted file mode 100644
index 781b96ff1..000000000
--- a/crates/ide_completion/src/completions/macro_in_item_position.rs
+++ /dev/null
@@ -1,48 +0,0 @@
1//! Completes macro invocations used in item position.
2
3use crate::{CompletionContext, Completions};
4
5// Ideally this should be removed and moved into `(un)qualified_path` respectively
6pub(crate) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
7 // Show only macros in top level.
8 if !ctx.expects_item() {
9 return;
10 }
11
12 ctx.scope.process_all_names(&mut |name, res| {
13 if let hir::ScopeDef::MacroDef(mac) = res {
14 acc.add_macro(ctx, Some(name.clone()), mac);
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, &res);
19 }
20 })
21}
22
23#[cfg(test)]
24mod tests {
25 use expect_test::{expect, Expect};
26
27 use crate::{test_utils::completion_list, CompletionKind};
28
29 fn check(ra_fixture: &str, expect: Expect) {
30 let actual = completion_list(ra_fixture, CompletionKind::Reference);
31 expect.assert_eq(&actual)
32 }
33
34 #[test]
35 fn completes_macros_as_item() {
36 check(
37 r#"
38macro_rules! foo { () => {} }
39fn foo() {}
40
41$0
42"#,
43 expect![[r#"
44 ma foo!(…) macro_rules! foo
45 "#]],
46 )
47 }
48}
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index 86bbb58e2..86eb21714 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
24 } 24 }
25 25
26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location { 26 let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
27 Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false), 27 Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
28 Some(ImmediateLocation::FieldAccess { 28 Some(ImmediateLocation::FieldAccess {
29 receiver: Some(it), 29 receiver: Some(it),
30 receiver_is_ambiguous_float_literal, 30 receiver_is_ambiguous_float_literal,
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index de58ce1cd..d58745fb4 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -7,11 +7,11 @@ 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() || ctx.expects_item() { 10 if ctx.is_path_disallowed() {
11 return; 11 return;
12 } 12 }
13 let path = match &ctx.path_qual { 13 let path = match ctx.path_qual() {
14 Some(path) => path.clone(), 14 Some(path) => path,
15 None => return, 15 None => return,
16 }; 16 };
17 17
@@ -20,7 +20,8 @@ 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() { 23
24 if ctx.expects_item() || ctx.expects_assoc_item() {
24 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution { 25 if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
25 let module_scope = module.scope(ctx.db, context_module); 26 let module_scope = module.scope(ctx.db, context_module);
26 for (name, def) in module_scope { 27 for (name, def) in module_scope {
@@ -631,17 +632,17 @@ impl MyStruct {
631"#, 632"#,
632 expect![[r##" 633 expect![[r##"
633 md bar 634 md bar
634 ma foo! #[macro_export] macro_rules! foo 635 ma foo!(…) #[macro_export] macro_rules! foo
635 "##]], 636 "##]],
636 ); 637 );
637 } 638 }
638 639
639 #[test] 640 #[test]
640 #[ignore] // FIXME doesn't complete anything atm
641 fn completes_in_item_list() { 641 fn completes_in_item_list() {
642 check( 642 check(
643 r#" 643 r#"
644struct MyStruct {} 644struct MyStruct {}
645#[macro_export]
645macro_rules! foo {} 646macro_rules! foo {}
646mod bar {} 647mod bar {}
647 648
@@ -649,7 +650,7 @@ crate::$0
649"#, 650"#,
650 expect![[r#" 651 expect![[r#"
651 md bar 652 md bar
652 ma foo! macro_rules! foo 653 ma foo!(…) #[macro_export] macro_rules! foo
653 "#]], 654 "#]],
654 ) 655 )
655 } 656 }
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 6e6a6eb92..b9862de67 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -3,8 +3,8 @@
3use ide_db::helpers::SnippetCap; 3use ide_db::helpers::SnippetCap;
4 4
5use crate::{ 5use crate::{
6 item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 6 context::PathCompletionContext, item::Builder, CompletionContext, CompletionItem,
7 Completions, 7 CompletionItemKind, CompletionKind, Completions,
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
@@ -14,15 +14,21 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17 if !(ctx.is_trivial_path && ctx.function_def.is_some()) { 17 if ctx.function_def.is_none() {
18 return; 18 return;
19 } 19 }
20
21 let can_be_stmt = match ctx.path_context {
22 Some(PathCompletionContext { is_trivial_path: true, can_be_stmt, .. }) => can_be_stmt,
23 _ => return,
24 };
25
20 let cap = match ctx.config.snippet_cap { 26 let cap = match ctx.config.snippet_cap {
21 Some(it) => it, 27 Some(it) => it,
22 None => return, 28 None => return,
23 }; 29 };
24 30
25 if ctx.can_be_stmt { 31 if can_be_stmt {
26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 32 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 33 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 } 34 }
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index 968c0254d..a60e5f43c 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -34,20 +34,13 @@
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit, Impl}, 37 ast::{self, edit},
38 display::function_declaration, 38 display::function_declaration,
39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T, 39 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, T,
40}; 40};
41use text_edit::TextEdit; 41use text_edit::TextEdit;
42 42
43use crate::{ 43use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51 44
52#[derive(Debug, PartialEq, Eq)] 45#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind { 46enum ImplCompletionKind {
@@ -58,7 +51,7 @@ enum ImplCompletionKind {
58} 51}
59 52
60pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 53pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
61 if let Some((kind, trigger, impl_def)) = completion_match(ctx) { 54 if let Some((kind, trigger, impl_def)) = completion_match(ctx.token.clone()) {
62 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { 55 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
63 hir::AssocItem::Function(fn_item) 56 hir::AssocItem::Function(fn_item)
64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => 57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
@@ -80,8 +73,7 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
80 } 73 }
81} 74}
82 75
83fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { 76fn completion_match(mut token: SyntaxToken) -> Option<(ImplCompletionKind, SyntaxNode, ast::Impl)> {
84 let mut token = ctx.token.clone();
85 // For keyword without name like `impl .. { fn $0 }`, the current position is inside 77 // For keyword without name like `impl .. { fn $0 }`, the current position is inside
86 // the whitespace token, which is outside `FN` syntax node. 78 // the whitespace token, which is outside `FN` syntax node.
87 // We need to follow the previous token in this case. 79 // We need to follow the previous token in this case.
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85..8b22933e0 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -5,26 +5,25 @@ use hir::ScopeDef;
5use crate::{CompletionContext, Completions}; 5use crate::{CompletionContext, Completions};
6 6
7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 7pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8 if !ctx.is_trivial_path { 8 if ctx.is_path_disallowed() || !ctx.is_trivial_path() {
9 return;
10 }
11 if ctx.is_path_disallowed() || ctx.expects_item() {
12 return; 9 return;
13 } 10 }
14 11
15 if ctx.expects_assoc_item() { 12 if ctx.expects_item() || ctx.expects_assoc_item() {
16 ctx.scope.process_all_names(&mut |name, def| { 13 // only show macros in {Assoc}ItemList
17 if let ScopeDef::MacroDef(macro_def) = def { 14 ctx.scope.process_all_names(&mut |name, res| {
18 acc.add_macro(ctx, Some(name.clone()), macro_def); 15 if let hir::ScopeDef::MacroDef(mac) = res {
16 acc.add_macro(ctx, Some(name.clone()), mac);
19 } 17 }
20 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def { 18 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
21 acc.add_resolution(ctx, name, &def); 19 acc.add_resolution(ctx, name, &res);
22 } 20 }
23 }); 21 });
24 return; 22 return;
25 } 23 }
26 24
27 if ctx.expects_use_tree() { 25 if ctx.expects_use_tree() {
26 // only show modules in a fresh UseTree
28 cov_mark::hit!(only_completes_modules_in_import); 27 cov_mark::hit!(only_completes_modules_in_import);
29 ctx.scope.process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
30 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res { 29 if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = res {
@@ -693,12 +692,11 @@ impl MyStruct {
693"#, 692"#,
694 expect![[r#" 693 expect![[r#"
695 md bar 694 md bar
696 ma foo! macro_rules! foo 695 ma foo!(…) macro_rules! foo
697 "#]], 696 "#]],
698 ) 697 )
699 } 698 }
700 699
701 // FIXME: The completions here currently come from `macro_in_item_position`, but they shouldn't
702 #[test] 700 #[test]
703 fn completes_in_item_list() { 701 fn completes_in_item_list() {
704 check( 702 check(
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index cb4f08e53..6177caa12 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -29,6 +29,29 @@ pub(crate) enum PatternRefutability {
29 Irrefutable, 29 Irrefutable,
30} 30}
31 31
32#[derive(Debug)]
33pub(crate) struct PathCompletionContext {
34 /// If this is a call with () already there
35 call_kind: Option<CallKind>,
36 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
37 pub(super) is_trivial_path: bool,
38 /// If not a trivial path, the prefix (qualifier).
39 pub(super) path_qual: Option<ast::Path>,
40 pub(super) is_path_type: bool,
41 pub(super) has_type_args: bool,
42 /// `true` if we are a statement or a last expr in the block.
43 pub(super) can_be_stmt: bool,
44 /// `true` if we expect an expression at the cursor position.
45 pub(super) is_expr: bool,
46 pub(super) in_loop_body: bool,
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
50pub(crate) enum CallKind {
51 Pat,
52 Mac,
53 Expr,
54}
32/// `CompletionContext` is created early during completion to figure out, where 55/// `CompletionContext` is created early during completion to figure out, where
33/// exactly is the cursor, syntax-wise. 56/// exactly is the cursor, syntax-wise.
34#[derive(Debug)] 57#[derive(Debug)]
@@ -45,14 +68,13 @@ pub(crate) struct CompletionContext<'a> {
45 pub(super) krate: Option<hir::Crate>, 68 pub(super) krate: Option<hir::Crate>,
46 pub(super) expected_name: Option<NameOrNameRef>, 69 pub(super) expected_name: Option<NameOrNameRef>,
47 pub(super) expected_type: Option<Type>, 70 pub(super) expected_type: Option<Type>,
48 pub(super) name_ref_syntax: Option<ast::NameRef>,
49
50 pub(super) use_item_syntax: Option<ast::Use>,
51 71
52 /// The parent function of the cursor position if it exists. 72 /// The parent function of the cursor position if it exists.
53 pub(super) function_def: Option<ast::Fn>, 73 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists. 74 /// The parent impl of the cursor position if it exists.
55 pub(super) impl_def: Option<ast::Impl>, 75 pub(super) impl_def: Option<ast::Impl>,
76 pub(super) name_ref_syntax: Option<ast::NameRef>,
77 pub(super) use_item_syntax: Option<ast::Use>,
56 78
57 // potentially set if we are completing a lifetime 79 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>, 80 pub(super) lifetime_syntax: Option<ast::Lifetime>,
@@ -67,29 +89,12 @@ pub(crate) struct CompletionContext<'a> {
67 pub(super) completion_location: Option<ImmediateLocation>, 89 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>, 90 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>, 91 pub(super) attribute_under_caret: Option<ast::Attr>,
92 pub(super) previous_token: Option<SyntaxToken>,
70 93
71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 94 pub(super) path_context: Option<PathCompletionContext>,
72 pub(super) active_parameter: Option<ActiveParameter>, 95 pub(super) active_parameter: Option<ActiveParameter>,
73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
74 pub(super) is_trivial_path: bool,
75 /// If not a trivial path, the prefix (qualifier).
76 pub(super) path_qual: Option<ast::Path>,
77 /// `true` if we are a statement or a last expr in the block.
78 pub(super) can_be_stmt: bool,
79 /// `true` if we expect an expression at the cursor position.
80 pub(super) is_expr: bool,
81 /// If this is a call (method or function) in particular, i.e. the () are already there.
82 pub(super) is_call: bool,
83 /// Like `is_call`, but for tuple patterns.
84 pub(super) is_pattern_call: bool,
85 /// If this is a macro call, i.e. the () are already there.
86 pub(super) is_macro_call: bool,
87 pub(super) is_path_type: bool,
88 pub(super) has_type_args: bool,
89 pub(super) locals: Vec<(String, Local)>, 96 pub(super) locals: Vec<(String, Local)>,
90 97
91 pub(super) previous_token: Option<SyntaxToken>,
92 pub(super) in_loop_body: bool,
93 pub(super) incomplete_let: bool, 98 pub(super) incomplete_let: bool,
94 99
95 no_completion_required: bool, 100 no_completion_required: bool,
@@ -136,36 +141,27 @@ impl<'a> CompletionContext<'a> {
136 original_token, 141 original_token,
137 token, 142 token,
138 krate, 143 krate,
139 lifetime_allowed: false,
140 expected_name: None, 144 expected_name: None,
141 expected_type: None, 145 expected_type: None,
146 function_def: None,
147 impl_def: None,
142 name_ref_syntax: None, 148 name_ref_syntax: None,
149 use_item_syntax: None,
143 lifetime_syntax: None, 150 lifetime_syntax: None,
144 lifetime_param_syntax: None, 151 lifetime_param_syntax: None,
145 function_def: None, 152 lifetime_allowed: false,
146 use_item_syntax: None,
147 impl_def: None,
148 active_parameter: ActiveParameter::at(db, position),
149 is_label_ref: false, 153 is_label_ref: false,
150 is_param: false,
151 is_pat_or_const: None, 154 is_pat_or_const: None,
152 is_trivial_path: false, 155 is_param: false,
153 path_qual: None,
154 can_be_stmt: false,
155 is_expr: false,
156 is_call: false,
157 is_pattern_call: false,
158 is_macro_call: false,
159 is_path_type: false,
160 has_type_args: false,
161 previous_token: None,
162 in_loop_body: false,
163 completion_location: None, 156 completion_location: None,
164 prev_sibling: None, 157 prev_sibling: None,
165 no_completion_required: false,
166 incomplete_let: false,
167 attribute_under_caret: None, 158 attribute_under_caret: None,
159 previous_token: None,
160 path_context: None,
161 active_parameter: ActiveParameter::at(db, position),
168 locals, 162 locals,
163 incomplete_let: false,
164 no_completion_required: false,
169 }; 165 };
170 166
171 let mut original_file = original_file.syntax().clone(); 167 let mut original_file = original_file.syntax().clone();
@@ -250,14 +246,14 @@ impl<'a> CompletionContext<'a> {
250 pub(crate) fn has_dot_receiver(&self) -> bool { 246 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!( 247 matches!(
252 &self.completion_location, 248 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) 249 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254 if receiver.is_some() 250 if receiver.is_some()
255 ) 251 )
256 } 252 }
257 253
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { 254 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location { 255 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver }) 256 Some(ImmediateLocation::MethodCall { receiver, .. })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), 257 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None, 258 _ => None,
263 } 259 }
@@ -275,11 +271,6 @@ impl<'a> CompletionContext<'a> {
275 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 271 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276 } 272 }
277 273
278 // fn expects_value(&self) -> bool {
279 pub(crate) fn expects_expression(&self) -> bool {
280 self.is_expr
281 }
282
283 pub(crate) fn has_block_expr_parent(&self) -> bool { 274 pub(crate) fn has_block_expr_parent(&self) -> bool {
284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285 } 276 }
@@ -316,6 +307,22 @@ impl<'a> CompletionContext<'a> {
316 ) || self.attribute_under_caret.is_some() 307 ) || self.attribute_under_caret.is_some()
317 } 308 }
318 309
310 pub(crate) fn expects_expression(&self) -> bool {
311 self.path_context.as_ref().map_or(false, |it| it.is_expr)
312 }
313
314 pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
315 self.path_context.as_ref().and_then(|it| it.call_kind)
316 }
317
318 pub(crate) fn is_trivial_path(&self) -> bool {
319 self.path_context.as_ref().map_or(false, |it| it.is_trivial_path)
320 }
321
322 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
323 self.path_context.as_ref().and_then(|it| it.path_qual.as_ref())
324 }
325
319 fn fill_impl_def(&mut self) { 326 fn fill_impl_def(&mut self) {
320 self.impl_def = self 327 self.impl_def = self
321 .sema 328 .sema
@@ -441,7 +448,6 @@ impl<'a> CompletionContext<'a> {
441 let for_is_prev2 = for_is_prev2(syntax_element.clone()); 448 let for_is_prev2 = for_is_prev2(syntax_element.clone());
442 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2 449 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
443 }; 450 };
444 self.in_loop_body = is_in_loop_body(syntax_element.clone());
445 451
446 self.incomplete_let = 452 self.incomplete_let =
447 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 453 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
@@ -549,10 +555,6 @@ impl<'a> CompletionContext<'a> {
549 self.name_ref_syntax = 555 self.name_ref_syntax =
550 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 556 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
551 557
552 if matches!(self.completion_location, Some(ImmediateLocation::ItemList)) {
553 return;
554 }
555
556 self.use_item_syntax = 558 self.use_item_syntax =
557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 559 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
558 560
@@ -568,22 +570,34 @@ impl<'a> CompletionContext<'a> {
568 }; 570 };
569 571
570 if let Some(segment) = ast::PathSegment::cast(parent) { 572 if let Some(segment) = ast::PathSegment::cast(parent) {
573 let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
574 call_kind: None,
575 is_trivial_path: false,
576 path_qual: None,
577 has_type_args: false,
578 is_path_type: false,
579 can_be_stmt: false,
580 is_expr: false,
581 in_loop_body: false,
582 });
583 path_ctx.in_loop_body = is_in_loop_body(name_ref.syntax());
571 let path = segment.parent_path(); 584 let path = segment.parent_path();
572 self.is_call = path
573 .syntax()
574 .parent()
575 .and_then(ast::PathExpr::cast)
576 .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
577 .is_some();
578 self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
579 self.is_pattern_call =
580 path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
581 585
582 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 586 if let Some(p) = path.syntax().parent() {
583 self.has_type_args = segment.generic_arg_list().is_some(); 587 path_ctx.call_kind = match_ast! {
588 match p {
589 ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
590 ast::MacroCall(it) => it.excl_token().and(Some(CallKind::Mac)),
591 ast::TupleStructPat(_it) => Some(CallKind::Pat),
592 _ => None
593 }
594 };
595 }
596 path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
597 path_ctx.has_type_args = segment.generic_arg_list().is_some();
584 598
585 if let Some(path) = path_or_use_tree_qualifier(&path) { 599 if let Some(path) = path_or_use_tree_qualifier(&path) {
586 self.path_qual = path 600 path_ctx.path_qual = path
587 .segment() 601 .segment()
588 .and_then(|it| { 602 .and_then(|it| {
589 find_node_with_range::<ast::PathSegment>( 603 find_node_with_range::<ast::PathSegment>(
@@ -601,11 +615,11 @@ impl<'a> CompletionContext<'a> {
601 } 615 }
602 } 616 }
603 617
604 self.is_trivial_path = true; 618 path_ctx.is_trivial_path = true;
605 619
606 // Find either enclosing expr statement (thing with `;`) or a 620 // Find either enclosing expr statement (thing with `;`) or a
607 // block. If block, check that we are the last expr. 621 // block. If block, check that we are the last expr.
608 self.can_be_stmt = name_ref 622 path_ctx.can_be_stmt = name_ref
609 .syntax() 623 .syntax()
610 .ancestors() 624 .ancestors()
611 .find_map(|node| { 625 .find_map(|node| {
@@ -621,10 +635,8 @@ impl<'a> CompletionContext<'a> {
621 None 635 None
622 }) 636 })
623 .unwrap_or(false); 637 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 638 path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 639 }
626 self.is_call |=
627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 } 640 }
629} 641}
630 642
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 6fb38f50d..18983aa01 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -158,7 +158,6 @@ pub fn completions(
158 completions::record::complete_record(&mut acc, &ctx); 158 completions::record::complete_record(&mut acc, &ctx);
159 completions::pattern::complete_pattern(&mut acc, &ctx); 159 completions::pattern::complete_pattern(&mut acc, &ctx);
160 completions::postfix::complete_postfix(&mut acc, &ctx); 160 completions::postfix::complete_postfix(&mut acc, &ctx);
161 completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
162 completions::trait_impl::complete_trait_impl(&mut acc, &ctx); 161 completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
163 completions::mod_::complete_mod(&mut acc, &ctx); 162 completions::mod_::complete_mod(&mut acc, &ctx);
164 completions::flyimport::import_on_the_fly(&mut acc, &ctx); 163 completions::flyimport::import_on_the_fly(&mut acc, &ctx);
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 080898aef..ee87bf461 100644
--- a/crates/ide_completion/src/patterns.rs
+++ b/crates/ide_completion/src/patterns.rs
@@ -4,7 +4,7 @@ use hir::Semantics;
4use ide_db::RootDatabase; 4use ide_db::RootDatabase;
5use syntax::{ 5use syntax::{
6 algo::non_trivia_sibling, 6 algo::non_trivia_sibling,
7 ast::{self, LoopBodyOwner}, 7 ast::{self, ArgListOwner, LoopBodyOwner},
8 match_ast, AstNode, Direction, SyntaxElement, 8 match_ast, AstNode, Direction, SyntaxElement,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, T, 10 SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation {
39 // Original file ast node 39 // Original file ast node
40 MethodCall { 40 MethodCall {
41 receiver: Option<ast::Expr>, 41 receiver: Option<ast::Expr>,
42 has_parens: bool,
42 }, 43 },
43 // Original file ast node 44 // Original file ast node
44 FieldAccess { 45 FieldAccess {
@@ -204,6 +205,7 @@ pub(crate) fn determine_location(
204 .receiver() 205 .receiver()
205 .map(|e| e.syntax().text_range()) 206 .map(|e| e.syntax().text_range())
206 .and_then(|r| find_node_with_range(original_file, r)), 207 .and_then(|r| find_node_with_range(original_file, r)),
208 has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
207 }, 209 },
208 _ => return None, 210 _ => return None,
209 } 211 }
@@ -270,9 +272,8 @@ fn test_for_is_prev2() {
270 check_pattern_is_applicable(r"for i i$0", for_is_prev2); 272 check_pattern_is_applicable(r"for i i$0", for_is_prev2);
271} 273}
272 274
273pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { 275pub(crate) fn is_in_loop_body(node: &SyntaxNode) -> bool {
274 element 276 node.ancestors()
275 .ancestors()
276 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR) 277 .take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
277 .find_map(|it| { 278 .find_map(|it| {
278 let loop_body = match_ast! { 279 let loop_body = match_ast! {
@@ -283,7 +284,7 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
283 _ => None, 284 _ => None,
284 } 285 }
285 }; 286 };
286 loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range())) 287 loop_body.filter(|it| it.syntax().text_range().contains_range(node.text_range()))
287 }) 288 })
288 .is_some() 289 .is_some()
289} 290}
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index a49a60711..750694432 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -275,8 +275,12 @@ impl<'a> Render<'a> {
275 }; 275 };
276 276
277 // Add `<>` for generic types 277 // Add `<>` for generic types
278 if self.ctx.completion.is_path_type 278 if self
279 && !self.ctx.completion.has_type_args 279 .ctx
280 .completion
281 .path_context
282 .as_ref()
283 .map_or(false, |it| it.is_path_type && !it.has_type_args)
280 && self.ctx.completion.config.add_call_parenthesis 284 && self.ctx.completion.config.add_call_parenthesis
281 { 285 {
282 if let Some(cap) = self.ctx.snippet_cap() { 286 if let Some(cap) = self.ctx.snippet_cap() {
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 6d062b3b9..c54752d30 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -2,7 +2,7 @@
2 2
3use itertools::Itertools; 3use itertools::Itertools;
4 4
5use crate::{item::Builder, CompletionContext}; 5use crate::{context::CallKind, item::Builder, patterns::ImmediateLocation, CompletionContext};
6 6
7#[derive(Debug)] 7#[derive(Debug)]
8pub(super) enum Params { 8pub(super) enum Params {
@@ -32,10 +32,12 @@ impl Builder {
32 cov_mark::hit!(no_parens_in_use_item); 32 cov_mark::hit!(no_parens_in_use_item);
33 return false; 33 return false;
34 } 34 }
35 if ctx.is_pattern_call { 35 if matches!(ctx.path_call_kind(), Some(CallKind::Expr) | Some(CallKind::Pat))
36 return false; 36 | matches!(
37 } 37 ctx.completion_location,
38 if ctx.is_call { 38 Some(ImmediateLocation::MethodCall { has_parens: true, .. })
39 )
40 {
39 return false; 41 return false;
40 } 42 }
41 43
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index 0dfba8acc..429d937c8 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -5,6 +5,7 @@ use ide_db::SymbolKind;
5use syntax::display::macro_label; 5use syntax::display::macro_label;
6 6
7use crate::{ 7use crate::{
8 context::CallKind,
8 item::{CompletionItem, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::RenderContext, 10 render::RenderContext,
10}; 11};
@@ -68,7 +69,8 @@ impl<'a> MacroRender<'a> {
68 } 69 }
69 70
70 fn needs_bang(&self) -> bool { 71 fn needs_bang(&self) -> bool {
71 self.ctx.completion.use_item_syntax.is_none() && !self.ctx.completion.is_macro_call 72 self.ctx.completion.use_item_syntax.is_none()
73 && !matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac))
72 } 74 }
73 75
74 fn label(&self) -> String { 76 fn label(&self) -> String {
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs
index bad277a95..933bcad55 100644
--- a/crates/ide_db/src/call_info.rs
+++ b/crates/ide_db/src/call_info.rs
@@ -223,9 +223,8 @@ impl FnCallNode {
223 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, 223 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
224 _ => return None, 224 _ => return None,
225 }), 225 }),
226
227 FnCallNode::MethodCallExpr(call_expr) => { 226 FnCallNode::MethodCallExpr(call_expr) => {
228 call_expr.syntax().children().filter_map(ast::NameRef::cast).next() 227 call_expr.syntax().children().find_map(ast::NameRef::cast)
229 } 228 }
230 } 229 }
231 } 230 }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 70511c5ca..40dd0da3e 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1510,7 +1510,7 @@ fn show_ref_command_link(
1510 snap: &GlobalStateSnapshot, 1510 snap: &GlobalStateSnapshot,
1511 position: &FilePosition, 1511 position: &FilePosition,
1512) -> Option<lsp_ext::CommandLinkGroup> { 1512) -> Option<lsp_ext::CommandLinkGroup> {
1513 if snap.config.hover().implementations { 1513 if snap.config.hover().references {
1514 if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) { 1514 if let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None) {
1515 let uri = to_proto::url(snap, position.file_id); 1515 let uri = to_proto::url(snap, position.file_id);
1516 let line_index = snap.file_line_index(position.file_id).ok()?; 1516 let line_index = snap.file_line_index(position.file_id).ok()?;
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 16b23adc6..e81f1e74c 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -1,6 +1,6 @@
1# Contributing Quick Start 1# Contributing Quick Start
2 2
3Rust Analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries. 3rust-analyzer is an ordinary Rust project, which is organized as a Cargo workspace, builds on stable and doesn't depend on C libraries.
4So, just 4So, just
5 5
6``` 6```
@@ -9,18 +9,18 @@ $ cargo test
9 9
10should be enough to get you started! 10should be enough to get you started!
11 11
12To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md) document. 12To learn more about how rust-analyzer works, see [./architecture.md](./architecture.md).
13It also explains the high-level layout of the source code. 13It also explains the high-level layout of the source code.
14Do skim through that document. 14Do skim through that document.
15 15
16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/. 16We also publish rustdoc docs to pages: https://rust-analyzer.github.io/rust-analyzer/ide/.
17Note though, that internal documentation is very incomplete. 17Note though, that the internal documentation is very incomplete.
18 18
19Various organizational and process issues are discussed in this document. 19Various organizational and process issues are discussed in this document.
20 20
21# Getting in Touch 21# Getting in Touch
22 22
23Rust Analyzer is a part of [RLS-2.0 working 23rust-analyzer is a part of the [RLS-2.0 working
24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0). 24group](https://github.com/rust-lang/compiler-team/tree/6a769c13656c0a6959ebc09e7b1f7c09b86fb9c0/working-groups/rls-2.0).
25Discussion happens in this Zulip stream: 25Discussion happens in this Zulip stream:
26 26
@@ -33,7 +33,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) 33* [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions)
34 issues have links to the code in question and tests. 34 issues have links to the code in question and tests.
35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22) 35* [Broken Window](https://github.com/rust-analyzer/rust-analyzer/issues?q=is:issue+is:open+label:%22Broken+Window%22)
36 are issues which are not critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt. 36 are issues which are not necessarily critical by themselves, but which should be fixed ASAP regardless, to avoid accumulation of technical debt.
37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), 37* [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy),
38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), 38 [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium),
39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard), 39 [E-hard](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-hard),
@@ -42,7 +42,9 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
42* [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and 42* [S-actionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-actionable) and
43 [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable) 43 [S-unactionable](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AS-unactionable)
44 specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed, 44 specify if there are concrete steps to resolve or advance an issue. Roughly, actionable issues need only work to be fixed,
45 while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. 45 while unactionable ones are blocked either on user feedback (providing a reproducible example), or on larger architectural
46 work or decisions. This classification is descriptive, not prescriptive, and might be wrong: Any unactionable issue might have a simple fix that we missed.
47 Each triaged issue should have one of these labels.
46* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) 48* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun)
47 is for cool, but probably hard stuff. 49 is for cool, but probably hard stuff.
48* [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design) 50* [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design)
@@ -206,7 +208,7 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release`
206./rust-rust-analyzer # Note the name! 208./rust-rust-analyzer # Note the name!
207``` 209```
208 210
209Additionally, it assumes that remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). 211Additionally, it assumes that the remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork).
210 212
211`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. 213`release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog.
212This step uses the `curl` and `jq` applications, which need to be available in `PATH`. 214This step uses the `curl` and `jq` applications, which need to be available in `PATH`.
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs
index 7d78d0d04..3b54b2489 100644
--- a/xtask/src/codegen/gen_lint_completions.rs
+++ b/xtask/src/codegen/gen_lint_completions.rs
@@ -27,7 +27,7 @@ pub(crate) fn generate_lint_completions() -> Result<()> {
27 generate_feature_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?; 27 generate_feature_descriptor(&mut contents, "./target/rust/src/doc/unstable-book/src".into())?;
28 contents.push('\n'); 28 contents.push('\n');
29 29
30 cmd!("curl http://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?; 30 cmd!("curl https://rust-lang.github.io/rust-clippy/master/lints.json --output ./target/clippy_lints.json").run()?;
31 generate_descriptor_clippy(&mut contents, &Path::new("./target/clippy_lints.json"))?; 31 generate_descriptor_clippy(&mut contents, &Path::new("./target/clippy_lints.json"))?;
32 let contents = reformat(&contents)?; 32 let contents = reformat(&contents)?;
33 33