Skip to main content

forge_lint/sol/info/
interface_naming.rs

1use super::InterfaceFileNaming;
2use crate::{
3    linter::{EarlyLintPass, Lint, LintContext},
4    sol::{Severity, SolLint},
5};
6use solar::ast;
7
8declare_forge_lint!(
9    INTERFACE_FILE_NAMING,
10    Severity::Info,
11    "interface-file-naming",
12    "interface file name is missing the `I` prefix"
13);
14
15declare_forge_lint!(
16    INTERFACE_NAMING,
17    Severity::Info,
18    "interface-naming",
19    "interface name is missing the `I` prefix"
20);
21
22impl<'ast> EarlyLintPass<'ast> for InterfaceFileNaming {
23    fn check_full_source_unit(
24        &mut self,
25        ctx: &LintContext<'ast, '_>,
26        unit: &'ast ast::SourceUnit<'ast>,
27    ) {
28        if !ctx.is_lint_enabled(INTERFACE_FILE_NAMING.id()) {
29            return;
30        }
31        // A file whose contract-like items are all interfaces is named after the first one.
32        let mut contracts = unit.items.iter().filter_map(|item| match &item.kind {
33            ast::ItemKind::Contract(c) => Some(c),
34            _ => None,
35        });
36        if let Some(first) = contracts.next()
37            && std::iter::once(first)
38                .chain(contracts)
39                .all(|c| c.kind == ast::ContractKind::Interface)
40            && let Some(file_name) = file_name(ctx, unit)
41            && !file_name.starts_with('I')
42        {
43            ctx.emit(&INTERFACE_FILE_NAMING, first.name.span);
44        }
45    }
46
47    fn check_item_contract(&mut self, ctx: &LintContext, contract: &'ast ast::ItemContract<'ast>) {
48        if contract.kind == ast::ContractKind::Interface && !contract.name.as_str().starts_with('I')
49        {
50            ctx.emit(&INTERFACE_NAMING, contract.name.span);
51        }
52    }
53}
54
55fn file_name(ctx: &LintContext, unit: &ast::SourceUnit<'_>) -> Option<String> {
56    let first_item_span = unit.items.first()?.span;
57    let file = ctx.session().source_map().lookup_source_file(first_item_span.lo());
58    Some(file.name.as_real()?.file_name()?.to_str()?.to_string())
59}