From 291ee9b5b3230f1a7e1c0e24e094e63b569a7043 Mon Sep 17 00:00:00 2001 From: feiyang Date: Sat, 24 Jan 2026 10:18:54 +0000 Subject: [PATCH 1/5] feature ignore/warn/select all rules --- crates/ty/src/args.rs | 6 +++--- crates/ty_project/src/metadata/options.rs | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/crates/ty/src/args.rs b/crates/ty/src/args.rs index 1e556461a2609c..3686004af17e86 100644 --- a/crates/ty/src/args.rs +++ b/crates/ty/src/args.rs @@ -315,7 +315,7 @@ impl clap::Args for RulesArg { clap::Arg::new("error") .long("error") .action(ArgAction::Append) - .help("Treat the given rule as having severity 'error'. Can be specified multiple times.") + .help("Treat the given rule as having severity 'error'. Can be specified multiple times. Use 'all' to apply to all rules.") .value_name("RULE") .help_heading(HELP_HEADING), ) @@ -323,7 +323,7 @@ impl clap::Args for RulesArg { clap::Arg::new("warn") .long("warn") .action(ArgAction::Append) - .help("Treat the given rule as having severity 'warn'. Can be specified multiple times.") + .help("Treat the given rule as having severity 'warn'. Can be specified multiple times. Use 'all' to apply to all rules.") .value_name("RULE") .help_heading(HELP_HEADING), ) @@ -331,7 +331,7 @@ impl clap::Args for RulesArg { clap::Arg::new("ignore") .long("ignore") .action(ArgAction::Append) - .help("Disables the rule. Can be specified multiple times.") + .help("Disables the rule. Can be specified multiple times. Use 'all' to apply to all rules.") .value_name("RULE") .help_heading(HELP_HEADING), ) diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs index 1eea1239acc2c5..6f1a510a27123a 100644 --- a/crates/ty_project/src/metadata/options.rs +++ b/crates/ty_project/src/metadata/options.rs @@ -882,13 +882,26 @@ impl Rules { for (rule_name, level) in &self.inner { let source = rule_name.source(); + let lint_source = match source { + ValueSource::File(_) => LintSource::File, + ValueSource::Cli => LintSource::Cli, + ValueSource::Editor => LintSource::Editor, + }; + + // Handle "all" as a special case - apply the level to all rules + if rule_name.eq_ignore_ascii_case("all") { + for lint in registry.lints() { + if let Ok(severity) = Severity::try_from(**level) { + selection.enable(*lint, severity, lint_source); + } else { + selection.disable(*lint); + } + } + continue; + } + match registry.get(rule_name) { Ok(lint) => { - let lint_source = match source { - ValueSource::File(_) => LintSource::File, - ValueSource::Cli => LintSource::Cli, - ValueSource::Editor => LintSource::Editor, - }; if let Ok(severity) = Severity::try_from(**level) { selection.enable(lint, severity, lint_source); } else { From cf0cde897b809ec93d0e500acd7e3508835b2682 Mon Sep 17 00:00:00 2001 From: feiyang Date: Sat, 24 Jan 2026 10:32:10 +0000 Subject: [PATCH 2/5] add tests --- crates/ty/tests/cli/rule_selection.rs | 233 ++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) diff --git a/crates/ty/tests/cli/rule_selection.rs b/crates/ty/tests/cli/rule_selection.rs index 56b944e05b3f16..65bcd1cc3d9a00 100644 --- a/crates/ty/tests/cli/rule_selection.rs +++ b/crates/ty/tests/cli/rule_selection.rs @@ -888,3 +888,236 @@ fn overrides_unknown_rules() -> anyhow::Result<()> { Ok(()) } + +/// The "all" keyword can be used to set all rules to a specific severity +#[test] +fn cli_all_rules_ignore() -> anyhow::Result<()> { + let case = CliTest::with_file( + "test.py", + r#" + import does_not_exit + + y = 4 / 0 + + prin(y) # unresolved-reference + "#, + )?; + + // Using --ignore all should disable all rules + assert_cmd_snapshot!( + case + .command() + .arg("--ignore") + .arg("all"), + @" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); + + Ok(()) +} + +/// The "all" keyword works with --warn to set all rules to warn severity +#[test] +fn cli_all_rules_warn() -> anyhow::Result<()> { + let case = CliTest::with_file( + "test.py", + r#" + prin(x) # unresolved-reference + "#, + )?; + + // Using --warn all should make all rules warnings (not errors) + assert_cmd_snapshot!( + case + .command() + .arg("--warn") + .arg("all"), + @" + success: true + exit_code: 0 + ----- stdout ----- + warning[unresolved-reference]: Name `prin` used when not defined + --> test.py:2:1 + | + 2 | prin(x) # unresolved-reference + | ^^^^ + | + info: rule `unresolved-reference` was selected on the command line + + warning[unresolved-reference]: Name `x` used when not defined + --> test.py:2:6 + | + 2 | prin(x) # unresolved-reference + | ^ + | + info: rule `unresolved-reference` was selected on the command line + + Found 2 diagnostics + + ----- stderr ----- + " + ); + + Ok(()) +} + +/// The "all" keyword can be overridden by subsequent specific rule settings +#[test] +fn cli_all_rules_with_override() -> anyhow::Result<()> { + let case = CliTest::with_file( + "test.py", + r#" + import does_not_exit + + y = 4 / 0 + + prin(y) # unresolved-reference + "#, + )?; + + // Using --ignore all followed by --error for a specific rule should + // disable all rules except the one specified + assert_cmd_snapshot!( + case + .command() + .arg("--ignore") + .arg("all") + .arg("--error") + .arg("unresolved-reference"), + @" + success: false + exit_code: 1 + ----- stdout ----- + error[unresolved-reference]: Name `prin` used when not defined + --> test.py:6:1 + | + 4 | y = 4 / 0 + 5 | + 6 | prin(y) # unresolved-reference + | ^^^^ + | + info: rule `unresolved-reference` was selected on the command line + + Found 1 diagnostic + + ----- stderr ----- + " + ); + + Ok(()) +} + +/// The "all" keyword is case-insensitive +#[test] +fn cli_all_rules_case_insensitive() -> anyhow::Result<()> { + let case = CliTest::with_file( + "test.py", + r#" + prin(x) # unresolved-reference + "#, + )?; + + // Using --ignore ALL (uppercase) should work the same as --ignore all + assert_cmd_snapshot!( + case + .command() + .arg("--ignore") + .arg("ALL"), + @" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); + + Ok(()) +} + +/// A specific rule can be set first and then overridden by "all" +#[test] +fn cli_specific_then_all() -> anyhow::Result<()> { + let case = CliTest::with_file( + "test.py", + r#" + prin(x) # unresolved-reference + "#, + )?; + + // Using --error for a specific rule followed by --ignore all should + // ignore all rules (including the previously set one) + assert_cmd_snapshot!( + case + .command() + .arg("--error") + .arg("unresolved-reference") + .arg("--ignore") + .arg("all"), + @" + success: true + exit_code: 0 + ----- stdout ----- + All checks passed! + + ----- stderr ----- + " + ); + + Ok(()) +} + +/// The "all" keyword works in configuration files +#[test] +fn configuration_all_rules() -> anyhow::Result<()> { + let case = CliTest::with_files([ + ( + "pyproject.toml", + r#" + [tool.ty.rules] + all = "ignore" + unresolved-reference = "error" + "#, + ), + ( + "test.py", + r#" + import does_not_exit + + y = 4 / 0 + + prin(y) # unresolved-reference + "#, + ), + ])?; + + // The "all" rule should be processed first, ignoring all rules, + // then unresolved-reference should be enabled as error + assert_cmd_snapshot!(case.command(), @" + success: false + exit_code: 1 + ----- stdout ----- + error[unresolved-reference]: Name `prin` used when not defined + --> test.py:6:1 + | + 4 | y = 4 / 0 + 5 | + 6 | prin(y) # unresolved-reference + | ^^^^ + | + info: rule `unresolved-reference` was selected in the configuration file + + Found 1 diagnostic + + ----- stderr ----- + "); + + Ok(()) +} From 46420b690fe1b005d5fabb2f1262bbac4e5096d7 Mon Sep 17 00:00:00 2001 From: feiyang Date: Sat, 24 Jan 2026 10:37:17 +0000 Subject: [PATCH 3/5] add comment --- crates/ty_project/src/metadata/options.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs index 6f1a510a27123a..d44364105d731f 100644 --- a/crates/ty_project/src/metadata/options.rs +++ b/crates/ty_project/src/metadata/options.rs @@ -894,6 +894,7 @@ impl Rules { if let Ok(severity) = Severity::try_from(**level) { selection.enable(*lint, severity, lint_source); } else { + // ignore selection.disable(*lint); } } From b9387c3b5d9a35bf6cc41c92038d20d5cfe4f62a Mon Sep 17 00:00:00 2001 From: feiyang Date: Tue, 27 Jan 2026 08:30:20 +0000 Subject: [PATCH 4/5] auto update docs --- crates/ty/docs/cli.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ty/docs/cli.md b/crates/ty/docs/cli.md index 5eedd09624c2d6..755af1b667c3fb 100644 --- a/crates/ty/docs/cli.md +++ b/crates/ty/docs/cli.md @@ -50,7 +50,7 @@ overriding a specific configuration option.

over all configuration files.

--config-file path

The path to a ty.toml file to use for configuration.

While ty configuration can be included in a pyproject.toml file, it is not allowed in this context.

-

May also be set with the TY_CONFIG_FILE environment variable.

--error rule

Treat the given rule as having severity 'error'. Can be specified multiple times.

+

May also be set with the TY_CONFIG_FILE environment variable.

--error rule

Treat the given rule as having severity 'error'. Can be specified multiple times. Use 'all' to apply to all rules.

--error-on-warning

Use exit code 1 if there are any warning-level diagnostics

--exclude exclude

Glob patterns for files to exclude from type checking.

Uses gitignore-style syntax to exclude files and directories from type checking. Supports patterns like tests/, *.tmp, **/__pycache__/**.

@@ -59,7 +59,7 @@ over all configuration files.

This is an advanced option that should usually only be used for first-party or third-party modules that are not installed into your Python environment in a conventional way. Use --python to point ty to your Python environment if it is in an unusual location.

--force-exclude

Enforce exclusions, even for paths passed to ty directly on the command-line. Use --no-force-exclude to disable

--help, -h

Print help (see a summary with '-h')

-
--ignore rule

Disables the rule. Can be specified multiple times.

+
--ignore rule

Disables the rule. Can be specified multiple times. Use 'all' to apply to all rules.

--no-progress

Hide all progress outputs.

For example, spinners or progress bars.

--output-format output-format

The format to use for printing diagnostic messages

@@ -95,7 +95,7 @@ over all configuration files.

--respect-ignore-files

Respect file exclusions via .gitignore and other standard ignore files. Use --no-respect-ignore-files to disable

--typeshed, --custom-typeshed-dir path

Custom directory to use for stdlib typeshed stubs

--verbose, -v

Use verbose output (or -vv and -vvv for more verbose output)

-
--warn rule

Treat the given rule as having severity 'warn'. Can be specified multiple times.

+
--warn rule

Treat the given rule as having severity 'warn'. Can be specified multiple times. Use 'all' to apply to all rules.

--watch, -W

Watch files for changes and recheck files related to the changed files

From e9218d1e327d937c20d7c81ff64bf380193220dc Mon Sep 17 00:00:00 2001 From: feiyang Date: Wed, 28 Jan 2026 08:12:18 +0000 Subject: [PATCH 5/5] share set lint level closure --- crates/ty_project/src/metadata/options.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/ty_project/src/metadata/options.rs b/crates/ty_project/src/metadata/options.rs index d44364105d731f..5428014b1a2b87 100644 --- a/crates/ty_project/src/metadata/options.rs +++ b/crates/ty_project/src/metadata/options.rs @@ -888,26 +888,25 @@ impl Rules { ValueSource::Editor => LintSource::Editor, }; + let mut set_lint_level = |lint| { + if let Ok(severity) = Severity::try_from(**level) { + selection.enable(lint, severity, lint_source); + } else { + selection.disable(lint); + } + }; + // Handle "all" as a special case - apply the level to all rules if rule_name.eq_ignore_ascii_case("all") { for lint in registry.lints() { - if let Ok(severity) = Severity::try_from(**level) { - selection.enable(*lint, severity, lint_source); - } else { - // ignore - selection.disable(*lint); - } + set_lint_level(*lint); } continue; } match registry.get(rule_name) { Ok(lint) => { - if let Ok(severity) = Severity::try_from(**level) { - selection.enable(lint, severity, lint_source); - } else { - selection.disable(lint); - } + set_lint_level(lint); } Err(error) => { // `system_path_to_file` can return `Err` if the file was deleted since the configuration