forge_lint/sol/info/
redundant_base_constructor_call.rs1use super::RedundantBaseConstructorCall;
2use crate::{
3 linter::{LateLintPass, LintContext, Suggestion},
4 sol::{Severity, SolLint},
5};
6use solar::{
7 interface::{BytePos, Span, diagnostics::Applicability},
8 sema::hir::{self, ItemId},
9};
10
11declare_forge_lint!(
12 REDUNDANT_BASE_CONSTRUCTOR_CALL,
13 Severity::Info,
14 "redundant-base-constructor-call",
15 "explicit empty base-constructor arguments are redundant"
16);
17
18impl<'hir> LateLintPass<'hir> for RedundantBaseConstructorCall {
19 fn check_contract(
20 &mut self,
21 ctx: &LintContext,
22 _gcx: solar::sema::Gcx<'hir>,
23 hir: &'hir hir::Hir<'hir>,
24 contract: &'hir hir::Contract<'hir>,
25 ) {
26 for m in contract.bases_args {
29 try_emit(ctx, hir, m, m.args.span);
30 }
31 }
32
33 fn check_function(
34 &mut self,
35 ctx: &LintContext,
36 _gcx: solar::sema::Gcx<'hir>,
37 hir: &'hir hir::Hir<'hir>,
38 func: &'hir hir::Function<'hir>,
39 ) {
40 if !matches!(func.kind, hir::FunctionKind::Constructor) {
42 return;
43 }
44 for m in func.modifiers {
45 if matches!(m.id, ItemId::Contract(_)) {
48 try_emit(ctx, hir, m, expand_to_leading_ws(ctx, m.span));
52 }
53 }
54 }
55}
56
57fn try_emit<'hir>(
58 ctx: &LintContext,
59 hir: &'hir hir::Hir<'hir>,
60 m: &'hir hir::Modifier<'hir>,
61 fix_span: Span,
62) {
63 let ItemId::Contract(base_id) = m.id else { return };
64
65 if m.args.is_dummy() {
67 return;
68 }
69 if !m.args.is_empty() {
71 return;
72 }
73
74 let base = hir.contract(base_id);
77 let redundant = match base.ctor {
78 None => true,
79 Some(c) => hir.function(c).parameters.is_empty(),
80 };
81 if !redundant {
82 return;
83 }
84
85 let safe_to_fix = ctx.span_to_snippet(m.args.span).map(|s| s.trim() == "()").unwrap_or(false);
88
89 if safe_to_fix {
90 ctx.emit_with_suggestion(
91 &REDUNDANT_BASE_CONSTRUCTOR_CALL,
92 m.args.span,
93 Suggestion::fix(String::new(), Applicability::MachineApplicable)
94 .with_span(fix_span)
95 .with_desc("remove redundant base-constructor call"),
96 );
97 } else {
98 ctx.emit(&REDUNDANT_BASE_CONSTRUCTOR_CALL, m.args.span);
99 }
100}
101
102fn expand_to_leading_ws(ctx: &LintContext, span: Span) -> Span {
107 if span.is_dummy() || span.lo() == BytePos(0) {
108 return span;
109 }
110 let prev = Span::new(span.lo() - BytePos(1), span.lo());
111 match ctx.span_to_snippet(prev).as_deref() {
112 Some(" " | "\t") => span.with_lo(span.lo() - BytePos(1)),
113 _ => span,
114 }
115}