Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruff 0.9 #15238

Merged
merged 18 commits into from
Jan 9, 2025
Merged

Ruff 0.9 #15238

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1110060
Ruff 2025 style guide (#13906)
MichaReiser Jan 3, 2025
b47398b
Remove formatter incompatibility warning for ISC001 (#15123)
MichaReiser Jan 3, 2025
e81032f
Update Black deviations to reflect 2025 style changes (#15127)
MichaReiser Jan 7, 2025
292fe76
[ruff-0.9] Stabilise `slice-to-remove-prefix-or-suffix` (`FURB188`) (…
AlexWaygood Jan 7, 2025
4644c64
[ruff-0.9] Stabilize `decimal-from-float-literal` (`RUF032`) (#15333)
dylwil3 Jan 8, 2025
59b0b59
[`flake8-pytest-style`] Stabilize "Detect more `pytest.mark.parametri…
InSyncWithFoo Jan 8, 2025
afa0650
[`pycodestyle`] Stabilize: Exempt `pytest.importorskip` calls (`E402`…
MichaReiser Jan 8, 2025
23643c4
[ruff-0.9] Stabilise two `flake8-builtins` rules (#15322)
AlexWaygood Jan 8, 2025
efaf2e8
Remove unnecessary `PreviewMode::Enabled` in tests (#15344)
MichaReiser Jan 8, 2025
8c47548
[`flake8-pyi`] Stabilize autofix for `redundant-numeric-union` (`PYI0…
MichaReiser Jan 8, 2025
78955b0
[`ruff`] Stabilize: Detect `attrs` dataclasses (`RUF008`, `RUF009`) (…
MichaReiser Jan 8, 2025
7913a89
[`flake8-pyi`]: Stabilize: Provide more automated fixes for `duplicat…
MichaReiser Jan 8, 2025
1d66c9f
[`flake8-pyi`] Stabilize: include all python file types for `PYI006` …
MichaReiser Jan 8, 2025
cf4aacb
Update formatter preview documentation (#15349)
MichaReiser Jan 8, 2025
236f953
[`ruff`] Stabilize `post-init-default` (RUF033) (#15352)
MichaReiser Jan 8, 2025
bd45d12
[`pylint`]: Stabilize `boolean-chained-comparison` (`PLR1716`) (#15354)
MichaReiser Jan 8, 2025
0490b5d
[`ruff`] Stabilize `useless-if-else` (`RUF034`) (#15351)
MichaReiser Jan 8, 2025
97d95da
Add f-string formatting to the docs (#15367)
dhruvmanila Jan 9, 2025
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
17 changes: 14 additions & 3 deletions crates/ruff/src/commands/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ pub(crate) fn format(
}

/// Format the file at the given [`Path`].
#[tracing::instrument(level="debug", skip_all, fields(path = %path.display()))]
#[tracing::instrument(level = "debug", skip_all, fields(path = %path.display()))]
pub(crate) fn format_path(
path: &Path,
settings: &FormatterSettings,
Expand Down Expand Up @@ -788,8 +788,6 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
let mut incompatible_rules = FxHashSet::default();
for setting in resolver.settings() {
for rule in [
// The formatter might collapse implicit string concatenation on a single line.
Rule::SingleLineImplicitStringConcatenation,
// Flags missing trailing commas when all arguments are on its own line:
// ```python
// def args(
Expand Down Expand Up @@ -829,6 +827,19 @@ pub(super) fn warn_incompatible_formatter_settings(resolver: &Resolver) {
warn_user_once!("The `format.indent-style=\"tab\"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `\"space\"`.");
}

if !setting
.linter
.rules
.enabled(Rule::SingleLineImplicitStringConcatenation)
&& setting
.linter
.rules
.enabled(Rule::MultiLineImplicitStringConcatenation)
&& !setting.linter.flake8_implicit_str_concat.allow_multiline
{
warn_user_once!("The `lint.flake8-implicit-str-concat.allow-multiline = false` option is incompatible with the formatter unless `ISC001` is enabled. We recommend enabling `ISC001` or setting `allow-multiline=true`.");
}

// Validate all rules that rely on tab styles.
if setting.linter.rules.enabled(Rule::DocstringTabIndentation)
&& setting.formatter.indent_style.is_tab()
Expand Down
12 changes: 8 additions & 4 deletions crates/ruff/tests/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ indent-width = 2

[lint]
select = ["ALL"]
ignore = ["D203", "D212"]
ignore = ["D203", "D212", "ISC001"]

[lint.isort]
lines-after-imports = 3
Expand All @@ -891,6 +891,9 @@ inline-quotes = "single"
docstring-quotes = "single"
multiline-quotes = "single"

[lint.flake8-implicit-str-concat]
allow-multiline = false

[format]
skip-magic-trailing-comma = true
indent-style = "tab"
Expand All @@ -915,8 +918,9 @@ def say_hy(name: str):
1 file reformatted

----- stderr -----
warning: The following rules may cause conflicts when used with the formatter: `COM812`, `ISC001`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding them to the `ignore` configuration.
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
warning: The `format.indent-style="tab"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
warning: The `lint.flake8-implicit-str-concat.allow-multiline = false` option is incompatible with the formatter unless `ISC001` is enabled. We recommend enabling `ISC001` or setting `allow-multiline=true`.
warning: The `format.indent-style="tab"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
warning: The `flake8-quotes.inline-quotes="single"` option is incompatible with the formatter's `format.quote-style="double"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `"single"` or `"double"`.
warning: The `flake8-quotes.multiline-quotes="single"` option is incompatible with the formatter. We recommend disabling `Q001` when using the formatter, which enforces double quotes for multiline strings. Alternatively, set the `flake8-quotes.multiline-quotes` option to `"double"`.`
Expand Down Expand Up @@ -974,7 +978,7 @@ def say_hy(name: str):
print(f"Hy {name}")

----- stderr -----
warning: The following rules may cause conflicts when used with the formatter: `COM812`, `ISC001`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding them to the `ignore` configuration.
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
warning: The `format.indent-style="tab"` option is incompatible with `W191`, which lints against all uses of tabs. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
warning: The `format.indent-style="tab"` option is incompatible with `D206`, with requires space-based indentation. We recommend disabling these rules when using the formatter, which enforces a consistent indentation style. Alternatively, set the `format.indent-style` option to `"space"`.
warning: The `flake8-quotes.inline-quotes="single"` option is incompatible with the formatter's `format.quote-style="double"`. We recommend disabling `Q000` and `Q003` when using the formatter, which enforces a consistent quote style. Alternatively, set both options to either `"single"` or `"double"`.
Expand Down Expand Up @@ -1114,7 +1118,7 @@ def say_hy(name: str):
----- stderr -----
warning: `incorrect-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `incorrect-blank-line-before-class`.
warning: `multi-line-summary-first-line` (D212) and `multi-line-summary-second-line` (D213) are incompatible. Ignoring `multi-line-summary-second-line`.
warning: The following rules may cause conflicts when used with the formatter: `COM812`, `ISC001`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding them to the `ignore` configuration.
warning: The following rule may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling this rule, either by removing it from the `select` or `extend-select` configuration, or adding it to the `ignore` configuration.
");
Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,12 @@ def func2() -> str | str: # PYI016: Duplicate union member `str`

# Test case for mixed union type
field34: typing.Union[list[int], str] | typing.Union[bytes, list[int]] # Error

field35: "int | str | int" # Error



# Technically, this falls into the domain of the rule but it is an unlikely edge case,
# only works if you have from `__future__ import annotations` at the top of the file,
# and stringified annotations are discouraged in stub files.
field36: "int | str" | int # Ok
12 changes: 5 additions & 7 deletions crates/ruff_linter/src/checkers/ast/analyze/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,13 +879,11 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
checker.diagnostics.push(diagnostic);
}
}
if checker.settings.preview.is_enabled()
&& checker.any_enabled(&[
Rule::PytestParametrizeNamesWrongType,
Rule::PytestParametrizeValuesWrongType,
Rule::PytestDuplicateParametrizeTestCases,
])
{
if checker.any_enabled(&[
Rule::PytestParametrizeNamesWrongType,
Rule::PytestParametrizeValuesWrongType,
Rule::PytestDuplicateParametrizeTestCases,
]) {
flake8_pytest_style::rules::parametrize(checker, call);
}
if checker.enabled(Rule::PytestUnittestAssertion) {
Expand Down
60 changes: 22 additions & 38 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,21 +309,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
body,
);
}
// In preview mode, calls are analyzed. To avoid duplicate diagnostics,
// skip analyzing the decorators.
if !checker.settings.preview.is_enabled()
&& checker.any_enabled(&[
Rule::PytestParametrizeNamesWrongType,
Rule::PytestParametrizeValuesWrongType,
Rule::PytestDuplicateParametrizeTestCases,
])
{
for decorator in decorator_list {
if let Some(call) = decorator.expression.as_call_expr() {
flake8_pytest_style::rules::parametrize(checker, call);
}
}
}

if checker.any_enabled(&[
Rule::PytestIncorrectMarkParenthesesStyle,
Rule::PytestUseFixturesWithoutParameters,
Expand Down Expand Up @@ -1213,40 +1199,38 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
}
}
if checker.any_enabled(&[Rule::BadVersionInfoComparison, Rule::BadVersionInfoOrder]) {
if checker.source_type.is_stub() || checker.settings.preview.is_enabled() {
fn bad_version_info_comparison(
checker: &mut Checker,
test: &Expr,
has_else_clause: bool,
) {
if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = test {
for value in values {
flake8_pyi::rules::bad_version_info_comparison(
checker,
value,
has_else_clause,
);
}
} else {
fn bad_version_info_comparison(
checker: &mut Checker,
test: &Expr,
has_else_clause: bool,
) {
if let Expr::BoolOp(ast::ExprBoolOp { values, .. }) = test {
for value in values {
flake8_pyi::rules::bad_version_info_comparison(
checker,
test,
value,
has_else_clause,
);
}
} else {
flake8_pyi::rules::bad_version_info_comparison(
checker,
test,
has_else_clause,
);
}
}

let has_else_clause =
elif_else_clauses.iter().any(|clause| clause.test.is_none());
let has_else_clause = elif_else_clauses.iter().any(|clause| clause.test.is_none());

bad_version_info_comparison(checker, test.as_ref(), has_else_clause);
for clause in elif_else_clauses {
if let Some(test) = clause.test.as_ref() {
bad_version_info_comparison(checker, test, has_else_clause);
}
bad_version_info_comparison(checker, test.as_ref(), has_else_clause);
for clause in elif_else_clauses {
if let Some(test) = clause.test.as_ref() {
bad_version_info_comparison(checker, test, has_else_clause);
}
}
}

if checker.enabled(Rule::IfKeyInDictDel) {
ruff::rules::if_key_in_dict_del(checker, if_);
}
Expand Down
4 changes: 1 addition & 3 deletions crates/ruff_linter/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,6 @@ impl<'a> Checker<'a> {

impl<'a> Visitor<'a> for Checker<'a> {
fn visit_stmt(&mut self, stmt: &'a Stmt) {
let in_preview = self.settings.preview.is_enabled();

// Step 0: Pre-processing
self.semantic.push_node(stmt);

Expand Down Expand Up @@ -514,7 +512,7 @@ impl<'a> Visitor<'a> for Checker<'a> {
|| imports::is_matplotlib_activation(stmt, self.semantic())
|| imports::is_sys_path_modification(stmt, self.semantic())
|| imports::is_os_environ_modification(stmt, self.semantic())
|| (in_preview && imports::is_pytest_importorskip(stmt, self.semantic())))
|| imports::is_pytest_importorskip(stmt, self.semantic()))
{
self.semantic.flags |= SemanticModelFlags::IMPORT_BOUNDARY;
}
Expand Down
14 changes: 7 additions & 7 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
(Pylint, "R1730") => (RuleGroup::Stable, rules::pylint::rules::IfStmtMinMax),
(Pylint, "R1716") => (RuleGroup::Preview, rules::pylint::rules::BooleanChainedComparison),
(Pylint, "R1716") => (RuleGroup::Stable, rules::pylint::rules::BooleanChainedComparison),
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
(Pylint, "R1736") => (RuleGroup::Stable, rules::pylint::rules::UnnecessaryListIndexLookup),
(Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison),
Expand Down Expand Up @@ -317,8 +317,8 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Flake8Builtins, "002") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinArgumentShadowing),
(Flake8Builtins, "003") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinAttributeShadowing),
(Flake8Builtins, "004") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinImportShadowing),
(Flake8Builtins, "005") => (RuleGroup::Preview, rules::flake8_builtins::rules::StdlibModuleShadowing),
(Flake8Builtins, "006") => (RuleGroup::Preview, rules::flake8_builtins::rules::BuiltinLambdaArgumentShadowing),
(Flake8Builtins, "005") => (RuleGroup::Stable, rules::flake8_builtins::rules::StdlibModuleShadowing),
(Flake8Builtins, "006") => (RuleGroup::Stable, rules::flake8_builtins::rules::BuiltinLambdaArgumentShadowing),

// flake8-bugbear
(Flake8Bugbear, "002") => (RuleGroup::Stable, rules::flake8_bugbear::rules::UnaryPrefixIncrementDecrement),
Expand Down Expand Up @@ -980,9 +980,9 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "029") => (RuleGroup::Preview, rules::ruff::rules::UnusedAsync),
(Ruff, "030") => (RuleGroup::Stable, rules::ruff::rules::AssertWithPrintMessage),
(Ruff, "031") => (RuleGroup::Preview, rules::ruff::rules::IncorrectlyParenthesizedTupleInSubscript),
(Ruff, "032") => (RuleGroup::Preview, rules::ruff::rules::DecimalFromFloatLiteral),
(Ruff, "033") => (RuleGroup::Preview, rules::ruff::rules::PostInitDefault),
(Ruff, "034") => (RuleGroup::Preview, rules::ruff::rules::UselessIfElse),
(Ruff, "032") => (RuleGroup::Stable, rules::ruff::rules::DecimalFromFloatLiteral),
(Ruff, "033") => (RuleGroup::Stable, rules::ruff::rules::PostInitDefault),
(Ruff, "034") => (RuleGroup::Stable, rules::ruff::rules::UselessIfElse),
(Ruff, "035") => (RuleGroup::Preview, rules::ruff::rules::UnsafeMarkupUse),
(Ruff, "036") => (RuleGroup::Preview, rules::ruff::rules::NoneNotAtEndOfUnion),
(Ruff, "037") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryEmptyIterableWithinDequeCall),
Expand Down Expand Up @@ -1108,7 +1108,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Refurb, "180") => (RuleGroup::Preview, rules::refurb::rules::MetaClassABCMeta),
(Refurb, "181") => (RuleGroup::Stable, rules::refurb::rules::HashlibDigestHex),
(Refurb, "187") => (RuleGroup::Stable, rules::refurb::rules::ListReverseCopy),
(Refurb, "188") => (RuleGroup::Preview, rules::refurb::rules::SliceToRemovePrefixOrSuffix),
(Refurb, "188") => (RuleGroup::Stable, rules::refurb::rules::SliceToRemovePrefixOrSuffix),
(Refurb, "189") => (RuleGroup::Preview, rules::refurb::rules::SubclassBuiltin),
(Refurb, "192") => (RuleGroup::Preview, rules::refurb::rules::SortedMinMax),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ use crate::rules::flake8_builtins::helpers::shadows_builtin;
///
/// ## Why is this bad?
/// Reusing a builtin name for the name of a lambda argument increases the
/// difficulty of reading and maintaining the code, and can cause
/// non-obvious errors, as readers may mistake the variable for the
/// builtin and vice versa.
/// difficulty of reading and maintaining the code and can cause
/// non-obvious errors. Readers may mistake the variable for the
/// builtin, and vice versa.
///
/// Builtins can be marked as exceptions to this rule via the
/// [`lint.flake8-builtins.builtins-ignorelist`] configuration option.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,6 @@ use crate::Locator;
/// ```python
/// z = "The quick brown fox."
/// ```
///
/// # Formatter compatibility
/// Use of this rule alongside the [formatter] must be handled with care.
/// Currently, the [formatter] can introduce new single-line implicitly
/// concatenated strings, therefore we suggest rerunning the linter and
/// [formatter] in the following order:
/// 1. Run the linter with this rule (`ISC001`) disabled
/// 2. Run the [formatter]
/// 3. Rerun the linter with this rule (`ISC001`) enabled
///
/// This is one of very few cases where the [formatter] can produce code that
/// contains lint violations. It is a known issue that should be resolved by the
/// new 2025 style guide.
///
/// [formatter]:https://docs.astral.sh/ruff/formatter/
#[derive(ViolationMetadata)]
pub(crate) struct SingleLineImplicitStringConcatenation;

Expand Down Expand Up @@ -96,19 +81,13 @@ impl Violation for SingleLineImplicitStringConcatenation {
/// ## Options
/// - `lint.flake8-implicit-str-concat.allow-multiline`
///
/// # Formatter compatibility
/// Use of this rule alongside the [formatter] must be handled with care.
/// Currently, the [formatter] can introduce new multi-line implicitly
/// concatenated strings, therefore we suggest rerunning the linter and
/// [formatter] in the following order:
///
/// 1. Run the linter with this rule (`ISC002`) disabled
/// 2. Run the [formatter]
/// 3. Rerun the linter with this rule (`ISC002`) enabled
/// ## Formatter compatibility
/// Using this rule with `allow-multiline = false` can be incompatible with the
/// formatter because the [formatter] can introduce new multi-line implicitly
/// concatenated strings. We recommend to either:
///
/// This is one of very few cases where the [formatter] can produce code that
/// contains lint violations. It is a known issue that should be resolved by the
/// new 2025 style guide.
/// * Enable `ISC001` to disallow all implicit concatenated strings
/// * Setting `allow-multiline = true`
///
/// [PEP 8]: https://peps.python.org/pep-0008/#maximum-line-length
/// [formatter]:https://docs.astral.sh/ruff/formatter/
Expand Down
2 changes: 0 additions & 2 deletions crates/ruff_linter/src/rules/flake8_pyi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,6 @@ mod tests {
}

#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",
Expand Down
Loading
Loading