Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 58 additions & 6 deletions crates/config/src/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
use clap::ValueEnum;
use core::fmt;
use serde::{Deserialize, Deserializer, Serialize};
use solar::interface::diagnostics::Level;
use solar::{
ast::{self as ast},
interface::diagnostics::Level,
};
use std::str::FromStr;
use yansi::Paint;

Expand All @@ -26,11 +29,8 @@ pub struct LinterConfig {
/// Defaults to true. Set to false to disable automatic linting during builds.
pub lint_on_build: bool,

/// Configurable patterns that should be excluded when performing `mixedCase` lint checks.
///
/// Default's to ["ERC", "URI"] to allow common names like `rescueERC20`, `ERC721TokenReceiver`
/// or `tokenURI`.
pub mixed_case_exceptions: Vec<String>,
/// Configuration specific to individual lints.
pub lint_specific: LintSpecificConfig,
}

impl Default for LinterConfig {
Expand All @@ -40,11 +40,63 @@ impl Default for LinterConfig {
severity: Vec::new(),
exclude_lints: Vec::new(),
ignore: Vec::new(),
lint_specific: LintSpecificConfig::default(),
}
}
}

/// Contract types that can be exempted from the multi-contract-file lint.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ContractException {
Interface,
Library,
AbstractContract,
}

/// Configuration specific to individual lints.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
pub struct LintSpecificConfig {
/// Configurable patterns that should be excluded when performing `mixedCase` lint checks.
///
/// Defaults to ["ERC", "URI"] to allow common names like `rescueERC20`, `ERC721TokenReceiver`
/// or `tokenURI`.
pub mixed_case_exceptions: Vec<String>,

/// Contract types that are allowed to appear multiple times in the same file.
///
/// Valid values: "interface", "library", "abstract_contract"
///
/// Defaults to an empty array (all contract types are flagged when multiple exist).
/// Note: Regular contracts cannot be exempted and will always be flagged when multiple exist.
pub multi_contract_file_exceptions: Vec<ContractException>,
}

impl Default for LintSpecificConfig {
fn default() -> Self {
Self {
mixed_case_exceptions: vec!["ERC".to_string(), "URI".to_string()],
multi_contract_file_exceptions: Vec::new(),
}
}
}

impl LintSpecificConfig {
/// Checks if a given contract kind is included in the list of exceptions
pub fn is_exempted(&self, contract_kind: &ast::ContractKind) -> bool {
let exception_to_check = match contract_kind {
ast::ContractKind::Interface => ContractException::Interface,
ast::ContractKind::Library => ContractException::Library,
ast::ContractKind::AbstractContract => ContractException::AbstractContract,
// Regular contracts are always linted
ast::ContractKind::Contract => return false,
};

self.multi_contract_file_exceptions.contains(&exception_to_check)
}
}

/// Severity of a lint.
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Serialize)]
pub enum Severity {
Expand Down
2 changes: 1 addition & 1 deletion crates/forge/src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl BuildArgs {
.collect(),
)
})
.with_mixed_case_exceptions(&config.lint.mixed_case_exceptions);
.with_lint_specific(&config.lint.lint_specific);

// Expand ignore globs and canonicalize from the get go
let ignored = expand_globs(&config.root, config.lint.ignore.iter())?
Expand Down
2 changes: 1 addition & 1 deletion crates/forge/src/cmd/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl LintArgs {
.with_lints(include)
.without_lints(exclude)
.with_severity(if severity.is_empty() { None } else { Some(severity) })
.with_mixed_case_exceptions(&config.lint.mixed_case_exceptions);
.with_lint_specific(&config.lint.lint_specific);

let output = ProjectCompiler::new().files(input.iter().cloned()).compile(&project)?;
let solar_sources = get_solar_sources_from_compile_output(&config, &output, Some(&input))?;
Expand Down
14 changes: 10 additions & 4 deletions crates/forge/tests/cli/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,13 @@ severity = []
exclude_lints = []
ignore = []
lint_on_build = true
[lint.lint_specific]
mixed_case_exceptions = [
"ERC",
"URI",
]
multi_contract_file_exceptions = []
[doc]
out = "docs"
Expand Down Expand Up @@ -1341,10 +1344,13 @@ forgetest_init!(test_default_config, |prj, cmd| {
"exclude_lints": [],
"ignore": [],
"lint_on_build": true,
"mixed_case_exceptions": [
"ERC",
"URI"
]
"lint_specific": {
"mixed_case_exceptions": [
"ERC",
"URI"
],
"multi_contract_file_exceptions": []
}
},
"doc": {
"out": "docs",
Expand Down
Loading
Loading