@@ -17,6 +17,17 @@ zig_lib_directory: ?[]const u8 = null,
1717/// fix any discovered issues instead of reporting them.
1818fix : bool = false ,
1919
20+ /// If set to true only errors will be reported. Warnings are silently ignored.
21+ ///
22+ /// By default, zlinter reports both warnings and errors. In some workflows,
23+ /// you may only want to report errors and ignore warnings — for example, in
24+ /// continuous Integration (CI) pipelines where only correctness issues.
25+ quiet : bool = false ,
26+
27+ /// If set, zlinter will fail (non-zero exit code) if more than the given number
28+ /// of warnings are reported.
29+ max_warnings : ? u32 = null ,
30+
2031/// Only lint or fix (if using the fix argument) the given files. These
2132/// are owned by the struct and should be freed by calling deinit. This will
2233/// replace any file resolution provided by the build file.
@@ -135,6 +146,7 @@ pub fn allocParse(
135146 const State = enum {
136147 parsing ,
137148 fix_arg ,
149+ quiet_arg ,
138150 verbose_arg ,
139151 help_arg ,
140152 zig_exe_arg ,
@@ -149,11 +161,13 @@ pub fn allocParse(
149161 rule_config_arg ,
150162 stdin_arg ,
151163 fix_passes_arg ,
164+ max_warnings_arg ,
152165 };
153166
154167 const flags : std .StaticStringMap (State ) = .initComptime (.{
155168 .{ "" , .parsing },
156169 .{ "--fix" , .fix_arg },
170+ .{ "--quiet" , .quiet_arg },
157171 .{ "--verbose" , .verbose_arg },
158172 .{ "--rule" , .rule_arg },
159173 .{ "--include" , .include_path_arg },
@@ -168,6 +182,7 @@ pub fn allocParse(
168182 .{ "--help" , .help_arg },
169183 .{ "-h" , .help_arg },
170184 .{ "--fix-passes" , .fix_passes_arg },
185+ .{ "--max-warnings" , .max_warnings_arg },
171186 });
172187
173188 state : switch (State .parsing ) {
@@ -272,6 +287,22 @@ pub fn allocParse(
272287 }});
273288 return error .InvalidArgs ;
274289 },
290+ .max_warnings_arg = > {
291+ index += 1 ;
292+
293+ if (index == args .len ) {
294+ rendering .process_printer .println (.err , "--max-warnings missing value" , .{});
295+ return error .InvalidArgs ;
296+ }
297+
298+ const error_message = "--max-warnings expects a u32" ;
299+ lint_args .max_warnings = std .fmt .parseInt (u32 , args [index ], 10 ) catch {
300+ rendering .process_printer .println (.err , error_message , .{});
301+ return error .InvalidArgs ;
302+ };
303+
304+ continue :state State .parsing ;
305+ },
275306 .fix_passes_arg = > {
276307 index += 1 ;
277308 if (index == args .len ) {
@@ -295,6 +326,10 @@ pub fn allocParse(
295326 lint_args .fix = true ;
296327 continue :state State .parsing ;
297328 },
329+ .quiet_arg = > {
330+ lint_args .quiet = true ;
331+ continue :state State .parsing ;
332+ },
298333 .verbose_arg = > {
299334 lint_args .verbose = true ;
300335 continue :state State .parsing ;
@@ -388,6 +423,8 @@ pub fn printHelp(printer: *rendering.Printer) void {
388423 .{ "--include" , "Only lint these paths, ignoring build.zig includes/excludes" },
389424 .{ "--exclude" , "Skip linting for these paths" },
390425 .{ "--filter" , "Limit linting to the specified resolved paths" },
426+ .{ "--quiet" , "Only report errors (not warnings)" },
427+ .{ "--max-warnings" , "Fail if there are more than this number of warnings" },
391428 .{ "--fix" , "Automatically fix some issues (only use with source control)" },
392429 .{ "--fix-passes" , std .fmt .comptimePrint ("Repeat fix this many times or until no more fixes are applied (Default {d})" , .{default_fix_passes }) },
393430 };
@@ -396,7 +433,7 @@ pub fn printHelp(printer: *rendering.Printer) void {
396433 inline for (0.. flags .len ) | i | width = @max (flags [i ][0 ].len , width );
397434
398435 printer .print (.out , "{s}Usage:{s} " , .{ printer .tty .ansiOrEmpty (&.{ .underline , .bold }), printer .tty .ansiOrEmpty (&.{.reset }) });
399- printer .print (.out , "zig build <lint step> -- [--include <path>...] [--exclude <path>...] [--filter <path>...] [--rule <name>...] [--fix]\n\n " , .{});
436+ printer .print (.out , "zig build <lint step> -- [--include <path>...] [--exclude <path>...] [--filter <path>...] [--rule <name>...] [--fix] [--quiet] [--max-warnings <u32>] \n\n " , .{});
400437 printer .print (.out , "{s}Options:{s}\n " , .{ printer .tty .ansiOrEmpty (&.{ .underline , .bold }), printer .tty .ansiOrEmpty (&.{.reset }) });
401438 for (flags ) | tuple | {
402439 printer .print (
@@ -410,6 +447,7 @@ pub fn printHelp(printer: *rendering.Printer) void {
410447 },
411448 );
412449 }
450+ printer .flush () catch @panic ("Failed to flush help docs" );
413451}
414452
415453fn notArgKey (arg : []const u8 ) bool {
@@ -449,6 +487,22 @@ test "allocParse with fix arg" {
449487 }, args );
450488}
451489
490+ test "allocParse with quiet arg" {
491+ var stdin_fbs = std .Io .Reader .fixed ("" );
492+
493+ const args = try allocParse (
494+ testing .cliArgs (&.{"--quiet" }),
495+ &.{},
496+ std .testing .allocator ,
497+ & stdin_fbs ,
498+ );
499+ defer args .deinit (std .testing .allocator );
500+
501+ try std .testing .expectEqualDeep (Args {
502+ .quiet = true ,
503+ }, args );
504+ }
505+
452506test "allocParse with verbose arg" {
453507 var stdin_fbs = std .Io .Reader .fixed ("" );
454508
@@ -838,7 +892,7 @@ test "allocParse with fix passes missing arg" {
838892}
839893
840894test "allocParse with invalid fix passes arg" {
841- inline for (&.{ "-1" , "256" , "a" }) | arg | {
895+ inline for (&.{ "-1" , "0" , " 256" , "a" }) | arg | {
842896 var stdin_fbs = std .Io .Reader .fixed ("" );
843897
844898 var stderr_sink : std.Io.Writer.Allocating = .init (std .testing .allocator );
@@ -1027,6 +1081,74 @@ test "allocParse with with missing rule config rule config path" {
10271081 try std .testing .expectEqualStrings ("--rule-config arg missing zon file path\n " , stderr_sink .written ());
10281082}
10291083
1084+ test "allocParse with min --max-warnings arg" {
1085+ var stdin_fbs = std .Io .Reader .fixed ("" );
1086+
1087+ const args = try allocParse (
1088+ testing .cliArgs (&.{ "--max-warnings" , "0" }),
1089+ &.{},
1090+ std .testing .allocator ,
1091+ & stdin_fbs ,
1092+ );
1093+ defer args .deinit (std .testing .allocator );
1094+
1095+ try std .testing .expectEqualDeep (Args {
1096+ .max_warnings = 0 ,
1097+ }, args );
1098+ }
1099+
1100+ test "allocParse with max --max-warnings arg" {
1101+ var stdin_fbs = std .Io .Reader .fixed ("" );
1102+
1103+ const args = try allocParse (
1104+ testing .cliArgs (&.{ "--max-warnings" , "4294967295" }),
1105+ &.{},
1106+ std .testing .allocator ,
1107+ & stdin_fbs ,
1108+ );
1109+ defer args .deinit (std .testing .allocator );
1110+
1111+ try std .testing .expectEqualDeep (Args {
1112+ .max_warnings = 4294967295 ,
1113+ }, args );
1114+ }
1115+
1116+ test "allocParse with fix --max-warnings arg" {
1117+ var stdin_fbs = std .Io .Reader .fixed ("" );
1118+
1119+ var stderr_sink : std.Io.Writer.Allocating = .init (std .testing .allocator );
1120+ defer stderr_sink .deinit ();
1121+ rendering .process_printer .stderr = & stderr_sink .writer ;
1122+
1123+ try std .testing .expectError (error .InvalidArgs , allocParse (
1124+ testing .cliArgs (&.{"--max-warnings" }),
1125+ &.{},
1126+ std .testing .allocator ,
1127+ & stdin_fbs ,
1128+ ));
1129+
1130+ try std .testing .expectEqualStrings ("--max-warnings missing value\n " , stderr_sink .written ());
1131+ }
1132+
1133+ test "allocParse with invalid --max-warnings arg" {
1134+ inline for (&.{ "-1" , "4294967296" , "a" }) | arg | {
1135+ var stdin_fbs = std .Io .Reader .fixed ("" );
1136+
1137+ var stderr_sink : std.Io.Writer.Allocating = .init (std .testing .allocator );
1138+ defer stderr_sink .deinit ();
1139+ rendering .process_printer .stderr = & stderr_sink .writer ;
1140+
1141+ try std .testing .expectError (error .InvalidArgs , allocParse (
1142+ testing .cliArgs (&.{ "--max-warnings" , arg }),
1143+ &.{},
1144+ std .testing .allocator ,
1145+ & stdin_fbs ,
1146+ ));
1147+
1148+ try std .testing .expectEqualStrings ("--max-warnings expects a u32\n " , stderr_sink .written ());
1149+ }
1150+ }
1151+
10301152const testing = struct {
10311153 inline fn cliArgs (args : []const [:0 ]const u8 ) [][:0 ]u8 {
10321154 assertTestOnly ();
0 commit comments