forge_lint/sol/info/
named_struct_fields.rs1use solar::sema::hir::{CallArgs, CallArgsKind, Expr, ExprKind, ItemId, Res};
2
3use crate::{
4 linter::{LateLintPass, LintContext, Suggestion},
5 sol::{Severity, SolLint, info::NamedStructFields},
6};
7
8declare_forge_lint!(
9 NAMED_STRUCT_FIELDS,
10 Severity::Info,
11 "named-struct-fields",
12 "prefer initializing structs with named fields"
13);
14
15impl<'hir> LateLintPass<'hir> for NamedStructFields {
16 fn check_expr(
17 &mut self,
18 ctx: &LintContext,
19 hir: &'hir solar::sema::hir::Hir<'hir>,
20 expr: &'hir solar::sema::hir::Expr<'hir>,
21 ) {
22 let ExprKind::Call(
23 Expr { kind: ExprKind::Ident([Res::Item(ItemId::Struct(struct_id))]), span, .. },
24 CallArgs { kind: CallArgsKind::Unnamed(args), .. },
25 _,
26 ) = &expr.kind
27 else {
28 return;
29 };
30
31 let strukt = hir.strukt(*struct_id);
32 let fields = &strukt.fields;
33
34 if fields.len() != args.len() || fields.is_empty() {
36 ctx.emit(&NAMED_STRUCT_FIELDS, expr.span);
38 return;
39 }
40
41 let Some(struct_name_snippet) = ctx.span_to_snippet(*span) else {
43 ctx.emit(&NAMED_STRUCT_FIELDS, expr.span);
45 return;
46 };
47
48 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 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 );
73 }
74}