@@ -42,7 +42,7 @@ use foundry_compilers::{
42
42
use regex:: Regex ;
43
43
use revm:: primitives:: hardfork:: SpecId ;
44
44
use semver:: Version ;
45
- use serde:: { Deserialize , Serialize , Serializer } ;
45
+ use serde:: { Deserialize , Deserializer , Serialize , Serializer , de } ;
46
46
use std:: {
47
47
borrow:: Cow ,
48
48
collections:: BTreeMap ,
@@ -306,7 +306,10 @@ pub struct Config {
306
306
/// list of file paths to ignore
307
307
#[ serde( rename = "ignored_warnings_from" ) ]
308
308
pub ignored_file_paths : Vec < PathBuf > ,
309
- /// When true, compiler warnings are treated as errors
309
+ /// Diagnostic level (minimum) at which the process should finish with a non-zero exit.
310
+ pub deny : DenyLevel ,
311
+ /// DEPRECATED: use `deny` instead.
312
+ #[ serde( default , skip_serializing) ]
310
313
pub deny_warnings : bool ,
311
314
/// Only run test functions matching the specified regex pattern.
312
315
#[ serde( rename = "match_test" ) ]
@@ -562,13 +565,109 @@ pub struct Config {
562
565
pub _non_exhaustive : ( ) ,
563
566
}
564
567
568
+ /// Diagnostic level (minimum) at which the process should finish with a non-zero exit.
569
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , clap:: ValueEnum , Default , Serialize ) ]
570
+ #[ serde( rename_all = "lowercase" ) ]
571
+ pub enum DenyLevel {
572
+ /// Always exit with zero code.
573
+ #[ default]
574
+ Never ,
575
+ /// Exit with a non-zero code if any warnings are found.
576
+ Warnings ,
577
+ /// Exit with a non-zero code if any notes or warnings are found.
578
+ Notes ,
579
+ }
580
+
581
+ // Custom deserialization to make `DenyLevel` parsing case-insensitive and backwards compatible with
582
+ // booleans.
583
+ impl < ' de > Deserialize < ' de > for DenyLevel {
584
+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
585
+ where
586
+ D : Deserializer < ' de > ,
587
+ {
588
+ struct DenyLevelVisitor ;
589
+
590
+ impl < ' de > de:: Visitor < ' de > for DenyLevelVisitor {
591
+ type Value = DenyLevel ;
592
+
593
+ fn expecting ( & self , formatter : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
594
+ formatter. write_str ( "one of the following strings: `never`, `warnings`, `notes`" )
595
+ }
596
+
597
+ fn visit_bool < E > ( self , value : bool ) -> Result < Self :: Value , E >
598
+ where
599
+ E : de:: Error ,
600
+ {
601
+ Ok ( DenyLevel :: from ( value) )
602
+ }
603
+
604
+ fn visit_str < E > ( self , value : & str ) -> Result < Self :: Value , E >
605
+ where
606
+ E : de:: Error ,
607
+ {
608
+ DenyLevel :: from_str ( value) . map_err ( de:: Error :: custom)
609
+ }
610
+ }
611
+
612
+ deserializer. deserialize_any ( DenyLevelVisitor )
613
+ }
614
+ }
615
+
616
+ impl FromStr for DenyLevel {
617
+ type Err = String ;
618
+
619
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
620
+ match s. to_lowercase ( ) . as_str ( ) {
621
+ "warnings" | "warning" | "w" => Ok ( Self :: Warnings ) ,
622
+ "notes" | "note" | "n" => Ok ( Self :: Notes ) ,
623
+ "never" | "false" | "f" => Ok ( Self :: Never ) ,
624
+ _ => Err ( format ! (
625
+ "unknown variant: found `{s}`, expected one of `never`, `warnings`, `notes`"
626
+ ) ) ,
627
+ }
628
+ }
629
+ }
630
+
631
+ impl From < bool > for DenyLevel {
632
+ fn from ( deny : bool ) -> Self {
633
+ if deny { Self :: Warnings } else { Self :: Never }
634
+ }
635
+ }
636
+
637
+ impl DenyLevel {
638
+ /// Returns `true` if the deny level includes warnings.
639
+ pub fn warnings ( & self ) -> bool {
640
+ match self {
641
+ Self :: Never => false ,
642
+ Self :: Warnings | Self :: Notes => true ,
643
+ }
644
+ }
645
+
646
+ /// Returns `true` if the deny level includes notes.
647
+ pub fn notes ( & self ) -> bool {
648
+ match self {
649
+ Self :: Never | Self :: Warnings => false ,
650
+ Self :: Notes => true ,
651
+ }
652
+ }
653
+
654
+ /// Returns `true` if the deny level is set to never (only errors).
655
+ pub fn never ( & self ) -> bool {
656
+ match self {
657
+ Self :: Never => true ,
658
+ Self :: Warnings | Self :: Notes => false ,
659
+ }
660
+ }
661
+ }
662
+
565
663
/// Mapping of fallback standalone sections. See [`FallbackProfileProvider`].
566
664
pub const STANDALONE_FALLBACK_SECTIONS : & [ ( & str , & str ) ] = & [ ( "invariant" , "fuzz" ) ] ;
567
665
568
666
/// Deprecated keys and their replacements.
569
667
///
570
668
/// See [Warning::DeprecatedKey]
571
- pub const DEPRECATIONS : & [ ( & str , & str ) ] = & [ ( "cancun" , "evm_version = Cancun" ) ] ;
669
+ pub const DEPRECATIONS : & [ ( & str , & str ) ] =
670
+ & [ ( "cancun" , "evm_version = Cancun" ) , ( "deny_warnings" , "deny = warnings" ) ] ;
572
671
573
672
impl Config {
574
673
/// The default profile: "default"
@@ -1051,7 +1150,7 @@ impl Config {
1051
1150
. paths ( paths)
1052
1151
. ignore_error_codes ( self . ignored_error_codes . iter ( ) . copied ( ) . map ( Into :: into) )
1053
1152
. ignore_paths ( self . ignored_file_paths . clone ( ) )
1054
- . set_compiler_severity_filter ( if self . deny_warnings {
1153
+ . set_compiler_severity_filter ( if self . deny . warnings ( ) {
1055
1154
Severity :: Warning
1056
1155
} else {
1057
1156
Severity :: Error
@@ -2160,6 +2259,13 @@ impl Config {
2160
2259
figment = figment. merge ( ( "evm_version" , version) ) ;
2161
2260
}
2162
2261
2262
+ // Normalize `deny` based on the provided `deny_warnings` version.
2263
+ if self . deny_warnings
2264
+ && let Ok ( DenyLevel :: Never ) = figment. extract_inner ( "deny" )
2265
+ {
2266
+ figment = figment. merge ( ( "deny" , DenyLevel :: Warnings ) ) ;
2267
+ }
2268
+
2163
2269
figment
2164
2270
}
2165
2271
}
@@ -2432,6 +2538,7 @@ impl Default for Config {
2432
2538
SolidityErrorCode :: TransientStorageUsed ,
2433
2539
] ,
2434
2540
ignored_file_paths : vec ! [ ] ,
2541
+ deny : DenyLevel :: Never ,
2435
2542
deny_warnings : false ,
2436
2543
via_ir : false ,
2437
2544
ast : false ,
@@ -3780,7 +3887,7 @@ mod tests {
3780
3887
gas_reports = ['*']
3781
3888
ignored_error_codes = [1878]
3782
3889
ignored_warnings_from = ["something"]
3783
- deny_warnings = false
3890
+ deny = "never"
3784
3891
initial_balance = '0xffffffffffffffffffffffff'
3785
3892
libraries = []
3786
3893
libs = ['lib']
@@ -6221,6 +6328,24 @@ mod tests {
6221
6328
} ) ;
6222
6329
}
6223
6330
6331
+ #[ test]
6332
+ fn test_deprecated_deny_warnings_is_handled ( ) {
6333
+ figment:: Jail :: expect_with ( |jail| {
6334
+ jail. create_file (
6335
+ "foundry.toml" ,
6336
+ r#"
6337
+ [profile.default]
6338
+ deny_warnings = true
6339
+ "# ,
6340
+ ) ?;
6341
+ let config = Config :: load ( ) . unwrap ( ) ;
6342
+
6343
+ // Assert that the deprecated flag is correctly interpreted
6344
+ assert_eq ! ( config. deny, DenyLevel :: Warnings ) ;
6345
+ Ok ( ( ) )
6346
+ } ) ;
6347
+ }
6348
+
6224
6349
#[ test]
6225
6350
fn test_evm_version_solc_compatibility_warning ( ) {
6226
6351
figment:: Jail :: expect_with ( |jail| {
0 commit comments