Skip to content

Commit 069d082

Browse files
mablronbjerg
andauthored
feat(lint): emit suggestions for named_struct_fields lint (#11982)
* feat(lint): enhance named struct fields lint with suggestions for auto-fix - Updated the `NamedStructFields` lint to emit suggestions for initializing structs with named fields. - Updated test data to reflect the new suggestion format in lint messages. * fix: warn without suggestion if missing struct/arg name * fix: field_name Co-authored-by: onbjerg <[email protected]> * fix: previous suggestion, arg/field swap - fmt - fix missing ')' --------- Co-authored-by: onbjerg <[email protected]>
1 parent 082fc7e commit 069d082

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

crates/lint/src/sol/info/named_struct_fields.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use solar::sema::hir::{CallArgs, CallArgsKind, Expr, ExprKind, ItemId, Res};
22

33
use crate::{
4-
linter::{LateLintPass, LintContext},
4+
linter::{LateLintPass, LintContext, Suggestion},
55
sol::{Severity, SolLint, info::NamedStructFields},
66
};
77

@@ -16,16 +16,59 @@ impl<'hir> LateLintPass<'hir> for NamedStructFields {
1616
fn check_expr(
1717
&mut self,
1818
ctx: &LintContext,
19-
_hir: &'hir solar::sema::hir::Hir<'hir>,
19+
hir: &'hir solar::sema::hir::Hir<'hir>,
2020
expr: &'hir solar::sema::hir::Expr<'hir>,
2121
) {
22-
if let ExprKind::Call(
23-
Expr { kind: ExprKind::Ident([Res::Item(ItemId::Struct(_struct_id))]), .. },
24-
CallArgs { kind: CallArgsKind::Unnamed(_args), .. },
22+
let ExprKind::Call(
23+
Expr { kind: ExprKind::Ident([Res::Item(ItemId::Struct(struct_id))]), span, .. },
24+
CallArgs { kind: CallArgsKind::Unnamed(args), .. },
2525
_,
2626
) = &expr.kind
27-
{
27+
else {
28+
return;
29+
};
30+
31+
let strukt = hir.strukt(*struct_id);
32+
let fields = &strukt.fields;
33+
34+
// Basic sanity conditions for a consistent auto-fix
35+
if fields.len() != args.len() || fields.is_empty() {
36+
// Emit without suggestion
2837
ctx.emit(&NAMED_STRUCT_FIELDS, expr.span);
38+
return;
2939
}
40+
41+
// Get struct name snippet and emit without suggestion if we can't get it
42+
let Some(struct_name_snippet) = ctx.span_to_snippet(*span) else {
43+
// Emit without suggestion if we can't get the struct name snippet
44+
ctx.emit(&NAMED_STRUCT_FIELDS, expr.span);
45+
return;
46+
};
47+
48+
// Collect field names and corresponding argument source snippets
49+
let mut field_assignments = Vec::new();
50+
for (field_id, arg) in fields.iter().zip(args.iter()) {
51+
let field = hir.variable(*field_id);
52+
53+
let Some((arg_snippet, field_name)) =
54+
ctx.span_to_snippet(arg.span).zip(field.name.map(|n| n.to_string()))
55+
else {
56+
// Emit without suggestion if we can't get argument snippet
57+
ctx.emit(&NAMED_STRUCT_FIELDS, expr.span);
58+
return;
59+
};
60+
61+
field_assignments.push(format!("{field_name}: {arg_snippet}"));
62+
}
63+
64+
ctx.emit_with_suggestion(
65+
&NAMED_STRUCT_FIELDS,
66+
expr.span,
67+
Suggestion::fix(
68+
format!("{}({{ {} }})", struct_name_snippet, field_assignments.join(", ")),
69+
solar::interface::diagnostics::Applicability::MachineApplicable,
70+
)
71+
.with_desc("consider using named fields"),
72+
);
3073
}
3174
}

crates/lint/testdata/NamedStructFields.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ note[named-struct-fields]: prefer initializing structs with named fields
22
--> ROOT/testdata/NamedStructFields.sol:LL:CC
33
|
44
LL | Person memory person = Person("Alice", 25, address(0));
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using named fields: `Person({ name: "Alice", age: 25, wallet: address(0) })`
66
|
77
= help: https://book.getfoundry.sh/reference/forge/forge-lint#named-struct-fields
88

0 commit comments

Comments
 (0)