Skip to main content

forge_lint/sol/info/
pragma_directive.rs

1use super::PragmaDirective;
2use crate::{
3    linter::{Lint, ProjectLintEmitter, ProjectLintPass, ProjectSource},
4    sol::{Severity, SolLint},
5};
6use solar::ast;
7
8declare_forge_lint!(
9    PRAGMA_INCONSISTENT,
10    Severity::Info,
11    "pragma-inconsistent",
12    "inconsistent Solidity pragma version requirements across the project"
13);
14
15impl<'ast> ProjectLintPass<'ast> for PragmaDirective {
16    fn check_project(&mut self, ctx: &ProjectLintEmitter<'_, '_>, sources: &[ProjectSource<'ast>]) {
17        if !ctx.is_lint_enabled(PRAGMA_INCONSISTENT.id()) {
18            return;
19        }
20        // Every `pragma solidity` directive across input sources, with its rendered version
21        // requirement for grouping, in a stable (path, position) order for snapshots.
22        let mut entries: Vec<(usize, _, String)> = sources
23            .iter()
24            .enumerate()
25            .flat_map(|(idx, source)| {
26                source.ast.items.iter().filter_map(move |item| match &item.kind {
27                    ast::ItemKind::Pragma(ast::PragmaDirective {
28                        tokens: ast::PragmaTokens::Version(ident, req),
29                        ..
30                    }) if ident.as_str() == "solidity" => Some((idx, item.span, req.to_string())),
31                    _ => None,
32                })
33            })
34            .collect();
35        entries.sort_by(|a, b| {
36            sources[a.0].path.cmp(&sources[b.0].path).then(a.1.lo().cmp(&b.1.lo()))
37        });
38
39        let mut distinct: Vec<&str> = entries.iter().map(|(_, _, req)| req.as_str()).collect();
40        distinct.sort_unstable();
41        distinct.dedup();
42        if let [(idx, span, _), ..] = entries.as_slice()
43            && distinct.len() > 1
44        {
45            let msg = format!(
46                "{} different Solidity pragma version requirements are used: `{}`",
47                distinct.len(),
48                distinct.join("`, `")
49            );
50            ctx.emit_with_msg(&sources[*idx], &PRAGMA_INCONSISTENT, *span, msg);
51        }
52    }
53}