forge_lint/sol/info/
pragma_directive.rs1use 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 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}