Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/ty/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,23 +315,23 @@ 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),
)
.arg(
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),
)
.arg(
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),
)
Expand Down
233 changes: 233 additions & 0 deletions crates/ty/tests/cli/rule_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
24 changes: 19 additions & 5 deletions crates/ty_project/src/metadata/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,13 +882,27 @@ 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 {
// ignore
selection.disable(*lint);
}
Comment thread
carljm marked this conversation as resolved.
Outdated
}
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 {
Expand Down
Loading