Skip to main content

forge_lint/sol/info/
named_struct_fields.rs

1use 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        // A fix needs one argument per field and every snippet available; otherwise the
30        // diagnostic is emitted without a suggestion.
31        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}