aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/folding_ranges.rs55
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs208
-rw-r--r--crates/ide_assists/src/tests.rs46
-rw-r--r--crates/ide_assists/src/utils.rs2
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs877
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
6 files changed, 1174 insertions, 16 deletions
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 45170dd29..4b1b24562 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -6,7 +6,7 @@ use syntax::{
6 ast::{self, AstNode, AstToken, VisibilityOwner}, 6 ast::{self, AstNode, AstToken, VisibilityOwner},
7 Direction, NodeOrToken, SourceFile, 7 Direction, NodeOrToken, SourceFile,
8 SyntaxKind::{self, *}, 8 SyntaxKind::{self, *},
9 SyntaxNode, TextRange, 9 SyntaxNode, TextRange, TextSize,
10}; 10};
11 11
12#[derive(Debug, PartialEq, Eq)] 12#[derive(Debug, PartialEq, Eq)]
@@ -16,6 +16,7 @@ pub enum FoldKind {
16 Mods, 16 Mods,
17 Block, 17 Block,
18 ArgList, 18 ArgList,
19 Region,
19} 20}
20 21
21#[derive(Debug)] 22#[derive(Debug)]
@@ -29,6 +30,8 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
29 let mut visited_comments = FxHashSet::default(); 30 let mut visited_comments = FxHashSet::default();
30 let mut visited_imports = FxHashSet::default(); 31 let mut visited_imports = FxHashSet::default();
31 let mut visited_mods = FxHashSet::default(); 32 let mut visited_mods = FxHashSet::default();
33 // regions can be nested, here is a LIFO buffer
34 let mut regions_starts: Vec<TextSize> = vec![];
32 35
33 for element in file.syntax().descendants_with_tokens() { 36 for element in file.syntax().descendants_with_tokens() {
34 // Fold items that span multiple lines 37 // Fold items that span multiple lines
@@ -48,10 +51,25 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
48 // Fold groups of comments 51 // Fold groups of comments
49 if let Some(comment) = ast::Comment::cast(token) { 52 if let Some(comment) = ast::Comment::cast(token) {
50 if !visited_comments.contains(&comment) { 53 if !visited_comments.contains(&comment) {
51 if let Some(range) = 54 // regions are not real comments
52 contiguous_range_for_comment(comment, &mut visited_comments) 55 if comment.text().trim().starts_with("// region:") {
53 { 56 regions_starts.push(comment.syntax().text_range().start());
54 res.push(Fold { range, kind: FoldKind::Comment }) 57 } else if comment.text().trim().starts_with("// endregion") {
58 if let Some(region) = regions_starts.pop() {
59 res.push(Fold {
60 range: TextRange::new(
61 region,
62 comment.syntax().text_range().end(),
63 ),
64 kind: FoldKind::Region,
65 })
66 }
67 } else {
68 if let Some(range) =
69 contiguous_range_for_comment(comment, &mut visited_comments)
70 {
71 res.push(Fold { range, kind: FoldKind::Comment })
72 }
55 } 73 }
56 } 74 }
57 } 75 }
@@ -175,9 +193,16 @@ fn contiguous_range_for_comment(
175 } 193 }
176 if let Some(c) = ast::Comment::cast(token) { 194 if let Some(c) = ast::Comment::cast(token) {
177 if c.kind() == group_kind { 195 if c.kind() == group_kind {
178 visited.insert(c.clone()); 196 // regions are not real comments
179 last = c; 197 if c.text().trim().starts_with("// region:")
180 continue; 198 || c.text().trim().starts_with("// endregion")
199 {
200 break;
201 } else {
202 visited.insert(c.clone());
203 last = c;
204 continue;
205 }
181 } 206 }
182 } 207 }
183 // The comment group ends because either: 208 // The comment group ends because either:
@@ -224,6 +249,7 @@ mod tests {
224 FoldKind::Mods => "mods", 249 FoldKind::Mods => "mods",
225 FoldKind::Block => "block", 250 FoldKind::Block => "block",
226 FoldKind::ArgList => "arglist", 251 FoldKind::ArgList => "arglist",
252 FoldKind::Region => "region",
227 }; 253 };
228 assert_eq!(kind, &attr.unwrap()); 254 assert_eq!(kind, &attr.unwrap());
229 } 255 }
@@ -418,4 +444,17 @@ fn foo<fold arglist>(
418"#, 444"#,
419 ) 445 )
420 } 446 }
447
448 #[test]
449 fn fold_region() {
450 check(
451 r#"
452// 1. some normal comment
453<fold region>// region: test
454// 2. some normal comment
455calling_function(x,y);
456// endregion: test</fold>
457"#,
458 )
459 }
421} 460}
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 98f3dc6ca..312ac7ac4 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -8,7 +8,7 @@ use syntax::{
8}; 8};
9use test_utils::mark; 9use test_utils::mark;
10 10
11use crate::{AssistContext, AssistId, AssistKind, Assists}; 11use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
12 12
13// Assist: extract_variable 13// Assist: extract_variable
14// 14//
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
54 54
55 let var_name = match &field_shorthand { 55 let var_name = match &field_shorthand {
56 Some(it) => it.to_string(), 56 Some(it) => it.to_string(),
57 None => "var_name".to_string(), 57 None => suggest_name::variable(&to_extract, &ctx.sema),
58 }; 58 };
59 let expr_range = match &field_shorthand { 59 let expr_range = match &field_shorthand {
60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), 60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
@@ -274,8 +274,8 @@ fn foo() {
274"#, 274"#,
275 r#" 275 r#"
276fn foo() { 276fn foo() {
277 let $0var_name = bar(1 + 1); 277 let $0bar = bar(1 + 1);
278 var_name 278 bar
279} 279}
280"#, 280"#,
281 ) 281 )
@@ -401,8 +401,8 @@ fn main() {
401", 401",
402 " 402 "
403fn main() { 403fn main() {
404 let $0var_name = bar.foo(); 404 let $0foo = bar.foo();
405 let v = var_name; 405 let v = foo;
406} 406}
407", 407",
408 ); 408 );
@@ -557,6 +557,202 @@ fn main() {
557 } 557 }
558 558
559 #[test] 559 #[test]
560 fn extract_var_name_from_type() {
561 check_assist(
562 extract_variable,
563 r#"
564struct Test(i32);
565
566fn foo() -> Test {
567 $0{ Test(10) }$0
568}
569"#,
570 r#"
571struct Test(i32);
572
573fn foo() -> Test {
574 let $0test = { Test(10) };
575 test
576}
577"#,
578 )
579 }
580
581 #[test]
582 fn extract_var_name_from_parameter() {
583 check_assist(
584 extract_variable,
585 r#"
586fn bar(test: u32, size: u32)
587
588fn foo() {
589 bar(1, $01+1$0);
590}
591"#,
592 r#"
593fn bar(test: u32, size: u32)
594
595fn foo() {
596 let $0size = 1+1;
597 bar(1, size);
598}
599"#,
600 )
601 }
602
603 #[test]
604 fn extract_var_parameter_name_has_precedence_over_type() {
605 check_assist(
606 extract_variable,
607 r#"
608struct TextSize(u32);
609fn bar(test: u32, size: TextSize)
610
611fn foo() {
612 bar(1, $0{ TextSize(1+1) }$0);
613}
614"#,
615 r#"
616struct TextSize(u32);
617fn bar(test: u32, size: TextSize)
618
619fn foo() {
620 let $0size = { TextSize(1+1) };
621 bar(1, size);
622}
623"#,
624 )
625 }
626
627 #[test]
628 fn extract_var_name_from_function() {
629 check_assist(
630 extract_variable,
631 r#"
632fn is_required(test: u32, size: u32) -> bool
633
634fn foo() -> bool {
635 $0is_required(1, 2)$0
636}
637"#,
638 r#"
639fn is_required(test: u32, size: u32) -> bool
640
641fn foo() -> bool {
642 let $0is_required = is_required(1, 2);
643 is_required
644}
645"#,
646 )
647 }
648
649 #[test]
650 fn extract_var_name_from_method() {
651 check_assist(
652 extract_variable,
653 r#"
654struct S;
655impl S {
656 fn bar(&self, n: u32) -> u32 { n }
657}
658
659fn foo() -> u32 {
660 $0S.bar(1)$0
661}
662"#,
663 r#"
664struct S;
665impl S {
666 fn bar(&self, n: u32) -> u32 { n }
667}
668
669fn foo() -> u32 {
670 let $0bar = S.bar(1);
671 bar
672}
673"#,
674 )
675 }
676
677 #[test]
678 fn extract_var_name_from_method_param() {
679 check_assist(
680 extract_variable,
681 r#"
682struct S;
683impl S {
684 fn bar(&self, n: u32, size: u32) { n }
685}
686
687fn foo() {
688 S.bar($01 + 1$0, 2)
689}
690"#,
691 r#"
692struct S;
693impl S {
694 fn bar(&self, n: u32, size: u32) { n }
695}
696
697fn foo() {
698 let $0n = 1 + 1;
699 S.bar(n, 2)
700}
701"#,
702 )
703 }
704
705 #[test]
706 fn extract_var_name_from_ufcs_method_param() {
707 check_assist(
708 extract_variable,
709 r#"
710struct S;
711impl S {
712 fn bar(&self, n: u32, size: u32) { n }
713}
714
715fn foo() {
716 S::bar(&S, $01 + 1$0, 2)
717}
718"#,
719 r#"
720struct S;
721impl S {
722 fn bar(&self, n: u32, size: u32) { n }
723}
724
725fn foo() {
726 let $0n = 1 + 1;
727 S::bar(&S, n, 2)
728}
729"#,
730 )
731 }
732
733 #[test]
734 fn extract_var_parameter_name_has_precedence_over_function() {
735 check_assist(
736 extract_variable,
737 r#"
738fn bar(test: u32, size: u32)
739
740fn foo() {
741 bar(1, $0symbol_size(1, 2)$0);
742}
743"#,
744 r#"
745fn bar(test: u32, size: u32)
746
747fn foo() {
748 let $0size = symbol_size(1, 2);
749 bar(1, size);
750}
751"#,
752 )
753 }
754
755 #[test]
560 fn test_extract_var_for_return_not_applicable() { 756 fn test_extract_var_for_return_not_applicable() {
561 check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } "); 757 check_assist_not_applicable(extract_variable, "fn foo() { $0return$0; } ");
562 } 758 }
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index b7f616760..66820058b 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -12,7 +12,7 @@ use ide_db::{
12 RootDatabase, 12 RootDatabase,
13}; 13};
14use stdx::{format_to, trim_indent}; 14use stdx::{format_to, trim_indent};
15use syntax::TextRange; 15use syntax::{ast, AstNode, TextRange};
16use test_utils::{assert_eq_text, extract_offset}; 16use test_utils::{assert_eq_text, extract_offset};
17 17
18use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 18use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists};
@@ -180,6 +180,50 @@ fn labels(assists: &[Assist]) -> String {
180 labels.into_iter().collect::<String>() 180 labels.into_iter().collect::<String>()
181} 181}
182 182
183pub(crate) type NameSuggestion = fn(&ast::Expr, &Semantics<'_, RootDatabase>) -> Option<String>;
184
185#[track_caller]
186pub(crate) fn check_name_suggestion(
187 suggestion: NameSuggestion,
188 ra_fixture: &str,
189 suggested_name: &str,
190) {
191 check_name(suggestion, ra_fixture, Some(suggested_name));
192}
193
194#[track_caller]
195pub(crate) fn check_name_suggestion_not_applicable(suggestion: NameSuggestion, ra_fixture: &str) {
196 check_name(suggestion, ra_fixture, None);
197}
198
199#[track_caller]
200fn check_name(suggestion: NameSuggestion, ra_fixture: &str, expected: Option<&str>) {
201 let (db, file_with_carret_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
202 let frange = FileRange { file_id: file_with_carret_id, range: range_or_offset.into() };
203
204 let sema = Semantics::new(&db);
205 let source_file = sema.parse(frange.file_id);
206 let element = source_file.syntax().covering_element(frange.range);
207 let expr =
208 element.ancestors().find_map(ast::Expr::cast).expect("selection is not an expression");
209 assert_eq!(
210 expr.syntax().text_range(),
211 frange.range,
212 "selection is not an expression(yet contained in one)"
213 );
214
215 let name = suggestion(&expr, &sema);
216
217 match (name, expected) {
218 (Some(name), Some(expected_name)) => {
219 assert_eq_text!(&name, expected_name);
220 }
221 (Some(_), None) => panic!("name suggestion should not be applicable"),
222 (None, Some(_)) => panic!("name suggestion is not applicable"),
223 (None, None) => (),
224 }
225}
226
183#[test] 227#[test]
184fn assist_order_field_struct() { 228fn assist_order_field_struct() {
185 let before = "struct Foo { $0bar: u32 }"; 229 let before = "struct Foo { $0bar: u32 }";
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 880ab6fe3..62f959082 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -1,5 +1,7 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2 2
3pub(crate) mod suggest_name;
4
3use std::ops; 5use std::ops;
4 6
5use ast::TypeBoundsOwner; 7use ast::TypeBoundsOwner;
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
new file mode 100644
index 000000000..d37c62642
--- /dev/null
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -0,0 +1,877 @@
1//! This module contains functions to suggest names for expressions, functions and other items
2
3use hir::Semantics;
4use ide_db::RootDatabase;
5use itertools::Itertools;
6use stdx::to_lower_snake_case;
7use syntax::{
8 ast::{self, NameOwner},
9 match_ast, AstNode,
10};
11
12/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
13const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"];
14/// Identifier names that won't be suggested, ever
15///
16/// **NOTE**: they all must be snake lower case
17const USELESS_NAMES: &[&str] =
18 &["new", "default", "option", "some", "none", "ok", "err", "str", "string"];
19/// Generic types replaced by their first argument
20///
21/// # Examples
22/// `Option<Name>` -> `Name`
23/// `Result<User, Error>` -> `User`
24const WRAPPER_TYPES: &[&str] = &["Box", "Option", "Result"];
25/// Prefixes to strip from methods names
26///
27/// # Examples
28/// `vec.as_slice()` -> `slice`
29/// `args.into_config()` -> `config`
30/// `bytes.to_vec()` -> `vec`
31const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
32/// Useless methods that are stripped from expression
33///
34/// # Examples
35/// `var.name().to_string()` -> `var.name()`
36const USELESS_METHODS: &[&str] = &[
37 "to_string",
38 "as_str",
39 "to_owned",
40 "as_ref",
41 "clone",
42 "cloned",
43 "expect",
44 "expect_none",
45 "unwrap",
46 "unwrap_none",
47 "unwrap_or",
48 "unwrap_or_default",
49 "unwrap_or_else",
50 "unwrap_unchecked",
51 "iter",
52 "into_iter",
53 "iter_mut",
54];
55
56/// Suggest name of variable for given expression
57///
58/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
59/// I.e. it doesn't look for names in scope.
60///
61/// # Current implementation
62///
63/// In current implementation, the function tries to get the name from
64/// the following sources:
65///
66/// * if expr is an argument to function/method, use paramter name
67/// * if expr is a function/method call, use function name
68/// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names)
69/// * fallback: `var_name`
70///
71/// It also applies heuristics to filter out less informative names
72///
73/// Currently it sticks to the first name found.
74pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
75 // `from_param` does not benifit from stripping
76 // it need the largest context possible
77 // so we check firstmost
78 if let Some(name) = from_param(expr, sema) {
79 return name;
80 }
81
82 let mut next_expr = Some(expr.clone());
83 while let Some(expr) = next_expr {
84 let name = from_call(&expr).or_else(|| from_type(&expr, sema));
85 if let Some(name) = name {
86 return name;
87 }
88
89 match expr {
90 ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
91 ast::Expr::BoxExpr(inner) => next_expr = inner.expr(),
92 ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
93 // ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
94 ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
95 ast::Expr::MethodCallExpr(method) if is_useless_method(&method) => {
96 next_expr = method.receiver();
97 }
98 ast::Expr::ParenExpr(inner) => next_expr = inner.expr(),
99 ast::Expr::TryExpr(inner) => next_expr = inner.expr(),
100 ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(ast::PrefixOp::Deref) => {
101 next_expr = prefix.expr()
102 }
103 _ => break,
104 }
105 }
106
107 "var_name".to_string()
108}
109
110fn normalize(name: &str) -> Option<String> {
111 let name = to_lower_snake_case(name);
112
113 if USELESS_NAMES.contains(&name.as_str()) {
114 return None;
115 }
116
117 if !is_valid_name(&name) {
118 return None;
119 }
120
121 Some(name)
122}
123
124fn is_valid_name(name: &str) -> bool {
125 match syntax::lex_single_syntax_kind(name) {
126 Some((syntax::SyntaxKind::IDENT, _error)) => true,
127 _ => false,
128 }
129}
130
131fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
132 let ident = method.name_ref().and_then(|it| it.ident_token());
133
134 if let Some(ident) = ident {
135 USELESS_METHODS.contains(&ident.text())
136 } else {
137 false
138 }
139}
140
141fn from_call(expr: &ast::Expr) -> Option<String> {
142 from_func_call(expr).or_else(|| from_method_call(expr))
143}
144
145fn from_func_call(expr: &ast::Expr) -> Option<String> {
146 let call = match expr {
147 ast::Expr::CallExpr(call) => call,
148 _ => return None,
149 };
150 let func = match call.expr()? {
151 ast::Expr::PathExpr(path) => path,
152 _ => return None,
153 };
154 let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
155 normalize(ident.text())
156}
157
158fn from_method_call(expr: &ast::Expr) -> Option<String> {
159 let method = match expr {
160 ast::Expr::MethodCallExpr(call) => call,
161 _ => return None,
162 };
163 let ident = method.name_ref()?.ident_token()?;
164 let mut name = ident.text();
165
166 if USELESS_METHODS.contains(&name) {
167 return None;
168 }
169
170 for prefix in USELESS_METHOD_PREFIXES {
171 if let Some(suffix) = name.strip_prefix(prefix) {
172 name = suffix;
173 break;
174 }
175 }
176
177 normalize(&name)
178}
179
180fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
181 let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
182 let args_parent = arg_list.syntax().parent()?;
183 let func = match_ast! {
184 match args_parent {
185 ast::CallExpr(call) => {
186 let func = call.expr()?;
187 let func_ty = sema.type_of_expr(&func)?;
188 func_ty.as_callable(sema.db)?
189 },
190 ast::MethodCallExpr(method) => sema.resolve_method_call_as_callable(&method)?,
191 _ => return None,
192 }
193 };
194
195 let (idx, _) = arg_list.args().find_position(|it| it == expr).unwrap();
196 let (pat, _) = func.params(sema.db).into_iter().nth(idx)?;
197 let pat = match pat? {
198 either::Either::Right(pat) => pat,
199 _ => return None,
200 };
201 let name = var_name_from_pat(&pat)?;
202 normalize(&name.to_string())
203}
204
205fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
206 match pat {
207 ast::Pat::IdentPat(var) => var.name(),
208 ast::Pat::RefPat(ref_pat) => var_name_from_pat(&ref_pat.pat()?),
209 ast::Pat::BoxPat(box_pat) => var_name_from_pat(&box_pat.pat()?),
210 _ => None,
211 }
212}
213
214fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<String> {
215 let ty = sema.type_of_expr(expr)?;
216 let ty = ty.remove_ref().unwrap_or(ty);
217
218 name_of_type(&ty, sema.db)
219}
220
221fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
222 let name = if let Some(adt) = ty.as_adt() {
223 let name = adt.name(db).to_string();
224
225 if WRAPPER_TYPES.contains(&name.as_str()) {
226 let inner_ty = ty.type_parameters().next()?;
227 return name_of_type(&inner_ty, db);
228 }
229
230 name
231 } else if let Some(trait_) = ty.as_dyn_trait() {
232 trait_name(&trait_, db)?
233 } else if let Some(traits) = ty.as_impl_traits(db) {
234 let mut iter = traits.into_iter().filter_map(|t| trait_name(&t, db));
235 let name = iter.next()?;
236 if iter.next().is_some() {
237 return None;
238 }
239 name
240 } else {
241 return None;
242 };
243 normalize(&name)
244}
245
246fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
247 let name = trait_.name(db).to_string();
248 if USELESS_TRAITS.contains(&name.as_str()) {
249 return None;
250 }
251 Some(name)
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 use crate::tests::check_name_suggestion;
259
260 mod from_func_call {
261 use super::*;
262
263 #[test]
264 fn no_args() {
265 check_name_suggestion(
266 |e, _| from_func_call(e),
267 r#"
268 fn foo() {
269 $0bar()$0
270 }"#,
271 "bar",
272 );
273 }
274
275 #[test]
276 fn single_arg() {
277 check_name_suggestion(
278 |e, _| from_func_call(e),
279 r#"
280 fn foo() {
281 $0bar(1)$0
282 }"#,
283 "bar",
284 );
285 }
286
287 #[test]
288 fn many_args() {
289 check_name_suggestion(
290 |e, _| from_func_call(e),
291 r#"
292 fn foo() {
293 $0bar(1, 2, 3)$0
294 }"#,
295 "bar",
296 );
297 }
298
299 #[test]
300 fn path() {
301 check_name_suggestion(
302 |e, _| from_func_call(e),
303 r#"
304 fn foo() {
305 $0i32::bar(1, 2, 3)$0
306 }"#,
307 "bar",
308 );
309 }
310
311 #[test]
312 fn generic_params() {
313 check_name_suggestion(
314 |e, _| from_func_call(e),
315 r#"
316 fn foo() {
317 $0bar::<i32>(1, 2, 3)$0
318 }"#,
319 "bar",
320 );
321 }
322 }
323
324 mod from_method_call {
325 use super::*;
326
327 #[test]
328 fn no_args() {
329 check_name_suggestion(
330 |e, _| from_method_call(e),
331 r#"
332 fn foo() {
333 $0bar.frobnicate()$0
334 }"#,
335 "frobnicate",
336 );
337 }
338
339 #[test]
340 fn generic_params() {
341 check_name_suggestion(
342 |e, _| from_method_call(e),
343 r#"
344 fn foo() {
345 $0bar.frobnicate::<i32, u32>()$0
346 }"#,
347 "frobnicate",
348 );
349 }
350
351 #[test]
352 fn to_name() {
353 check_name_suggestion(
354 |e, _| from_method_call(e),
355 r#"
356 struct Args;
357 struct Config;
358 impl Args {
359 fn to_config(&self) -> Config {}
360 }
361 fn foo() {
362 $0Args.to_config()$0;
363 }"#,
364 "config",
365 );
366 }
367 }
368
369 mod from_param {
370 use crate::tests::check_name_suggestion_not_applicable;
371
372 use super::*;
373
374 #[test]
375 fn plain_func() {
376 check_name_suggestion(
377 from_param,
378 r#"
379 fn bar(n: i32, m: u32);
380 fn foo() {
381 bar($01$0, 2)
382 }"#,
383 "n",
384 );
385 }
386
387 #[test]
388 fn mut_param() {
389 check_name_suggestion(
390 from_param,
391 r#"
392 fn bar(mut n: i32, m: u32);
393 fn foo() {
394 bar($01$0, 2)
395 }"#,
396 "n",
397 );
398 }
399
400 #[test]
401 fn func_does_not_exist() {
402 check_name_suggestion_not_applicable(
403 from_param,
404 r#"
405 fn foo() {
406 bar($01$0, 2)
407 }"#,
408 );
409 }
410
411 #[test]
412 fn unnamed_param() {
413 check_name_suggestion_not_applicable(
414 from_param,
415 r#"
416 fn bar(_: i32, m: u32);
417 fn foo() {
418 bar($01$0, 2)
419 }"#,
420 );
421 }
422
423 #[test]
424 fn tuple_pat() {
425 check_name_suggestion_not_applicable(
426 from_param,
427 r#"
428 fn bar((n, k): (i32, i32), m: u32);
429 fn foo() {
430 bar($0(1, 2)$0, 3)
431 }"#,
432 );
433 }
434
435 #[test]
436 fn ref_pat() {
437 check_name_suggestion(
438 from_param,
439 r#"
440 fn bar(&n: &i32, m: u32);
441 fn foo() {
442 bar($0&1$0, 3)
443 }"#,
444 "n",
445 );
446 }
447
448 #[test]
449 fn box_pat() {
450 check_name_suggestion(
451 from_param,
452 r#"
453 fn bar(box n: &i32, m: u32);
454 fn foo() {
455 bar($01$0, 3)
456 }"#,
457 "n",
458 );
459 }
460
461 #[test]
462 fn param_out_of_index() {
463 check_name_suggestion_not_applicable(
464 from_param,
465 r#"
466 fn bar(n: i32, m: u32);
467 fn foo() {
468 bar(1, 2, $03$0)
469 }"#,
470 );
471 }
472
473 #[test]
474 fn generic_param_resolved() {
475 check_name_suggestion(
476 from_param,
477 r#"
478 fn bar<T>(n: T, m: u32);
479 fn foo() {
480 bar($01$0, 2)
481 }"#,
482 "n",
483 );
484 }
485
486 #[test]
487 fn generic_param_unresolved() {
488 check_name_suggestion(
489 from_param,
490 r#"
491 fn bar<T>(n: T, m: u32);
492 fn foo<T>(x: T) {
493 bar($0x$0, 2)
494 }"#,
495 "n",
496 );
497 }
498
499 #[test]
500 fn method() {
501 check_name_suggestion(
502 from_param,
503 r#"
504 struct S;
505 impl S {
506 fn bar(&self, n: i32, m: u32);
507 }
508 fn foo() {
509 S.bar($01$0, 2)
510 }"#,
511 "n",
512 );
513 }
514
515 #[test]
516 fn method_ufcs() {
517 check_name_suggestion(
518 from_param,
519 r#"
520 struct S;
521 impl S {
522 fn bar(&self, n: i32, m: u32);
523 }
524 fn foo() {
525 S::bar(&S, $01$0, 2)
526 }"#,
527 "n",
528 );
529 }
530
531 #[test]
532 fn method_self() {
533 check_name_suggestion_not_applicable(
534 from_param,
535 r#"
536 struct S;
537 impl S {
538 fn bar(&self, n: i32, m: u32);
539 }
540 fn foo() {
541 S::bar($0&S$0, 1, 2)
542 }"#,
543 );
544 }
545
546 #[test]
547 fn method_self_named() {
548 check_name_suggestion(
549 from_param,
550 r#"
551 struct S;
552 impl S {
553 fn bar(strukt: &Self, n: i32, m: u32);
554 }
555 fn foo() {
556 S::bar($0&S$0, 1, 2)
557 }"#,
558 "strukt",
559 );
560 }
561 }
562
563 mod from_type {
564 use crate::tests::check_name_suggestion_not_applicable;
565
566 use super::*;
567
568 #[test]
569 fn i32() {
570 check_name_suggestion_not_applicable(
571 from_type,
572 r#"
573 fn foo() {
574 let _: i32 = $01$0;
575 }"#,
576 );
577 }
578
579 #[test]
580 fn u64() {
581 check_name_suggestion_not_applicable(
582 from_type,
583 r#"
584 fn foo() {
585 let _: u64 = $01$0;
586 }"#,
587 );
588 }
589
590 #[test]
591 fn bool() {
592 check_name_suggestion_not_applicable(
593 from_type,
594 r#"
595 fn foo() {
596 let _: bool = $0true$0;
597 }"#,
598 );
599 }
600
601 #[test]
602 fn struct_unit() {
603 check_name_suggestion(
604 from_type,
605 r#"
606 struct Seed;
607 fn foo() {
608 let _ = $0Seed$0;
609 }"#,
610 "seed",
611 );
612 }
613
614 #[test]
615 fn struct_unit_to_snake() {
616 check_name_suggestion(
617 from_type,
618 r#"
619 struct SeedState;
620 fn foo() {
621 let _ = $0SeedState$0;
622 }"#,
623 "seed_state",
624 );
625 }
626
627 #[test]
628 fn struct_single_arg() {
629 check_name_suggestion(
630 from_type,
631 r#"
632 struct Seed(u32);
633 fn foo() {
634 let _ = $0Seed(0)$0;
635 }"#,
636 "seed",
637 );
638 }
639
640 #[test]
641 fn struct_with_fields() {
642 check_name_suggestion(
643 from_type,
644 r#"
645 struct Seed { value: u32 }
646 fn foo() {
647 let _ = $0Seed { value: 0 }$0;
648 }"#,
649 "seed",
650 );
651 }
652
653 #[test]
654 fn enum_() {
655 check_name_suggestion(
656 from_type,
657 r#"
658 enum Kind { A, B }
659 fn foo() {
660 let _ = $0Kind::A$0;
661 }"#,
662 "kind",
663 );
664 }
665
666 #[test]
667 fn enum_generic_resolved() {
668 check_name_suggestion(
669 from_type,
670 r#"
671 enum Kind<T> { A(T), B }
672 fn foo() {
673 let _ = $0Kind::A(1)$0;
674 }"#,
675 "kind",
676 );
677 }
678
679 #[test]
680 fn enum_generic_unresolved() {
681 check_name_suggestion(
682 from_type,
683 r#"
684 enum Kind<T> { A(T), B }
685 fn foo<T>(x: T) {
686 let _ = $0Kind::A(x)$0;
687 }"#,
688 "kind",
689 );
690 }
691
692 #[test]
693 fn dyn_trait() {
694 check_name_suggestion(
695 from_type,
696 r#"
697 trait DynHandler {}
698 fn bar() -> dyn DynHandler {}
699 fn foo() {
700 $0bar()$0;
701 }"#,
702 "dyn_handler",
703 );
704 }
705
706 #[test]
707 fn impl_trait() {
708 check_name_suggestion(
709 from_type,
710 r#"
711 trait StaticHandler {}
712 fn bar() -> impl StaticHandler {}
713 fn foo() {
714 $0bar()$0;
715 }"#,
716 "static_handler",
717 );
718 }
719
720 #[test]
721 fn impl_trait_plus_clone() {
722 check_name_suggestion(
723 from_type,
724 r#"
725 trait StaticHandler {}
726 trait Clone {}
727 fn bar() -> impl StaticHandler + Clone {}
728 fn foo() {
729 $0bar()$0;
730 }"#,
731 "static_handler",
732 );
733 }
734
735 #[test]
736 fn impl_trait_plus_lifetime() {
737 check_name_suggestion(
738 from_type,
739 r#"
740 trait StaticHandler {}
741 trait Clone {}
742 fn bar<'a>(&'a i32) -> impl StaticHandler + 'a {}
743 fn foo() {
744 $0bar(&1)$0;
745 }"#,
746 "static_handler",
747 );
748 }
749
750 #[test]
751 fn impl_trait_plus_trait() {
752 check_name_suggestion_not_applicable(
753 from_type,
754 r#"
755 trait Handler {}
756 trait StaticHandler {}
757 fn bar() -> impl StaticHandler + Handler {}
758 fn foo() {
759 $0bar()$0;
760 }"#,
761 );
762 }
763
764 #[test]
765 fn ref_value() {
766 check_name_suggestion(
767 from_type,
768 r#"
769 struct Seed;
770 fn bar() -> &Seed {}
771 fn foo() {
772 $0bar()$0;
773 }"#,
774 "seed",
775 );
776 }
777
778 #[test]
779 fn box_value() {
780 check_name_suggestion(
781 from_type,
782 r#"
783 struct Box<T>(*const T);
784 struct Seed;
785 fn bar() -> Box<Seed> {}
786 fn foo() {
787 $0bar()$0;
788 }"#,
789 "seed",
790 );
791 }
792
793 #[test]
794 fn box_generic() {
795 check_name_suggestion_not_applicable(
796 from_type,
797 r#"
798 struct Box<T>(*const T);
799 fn bar<T>() -> Box<T> {}
800 fn foo<T>() {
801 $0bar::<T>()$0;
802 }"#,
803 );
804 }
805
806 #[test]
807 fn option_value() {
808 check_name_suggestion(
809 from_type,
810 r#"
811 enum Option<T> { Some(T) }
812 struct Seed;
813 fn bar() -> Option<Seed> {}
814 fn foo() {
815 $0bar()$0;
816 }"#,
817 "seed",
818 );
819 }
820
821 #[test]
822 fn result_value() {
823 check_name_suggestion(
824 from_type,
825 r#"
826 enum Result<T, E> { Ok(T), Err(E) }
827 struct Seed;
828 struct Error;
829 fn bar() -> Result<Seed, Error> {}
830 fn foo() {
831 $0bar()$0;
832 }"#,
833 "seed",
834 );
835 }
836 }
837
838 mod variable {
839 use super::*;
840
841 #[test]
842 fn ref_call() {
843 check_name_suggestion(
844 |e, c| Some(variable(e, c)),
845 r#"
846 fn foo() {
847 $0&bar(1, 3)$0
848 }"#,
849 "bar",
850 );
851 }
852
853 #[test]
854 fn name_to_string() {
855 check_name_suggestion(
856 |e, c| Some(variable(e, c)),
857 r#"
858 fn foo() {
859 $0function.name().to_string()$0
860 }"#,
861 "name",
862 );
863 }
864
865 #[test]
866 fn nested_useless_method() {
867 check_name_suggestion(
868 |e, c| Some(variable(e, c)),
869 r#"
870 fn foo() {
871 $0function.name().as_ref().unwrap().to_string()$0
872 }"#,
873 "name",
874 );
875 }
876 }
877}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 70cb7fbab..c1ca88df6 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -474,7 +474,7 @@ pub(crate) fn folding_range(
474 let kind = match fold.kind { 474 let kind = match fold.kind {
475 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment), 475 FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
476 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports), 476 FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
477 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList => None, 477 FoldKind::Mods | FoldKind::Block | FoldKind::ArgList | FoldKind::Region => None,
478 }; 478 };
479 479
480 let range = range(line_index, fold.range); 480 let range = range(line_index, fold.range);