forge_lint/sol/info/
named_struct_fields.rs1use super::NamedStructFields;
2use crate::{
3 linter::{LateLintPass, LintContext, Suggestion},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 interface::diagnostics::Applicability,
8 sema::{
9 Gcx,
10 hir::{CallArgs, CallArgsKind, Expr, ExprKind, ItemId, Res},
11 },
12};
13
14declare_forge_lint!(
15 NAMED_STRUCT_FIELDS,
16 Severity::Info,
17 "named-struct-fields",
18 "struct is initialized with positional fields"
19);
20
21impl<'gcx> LateLintPass<'gcx> for NamedStructFields {
22 fn check_expr(&mut self, ctx: &LintContext, gcx: Gcx<'gcx>, expr: &'gcx Expr<'gcx>) {
23 let ExprKind::Call(callee, CallArgs { kind: CallArgsKind::Unnamed(args), .. }) = &expr.kind
24 else {
25 return;
26 };
27 let Some(Res::Item(ItemId::Struct(struct_id))) = gcx.resolved_expr(callee) else { return };
28 let fields = gcx.hir.strukt(struct_id).fields;
31 let fix = (!fields.is_empty() && fields.len() == args.len()).then(|| {
32 let assignments = fields
33 .iter()
34 .zip(*args)
35 .map(|(field, arg)| {
36 Some(format!(
37 "{}: {}",
38 gcx.hir.variable(*field).name?,
39 ctx.span_to_snippet(arg.span)?
40 ))
41 })
42 .collect::<Option<Vec<_>>>()?;
43 Some(format!("{}({{ {} }})", ctx.span_to_snippet(callee.span)?, assignments.join(", ")))
44 });
45 match fix.flatten() {
46 Some(fix) => ctx.emit_with_suggestion(
47 &NAMED_STRUCT_FIELDS,
48 expr.span,
49 Suggestion::fix(fix, Applicability::MachineApplicable)
50 .with_desc("consider using named fields"),
51 ),
52 None => ctx.emit(&NAMED_STRUCT_FIELDS, expr.span),
53 }
54 }
55}