aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.rs11
-rw-r--r--crates/ide_completion/src/completions/postfix.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs4
-rw-r--r--crates/ide_completion/src/completions/snippet.rs4
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/ide_completion/src/context.rs120
-rw-r--r--crates/ide_completion/src/patterns.rs4
-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
12 files changed, 108 insertions, 75 deletions
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..0ca97a0e4 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -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);
@@ -128,7 +129,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
128 } 129 }
129 130
130 if ctx.in_loop_body { 131 if ctx.in_loop_body {
131 if ctx.can_be_stmt { 132 if ctx.can_be_stmt() {
132 add_keyword("continue", "continue;"); 133 add_keyword("continue", "continue;");
133 add_keyword("break", "break;"); 134 add_keyword("break", "break;");
134 } else { 135 } else {
@@ -137,7 +138,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137 } 138 }
138 } 139 }
139 140
140 if !ctx.is_trivial_path { 141 if !ctx.is_trivial_path() {
141 return; 142 return;
142 } 143 }
143 let fn_def = match &ctx.function_def { 144 let fn_def = match &ctx.function_def {
@@ -147,7 +148,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147 148
148 add_keyword( 149 add_keyword(
149 "return", 150 "return",
150 match (ctx.can_be_stmt, fn_def.ret_type().is_some()) { 151 match (ctx.can_be_stmt(), fn_def.ret_type().is_some()) {
151 (true, true) => "return $0;", 152 (true, true) => "return $0;",
152 (true, false) => "return;", 153 (true, false) => "return;",
153 (false, true) => "return $0", 154 (false, true) => "return $0",
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..c072de7b5 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -10,8 +10,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
10 if ctx.is_path_disallowed() || ctx.expects_item() { 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() {
14 Some(path) => path.clone(), 14 Some(path) => path,
15 None => return, 15 None => return,
16 }; 16 };
17 17
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 6e6a6eb92..59a338e7b 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -14,7 +14,7 @@ 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.is_trivial_path() && ctx.function_def.is_some()) {
18 return; 18 return;
19 } 19 }
20 let cap = match ctx.config.snippet_cap { 20 let cap = match ctx.config.snippet_cap {
@@ -22,7 +22,7 @@ pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionConte
22 None => return, 22 None => return,
23 }; 23 };
24 24
25 if ctx.can_be_stmt { 25 if ctx.can_be_stmt() {
26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 26 snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc); 27 snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
28 } 28 }
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index bd955aa85..f321ed52b 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -5,7 +5,7 @@ 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_trivial_path() {
9 return; 9 return;
10 } 10 }
11 if ctx.is_path_disallowed() || ctx.expects_item() { 11 if ctx.is_path_disallowed() || ctx.expects_item() {
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index cb4f08e53..20e033d31 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -29,6 +29,28 @@ 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}
47
48#[derive(Copy, Clone, Debug, PartialEq, Eq)]
49pub(crate) enum CallKind {
50 Pat,
51 Mac,
52 Expr,
53}
32/// `CompletionContext` is created early during completion to figure out, where 54/// `CompletionContext` is created early during completion to figure out, where
33/// exactly is the cursor, syntax-wise. 55/// exactly is the cursor, syntax-wise.
34#[derive(Debug)] 56#[derive(Debug)]
@@ -68,24 +90,9 @@ pub(crate) struct CompletionContext<'a> {
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>,
70 92
93 pub(super) path_context: Option<PathCompletionContext>,
71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 94 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
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>, 98 pub(super) previous_token: Option<SyntaxToken>,
@@ -149,15 +156,7 @@ impl<'a> CompletionContext<'a> {
149 is_label_ref: false, 156 is_label_ref: false,
150 is_param: false, 157 is_param: false,
151 is_pat_or_const: None, 158 is_pat_or_const: None,
152 is_trivial_path: false, 159 path_context: None,
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, 160 previous_token: None,
162 in_loop_body: false, 161 in_loop_body: false,
163 completion_location: None, 162 completion_location: None,
@@ -250,14 +249,14 @@ impl<'a> CompletionContext<'a> {
250 pub(crate) fn has_dot_receiver(&self) -> bool { 249 pub(crate) fn has_dot_receiver(&self) -> bool {
251 matches!( 250 matches!(
252 &self.completion_location, 251 &self.completion_location,
253 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver }) 252 Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254 if receiver.is_some() 253 if receiver.is_some()
255 ) 254 )
256 } 255 }
257 256
258 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> { 257 pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259 match &self.completion_location { 258 match &self.completion_location {
260 Some(ImmediateLocation::MethodCall { receiver }) 259 Some(ImmediateLocation::MethodCall { receiver, .. })
261 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(), 260 | Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262 _ => None, 261 _ => None,
263 } 262 }
@@ -275,11 +274,6 @@ impl<'a> CompletionContext<'a> {
275 matches!(self.completion_location, Some(ImmediateLocation::ItemList)) 274 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276 } 275 }
277 276
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 { 277 pub(crate) fn has_block_expr_parent(&self) -> bool {
284 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) 278 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285 } 279 }
@@ -316,6 +310,26 @@ impl<'a> CompletionContext<'a> {
316 ) || self.attribute_under_caret.is_some() 310 ) || self.attribute_under_caret.is_some()
317 } 311 }
318 312
313 pub(crate) fn expects_expression(&self) -> bool {
314 self.path_context.as_ref().map_or(false, |it| it.is_expr)
315 }
316
317 pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
318 self.path_context.as_ref().and_then(|it| it.call_kind)
319 }
320
321 pub(crate) fn is_trivial_path(&self) -> bool {
322 self.path_context.as_ref().map_or(false, |it| it.is_trivial_path)
323 }
324
325 pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
326 self.path_context.as_ref().and_then(|it| it.path_qual.as_ref())
327 }
328
329 pub(crate) fn can_be_stmt(&self) -> bool {
330 self.path_context.as_ref().map_or(false, |it| it.can_be_stmt)
331 }
332
319 fn fill_impl_def(&mut self) { 333 fn fill_impl_def(&mut self) {
320 self.impl_def = self 334 self.impl_def = self
321 .sema 335 .sema
@@ -568,22 +582,32 @@ impl<'a> CompletionContext<'a> {
568 }; 582 };
569 583
570 if let Some(segment) = ast::PathSegment::cast(parent) { 584 if let Some(segment) = ast::PathSegment::cast(parent) {
585 let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
586 call_kind: None,
587 is_trivial_path: false,
588 path_qual: None,
589 has_type_args: false,
590 is_path_type: false,
591 can_be_stmt: false,
592 is_expr: false,
593 });
571 let path = segment.parent_path(); 594 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 595
582 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 596 if let Some(p) = path.syntax().parent() {
583 self.has_type_args = segment.generic_arg_list().is_some(); 597 path_ctx.call_kind = match_ast! {
598 match p {
599 ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
600 ast::MacroCall(_it) => Some(CallKind::Mac),
601 ast::TupleStructPat(_it) => Some(CallKind::Pat),
602 _ => None
603 }
604 };
605 }
606 path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
607 path_ctx.has_type_args = segment.generic_arg_list().is_some();
584 608
585 if let Some(path) = path_or_use_tree_qualifier(&path) { 609 if let Some(path) = path_or_use_tree_qualifier(&path) {
586 self.path_qual = path 610 path_ctx.path_qual = path
587 .segment() 611 .segment()
588 .and_then(|it| { 612 .and_then(|it| {
589 find_node_with_range::<ast::PathSegment>( 613 find_node_with_range::<ast::PathSegment>(
@@ -601,11 +625,11 @@ impl<'a> CompletionContext<'a> {
601 } 625 }
602 } 626 }
603 627
604 self.is_trivial_path = true; 628 path_ctx.is_trivial_path = true;
605 629
606 // Find either enclosing expr statement (thing with `;`) or a 630 // Find either enclosing expr statement (thing with `;`) or a
607 // block. If block, check that we are the last expr. 631 // block. If block, check that we are the last expr.
608 self.can_be_stmt = name_ref 632 path_ctx.can_be_stmt = name_ref
609 .syntax() 633 .syntax()
610 .ancestors() 634 .ancestors()
611 .find_map(|node| { 635 .find_map(|node| {
@@ -621,10 +645,8 @@ impl<'a> CompletionContext<'a> {
621 None 645 None
622 }) 646 })
623 .unwrap_or(false); 647 .unwrap_or(false);
624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 648 path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625 } 649 }
626 self.is_call |=
627 matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628 } 650 }
629} 651}
630 652
diff --git a/crates/ide_completion/src/patterns.rs b/crates/ide_completion/src/patterns.rs
index 080898aef..251d76fe9 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 }
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 {