mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-30 19:49:36 +00:00 
			
		
		
		
	Merge pull request #19945 from ChayimFriedman2/private-field-quickfix
feat: Add the quickfix for increasing visibility of a private field to the private-field diagnostic (previously it was only on no-such-field)
This commit is contained in:
		
						commit
						eb25f5e85b
					
				
					 2 changed files with 125 additions and 32 deletions
				
			
		|  | @ -1,5 +1,4 @@ | ||||||
| use either::Either; | use either::Either; | ||||||
| use hir::{Field, HasCrate}; |  | ||||||
| use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase}; | use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase}; | ||||||
| use ide_db::text_edit::TextEdit; | use ide_db::text_edit::TextEdit; | ||||||
| use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange}; | use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange}; | ||||||
|  | @ -8,7 +7,10 @@ use syntax::{ | ||||||
|     ast::{self, edit::IndentLevel, make}, |     ast::{self, edit::IndentLevel, make}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; | use crate::{ | ||||||
|  |     Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix, | ||||||
|  |     handlers::private_field::field_is_private_fixes, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| // Diagnostic: no-such-field
 | // Diagnostic: no-such-field
 | ||||||
| //
 | //
 | ||||||
|  | @ -37,8 +39,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis | ||||||
|                 field_is_private_fixes( |                 field_is_private_fixes( | ||||||
|                     &ctx.sema, |                     &ctx.sema, | ||||||
|                     d.field.file_id.original_file(ctx.sema.db), |                     d.field.file_id.original_file(ctx.sema.db), | ||||||
|                     node, |  | ||||||
|                     private_field, |                     private_field, | ||||||
|  |                     ctx.sema.original_range(node.syntax()).range, | ||||||
|                 ) |                 ) | ||||||
|             } else { |             } else { | ||||||
|                 missing_record_expr_field_fixes( |                 missing_record_expr_field_fixes( | ||||||
|  | @ -52,31 +54,6 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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( | fn missing_record_expr_field_fixes( | ||||||
|     sema: &Semantics<'_, RootDatabase>, |     sema: &Semantics<'_, RootDatabase>, | ||||||
|     usage_file_id: EditionedFileId, |     usage_file_id: EditionedFileId, | ||||||
|  |  | ||||||
|  | @ -1,4 +1,8 @@ | ||||||
| use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; | use hir::{EditionedFileId, FileRange, HasCrate, HasSource, Semantics}; | ||||||
|  | use ide_db::{RootDatabase, assists::Assist, source_change::SourceChange, text_edit::TextEdit}; | ||||||
|  | use syntax::{AstNode, TextRange, TextSize, ast::HasVisibility}; | ||||||
|  | 
 | ||||||
|  | use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; | ||||||
| 
 | 
 | ||||||
| // Diagnostic: private-field
 | // Diagnostic: private-field
 | ||||||
| //
 | //
 | ||||||
|  | @ -16,11 +20,59 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) | ||||||
|         d.expr.map(|it| it.into()), |         d.expr.map(|it| it.into()), | ||||||
|     ) |     ) | ||||||
|     .stable() |     .stable() | ||||||
|  |     .with_fixes(field_is_private_fixes( | ||||||
|  |         &ctx.sema, | ||||||
|  |         d.expr.file_id.original_file(ctx.sema.db), | ||||||
|  |         d.field, | ||||||
|  |         ctx.sema.original_range(d.expr.to_node(ctx.sema.db).syntax()).range, | ||||||
|  |     )) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) fn field_is_private_fixes( | ||||||
|  |     sema: &Semantics<'_, RootDatabase>, | ||||||
|  |     usage_file_id: EditionedFileId, | ||||||
|  |     private_field: hir::Field, | ||||||
|  |     fix_range: TextRange, | ||||||
|  | ) -> 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 mut visibility_text = if usage_crate == def_crate { "pub(crate) " } else { "pub " }; | ||||||
|  | 
 | ||||||
|  |     let source = private_field.source(sema.db)?; | ||||||
|  |     let existing_visibility = match &source.value { | ||||||
|  |         hir::FieldSource::Named(it) => it.visibility(), | ||||||
|  |         hir::FieldSource::Pos(it) => it.visibility(), | ||||||
|  |     }; | ||||||
|  |     let range = match existing_visibility { | ||||||
|  |         Some(visibility) => { | ||||||
|  |             // If there is an existing visibility, don't insert whitespace after.
 | ||||||
|  |             visibility_text = visibility_text.trim_end(); | ||||||
|  |             source.with_value(visibility.syntax()).original_file_range_opt(sema.db)?.0 | ||||||
|  |         } | ||||||
|  |         None => { | ||||||
|  |             let (range, _) = source.syntax().original_file_range_opt(sema.db)?; | ||||||
|  |             FileRange { | ||||||
|  |                 file_id: range.file_id, | ||||||
|  |                 range: TextRange::at(range.range.start(), TextSize::new(0)), | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     let source_change = SourceChange::from_text_edit( | ||||||
|  |         range.file_id.file_id(sema.db), | ||||||
|  |         TextEdit::replace(range.range, visibility_text.into()), | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     Some(vec![fix( | ||||||
|  |         "increase_field_visibility", | ||||||
|  |         "Increase field visibility", | ||||||
|  |         source_change, | ||||||
|  |         fix_range, | ||||||
|  |     )]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use crate::tests::check_diagnostics; |     use crate::tests::{check_diagnostics, check_fix}; | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn private_field() { |     fn private_field() { | ||||||
|  | @ -29,7 +81,7 @@ mod tests { | ||||||
| mod module { pub struct Struct { field: u32 } } | mod module { pub struct Struct { field: u32 } } | ||||||
| fn main(s: module::Struct) { | fn main(s: module::Struct) { | ||||||
|     s.field; |     s.field; | ||||||
|   //^^^^^^^ error: field `field` of `Struct` is private
 |   //^^^^^^^ 💡 error: field `field` of `Struct` is private
 | ||||||
| } | } | ||||||
| "#,
 | "#,
 | ||||||
|         ); |         ); | ||||||
|  | @ -42,7 +94,7 @@ fn main(s: module::Struct) { | ||||||
| mod module { pub struct Struct(u32); } | mod module { pub struct Struct(u32); } | ||||||
| fn main(s: module::Struct) { | fn main(s: module::Struct) { | ||||||
|     s.0; |     s.0; | ||||||
|   //^^^ error: field `0` of `Struct` is private
 |   //^^^ 💡 error: field `0` of `Struct` is private
 | ||||||
| } | } | ||||||
| "#,
 | "#,
 | ||||||
|         ); |         ); | ||||||
|  | @ -113,4 +165,68 @@ fn main() { | ||||||
| "#,
 | "#,
 | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn change_visibility_fix() { | ||||||
|  |         check_fix( | ||||||
|  |             r#" | ||||||
|  | pub mod foo { | ||||||
|  |     pub mod bar { | ||||||
|  |         pub struct Struct { | ||||||
|  |             field: i32, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn foo(v: foo::bar::Struct) { | ||||||
|  |     v.field$0; | ||||||
|  | } | ||||||
|  |             "#,
 | ||||||
|  |             r#" | ||||||
|  | pub mod foo { | ||||||
|  |     pub mod bar { | ||||||
|  |         pub struct Struct { | ||||||
|  |             pub(crate) field: i32, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn foo(v: foo::bar::Struct) { | ||||||
|  |     v.field; | ||||||
|  | } | ||||||
|  |             "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn change_visibility_with_existing_visibility() { | ||||||
|  |         check_fix( | ||||||
|  |             r#" | ||||||
|  | pub mod foo { | ||||||
|  |     pub mod bar { | ||||||
|  |         pub struct Struct { | ||||||
|  |             pub(super) field: i32, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn foo(v: foo::bar::Struct) { | ||||||
|  |     v.field$0; | ||||||
|  | } | ||||||
|  |             "#,
 | ||||||
|  |             r#" | ||||||
|  | pub mod foo { | ||||||
|  |     pub mod bar { | ||||||
|  |         pub struct Struct { | ||||||
|  |             pub(crate) field: i32, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn foo(v: foo::bar::Struct) { | ||||||
|  |     v.field; | ||||||
|  | } | ||||||
|  |             "#,
 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lukas Wirth
						Lukas Wirth