aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-07-11 10:51:31 +0100
committerGitHub <[email protected]>2020-07-11 10:51:31 +0100
commit6c1546c3a495785cf293dbbfb7f65ea124602528 (patch)
treedfad676d6188fcd77e7f7e2c913c74368178557b
parent0366a85052904c5c6efa3b3ecddc35dce85337c4 (diff)
parent43079ba80d79a75851f64ffc68d60711191f8307 (diff)
Merge #4996
4996: Correctly generate new struct field in file containing struct def r=matklad a=TimoFreiberg WIP because the test doesn't pass. Testing the fix by hand looked good, although quickfixes seem to not support setting the editor cursor yet, which i think we want for "generate missing defs from usage" fixes. Co-authored-by: Timo Freiberg <[email protected]>
-rw-r--r--crates/ra_ide/src/diagnostics.rs58
1 files changed, 55 insertions, 3 deletions
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 71fd45e5d..3afe5381a 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -131,7 +131,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
131 131
132fn missing_struct_field_fix( 132fn missing_struct_field_fix(
133 sema: &Semantics<RootDatabase>, 133 sema: &Semantics<RootDatabase>,
134 file_id: FileId, 134 usage_file_id: FileId,
135 d: &hir::diagnostics::NoSuchField, 135 d: &hir::diagnostics::NoSuchField,
136) -> Option<Fix> { 136) -> Option<Fix> {
137 let record_expr = sema.ast(d); 137 let record_expr = sema.ast(d);
@@ -139,25 +139,30 @@ fn missing_struct_field_fix(
139 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?; 139 let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?;
140 let def_id = sema.resolve_variant(record_lit)?; 140 let def_id = sema.resolve_variant(record_lit)?;
141 let module; 141 let module;
142 let def_file_id;
142 let record_fields = match VariantDef::from(def_id) { 143 let record_fields = match VariantDef::from(def_id) {
143 VariantDef::Struct(s) => { 144 VariantDef::Struct(s) => {
144 module = s.module(sema.db); 145 module = s.module(sema.db);
145 let source = s.source(sema.db); 146 let source = s.source(sema.db);
147 def_file_id = source.file_id;
146 let fields = source.value.field_def_list()?; 148 let fields = source.value.field_def_list()?;
147 record_field_def_list(fields)? 149 record_field_def_list(fields)?
148 } 150 }
149 VariantDef::Union(u) => { 151 VariantDef::Union(u) => {
150 module = u.module(sema.db); 152 module = u.module(sema.db);
151 let source = u.source(sema.db); 153 let source = u.source(sema.db);
154 def_file_id = source.file_id;
152 source.value.record_field_def_list()? 155 source.value.record_field_def_list()?
153 } 156 }
154 VariantDef::EnumVariant(e) => { 157 VariantDef::EnumVariant(e) => {
155 module = e.module(sema.db); 158 module = e.module(sema.db);
156 let source = e.source(sema.db); 159 let source = e.source(sema.db);
160 def_file_id = source.file_id;
157 let fields = source.value.field_def_list()?; 161 let fields = source.value.field_def_list()?;
158 record_field_def_list(fields)? 162 record_field_def_list(fields)?
159 } 163 }
160 }; 164 };
165 let def_file_id = def_file_id.original_file(sema.db);
161 166
162 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; 167 let new_field_type = sema.type_of_expr(&record_expr.expr()?)?;
163 if new_field_type.is_unknown() { 168 if new_field_type.is_unknown() {
@@ -172,7 +177,11 @@ fn missing_struct_field_fix(
172 let last_field_syntax = last_field.syntax(); 177 let last_field_syntax = last_field.syntax();
173 let indent = IndentLevel::from_node(last_field_syntax); 178 let indent = IndentLevel::from_node(last_field_syntax);
174 179
175 let mut new_field = format!("\n{}{}", indent, new_field); 180 let mut new_field = new_field.to_string();
181 if usage_file_id != def_file_id {
182 new_field = format!("pub(crate) {}", new_field);
183 }
184 new_field = format!("\n{}{}", indent, new_field);
176 185
177 let needs_comma = !last_field_syntax.to_string().ends_with(","); 186 let needs_comma = !last_field_syntax.to_string().ends_with(",");
178 if needs_comma { 187 if needs_comma {
@@ -180,7 +189,7 @@ fn missing_struct_field_fix(
180 } 189 }
181 190
182 let source_change = SourceFileEdit { 191 let source_change = SourceFileEdit {
183 file_id, 192 file_id: def_file_id,
184 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), 193 edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field),
185 }; 194 };
186 let fix = Fix::new("Create field", source_change.into()); 195 let fix = Fix::new("Create field", source_change.into());
@@ -309,6 +318,25 @@ mod tests {
309 ); 318 );
310 } 319 }
311 320
321 /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker
322 /// which has a fix that can apply to other files.
323 fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) {
324 let ra_fixture_after = &trim_indent(ra_fixture_after);
325 let (analysis, file_pos) = analysis_and_position(ra_fixture_before);
326 let current_file_id = file_pos.file_id;
327 let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap();
328 let mut fix = diagnostic.fix.unwrap();
329 let edit = fix.source_change.source_file_edits.pop().unwrap();
330 let changed_file_id = edit.file_id;
331 let before = analysis.file_text(changed_file_id).unwrap();
332 let actual = {
333 let mut actual = before.to_string();
334 edit.edit.apply(&mut actual);
335 actual
336 };
337 assert_eq_text!(ra_fixture_after, &actual);
338 }
339
312 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics 340 /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
313 /// apply to the file containing the cursor. 341 /// apply to the file containing the cursor.
314 fn check_no_diagnostics(ra_fixture: &str) { 342 fn check_no_diagnostics(ra_fixture: &str) {
@@ -732,4 +760,28 @@ struct Foo {
732", 760",
733 ) 761 )
734 } 762 }
763
764 #[test]
765 fn test_add_field_in_other_file_from_usage() {
766 check_apply_diagnostic_fix_in_other_file(
767 r"
768 //- /main.rs
769 mod foo;
770
771 fn main() {
772 <|>foo::Foo { bar: 3, baz: false};
773 }
774 //- /foo.rs
775 struct Foo {
776 bar: i32
777 }
778 ",
779 r"
780 struct Foo {
781 bar: i32,
782 pub(crate) baz: bool
783 }
784 ",
785 )
786 }
735} 787}