forge_lint/sol/info/
interface_naming.rs1use crate::{
2 linter::{EarlyLintPass, Lint, LintContext},
3 sol::{Severity, SolLint, info::InterfaceFileNaming},
4};
5
6use solar::ast::{self as ast};
7
8declare_forge_lint!(
9 INTERFACE_FILE_NAMING,
10 Severity::Info,
11 "interface-file-naming",
12 "interface file names should be prefixed with 'I'"
13);
14
15declare_forge_lint!(
16 INTERFACE_NAMING,
17 Severity::Info,
18 "interface-naming",
19 "interface names should be prefixed with 'I'"
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
32 if let Some(file_name) = file_name(ctx, unit)
33 && !file_name.starts_with('I')
34 && unit.items.iter().all(|item| match &item.kind {
35 ast::ItemKind::Contract(c) => c.kind == ast::ContractKind::Interface,
36 _ => true,
37 })
38 && let Some(c) = unit.items.iter().find_map(|item| match &item.kind {
39 ast::ItemKind::Contract(c) => Some(c),
40 _ => None,
41 })
42 {
43 ctx.emit(&INTERFACE_FILE_NAMING, c.name.span);
44 }
45 }
46
47 fn check_item_contract(&mut self, ctx: &LintContext, contract: &'ast ast::ItemContract<'ast>) {
48 if ctx.is_lint_enabled(INTERFACE_NAMING.id())
49 && contract.kind == ast::ContractKind::Interface
50 && !contract.name.as_str().starts_with('I')
51 {
52 ctx.emit(&INTERFACE_NAMING, contract.name.span);
53 }
54 }
55}
56
57fn file_name(ctx: &LintContext, unit: &ast::SourceUnit) -> Option<String> {
58 let first_item_span = unit.items.first()?.span;
59 let file = ctx.session().source_map().lookup_source_file(first_item_span.lo());
60 let file_name = file.name.as_real()?.file_name()?.to_str()?;
61 Some(file_name.to_string())
62}