Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/handlers/no_such_field.rs')
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/no_such_field.rs | 157 |
1 files changed, 127 insertions, 30 deletions
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index 84fb467a5c..ef42f2dc74 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -1,4 +1,5 @@ use either::Either; +use hir::{Field, HasCrate}; use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase}; use ide_db::text_edit::TextEdit; use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange}; @@ -13,44 +14,69 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; // // This diagnostic is triggered if created structure does not have field provided in record. pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic { - let node = d.field.map(Into::into); - if d.private { - // FIXME: quickfix to add required visibility - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcHardError("E0451"), - "field is private", - node, - ) - .stable() + let (code, message) = if d.private.is_some() { + ("E0451", "field is private") + } else if let VariantId::EnumVariantId(_) = d.variant { + ("E0559", "no such field") } else { - Diagnostic::new_with_syntax_node_ptr( - ctx, - match d.variant { - VariantId::EnumVariantId(_) => DiagnosticCode::RustcHardError("E0559"), - _ => DiagnosticCode::RustcHardError("E0560"), - }, - "no such field", - node, - ) + ("E0560", "no such field") + }; + + let node = d.field.map(Into::into); + Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::RustcHardError(code), message, node) .stable() .with_fixes(fixes(ctx, d)) - } } fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> { // FIXME: quickfix for pattern let root = ctx.sema.db.parse_or_expand(d.field.file_id); match &d.field.value.to_node(&root) { - Either::Left(node) => missing_record_expr_field_fixes( - &ctx.sema, - d.field.file_id.original_file(ctx.sema.db), - node, - ), + Either::Left(node) => { + if let Some(private_field) = d.private { + field_is_private_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + node, + private_field, + ) + } else { + missing_record_expr_field_fixes( + &ctx.sema, + d.field.file_id.original_file(ctx.sema.db), + node, + ) + } + } _ => None, } } +fn field_is_private_fixes( + sema: &Semantics<'_, RootDatabase>, + usage_file_id: EditionedFileId, + record_expr_field: &ast::RecordExprField, + private_field: Field, +) -> Option<Vec<Assist>> { + let def_crate = private_field.krate(sema.db); + let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate(); + let visibility = if usage_crate == def_crate { "pub(crate) " } else { "pub " }; + + let source = private_field.source(sema.db)?; + let (range, _) = source.syntax().original_file_range_opt(sema.db)?; + let source_change = SourceChange::from_text_edit( + range.file_id.file_id(sema.db), + TextEdit::insert(range.range.start(), visibility.into()), + ); + + Some(vec![fix( + "increase_field_visibility", + "Increase field visibility", + source_change, + sema.original_range(record_expr_field.syntax()).range, + )]) +} + fn missing_record_expr_field_fixes( sema: &Semantics<'_, RootDatabase>, usage_file_id: EditionedFileId, @@ -118,7 +144,7 @@ fn missing_record_expr_field_fixes( "create_field", "Create field", source_change, - record_expr_field.syntax().text_range(), + sema.original_range(record_expr_field.syntax()).range, )]); fn record_field_list(field_def_list: ast::FieldList) -> Option<ast::RecordFieldList> { @@ -387,15 +413,15 @@ fn f(s@m::Struct { // assignee expression m::Struct { field: 0, - //^^^^^^^^ error: field is private + //^^^^^^^^ 💡 error: field is private field2 - //^^^^^^ error: field is private + //^^^^^^ 💡 error: field is private } = s; m::Struct { field: 0, - //^^^^^^^^ error: field is private + //^^^^^^^^ 💡 error: field is private field2 - //^^^^^^ error: field is private + //^^^^^^ 💡 error: field is private }; } "#, @@ -403,6 +429,77 @@ fn f(s@m::Struct { } #[test] + fn test_struct_field_private_same_crate_fix() { + check_diagnostics( + r#" +mod m { + pub struct Struct { + field: u32, + } +} +fn f() { + let _ = m::Struct { + field: 0, + //^^^^^^^^ 💡 error: field is private + }; +} +"#, + ); + + check_fix( + r#" +mod m { + pub struct Struct { + field: u32, + } +} +fn f() { + let _ = m::Struct { + field$0: 0, + }; +} +"#, + r#" +mod m { + pub struct Struct { + pub(crate) field: u32, + } +} +fn f() { + let _ = m::Struct { + field: 0, + }; +} +"#, + ); + } + + #[test] + fn test_struct_field_private_other_crate_fix() { + check_fix( + r#" +//- /lib.rs crate:another_crate +pub struct Struct { + field: u32, +} +//- /lib.rs crate:this_crate deps:another_crate +use another_crate; + +fn f() { + let _ = another_crate::Struct { + field$0: 0, + }; +} +"#, + r#" +pub struct Struct { + pub field: u32, +} +"#, + ); + } + + #[test] fn editions_between_macros() { check_diagnostics( r#" |