From 43079ba80d79a75851f64ffc68d60711191f8307 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Mon, 22 Jun 2020 21:29:11 +0200 Subject: [PATCH] Correctly generate new struct field in file containing struct def --- crates/ra_ide/src/diagnostics.rs | 58 ++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 71fd45e5df..3afe5381a0 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 fn missing_struct_field_fix( sema: &Semantics, - file_id: FileId, + usage_file_id: FileId, d: &hir::diagnostics::NoSuchField, ) -> Option { let record_expr = sema.ast(d); @@ -139,25 +139,30 @@ fn missing_struct_field_fix( let record_lit = ast::RecordLit::cast(record_expr.syntax().parent()?.parent()?)?; let def_id = sema.resolve_variant(record_lit)?; let module; + let def_file_id; let record_fields = match VariantDef::from(def_id) { VariantDef::Struct(s) => { module = s.module(sema.db); let source = s.source(sema.db); + def_file_id = source.file_id; let fields = source.value.field_def_list()?; record_field_def_list(fields)? } VariantDef::Union(u) => { module = u.module(sema.db); let source = u.source(sema.db); + def_file_id = source.file_id; source.value.record_field_def_list()? } VariantDef::EnumVariant(e) => { module = e.module(sema.db); let source = e.source(sema.db); + def_file_id = source.file_id; let fields = source.value.field_def_list()?; record_field_def_list(fields)? } }; + let def_file_id = def_file_id.original_file(sema.db); let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; if new_field_type.is_unknown() { @@ -172,7 +177,11 @@ fn missing_struct_field_fix( let last_field_syntax = last_field.syntax(); let indent = IndentLevel::from_node(last_field_syntax); - let mut new_field = format!("\n{}{}", indent, new_field); + let mut new_field = new_field.to_string(); + if usage_file_id != def_file_id { + new_field = format!("pub(crate) {}", new_field); + } + new_field = format!("\n{}{}", indent, new_field); let needs_comma = !last_field_syntax.to_string().ends_with(","); if needs_comma { @@ -180,7 +189,7 @@ fn missing_struct_field_fix( } let source_change = SourceFileEdit { - file_id, + file_id: def_file_id, edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), }; let fix = Fix::new("Create field", source_change.into()); @@ -309,6 +318,25 @@ mod tests { ); } + /// Checks that a diagnostic applies to the file containing the `<|>` cursor marker + /// which has a fix that can apply to other files. + fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { + let ra_fixture_after = &trim_indent(ra_fixture_after); + let (analysis, file_pos) = analysis_and_position(ra_fixture_before); + let current_file_id = file_pos.file_id; + let diagnostic = analysis.diagnostics(current_file_id).unwrap().pop().unwrap(); + let mut fix = diagnostic.fix.unwrap(); + let edit = fix.source_change.source_file_edits.pop().unwrap(); + let changed_file_id = edit.file_id; + let before = analysis.file_text(changed_file_id).unwrap(); + let actual = { + let mut actual = before.to_string(); + edit.edit.apply(&mut actual); + actual + }; + assert_eq_text!(ra_fixture_after, &actual); + } + /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics /// apply to the file containing the cursor. fn check_no_diagnostics(ra_fixture: &str) { @@ -732,4 +760,28 @@ struct Foo { ", ) } + + #[test] + fn test_add_field_in_other_file_from_usage() { + check_apply_diagnostic_fix_in_other_file( + r" + //- /main.rs + mod foo; + + fn main() { + <|>foo::Foo { bar: 3, baz: false}; + } + //- /foo.rs + struct Foo { + bar: i32 + } + ", + r" + struct Foo { + bar: i32, + pub(crate) baz: bool + } + ", + ) + } }