forge_lint/sol/info/
multi_contract_file.rs1use crate::{
2 linter::{EarlyLintPass, Lint, LintContext},
3 sol::{Severity, SolLint},
4};
5
6use foundry_config::lint::LintSpecificConfig;
7use solar::ast;
8use std::sync::Arc;
9
10declare_forge_lint!(
11 MULTI_CONTRACT_FILE,
12 Severity::Info,
13 "multi-contract-file",
14 "prefer having only one contract, interface or library per file"
15);
16
17#[derive(Debug)]
18pub(super) struct MultiContractFilePass {
19 config: Arc<LintSpecificConfig>,
20}
21
22impl MultiContractFilePass {
23 pub(super) const fn new(config: Arc<LintSpecificConfig>) -> Self {
24 Self { config }
25 }
26}
27
28impl<'ast> EarlyLintPass<'ast> for MultiContractFilePass {
29 fn check_full_source_unit(
30 &mut self,
31 ctx: &LintContext<'ast, '_>,
32 unit: &'ast ast::SourceUnit<'ast>,
33 ) {
34 if !ctx.is_lint_enabled(MULTI_CONTRACT_FILE.id()) {
35 return;
36 }
37
38 let relevant_spans: Vec<_> = unit
40 .items
41 .iter()
42 .filter_map(|item| match &item.kind {
43 ast::ItemKind::Contract(c) => {
44 (!self.config.is_exempted(&c.kind)).then_some(c.name.span)
45 }
46 _ => None,
47 })
48 .collect();
49
50 if relevant_spans.len() > 1 {
52 for span in relevant_spans {
53 ctx.emit(&MULTI_CONTRACT_FILE, span);
54 }
55 }
56 }
57}