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 hir: &'hir hir::Hir<'hir>,
23 contract: &'hir hir::Contract<'hir>,
24 ) {
25 for m in contract.bases_args {
28 try_emit(ctx, hir, m, m.args.span);
29 }
30 }
31
32 fn check_function(
33 &mut self,
34 ctx: &LintContext,
35 hir: &'hir hir::Hir<'hir>,
36 func: &'hir hir::Function<'hir>,
37 ) {
38 if !matches!(func.kind, hir::FunctionKind::Constructor) {
40 return;
41 }
42 for m in func.modifiers {
43 if matches!(m.id, ItemId::Contract(_)) {
46 try_emit(ctx, hir, m, expand_to_leading_ws(ctx, m.span));
50 }
51 }
52 }
53}
54
55fn try_emit<'hir>(
56 ctx: &LintContext,
57 hir: &'hir hir::Hir<'hir>,
58 m: &'hir hir::Modifier<'hir>,
59 fix_span: Span,
60) {
61 let ItemId::Contract(base_id) = m.id else { return };
62
63 if m.args.is_dummy() {
65 return;
66 }
67 if !m.args.is_empty() {
69 return;
70 }
71
72 let base = hir.contract(base_id);
75 let redundant = match base.ctor {
76 None => true,
77 Some(c) => hir.function(c).parameters.is_empty(),
78 };
79 if !redundant {
80 return;
81 }
82
83 let safe_to_fix = ctx.span_to_snippet(m.args.span).map(|s| s.trim() == "()").unwrap_or(false);
86
87 if safe_to_fix {
88 ctx.emit_with_suggestion(
89 &REDUNDANT_BASE_CONSTRUCTOR_CALL,
90 m.args.span,
91 Suggestion::fix(String::new(), Applicability::MachineApplicable)
92 .with_span(fix_span)
93 .with_desc("remove redundant base-constructor call"),
94 );
95 } else {
96 ctx.emit(&REDUNDANT_BASE_CONSTRUCTOR_CALL, m.args.span);
97 }
98}
99
100fn expand_to_leading_ws(ctx: &LintContext, span: Span) -> Span {
105 if span.is_dummy() || span.lo() == BytePos(0) {
106 return span;
107 }
108 let prev = Span::new(span.lo() - BytePos(1), span.lo());
109 match ctx.span_to_snippet(prev).as_deref() {
110 Some(" " | "\t") => span.with_lo(span.lo() - BytePos(1)),
111 _ => span,
112 }
113}