feat: warnings as errors (#1838)
* feat: warnings as errors * changed the bool arg to Severity and updated its traits * reformat the test based on the linter * renamed variable based on property type change and changed a few refs * updated changelog * revert changelog iden change * added test for combining compiler severity filter and ignored error codes and adjusted has error for the added test case * adjusted has_error to utilize ge functionality in case of info errors Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
parent
8de8a29dc2
commit
b06452c6ea
|
@ -1733,8 +1733,9 @@ impl fmt::Display for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Default)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
|
#[default]
|
||||||
Error,
|
Error,
|
||||||
Warning,
|
Warning,
|
||||||
Info,
|
Info,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
artifacts::{
|
artifacts::{
|
||||||
contract::{CompactContractBytecode, CompactContractRef, Contract},
|
contract::{CompactContractBytecode, CompactContractRef, Contract},
|
||||||
Error,
|
Error, Severity,
|
||||||
},
|
},
|
||||||
buildinfo::RawBuildInfo,
|
buildinfo::RawBuildInfo,
|
||||||
info::ContractInfoRef,
|
info::ContractInfoRef,
|
||||||
|
@ -31,6 +31,8 @@ pub struct ProjectCompileOutput<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
pub(crate) cached_artifacts: Artifacts<T::Artifact>,
|
pub(crate) cached_artifacts: Artifacts<T::Artifact>,
|
||||||
/// errors that should be omitted
|
/// errors that should be omitted
|
||||||
pub(crate) ignored_error_codes: Vec<u64>,
|
pub(crate) ignored_error_codes: Vec<u64>,
|
||||||
|
/// set minimum level of severity that is treated as an error
|
||||||
|
pub(crate) compiler_severity_filter: Severity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
|
@ -197,7 +199,7 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
||||||
|
|
||||||
/// Whether there were errors
|
/// Whether there were errors
|
||||||
pub fn has_compiler_errors(&self) -> bool {
|
pub fn has_compiler_errors(&self) -> bool {
|
||||||
self.compiler_output.has_error()
|
self.compiler_output.has_error(&self.ignored_error_codes, &self.compiler_severity_filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether there were warnings
|
/// Whether there were warnings
|
||||||
|
@ -398,7 +400,7 @@ impl<T: ArtifactOutput> fmt::Display for ProjectCompileOutput<T> {
|
||||||
if self.compiler_output.is_unchanged() {
|
if self.compiler_output.is_unchanged() {
|
||||||
f.write_str("Nothing to compile")
|
f.write_str("Nothing to compile")
|
||||||
} else {
|
} else {
|
||||||
self.compiler_output.diagnostics(&self.ignored_error_codes).fmt(f)
|
self.compiler_output.diagnostics(&self.ignored_error_codes, self.compiler_severity_filter.clone()).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,8 +428,16 @@ impl AggregatedCompilerOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the output contains a compiler error
|
/// Whether the output contains a compiler error
|
||||||
pub fn has_error(&self) -> bool {
|
pub fn has_error(&self, ignored_error_codes: &[u64], compiler_severity_filter: &Severity) -> bool {
|
||||||
self.errors.iter().any(|err| err.severity.is_error())
|
self.errors.iter().any(|err| {
|
||||||
|
if compiler_severity_filter.ge(&err.severity) {
|
||||||
|
if compiler_severity_filter.is_warning() {
|
||||||
|
return self.has_warning(ignored_error_codes)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the output contains a compiler warning
|
/// Whether the output contains a compiler warning
|
||||||
|
@ -441,8 +451,8 @@ impl AggregatedCompilerOutput {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics<'a>(&'a self, ignored_error_codes: &'a [u64]) -> OutputDiagnostics {
|
pub fn diagnostics<'a>(&'a self, ignored_error_codes: &'a [u64], compiler_severity_filter: Severity) -> OutputDiagnostics {
|
||||||
OutputDiagnostics { compiler_output: self, ignored_error_codes }
|
OutputDiagnostics { compiler_output: self, ignored_error_codes, compiler_severity_filter }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
@ -702,12 +712,14 @@ pub struct OutputDiagnostics<'a> {
|
||||||
compiler_output: &'a AggregatedCompilerOutput,
|
compiler_output: &'a AggregatedCompilerOutput,
|
||||||
/// the error codes to ignore
|
/// the error codes to ignore
|
||||||
ignored_error_codes: &'a [u64],
|
ignored_error_codes: &'a [u64],
|
||||||
|
/// set minimum level of severity that is treated as an error
|
||||||
|
compiler_severity_filter: Severity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OutputDiagnostics<'a> {
|
impl<'a> OutputDiagnostics<'a> {
|
||||||
/// Returns true if there is at least one error of high severity
|
/// Returns true if there is at least one error of high severity
|
||||||
pub fn has_error(&self) -> bool {
|
pub fn has_error(&self) -> bool {
|
||||||
self.compiler_output.has_error()
|
self.compiler_output.has_error(&self.ignored_error_codes, &self.compiler_severity_filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if there is at least one warning
|
/// Returns true if there is at least one warning
|
||||||
|
|
|
@ -311,7 +311,7 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> {
|
||||||
ctx,
|
ctx,
|
||||||
&project.paths,
|
&project.paths,
|
||||||
)
|
)
|
||||||
} else if output.has_error() {
|
} else if output.has_error(&project.ignored_error_codes, &project.compiler_severity_filter) {
|
||||||
trace!("skip writing cache file due to solc errors: {:?}", output.errors);
|
trace!("skip writing cache file due to solc errors: {:?}", output.errors);
|
||||||
project.artifacts_handler().output_to_artifacts(
|
project.artifacts_handler().output_to_artifacts(
|
||||||
&output.contracts,
|
&output.contracts,
|
||||||
|
@ -358,14 +358,17 @@ impl<'a, T: ArtifactOutput> ArtifactsState<'a, T> {
|
||||||
fn write_cache(self) -> Result<ProjectCompileOutput<T>> {
|
fn write_cache(self) -> Result<ProjectCompileOutput<T>> {
|
||||||
trace!("write cache");
|
trace!("write cache");
|
||||||
let ArtifactsState { output, cache, compiled_artifacts } = self;
|
let ArtifactsState { output, cache, compiled_artifacts } = self;
|
||||||
let ignored_error_codes = cache.project().ignored_error_codes.clone();
|
let project = cache.project();
|
||||||
let skip_write_to_disk = cache.project().no_artifacts || output.has_error();
|
let ignored_error_codes = project.ignored_error_codes.clone();
|
||||||
|
let compiler_severity_filter = project.compiler_severity_filter.clone();
|
||||||
|
let skip_write_to_disk = project.no_artifacts || output.has_error(&ignored_error_codes, &compiler_severity_filter);
|
||||||
let cached_artifacts = cache.consume(&compiled_artifacts, !skip_write_to_disk)?;
|
let cached_artifacts = cache.consume(&compiled_artifacts, !skip_write_to_disk)?;
|
||||||
Ok(ProjectCompileOutput {
|
Ok(ProjectCompileOutput {
|
||||||
compiler_output: output,
|
compiler_output: output,
|
||||||
compiled_artifacts,
|
compiled_artifacts,
|
||||||
cached_artifacts,
|
cached_artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
|
compiler_severity_filter
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ use crate::{
|
||||||
error::{SolcError, SolcIoError},
|
error::{SolcError, SolcIoError},
|
||||||
sources::{VersionedSourceFile, VersionedSourceFiles},
|
sources::{VersionedSourceFile, VersionedSourceFiles},
|
||||||
};
|
};
|
||||||
use artifacts::contract::Contract;
|
use artifacts::{contract::Contract, Severity};
|
||||||
use compile::output::contracts::VersionedContracts;
|
use compile::output::contracts::VersionedContracts;
|
||||||
use error::Result;
|
use error::Result;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
@ -73,6 +73,8 @@ pub struct Project<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
pub artifacts: T,
|
pub artifacts: T,
|
||||||
/// Errors/Warnings which match these error codes are not going to be logged
|
/// Errors/Warnings which match these error codes are not going to be logged
|
||||||
pub ignored_error_codes: Vec<u64>,
|
pub ignored_error_codes: Vec<u64>,
|
||||||
|
/// The minimum severity level that is treated as a compiler error
|
||||||
|
pub compiler_severity_filter: Severity,
|
||||||
/// The paths which will be allowed for library inclusion
|
/// The paths which will be allowed for library inclusion
|
||||||
pub allowed_paths: AllowedLibPaths,
|
pub allowed_paths: AllowedLibPaths,
|
||||||
/// The paths which will be used with solc's `--include-path` attribute
|
/// The paths which will be used with solc's `--include-path` attribute
|
||||||
|
@ -569,6 +571,8 @@ pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||||
artifacts: T,
|
artifacts: T,
|
||||||
/// Which error codes to ignore
|
/// Which error codes to ignore
|
||||||
pub ignored_error_codes: Vec<u64>,
|
pub ignored_error_codes: Vec<u64>,
|
||||||
|
/// The minimum severity level that is treated as a compiler error
|
||||||
|
compiler_severity_filter: Severity,
|
||||||
/// All allowed paths for solc's `--allowed-paths`
|
/// All allowed paths for solc's `--allowed-paths`
|
||||||
allowed_paths: AllowedLibPaths,
|
allowed_paths: AllowedLibPaths,
|
||||||
/// Paths to use for solc's `--include-path`
|
/// Paths to use for solc's `--include-path`
|
||||||
|
@ -591,6 +595,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
slash_paths: true,
|
slash_paths: true,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes: Vec::new(),
|
ignored_error_codes: Vec::new(),
|
||||||
|
compiler_severity_filter: Severity::Error,
|
||||||
allowed_paths: Default::default(),
|
allowed_paths: Default::default(),
|
||||||
include_paths: Default::default(),
|
include_paths: Default::default(),
|
||||||
solc_jobs: None,
|
solc_jobs: None,
|
||||||
|
@ -629,6 +634,12 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn set_compiler_severity_filter(mut self, compiler_severity_filter: Severity) -> Self {
|
||||||
|
self.compiler_severity_filter = compiler_severity_filter;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Disables cached builds
|
/// Disables cached builds
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn ephemeral(self) -> Self {
|
pub fn ephemeral(self) -> Self {
|
||||||
|
@ -727,6 +738,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
no_artifacts,
|
no_artifacts,
|
||||||
auto_detect,
|
auto_detect,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
|
compiler_severity_filter,
|
||||||
allowed_paths,
|
allowed_paths,
|
||||||
include_paths,
|
include_paths,
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
|
@ -746,6 +758,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
slash_paths,
|
slash_paths,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
|
compiler_severity_filter,
|
||||||
allowed_paths,
|
allowed_paths,
|
||||||
include_paths,
|
include_paths,
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
|
@ -803,6 +816,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
auto_detect,
|
auto_detect,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
|
compiler_severity_filter,
|
||||||
mut allowed_paths,
|
mut allowed_paths,
|
||||||
include_paths,
|
include_paths,
|
||||||
solc_jobs,
|
solc_jobs,
|
||||||
|
@ -834,6 +848,7 @@ impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||||
auto_detect,
|
auto_detect,
|
||||||
artifacts,
|
artifacts,
|
||||||
ignored_error_codes,
|
ignored_error_codes,
|
||||||
|
compiler_severity_filter,
|
||||||
allowed_paths,
|
allowed_paths,
|
||||||
include_paths,
|
include_paths,
|
||||||
solc_jobs: solc_jobs.unwrap_or_else(num_cpus::get),
|
solc_jobs: solc_jobs.unwrap_or_else(num_cpus::get),
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
pragma solidity 0.8.6;
|
||||||
|
|
||||||
|
contract LicenseWarning {}
|
|
@ -1618,6 +1618,81 @@ fn can_compile_model_checker_sample() {
|
||||||
assert!(compiled.has_compiler_warnings());
|
assert!(compiled.has_compiler_warnings());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compiler_severity_filter() {
|
||||||
|
fn gen_test_data_warning_path() -> ProjectPathsConfig {
|
||||||
|
let root =
|
||||||
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/test-contract-warnings");
|
||||||
|
let paths = ProjectPathsConfig::builder().sources(root).build().unwrap();
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
let project = Project::builder()
|
||||||
|
.no_artifacts()
|
||||||
|
.paths(gen_test_data_warning_path())
|
||||||
|
.ephemeral()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.has_compiler_warnings());
|
||||||
|
assert!(!compiled.has_compiler_errors());
|
||||||
|
|
||||||
|
let project = Project::builder()
|
||||||
|
.no_artifacts()
|
||||||
|
.paths(gen_test_data_warning_path())
|
||||||
|
.ephemeral()
|
||||||
|
.set_compiler_severity_filter(ethers_solc::artifacts::Severity::Warning)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.has_compiler_warnings());
|
||||||
|
assert!(compiled.has_compiler_errors());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compiler_severity_filter_and_ignored_error_codes() {
|
||||||
|
fn gen_test_data_licensing_warning() -> ProjectPathsConfig {
|
||||||
|
let root =
|
||||||
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/test-contract-warnings/LicenseWarning.sol");
|
||||||
|
let paths = ProjectPathsConfig::builder().sources(root).build().unwrap();
|
||||||
|
paths
|
||||||
|
}
|
||||||
|
|
||||||
|
let missing_license_error_code = 1878;
|
||||||
|
|
||||||
|
let project = Project::builder()
|
||||||
|
.no_artifacts()
|
||||||
|
.paths(gen_test_data_licensing_warning())
|
||||||
|
.ephemeral()
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(compiled.has_compiler_warnings());
|
||||||
|
|
||||||
|
let project = Project::builder()
|
||||||
|
.no_artifacts()
|
||||||
|
.paths(gen_test_data_licensing_warning())
|
||||||
|
.ephemeral()
|
||||||
|
.ignore_error_code(missing_license_error_code)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(!compiled.has_compiler_warnings());
|
||||||
|
assert!(!compiled.has_compiler_errors());
|
||||||
|
|
||||||
|
let project = Project::builder()
|
||||||
|
.no_artifacts()
|
||||||
|
.paths(gen_test_data_licensing_warning())
|
||||||
|
.ephemeral()
|
||||||
|
.ignore_error_code(missing_license_error_code)
|
||||||
|
.set_compiler_severity_filter(ethers_solc::artifacts::Severity::Warning)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let compiled = project.compile().unwrap();
|
||||||
|
assert!(!compiled.has_compiler_warnings());
|
||||||
|
assert!(!compiled.has_compiler_errors());
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_solc_if_exists(version: &Version) {
|
fn remove_solc_if_exists(version: &Version) {
|
||||||
if Solc::find_svm_installed_version(version.to_string()).unwrap().is_some() {
|
if Solc::find_svm_installed_version(version.to_string()).unwrap().is_some() {
|
||||||
svm::remove_version(version).expect("failed to remove version")
|
svm::remove_version(version).expect("failed to remove version")
|
||||||
|
|
Loading…
Reference in New Issue