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), .. }, _) =
24 &expr.kind
25 else {
26 return;
27 };
28 let Some(Res::Item(ItemId::Struct(struct_id))) = gcx.resolved_expr(callee) else { return };
29 let fields = gcx.hir.strukt(struct_id).fields;
32 let fix = (!fields.is_empty() && fields.len() == args.len()).then(|| {
33 let assignments = fields
34 .iter()
35 .zip(*args)
36 .map(|(field, arg)| {
37 Some(format!(
38 "{}: {}",
39 gcx.hir.variable(*field).name?,
40 ctx.span_to_snippet(arg.span)?
41 ))
42 })
43 .collect::<Option<Vec<_>>>()?;
44 Some(format!("{}({{ {} }})", ctx.span_to_snippet(callee.span)?, assignments.join(", ")))
45 });
46 match fix.flatten() {
47 Some(fix) => ctx.emit_with_suggestion(
48 &NAMED_STRUCT_FIELDS,
49 expr.span,
50 Suggestion::fix(fix, Applicability::MachineApplicable)
51 .with_desc("consider using named fields"),
52 ),
53 None => ctx.emit(&NAMED_STRUCT_FIELDS, expr.span),
54 }
55 }
56}