Skip to content
This repository was archived by the owner on Apr 20, 2026. It is now read-only.

Commit 94f5656

Browse files
authored
feat: add ignored_error_codes_from option to Project and ProjectBuilder (#361)
- Related to foundry-rs/foundry#9516 - Blocking foundry-rs/foundry#13841
1 parent a5b4073 commit 94f5656

4 files changed

Lines changed: 120 additions & 15 deletions

File tree

crates/compilers/src/compile/output/mod.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ pub struct ProjectCompileOutput<
7676
pub(crate) cached_artifacts: Artifacts<T::Artifact>,
7777
/// errors that should be omitted
7878
pub(crate) ignored_error_codes: Vec<u64>,
79+
/// errors that should be omitted from each path prefix
80+
pub(crate) ignored_error_codes_from: Vec<(PathBuf, Vec<u64>)>,
7981
/// paths that should be omitted
8082
pub(crate) ignored_file_paths: Vec<PathBuf>,
8183
/// set minimum level of severity that is treated as an error
@@ -487,14 +489,19 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>>
487489
pub fn has_compiler_errors(&self) -> bool {
488490
self.compiler_output.has_error(
489491
&self.ignored_error_codes,
492+
&self.ignored_error_codes_from,
490493
&self.ignored_file_paths,
491494
&self.compiler_severity_filter,
492495
)
493496
}
494497

495498
/// Returns whether any warnings were emitted by the compiler.
496499
pub fn has_compiler_warnings(&self) -> bool {
497-
self.compiler_output.has_warning(&self.ignored_error_codes, &self.ignored_file_paths)
500+
self.compiler_output.has_warning(
501+
&self.ignored_error_codes,
502+
&self.ignored_error_codes_from,
503+
&self.ignored_file_paths,
504+
)
498505
}
499506

500507
/// Panics if any errors were emitted by the compiler.
@@ -521,6 +528,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> fmt
521528
self.compiler_output
522529
.diagnostics(
523530
&self.ignored_error_codes,
531+
&self.ignored_error_codes_from,
524532
&self.ignored_file_paths,
525533
self.compiler_severity_filter,
526534
)
@@ -565,12 +573,14 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
565573
pub fn diagnostics<'a>(
566574
&'a self,
567575
ignored_error_codes: &'a [u64],
576+
ignored_error_codes_from: &'a [(PathBuf, Vec<u64>)],
568577
ignored_file_paths: &'a [PathBuf],
569578
compiler_severity_filter: Severity,
570579
) -> OutputDiagnostics<'a, C> {
571580
OutputDiagnostics {
572581
compiler_output: self,
573582
ignored_error_codes,
583+
ignored_error_codes_from,
574584
ignored_file_paths,
575585
compiler_severity_filter,
576586
}
@@ -831,6 +841,7 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
831841
pub fn has_error(
832842
&self,
833843
ignored_error_codes: &[u64],
844+
ignored_error_codes_from: &[(PathBuf, Vec<u64>)],
834845
ignored_file_paths: &[PathBuf],
835846
compiler_severity_filter: &Severity,
836847
) -> bool {
@@ -843,7 +854,11 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
843854
if compiler_severity_filter.ge(&err.severity()) {
844855
if compiler_severity_filter.is_warning() {
845856
// skip ignored error codes and file path from warnings
846-
return self.has_warning(ignored_error_codes, ignored_file_paths);
857+
return self.has_warning(
858+
ignored_error_codes,
859+
ignored_error_codes_from,
860+
ignored_file_paths,
861+
);
847862
}
848863
return true;
849864
}
@@ -853,15 +868,26 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
853868

854869
/// Checks if there are any compiler warnings that are not ignored by the specified error codes
855870
/// and file paths.
856-
pub fn has_warning(&self, ignored_error_codes: &[u64], ignored_file_paths: &[PathBuf]) -> bool {
857-
self.errors
858-
.iter()
859-
.any(|error| !self.should_ignore(ignored_error_codes, ignored_file_paths, error))
871+
pub fn has_warning(
872+
&self,
873+
ignored_error_codes: &[u64],
874+
ignored_error_codes_from: &[(PathBuf, Vec<u64>)],
875+
ignored_file_paths: &[PathBuf],
876+
) -> bool {
877+
self.errors.iter().any(|error| {
878+
!self.should_ignore(
879+
ignored_error_codes,
880+
ignored_error_codes_from,
881+
ignored_file_paths,
882+
error,
883+
)
884+
})
860885
}
861886

862887
pub fn should_ignore(
863888
&self,
864889
ignored_error_codes: &[u64],
890+
ignored_error_codes_from: &[(PathBuf, Vec<u64>)],
865891
ignored_file_paths: &[PathBuf],
866892
error: &C::CompilationError,
867893
) -> bool {
@@ -877,6 +903,9 @@ impl<C: Compiler> AggregatedCompilerOutput<C> {
877903
let path = Path::new(&loc.file);
878904
ignore |=
879905
ignored_file_paths.iter().any(|ignored_path| path.starts_with(ignored_path));
906+
ignore |= ignored_error_codes_from
907+
.iter()
908+
.any(|(prefix, codes)| path.starts_with(prefix) && codes.contains(&code));
880909

881910
// we ignore spdx and contract size warnings in test
882911
// files. if we are looking at one of these warnings
@@ -909,6 +938,8 @@ pub struct OutputDiagnostics<'a, C: Compiler> {
909938
compiler_output: &'a AggregatedCompilerOutput<C>,
910939
/// the error codes to ignore
911940
ignored_error_codes: &'a [u64],
941+
/// the error codes to ignore from each path prefix
942+
ignored_error_codes_from: &'a [(PathBuf, Vec<u64>)],
912943
/// the file paths to ignore
913944
ignored_file_paths: &'a [PathBuf],
914945
/// set minimum level of severity that is treated as an error
@@ -920,14 +951,19 @@ impl<C: Compiler> OutputDiagnostics<'_, C> {
920951
pub fn has_error(&self) -> bool {
921952
self.compiler_output.has_error(
922953
self.ignored_error_codes,
954+
self.ignored_error_codes_from,
923955
self.ignored_file_paths,
924956
&self.compiler_severity_filter,
925957
)
926958
}
927959

928960
/// Returns true if there is at least one warning
929961
pub fn has_warning(&self) -> bool {
930-
self.compiler_output.has_warning(self.ignored_error_codes, self.ignored_file_paths)
962+
self.compiler_output.has_warning(
963+
self.ignored_error_codes,
964+
self.ignored_error_codes_from,
965+
self.ignored_file_paths,
966+
)
931967
}
932968
}
933969

@@ -945,6 +981,7 @@ impl<C: Compiler> fmt::Display for OutputDiagnostics<'_, C> {
945981
for err in &self.compiler_output.errors {
946982
if !self.compiler_output.should_ignore(
947983
self.ignored_error_codes,
984+
self.ignored_error_codes_from,
948985
self.ignored_file_paths,
949986
err,
950987
) {

crates/compilers/src/compile/project.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
319319
)
320320
} else if output.has_error(
321321
&project.ignored_error_codes,
322+
&project.ignored_error_codes_from,
322323
&project.ignored_file_paths,
323324
&project.compiler_severity_filter,
324325
) {
@@ -374,10 +375,15 @@ impl<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
374375
let ArtifactsState { output, cache, compiled_artifacts } = self;
375376
let project = cache.project();
376377
let ignored_error_codes = project.ignored_error_codes.clone();
378+
let ignored_error_codes_from = project.ignored_error_codes_from.clone();
377379
let ignored_file_paths = project.ignored_file_paths.clone();
378380
let compiler_severity_filter = project.compiler_severity_filter;
379-
let has_error =
380-
output.has_error(&ignored_error_codes, &ignored_file_paths, &compiler_severity_filter);
381+
let has_error = output.has_error(
382+
&ignored_error_codes,
383+
&ignored_error_codes_from,
384+
&ignored_file_paths,
385+
&compiler_severity_filter,
386+
);
381387
let skip_write_to_disk = project.no_artifacts || has_error;
382388
trace!(has_error, project.no_artifacts, skip_write_to_disk, cache_path=?project.cache_path(),"prepare writing cache file");
383389

@@ -401,6 +407,7 @@ impl<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
401407
compiled_artifacts,
402408
cached_artifacts,
403409
ignored_error_codes,
410+
ignored_error_codes_from,
404411
ignored_file_paths,
405412
compiler_severity_filter,
406413
builds,

crates/compilers/src/lib.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ pub struct Project<
106106
pub artifacts: T,
107107
/// Errors/Warnings which match these error codes are not going to be logged
108108
pub ignored_error_codes: Vec<u64>,
109+
/// Errors/Warnings which match these error codes from these path prefixes are not going to be
110+
/// logged
111+
pub ignored_error_codes_from: Vec<(PathBuf, Vec<u64>)>,
109112
/// Errors/Warnings which match these file paths are not going to be logged
110113
pub ignored_file_paths: Vec<PathBuf>,
111114
/// The minimum severity level that is treated as a compiler error
@@ -463,6 +466,8 @@ pub struct ProjectBuilder<
463466
artifacts: T,
464467
/// Which error codes to ignore
465468
pub ignored_error_codes: Vec<u64>,
469+
/// Which error codes to ignore for files matching each path prefix
470+
pub ignored_error_codes_from: Vec<(PathBuf, Vec<u64>)>,
466471
/// Which file paths to ignore
467472
pub ignored_file_paths: Vec<PathBuf>,
468473
/// The minimum severity level that is treated as a compiler error
@@ -484,6 +489,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
484489
slash_paths: true,
485490
artifacts,
486491
ignored_error_codes: Vec::new(),
492+
ignored_error_codes_from: Vec::new(),
487493
ignored_file_paths: Vec::new(),
488494
compiler_severity_filter: Severity::Error,
489495
solc_jobs: None,
@@ -520,6 +526,15 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
520526
self
521527
}
522528

529+
#[must_use]
530+
pub fn ignore_error_codes_from(
531+
mut self,
532+
pairs: impl IntoIterator<Item = (PathBuf, Vec<u64>)>,
533+
) -> Self {
534+
self.ignored_error_codes_from.extend(pairs);
535+
self
536+
}
537+
523538
pub fn ignore_paths(mut self, paths: Vec<PathBuf>) -> Self {
524539
self.ignored_file_paths = paths;
525540
self
@@ -643,6 +658,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
643658
cached,
644659
no_artifacts,
645660
ignored_error_codes,
661+
ignored_error_codes_from,
646662
compiler_severity_filter,
647663
solc_jobs,
648664
offline,
@@ -665,6 +681,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
665681
slash_paths,
666682
artifacts,
667683
ignored_error_codes,
684+
ignored_error_codes_from,
668685
ignored_file_paths,
669686
compiler_severity_filter,
670687
solc_jobs,
@@ -681,6 +698,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
681698
no_artifacts,
682699
artifacts,
683700
ignored_error_codes,
701+
ignored_error_codes_from,
684702
ignored_file_paths,
685703
compiler_severity_filter,
686704
solc_jobs,
@@ -708,6 +726,7 @@ impl<C: Compiler, T: ArtifactOutput<CompilerContract = C::CompilerContract>> Pro
708726
no_artifacts,
709727
artifacts,
710728
ignored_error_codes,
729+
ignored_error_codes_from,
711730
ignored_file_paths,
712731
compiler_severity_filter,
713732
solc_jobs: solc_jobs

crates/compilers/tests/project.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,7 +2205,7 @@ fn test_severity_warnings() {
22052205
tmp.add_source("A", content).unwrap();
22062206

22072207
let out = tmp.compile().unwrap();
2208-
assert!(out.output().has_error(&[], &[], &Severity::Warning));
2208+
assert!(out.output().has_error(&[], &[], &[], &Severity::Warning));
22092209

22102210
let content = r"
22112211
// SPDX-License-Identifier: MIT OR Apache-2.0
@@ -2215,7 +2215,7 @@ fn test_severity_warnings() {
22152215
tmp.add_source("A", content).unwrap();
22162216

22172217
let out = tmp.compile().unwrap();
2218-
assert!(!out.output().has_error(&[], &[], &Severity::Warning));
2218+
assert!(!out.output().has_error(&[], &[], &[], &Severity::Warning));
22192219

22202220
let content = r"
22212221
// SPDX-License-Identifier: MIT OR Apache-2.0
@@ -2229,7 +2229,7 @@ fn test_severity_warnings() {
22292229
tmp.add_source("A", content).unwrap();
22302230

22312231
let out = tmp.compile().unwrap();
2232-
assert!(out.output().has_error(&[], &[], &Severity::Warning));
2232+
assert!(out.output().has_error(&[], &[], &[], &Severity::Warning));
22332233
}
22342234

22352235
#[test]
@@ -2840,6 +2840,7 @@ fn compile_project_with_options(
28402840
severity_filter: Option<foundry_compilers_artifacts::Severity>,
28412841
ignore_paths: Option<Vec<PathBuf>>,
28422842
ignore_error_code: Option<u64>,
2843+
ignore_error_codes_from: Option<Vec<(PathBuf, Vec<u64>)>>,
28432844
) -> ProjectCompileOutput<MultiCompiler> {
28442845
let mut builder =
28452846
Project::builder().no_artifacts().paths(gen_test_data_licensing_warning()).ephemeral();
@@ -2853,14 +2854,17 @@ fn compile_project_with_options(
28532854
if let Some(severity) = severity_filter {
28542855
builder = builder.set_compiler_severity_filter(severity);
28552856
}
2857+
if let Some(pairs) = ignore_error_codes_from {
2858+
builder = builder.ignore_error_codes_from(pairs);
2859+
}
28562860

28572861
let project = builder.build(Default::default()).unwrap();
28582862
project.compile().unwrap()
28592863
}
28602864

28612865
#[test]
28622866
fn test_compiler_ignored_file_paths() {
2863-
let compiled = compile_project_with_options(None, None, None);
2867+
let compiled = compile_project_with_options(None, None, None, None);
28642868
// no ignored paths set, so the warning should be present
28652869
assert!(compiled.has_compiler_warnings());
28662870
compiled.assert_success();
@@ -2871,28 +2875,66 @@ fn test_compiler_ignored_file_paths() {
28712875
Some(foundry_compilers_artifacts::Severity::Warning),
28722876
Some(vec![testdata]),
28732877
None,
2878+
None,
28742879
);
28752880

28762881
// ignored paths set, so the warning shouldnt be present
28772882
assert!(!compiled.has_compiler_warnings());
28782883
compiled.assert_success();
28792884
}
28802885

2886+
#[test]
2887+
fn test_compiler_ignored_error_codes_from() {
2888+
let missing_license_error_code = 1878;
2889+
2890+
// no ignored_error_codes_from set, warning should be present
2891+
let compiled = compile_project_with_options(None, None, None, None);
2892+
assert!(compiled.has_compiler_warnings());
2893+
2894+
let testdata =
2895+
canonicalize(Path::new(env!("CARGO_MANIFEST_DIR")).join("../../test-data")).unwrap();
2896+
2897+
// matching prefix and wrong error code, warning should be present
2898+
let compiled =
2899+
compile_project_with_options(None, None, None, Some(vec![(testdata.clone(), vec![9999])]));
2900+
assert!(compiled.has_compiler_warnings());
2901+
2902+
// non-matching prefix and correct error code, warning should be present
2903+
let compiled = compile_project_with_options(
2904+
None,
2905+
None,
2906+
None,
2907+
Some(vec![(PathBuf::from("nonexistent"), vec![missing_license_error_code])]),
2908+
);
2909+
assert!(compiled.has_compiler_warnings());
2910+
2911+
// matching prefix and matching error code, warning shouldn't be present
2912+
let compiled = compile_project_with_options(
2913+
None,
2914+
None,
2915+
None,
2916+
Some(vec![(testdata, vec![missing_license_error_code])]),
2917+
);
2918+
assert!(!compiled.has_compiler_warnings());
2919+
compiled.assert_success();
2920+
}
2921+
28812922
#[test]
28822923
fn test_compiler_severity_filter_and_ignored_error_codes() {
28832924
let missing_license_error_code = 1878;
28842925

2885-
let compiled = compile_project_with_options(None, None, None);
2926+
let compiled = compile_project_with_options(None, None, None, None);
28862927
assert!(compiled.has_compiler_warnings());
28872928

2888-
let compiled = compile_project_with_options(None, None, Some(missing_license_error_code));
2929+
let compiled = compile_project_with_options(None, None, Some(missing_license_error_code), None);
28892930
assert!(!compiled.has_compiler_warnings());
28902931
compiled.assert_success();
28912932

28922933
let compiled = compile_project_with_options(
28932934
Some(foundry_compilers_artifacts::Severity::Warning),
28942935
None,
28952936
Some(missing_license_error_code),
2937+
None,
28962938
);
28972939
assert!(!compiled.has_compiler_warnings());
28982940
compiled.assert_success();

0 commit comments

Comments
 (0)