diff options
Diffstat (limited to 'crates/ra_ide_api')
18 files changed, 284 insertions, 65 deletions
diff --git a/crates/ra_ide_api/src/completion.rs b/crates/ra_ide_api/src/completion.rs index a6b68be75..a4f080adc 100644 --- a/crates/ra_ide_api/src/completion.rs +++ b/crates/ra_ide_api/src/completion.rs | |||
@@ -3,8 +3,8 @@ mod completion_context; | |||
3 | mod presentation; | 3 | mod presentation; |
4 | 4 | ||
5 | mod complete_dot; | 5 | mod complete_dot; |
6 | mod complete_struct_literal; | 6 | mod complete_record_literal; |
7 | mod complete_struct_pattern; | 7 | mod complete_record_pattern; |
8 | mod complete_pattern; | 8 | mod complete_pattern; |
9 | mod complete_fn_param; | 9 | mod complete_fn_param; |
10 | mod complete_keyword; | 10 | mod complete_keyword; |
@@ -65,8 +65,8 @@ pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Opti | |||
65 | complete_path::complete_path(&mut acc, &ctx); | 65 | complete_path::complete_path(&mut acc, &ctx); |
66 | complete_scope::complete_scope(&mut acc, &ctx); | 66 | complete_scope::complete_scope(&mut acc, &ctx); |
67 | complete_dot::complete_dot(&mut acc, &ctx); | 67 | complete_dot::complete_dot(&mut acc, &ctx); |
68 | complete_struct_literal::complete_struct_literal(&mut acc, &ctx); | 68 | complete_record_literal::complete_record_literal(&mut acc, &ctx); |
69 | complete_struct_pattern::complete_struct_pattern(&mut acc, &ctx); | 69 | complete_record_pattern::complete_record_pattern(&mut acc, &ctx); |
70 | complete_pattern::complete_pattern(&mut acc, &ctx); | 70 | complete_pattern::complete_pattern(&mut acc, &ctx); |
71 | complete_postfix::complete_postfix(&mut acc, &ctx); | 71 | complete_postfix::complete_postfix(&mut acc, &ctx); |
72 | Some(acc) | 72 | Some(acc) |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index d43ff2eec..27256f879 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -45,7 +45,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) | |||
45 | // FIXME unions | 45 | // FIXME unions |
46 | TypeCtor::Tuple { .. } => { | 46 | TypeCtor::Tuple { .. } => { |
47 | for (i, ty) in a_ty.parameters.iter().enumerate() { | 47 | for (i, ty) in a_ty.parameters.iter().enumerate() { |
48 | acc.add_pos_field(ctx, i, ty); | 48 | acc.add_tuple_field(ctx, i, ty); |
49 | } | 49 | } |
50 | } | 50 | } |
51 | _ => {} | 51 | _ => {} |
diff --git a/crates/ra_ide_api/src/completion/complete_struct_literal.rs b/crates/ra_ide_api/src/completion/complete_record_literal.rs index 6aa41f498..6b929a8ac 100644 --- a/crates/ra_ide_api/src/completion/complete_struct_literal.rs +++ b/crates/ra_ide_api/src/completion/complete_record_literal.rs | |||
@@ -3,11 +3,11 @@ use hir::Substs; | |||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
5 | /// Complete fields in fields literals. | 5 | /// Complete fields in fields literals. |
6 | pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) { | 6 | pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { |
7 | let (ty, variant) = match ctx.struct_lit_syntax.as_ref().and_then(|it| { | 7 | let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { |
8 | Some(( | 8 | Some(( |
9 | ctx.analyzer.type_of(ctx.db, &it.clone().into())?, | 9 | ctx.analyzer.type_of(ctx.db, &it.clone().into())?, |
10 | ctx.analyzer.resolve_struct_literal(it)?, | 10 | ctx.analyzer.resolve_record_literal(it)?, |
11 | )) | 11 | )) |
12 | }) { | 12 | }) { |
13 | Some(it) => it, | 13 | Some(it) => it, |
@@ -30,7 +30,7 @@ mod tests { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | #[test] | 32 | #[test] |
33 | fn test_struct_literal_field() { | 33 | fn test_record_literal_field() { |
34 | let completions = complete( | 34 | let completions = complete( |
35 | r" | 35 | r" |
36 | struct A { the_field: u32 } | 36 | struct A { the_field: u32 } |
@@ -54,7 +54,7 @@ mod tests { | |||
54 | } | 54 | } |
55 | 55 | ||
56 | #[test] | 56 | #[test] |
57 | fn test_struct_literal_enum_variant() { | 57 | fn test_record_literal_enum_variant() { |
58 | let completions = complete( | 58 | let completions = complete( |
59 | r" | 59 | r" |
60 | enum E { | 60 | enum E { |
@@ -80,7 +80,7 @@ mod tests { | |||
80 | } | 80 | } |
81 | 81 | ||
82 | #[test] | 82 | #[test] |
83 | fn test_struct_literal_two_structs() { | 83 | fn test_record_literal_two_structs() { |
84 | let completions = complete( | 84 | let completions = complete( |
85 | r" | 85 | r" |
86 | struct A { a: u32 } | 86 | struct A { a: u32 } |
@@ -106,7 +106,7 @@ mod tests { | |||
106 | } | 106 | } |
107 | 107 | ||
108 | #[test] | 108 | #[test] |
109 | fn test_struct_literal_generic_struct() { | 109 | fn test_record_literal_generic_struct() { |
110 | let completions = complete( | 110 | let completions = complete( |
111 | r" | 111 | r" |
112 | struct A<T> { a: T } | 112 | struct A<T> { a: T } |
diff --git a/crates/ra_ide_api/src/completion/complete_struct_pattern.rs b/crates/ra_ide_api/src/completion/complete_record_pattern.rs index d0dde5930..8c8b47ea4 100644 --- a/crates/ra_ide_api/src/completion/complete_struct_pattern.rs +++ b/crates/ra_ide_api/src/completion/complete_record_pattern.rs | |||
@@ -2,11 +2,11 @@ use hir::Substs; | |||
2 | 2 | ||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
5 | pub(super) fn complete_struct_pattern(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { |
6 | let (ty, variant) = match ctx.struct_lit_pat.as_ref().and_then(|it| { | 6 | let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { |
7 | Some(( | 7 | Some(( |
8 | ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, | 8 | ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, |
9 | ctx.analyzer.resolve_struct_pattern(it)?, | 9 | ctx.analyzer.resolve_record_pattern(it)?, |
10 | )) | 10 | )) |
11 | }) { | 11 | }) { |
12 | Some(it) => it, | 12 | Some(it) => it, |
@@ -29,7 +29,7 @@ mod tests { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | #[test] | 31 | #[test] |
32 | fn test_struct_pattern_field() { | 32 | fn test_record_pattern_field() { |
33 | let completions = complete( | 33 | let completions = complete( |
34 | r" | 34 | r" |
35 | struct S { foo: u32 } | 35 | struct S { foo: u32 } |
@@ -56,7 +56,7 @@ mod tests { | |||
56 | } | 56 | } |
57 | 57 | ||
58 | #[test] | 58 | #[test] |
59 | fn test_struct_pattern_enum_variant() { | 59 | fn test_record_pattern_enum_variant() { |
60 | let completions = complete( | 60 | let completions = complete( |
61 | r" | 61 | r" |
62 | enum E { | 62 | enum E { |
diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index dfaa9ce69..7139947b3 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs | |||
@@ -20,8 +20,8 @@ pub(crate) struct CompletionContext<'a> { | |||
20 | pub(super) module: Option<hir::Module>, | 20 | pub(super) module: Option<hir::Module>, |
21 | pub(super) function_syntax: Option<ast::FnDef>, | 21 | pub(super) function_syntax: Option<ast::FnDef>, |
22 | pub(super) use_item_syntax: Option<ast::UseItem>, | 22 | pub(super) use_item_syntax: Option<ast::UseItem>, |
23 | pub(super) struct_lit_syntax: Option<ast::StructLit>, | 23 | pub(super) record_lit_syntax: Option<ast::RecordLit>, |
24 | pub(super) struct_lit_pat: Option<ast::StructPat>, | 24 | pub(super) record_lit_pat: Option<ast::RecordPat>, |
25 | pub(super) is_param: bool, | 25 | pub(super) is_param: bool, |
26 | /// If a name-binding or reference to a const in a pattern. | 26 | /// If a name-binding or reference to a const in a pattern. |
27 | /// Irrefutable patterns (like let) are excluded. | 27 | /// Irrefutable patterns (like let) are excluded. |
@@ -60,8 +60,8 @@ impl<'a> CompletionContext<'a> { | |||
60 | module, | 60 | module, |
61 | function_syntax: None, | 61 | function_syntax: None, |
62 | use_item_syntax: None, | 62 | use_item_syntax: None, |
63 | struct_lit_syntax: None, | 63 | record_lit_syntax: None, |
64 | struct_lit_pat: None, | 64 | record_lit_pat: None, |
65 | is_param: false, | 65 | is_param: false, |
66 | is_pat_binding: false, | 66 | is_pat_binding: false, |
67 | is_trivial_path: false, | 67 | is_trivial_path: false, |
@@ -120,8 +120,8 @@ impl<'a> CompletionContext<'a> { | |||
120 | self.is_param = true; | 120 | self.is_param = true; |
121 | return; | 121 | return; |
122 | } | 122 | } |
123 | if name.syntax().ancestors().find_map(ast::FieldPatList::cast).is_some() { | 123 | if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { |
124 | self.struct_lit_pat = | 124 | self.record_lit_pat = |
125 | find_node_at_offset(original_parse.tree().syntax(), self.offset); | 125 | find_node_at_offset(original_parse.tree().syntax(), self.offset); |
126 | } | 126 | } |
127 | } | 127 | } |
@@ -129,8 +129,8 @@ impl<'a> CompletionContext<'a> { | |||
129 | 129 | ||
130 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { | 130 | fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { |
131 | let name_range = name_ref.syntax().text_range(); | 131 | let name_range = name_ref.syntax().text_range(); |
132 | if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() { | 132 | if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { |
133 | self.struct_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); | 133 | self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); |
134 | } | 134 | } |
135 | 135 | ||
136 | let top_node = name_ref | 136 | let top_node = name_ref |
diff --git a/crates/ra_ide_api/src/completion/presentation.rs b/crates/ra_ide_api/src/completion/presentation.rs index 2b3f98482..147ceda0c 100644 --- a/crates/ra_ide_api/src/completion/presentation.rs +++ b/crates/ra_ide_api/src/completion/presentation.rs | |||
@@ -28,7 +28,7 @@ impl Completions { | |||
28 | .add_to(self); | 28 | .add_to(self); |
29 | } | 29 | } |
30 | 30 | ||
31 | pub(crate) fn add_pos_field(&mut self, ctx: &CompletionContext, field: usize, ty: &hir::Ty) { | 31 | pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &hir::Ty) { |
32 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string()) | 32 | CompletionItem::new(CompletionKind::Reference, ctx.source_range(), field.to_string()) |
33 | .kind(CompletionItemKind::Field) | 33 | .kind(CompletionItemKind::Field) |
34 | .detail(ty.display(ctx.db).to_string()) | 34 | .detail(ty.display(ctx.db).to_string()) |
diff --git a/crates/ra_ide_api/src/diagnostics.rs b/crates/ra_ide_api/src/diagnostics.rs index 98b840b26..1a4882824 100644 --- a/crates/ra_ide_api/src/diagnostics.rs +++ b/crates/ra_ide_api/src/diagnostics.rs | |||
@@ -9,7 +9,7 @@ use ra_assists::ast_editor::{AstBuilder, AstEditor}; | |||
9 | use ra_db::SourceDatabase; | 9 | use ra_db::SourceDatabase; |
10 | use ra_prof::profile; | 10 | use ra_prof::profile; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{ |
12 | ast::{self, AstNode, NamedField}, | 12 | ast::{self, AstNode, RecordField}, |
13 | Location, SyntaxNode, TextRange, T, | 13 | Location, SyntaxNode, TextRange, T, |
14 | }; | 14 | }; |
15 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 15 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
@@ -62,7 +62,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
62 | let node = d.ast(db); | 62 | let node = d.ast(db); |
63 | let mut ast_editor = AstEditor::new(node); | 63 | let mut ast_editor = AstEditor::new(node); |
64 | for f in d.missed_fields.iter() { | 64 | for f in d.missed_fields.iter() { |
65 | ast_editor.append_field(&AstBuilder::<NamedField>::from_name(f)); | 65 | ast_editor.append_field(&AstBuilder::<RecordField>::from_name(f)); |
66 | } | 66 | } |
67 | 67 | ||
68 | let mut builder = TextEditBuilder::default(); | 68 | let mut builder = TextEditBuilder::default(); |
@@ -75,6 +75,19 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
75 | severity: Severity::Error, | 75 | severity: Severity::Error, |
76 | fix: Some(fix), | 76 | fix: Some(fix), |
77 | }) | 77 | }) |
78 | }) | ||
79 | .on::<hir::diagnostics::MissingOkInTailExpr, _>(|d| { | ||
80 | let node = d.ast(db); | ||
81 | let mut builder = TextEditBuilder::default(); | ||
82 | let replacement = format!("Ok({})", node.syntax()); | ||
83 | builder.replace(node.syntax().text_range(), replacement); | ||
84 | let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, builder.finish()); | ||
85 | res.borrow_mut().push(Diagnostic { | ||
86 | range: d.highlight_range(), | ||
87 | message: d.message(), | ||
88 | severity: Severity::Error, | ||
89 | fix: Some(fix), | ||
90 | }) | ||
78 | }); | 91 | }); |
79 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { | 92 | if let Some(m) = source_binder::module_from_file_id(db, file_id) { |
80 | m.diagnostics(db, &mut sink); | 93 | m.diagnostics(db, &mut sink); |
@@ -141,20 +154,20 @@ fn check_struct_shorthand_initialization( | |||
141 | file_id: FileId, | 154 | file_id: FileId, |
142 | node: &SyntaxNode, | 155 | node: &SyntaxNode, |
143 | ) -> Option<()> { | 156 | ) -> Option<()> { |
144 | let struct_lit = ast::StructLit::cast(node.clone())?; | 157 | let record_lit = ast::RecordLit::cast(node.clone())?; |
145 | let named_field_list = struct_lit.named_field_list()?; | 158 | let record_field_list = record_lit.record_field_list()?; |
146 | for named_field in named_field_list.fields() { | 159 | for record_field in record_field_list.fields() { |
147 | if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) { | 160 | if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { |
148 | let field_name = name_ref.syntax().text().to_string(); | 161 | let field_name = name_ref.syntax().text().to_string(); |
149 | let field_expr = expr.syntax().text().to_string(); | 162 | let field_expr = expr.syntax().text().to_string(); |
150 | if field_name == field_expr { | 163 | if field_name == field_expr { |
151 | let mut edit_builder = TextEditBuilder::default(); | 164 | let mut edit_builder = TextEditBuilder::default(); |
152 | edit_builder.delete(named_field.syntax().text_range()); | 165 | edit_builder.delete(record_field.syntax().text_range()); |
153 | edit_builder.insert(named_field.syntax().text_range().start(), field_name); | 166 | edit_builder.insert(record_field.syntax().text_range().start(), field_name); |
154 | let edit = edit_builder.finish(); | 167 | let edit = edit_builder.finish(); |
155 | 168 | ||
156 | acc.push(Diagnostic { | 169 | acc.push(Diagnostic { |
157 | range: named_field.syntax().text_range(), | 170 | range: record_field.syntax().text_range(), |
158 | message: "Shorthand struct initialization".to_string(), | 171 | message: "Shorthand struct initialization".to_string(), |
159 | severity: Severity::WeakWarning, | 172 | severity: Severity::WeakWarning, |
160 | fix: Some(SourceChange::source_file_edit( | 173 | fix: Some(SourceChange::source_file_edit( |
@@ -171,10 +184,11 @@ fn check_struct_shorthand_initialization( | |||
171 | #[cfg(test)] | 184 | #[cfg(test)] |
172 | mod tests { | 185 | mod tests { |
173 | use insta::assert_debug_snapshot_matches; | 186 | use insta::assert_debug_snapshot_matches; |
187 | use join_to_string::join; | ||
174 | use ra_syntax::SourceFile; | 188 | use ra_syntax::SourceFile; |
175 | use test_utils::assert_eq_text; | 189 | use test_utils::assert_eq_text; |
176 | 190 | ||
177 | use crate::mock_analysis::single_file; | 191 | use crate::mock_analysis::{analysis_and_position, single_file}; |
178 | 192 | ||
179 | use super::*; | 193 | use super::*; |
180 | 194 | ||
@@ -203,6 +217,48 @@ mod tests { | |||
203 | assert_eq_text!(after, &actual); | 217 | assert_eq_text!(after, &actual); |
204 | } | 218 | } |
205 | 219 | ||
220 | /// Takes a multi-file input fixture with annotated cursor positions, | ||
221 | /// and checks that: | ||
222 | /// * a diagnostic is produced | ||
223 | /// * this diagnostic touches the input cursor position | ||
224 | /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied | ||
225 | fn check_apply_diagnostic_fix_from_position(fixture: &str, after: &str) { | ||
226 | let (analysis, file_position) = analysis_and_position(fixture); | ||
227 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); | ||
228 | let mut fix = diagnostic.fix.unwrap(); | ||
229 | let edit = fix.source_file_edits.pop().unwrap().edit; | ||
230 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | ||
231 | let actual = edit.apply(&target_file_contents); | ||
232 | |||
233 | // Strip indent and empty lines from `after`, to match the behaviour of | ||
234 | // `parse_fixture` called from `analysis_and_position`. | ||
235 | let margin = fixture | ||
236 | .lines() | ||
237 | .filter(|it| it.trim_start().starts_with("//-")) | ||
238 | .map(|it| it.len() - it.trim_start().len()) | ||
239 | .next() | ||
240 | .expect("empty fixture"); | ||
241 | let after = join(after.lines().filter_map(|line| { | ||
242 | if line.len() > margin { | ||
243 | Some(&line[margin..]) | ||
244 | } else { | ||
245 | None | ||
246 | } | ||
247 | })) | ||
248 | .separator("\n") | ||
249 | .suffix("\n") | ||
250 | .to_string(); | ||
251 | |||
252 | assert_eq_text!(&after, &actual); | ||
253 | assert!( | ||
254 | diagnostic.range.start() <= file_position.offset | ||
255 | && diagnostic.range.end() >= file_position.offset, | ||
256 | "diagnostic range {} does not touch cursor position {}", | ||
257 | diagnostic.range, | ||
258 | file_position.offset | ||
259 | ); | ||
260 | } | ||
261 | |||
206 | fn check_apply_diagnostic_fix(before: &str, after: &str) { | 262 | fn check_apply_diagnostic_fix(before: &str, after: &str) { |
207 | let (analysis, file_id) = single_file(before); | 263 | let (analysis, file_id) = single_file(before); |
208 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | 264 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); |
@@ -212,6 +268,14 @@ mod tests { | |||
212 | assert_eq_text!(after, &actual); | 268 | assert_eq_text!(after, &actual); |
213 | } | 269 | } |
214 | 270 | ||
271 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | ||
272 | /// apply to the file containing the cursor. | ||
273 | fn check_no_diagnostic_for_target_file(fixture: &str) { | ||
274 | let (analysis, file_position) = analysis_and_position(fixture); | ||
275 | let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); | ||
276 | assert_eq!(diagnostics.len(), 0); | ||
277 | } | ||
278 | |||
215 | fn check_no_diagnostic(content: &str) { | 279 | fn check_no_diagnostic(content: &str) { |
216 | let (analysis, file_id) = single_file(content); | 280 | let (analysis, file_id) = single_file(content); |
217 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | 281 | let diagnostics = analysis.diagnostics(file_id).unwrap(); |
@@ -219,6 +283,155 @@ mod tests { | |||
219 | } | 283 | } |
220 | 284 | ||
221 | #[test] | 285 | #[test] |
286 | fn test_wrap_return_type() { | ||
287 | let before = r#" | ||
288 | //- /main.rs | ||
289 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
290 | |||
291 | fn div(x: i32, y: i32) -> Result<i32, String> { | ||
292 | if y == 0 { | ||
293 | return Err("div by zero".into()); | ||
294 | } | ||
295 | x / y<|> | ||
296 | } | ||
297 | |||
298 | //- /std/lib.rs | ||
299 | pub mod string { | ||
300 | pub struct String { } | ||
301 | } | ||
302 | pub mod result { | ||
303 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
304 | } | ||
305 | "#; | ||
306 | let after = r#" | ||
307 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
308 | |||
309 | fn div(x: i32, y: i32) -> Result<i32, String> { | ||
310 | if y == 0 { | ||
311 | return Err("div by zero".into()); | ||
312 | } | ||
313 | Ok(x / y) | ||
314 | } | ||
315 | "#; | ||
316 | check_apply_diagnostic_fix_from_position(before, after); | ||
317 | } | ||
318 | |||
319 | #[test] | ||
320 | fn test_wrap_return_type_handles_generic_functions() { | ||
321 | let before = r#" | ||
322 | //- /main.rs | ||
323 | use std::result::Result::{self, Ok, Err}; | ||
324 | |||
325 | fn div<T>(x: T) -> Result<T, i32> { | ||
326 | if x == 0 { | ||
327 | return Err(7); | ||
328 | } | ||
329 | <|>x | ||
330 | } | ||
331 | |||
332 | //- /std/lib.rs | ||
333 | pub mod result { | ||
334 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
335 | } | ||
336 | "#; | ||
337 | let after = r#" | ||
338 | use std::result::Result::{self, Ok, Err}; | ||
339 | |||
340 | fn div<T>(x: T) -> Result<T, i32> { | ||
341 | if x == 0 { | ||
342 | return Err(7); | ||
343 | } | ||
344 | Ok(x) | ||
345 | } | ||
346 | "#; | ||
347 | check_apply_diagnostic_fix_from_position(before, after); | ||
348 | } | ||
349 | |||
350 | #[test] | ||
351 | fn test_wrap_return_type_handles_type_aliases() { | ||
352 | let before = r#" | ||
353 | //- /main.rs | ||
354 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
355 | |||
356 | type MyResult<T> = Result<T, String>; | ||
357 | |||
358 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
359 | if y == 0 { | ||
360 | return Err("div by zero".into()); | ||
361 | } | ||
362 | x <|>/ y | ||
363 | } | ||
364 | |||
365 | //- /std/lib.rs | ||
366 | pub mod string { | ||
367 | pub struct String { } | ||
368 | } | ||
369 | pub mod result { | ||
370 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
371 | } | ||
372 | "#; | ||
373 | let after = r#" | ||
374 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
375 | |||
376 | type MyResult<T> = Result<T, String>; | ||
377 | fn div(x: i32, y: i32) -> MyResult<i32> { | ||
378 | if y == 0 { | ||
379 | return Err("div by zero".into()); | ||
380 | } | ||
381 | Ok(x / y) | ||
382 | } | ||
383 | "#; | ||
384 | check_apply_diagnostic_fix_from_position(before, after); | ||
385 | } | ||
386 | |||
387 | #[test] | ||
388 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | ||
389 | let content = r#" | ||
390 | //- /main.rs | ||
391 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
392 | |||
393 | fn foo() -> Result<String, i32> { | ||
394 | 0<|> | ||
395 | } | ||
396 | |||
397 | //- /std/lib.rs | ||
398 | pub mod string { | ||
399 | pub struct String { } | ||
400 | } | ||
401 | pub mod result { | ||
402 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
403 | } | ||
404 | "#; | ||
405 | check_no_diagnostic_for_target_file(content); | ||
406 | } | ||
407 | |||
408 | #[test] | ||
409 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { | ||
410 | let content = r#" | ||
411 | //- /main.rs | ||
412 | use std::{string::String, result::Result::{self, Ok, Err}}; | ||
413 | |||
414 | enum SomeOtherEnum { | ||
415 | Ok(i32), | ||
416 | Err(String), | ||
417 | } | ||
418 | |||
419 | fn foo() -> SomeOtherEnum { | ||
420 | 0<|> | ||
421 | } | ||
422 | |||
423 | //- /std/lib.rs | ||
424 | pub mod string { | ||
425 | pub struct String { } | ||
426 | } | ||
427 | pub mod result { | ||
428 | pub enum Result<T, E> { Ok(T), Err(E) } | ||
429 | } | ||
430 | "#; | ||
431 | check_no_diagnostic_for_target_file(content); | ||
432 | } | ||
433 | |||
434 | #[test] | ||
222 | fn test_fill_struct_fields_empty() { | 435 | fn test_fill_struct_fields_empty() { |
223 | let before = r" | 436 | let before = r" |
224 | struct TestStruct { | 437 | struct TestStruct { |
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 84fabdb9e..c85214bb3 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -314,7 +314,7 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option | |||
314 | .visit(|it: ast::TypeAliasDef| it.doc_comment_text()) | 314 | .visit(|it: ast::TypeAliasDef| it.doc_comment_text()) |
315 | .visit(|it: ast::ConstDef| it.doc_comment_text()) | 315 | .visit(|it: ast::ConstDef| it.doc_comment_text()) |
316 | .visit(|it: ast::StaticDef| it.doc_comment_text()) | 316 | .visit(|it: ast::StaticDef| it.doc_comment_text()) |
317 | .visit(|it: ast::NamedFieldDef| it.doc_comment_text()) | 317 | .visit(|it: ast::RecordFieldDef| it.doc_comment_text()) |
318 | .visit(|it: ast::EnumVariant| it.doc_comment_text()) | 318 | .visit(|it: ast::EnumVariant| it.doc_comment_text()) |
319 | .visit(|it: ast::MacroCall| it.doc_comment_text()) | 319 | .visit(|it: ast::MacroCall| it.doc_comment_text()) |
320 | .accept(&node)? | 320 | .accept(&node)? |
@@ -336,7 +336,7 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> | |||
336 | .visit(|node: ast::TypeAliasDef| node.short_label()) | 336 | .visit(|node: ast::TypeAliasDef| node.short_label()) |
337 | .visit(|node: ast::ConstDef| node.short_label()) | 337 | .visit(|node: ast::ConstDef| node.short_label()) |
338 | .visit(|node: ast::StaticDef| node.short_label()) | 338 | .visit(|node: ast::StaticDef| node.short_label()) |
339 | .visit(|node: ast::NamedFieldDef| node.short_label()) | 339 | .visit(|node: ast::RecordFieldDef| node.short_label()) |
340 | .visit(|node: ast::EnumVariant| node.short_label()) | 340 | .visit(|node: ast::EnumVariant| node.short_label()) |
341 | .accept(&node)? | 341 | .accept(&node)? |
342 | } | 342 | } |
diff --git a/crates/ra_ide_api/src/display/short_label.rs b/crates/ra_ide_api/src/display/short_label.rs index 825a033ee..b16d504e1 100644 --- a/crates/ra_ide_api/src/display/short_label.rs +++ b/crates/ra_ide_api/src/display/short_label.rs | |||
@@ -53,7 +53,7 @@ impl ShortLabel for ast::StaticDef { | |||
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
56 | impl ShortLabel for ast::NamedFieldDef { | 56 | impl ShortLabel for ast::RecordFieldDef { |
57 | fn short_label(&self) -> Option<String> { | 57 | fn short_label(&self) -> Option<String> { |
58 | short_label_from_ascribed_node(self, "") | 58 | short_label_from_ascribed_node(self, "") |
59 | } | 59 | } |
diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index b026dfa59..a2025ed59 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs | |||
@@ -124,7 +124,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
124 | let ty = td.type_ref(); | 124 | let ty = td.type_ref(); |
125 | decl_with_type_ref(td, ty) | 125 | decl_with_type_ref(td, ty) |
126 | }) | 126 | }) |
127 | .visit(decl_with_ascription::<ast::NamedFieldDef>) | 127 | .visit(decl_with_ascription::<ast::RecordFieldDef>) |
128 | .visit(decl_with_ascription::<ast::ConstDef>) | 128 | .visit(decl_with_ascription::<ast::ConstDef>) |
129 | .visit(decl_with_ascription::<ast::StaticDef>) | 129 | .visit(decl_with_ascription::<ast::StaticDef>) |
130 | .visit(|im: ast::ImplBlock| { | 130 | .visit(|im: ast::ImplBlock| { |
@@ -222,7 +222,7 @@ fn very_obsolete() {} | |||
222 | label: "x", | 222 | label: "x", |
223 | navigation_range: [18; 19), | 223 | navigation_range: [18; 19), |
224 | node_range: [18; 24), | 224 | node_range: [18; 24), |
225 | kind: NAMED_FIELD_DEF, | 225 | kind: RECORD_FIELD_DEF, |
226 | detail: Some( | 226 | detail: Some( |
227 | "i32", | 227 | "i32", |
228 | ), | 228 | ), |
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs index edbf622c1..e990eb0d1 100644 --- a/crates/ra_ide_api/src/extend_selection.rs +++ b/crates/ra_ide_api/src/extend_selection.rs | |||
@@ -18,11 +18,11 @@ pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRang | |||
18 | fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> { | 18 | fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange> { |
19 | let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; | 19 | let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; |
20 | let list_kinds = [ | 20 | let list_kinds = [ |
21 | FIELD_PAT_LIST, | 21 | RECORD_FIELD_PAT_LIST, |
22 | MATCH_ARM_LIST, | 22 | MATCH_ARM_LIST, |
23 | NAMED_FIELD_DEF_LIST, | 23 | RECORD_FIELD_DEF_LIST, |
24 | POS_FIELD_DEF_LIST, | 24 | TUPLE_FIELD_DEF_LIST, |
25 | NAMED_FIELD_LIST, | 25 | RECORD_FIELD_LIST, |
26 | ENUM_VARIANT_LIST, | 26 | ENUM_VARIANT_LIST, |
27 | USE_TREE_LIST, | 27 | USE_TREE_LIST, |
28 | TYPE_PARAM_LIST, | 28 | TYPE_PARAM_LIST, |
diff --git a/crates/ra_ide_api/src/folding_ranges.rs b/crates/ra_ide_api/src/folding_ranges.rs index e60ae8cf6..3ab6c195e 100644 --- a/crates/ra_ide_api/src/folding_ranges.rs +++ b/crates/ra_ide_api/src/folding_ranges.rs | |||
@@ -81,8 +81,14 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { | |||
81 | match kind { | 81 | match kind { |
82 | COMMENT => Some(FoldKind::Comment), | 82 | COMMENT => Some(FoldKind::Comment), |
83 | USE_ITEM => Some(FoldKind::Imports), | 83 | USE_ITEM => Some(FoldKind::Imports), |
84 | NAMED_FIELD_DEF_LIST | FIELD_PAT_LIST | ITEM_LIST | EXTERN_ITEM_LIST | USE_TREE_LIST | 84 | RECORD_FIELD_DEF_LIST |
85 | | BLOCK | ENUM_VARIANT_LIST | TOKEN_TREE => Some(FoldKind::Block), | 85 | | RECORD_FIELD_PAT_LIST |
86 | | ITEM_LIST | ||
87 | | EXTERN_ITEM_LIST | ||
88 | | USE_TREE_LIST | ||
89 | | BLOCK | ||
90 | | ENUM_VARIANT_LIST | ||
91 | | TOKEN_TREE => Some(FoldKind::Block), | ||
86 | _ => None, | 92 | _ => None, |
87 | } | 93 | } |
88 | } | 94 | } |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index ddd55a9c1..28529a2de 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -178,7 +178,7 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> | |||
178 | node.short_label(), | 178 | node.short_label(), |
179 | ) | 179 | ) |
180 | }) | 180 | }) |
181 | .visit(|node: ast::NamedFieldDef| { | 181 | .visit(|node: ast::RecordFieldDef| { |
182 | NavigationTarget::from_named( | 182 | NavigationTarget::from_named( |
183 | file_id, | 183 | file_id, |
184 | &node, | 184 | &node, |
@@ -344,13 +344,13 @@ mod tests { | |||
344 | foo.spam<|>; | 344 | foo.spam<|>; |
345 | } | 345 | } |
346 | ", | 346 | ", |
347 | "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", | 347 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
348 | ); | 348 | ); |
349 | } | 349 | } |
350 | 350 | ||
351 | #[test] | 351 | #[test] |
352 | fn goto_definition_works_for_named_fields() { | 352 | fn goto_definition_works_for_record_fields() { |
353 | covers!(goto_definition_works_for_named_fields); | 353 | covers!(goto_definition_works_for_record_fields); |
354 | check_goto( | 354 | check_goto( |
355 | " | 355 | " |
356 | //- /lib.rs | 356 | //- /lib.rs |
@@ -364,7 +364,7 @@ mod tests { | |||
364 | } | 364 | } |
365 | } | 365 | } |
366 | ", | 366 | ", |
367 | "spam NAMED_FIELD_DEF FileId(1) [17; 26) [17; 21)", | 367 | "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)", |
368 | ); | 368 | ); |
369 | } | 369 | } |
370 | #[test] | 370 | #[test] |
@@ -473,7 +473,7 @@ mod tests { | |||
473 | field<|>: string, | 473 | field<|>: string, |
474 | } | 474 | } |
475 | "#, | 475 | "#, |
476 | "field NAMED_FIELD_DEF FileId(1) [17; 30) [17; 22)", | 476 | "field RECORD_FIELD_DEF FileId(1) [17; 30) [17; 22)", |
477 | ); | 477 | ); |
478 | 478 | ||
479 | check_goto( | 479 | check_goto( |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 2a5ac7821..1981e62d3 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -197,7 +197,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
197 | .visit(|node: ast::TraitDef| { | 197 | .visit(|node: ast::TraitDef| { |
198 | hover_text(node.doc_comment_text(), node.short_label()) | 198 | hover_text(node.doc_comment_text(), node.short_label()) |
199 | }) | 199 | }) |
200 | .visit(|node: ast::NamedFieldDef| { | 200 | .visit(|node: ast::RecordFieldDef| { |
201 | hover_text(node.doc_comment_text(), node.short_label()) | 201 | hover_text(node.doc_comment_text(), node.short_label()) |
202 | }) | 202 | }) |
203 | .visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label())) | 203 | .visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label())) |
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 735f3166c..61ccb74b6 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -125,13 +125,13 @@ fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | |||
125 | pats_to_process.push_back(arg_pat); | 125 | pats_to_process.push_back(arg_pat); |
126 | } | 126 | } |
127 | } | 127 | } |
128 | ast::Pat::StructPat(struct_pat) => { | 128 | ast::Pat::RecordPat(record_pat) => { |
129 | if let Some(pat_list) = struct_pat.field_pat_list() { | 129 | if let Some(pat_list) = record_pat.record_field_pat_list() { |
130 | pats_to_process.extend( | 130 | pats_to_process.extend( |
131 | pat_list | 131 | pat_list |
132 | .field_pats() | 132 | .record_field_pats() |
133 | .filter_map(|field_pat| { | 133 | .filter_map(|record_field_pat| { |
134 | field_pat | 134 | record_field_pat |
135 | .pat() | 135 | .pat() |
136 | .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT) | 136 | .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT) |
137 | }) | 137 | }) |
diff --git a/crates/ra_ide_api/src/marks.rs b/crates/ra_ide_api/src/marks.rs index 9cb991de5..c3752cc54 100644 --- a/crates/ra_ide_api/src/marks.rs +++ b/crates/ra_ide_api/src/marks.rs | |||
@@ -3,7 +3,7 @@ test_utils::marks!( | |||
3 | goto_definition_works_for_macros | 3 | goto_definition_works_for_macros |
4 | goto_definition_works_for_methods | 4 | goto_definition_works_for_methods |
5 | goto_definition_works_for_fields | 5 | goto_definition_works_for_fields |
6 | goto_definition_works_for_named_fields | 6 | goto_definition_works_for_record_fields |
7 | call_info_bad_offset | 7 | call_info_bad_offset |
8 | dont_complete_current_use | 8 | dont_complete_current_use |
9 | dont_complete_primitive_in_use | 9 | dont_complete_primitive_in_use |
diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs index f7db6c826..34a8bcc36 100644 --- a/crates/ra_ide_api/src/name_ref_kind.rs +++ b/crates/ra_ide_api/src/name_ref_kind.rs | |||
@@ -54,12 +54,12 @@ pub(crate) fn classify_name_ref( | |||
54 | } | 54 | } |
55 | 55 | ||
56 | // It could also be a named field | 56 | // It could also be a named field |
57 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::NamedField::cast) { | 57 | if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::RecordField::cast) { |
58 | tested_by!(goto_definition_works_for_named_fields); | 58 | tested_by!(goto_definition_works_for_record_fields); |
59 | 59 | ||
60 | let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast); | 60 | let record_lit = field_expr.syntax().ancestors().find_map(ast::RecordLit::cast); |
61 | 61 | ||
62 | if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, &lit.into())) { | 62 | if let Some(ty) = record_lit.and_then(|lit| analyzer.type_of(db, &lit.into())) { |
63 | if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { | 63 | if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() { |
64 | let hir_path = hir::Path::from_name_ref(name_ref); | 64 | let hir_path = hir::Path::from_name_ref(name_ref); |
65 | let hir_name = hir_path.as_ident().unwrap(); | 65 | let hir_name = hir_path.as_ident().unwrap(); |
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 448acffc8..06ccf0728 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs | |||
@@ -165,7 +165,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa | |||
165 | TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => { | 165 | TYPE_PARAM | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => { |
166 | "type" | 166 | "type" |
167 | } | 167 | } |
168 | NAMED_FIELD_DEF => "field", | 168 | RECORD_FIELD_DEF => "field", |
169 | _ => "function", | 169 | _ => "function", |
170 | }) | 170 | }) |
171 | .unwrap_or("function") | 171 | .unwrap_or("function") |