Skip to content

Commit eb25f5e

Browse files
authored
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)
2 parents 2c25e43 + ba6f6e2 commit eb25f5e

File tree

2 files changed

+125
-32
lines changed

2 files changed

+125
-32
lines changed

crates/ide-diagnostics/src/handlers/no_such_field.rs

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use either::Either;
2-
use hir::{Field, HasCrate};
32
use hir::{HasSource, HirDisplay, Semantics, VariantId, db::ExpandDatabase};
43
use ide_db::text_edit::TextEdit;
54
use ide_db::{EditionedFileId, RootDatabase, source_change::SourceChange};
@@ -8,7 +7,10 @@ use syntax::{
87
ast::{self, edit::IndentLevel, make},
98
};
109

11-
use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
10+
use crate::{
11+
Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix,
12+
handlers::private_field::field_is_private_fixes,
13+
};
1214

1315
// Diagnostic: no-such-field
1416
//
@@ -37,8 +39,8 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis
3739
field_is_private_fixes(
3840
&ctx.sema,
3941
d.field.file_id.original_file(ctx.sema.db),
40-
node,
4142
private_field,
43+
ctx.sema.original_range(node.syntax()).range,
4244
)
4345
} else {
4446
missing_record_expr_field_fixes(
@@ -52,31 +54,6 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assis
5254
}
5355
}
5456

55-
fn field_is_private_fixes(
56-
sema: &Semantics<'_, RootDatabase>,
57-
usage_file_id: EditionedFileId,
58-
record_expr_field: &ast::RecordExprField,
59-
private_field: Field,
60-
) -> Option<Vec<Assist>> {
61-
let def_crate = private_field.krate(sema.db);
62-
let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate();
63-
let visibility = if usage_crate == def_crate { "pub(crate) " } else { "pub " };
64-
65-
let source = private_field.source(sema.db)?;
66-
let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
67-
let source_change = SourceChange::from_text_edit(
68-
range.file_id.file_id(sema.db),
69-
TextEdit::insert(range.range.start(), visibility.into()),
70-
);
71-
72-
Some(vec![fix(
73-
"increase_field_visibility",
74-
"Increase field visibility",
75-
source_change,
76-
sema.original_range(record_expr_field.syntax()).range,
77-
)])
78-
}
79-
8057
fn missing_record_expr_field_fixes(
8158
sema: &Semantics<'_, RootDatabase>,
8259
usage_file_id: EditionedFileId,

crates/ide-diagnostics/src/handlers/private_field.rs

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
1+
use hir::{EditionedFileId, FileRange, HasCrate, HasSource, Semantics};
2+
use ide_db::{RootDatabase, assists::Assist, source_change::SourceChange, text_edit::TextEdit};
3+
use syntax::{AstNode, TextRange, TextSize, ast::HasVisibility};
4+
5+
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
26

37
// Diagnostic: private-field
48
//
@@ -16,11 +20,59 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
1620
d.expr.map(|it| it.into()),
1721
)
1822
.stable()
23+
.with_fixes(field_is_private_fixes(
24+
&ctx.sema,
25+
d.expr.file_id.original_file(ctx.sema.db),
26+
d.field,
27+
ctx.sema.original_range(d.expr.to_node(ctx.sema.db).syntax()).range,
28+
))
29+
}
30+
31+
pub(crate) fn field_is_private_fixes(
32+
sema: &Semantics<'_, RootDatabase>,
33+
usage_file_id: EditionedFileId,
34+
private_field: hir::Field,
35+
fix_range: TextRange,
36+
) -> Option<Vec<Assist>> {
37+
let def_crate = private_field.krate(sema.db);
38+
let usage_crate = sema.file_to_module_def(usage_file_id.file_id(sema.db))?.krate();
39+
let mut visibility_text = if usage_crate == def_crate { "pub(crate) " } else { "pub " };
40+
41+
let source = private_field.source(sema.db)?;
42+
let existing_visibility = match &source.value {
43+
hir::FieldSource::Named(it) => it.visibility(),
44+
hir::FieldSource::Pos(it) => it.visibility(),
45+
};
46+
let range = match existing_visibility {
47+
Some(visibility) => {
48+
// If there is an existing visibility, don't insert whitespace after.
49+
visibility_text = visibility_text.trim_end();
50+
source.with_value(visibility.syntax()).original_file_range_opt(sema.db)?.0
51+
}
52+
None => {
53+
let (range, _) = source.syntax().original_file_range_opt(sema.db)?;
54+
FileRange {
55+
file_id: range.file_id,
56+
range: TextRange::at(range.range.start(), TextSize::new(0)),
57+
}
58+
}
59+
};
60+
let source_change = SourceChange::from_text_edit(
61+
range.file_id.file_id(sema.db),
62+
TextEdit::replace(range.range, visibility_text.into()),
63+
);
64+
65+
Some(vec![fix(
66+
"increase_field_visibility",
67+
"Increase field visibility",
68+
source_change,
69+
fix_range,
70+
)])
1971
}
2072

2173
#[cfg(test)]
2274
mod tests {
23-
use crate::tests::check_diagnostics;
75+
use crate::tests::{check_diagnostics, check_fix};
2476

2577
#[test]
2678
fn private_field() {
@@ -29,7 +81,7 @@ mod tests {
2981
mod module { pub struct Struct { field: u32 } }
3082
fn main(s: module::Struct) {
3183
s.field;
32-
//^^^^^^^ error: field `field` of `Struct` is private
84+
//^^^^^^^ 💡 error: field `field` of `Struct` is private
3385
}
3486
"#,
3587
);
@@ -42,7 +94,7 @@ fn main(s: module::Struct) {
4294
mod module { pub struct Struct(u32); }
4395
fn main(s: module::Struct) {
4496
s.0;
45-
//^^^ error: field `0` of `Struct` is private
97+
//^^^ 💡 error: field `0` of `Struct` is private
4698
}
4799
"#,
48100
);
@@ -113,4 +165,68 @@ fn main() {
113165
"#,
114166
);
115167
}
168+
169+
#[test]
170+
fn change_visibility_fix() {
171+
check_fix(
172+
r#"
173+
pub mod foo {
174+
pub mod bar {
175+
pub struct Struct {
176+
field: i32,
177+
}
178+
}
179+
}
180+
181+
fn foo(v: foo::bar::Struct) {
182+
v.field$0;
183+
}
184+
"#,
185+
r#"
186+
pub mod foo {
187+
pub mod bar {
188+
pub struct Struct {
189+
pub(crate) field: i32,
190+
}
191+
}
192+
}
193+
194+
fn foo(v: foo::bar::Struct) {
195+
v.field;
196+
}
197+
"#,
198+
);
199+
}
200+
201+
#[test]
202+
fn change_visibility_with_existing_visibility() {
203+
check_fix(
204+
r#"
205+
pub mod foo {
206+
pub mod bar {
207+
pub struct Struct {
208+
pub(super) field: i32,
209+
}
210+
}
211+
}
212+
213+
fn foo(v: foo::bar::Struct) {
214+
v.field$0;
215+
}
216+
"#,
217+
r#"
218+
pub mod foo {
219+
pub mod bar {
220+
pub struct Struct {
221+
pub(crate) field: i32,
222+
}
223+
}
224+
}
225+
226+
fn foo(v: foo::bar::Struct) {
227+
v.field;
228+
}
229+
"#,
230+
);
231+
}
116232
}

0 commit comments

Comments
 (0)