diff --git a/crates/ruff_cli/src/commands/format.rs b/crates/ruff_cli/src/commands/format.rs index 2e88d21b737f9d..9099d8868202ea 100644 --- a/crates/ruff_cli/src/commands/format.rs +++ b/crates/ruff_cli/src/commands/format.rs @@ -18,11 +18,11 @@ use ruff_diagnostics::SourceMap; use ruff_linter::fs; use ruff_linter::logging::LogLevel; use ruff_linter::registry::Rule; -use ruff_linter::settings::rule_table::RuleTable; +use ruff_linter::rules::flake8_quotes::settings::Quote; use ruff_linter::source_kind::{SourceError, SourceKind}; use ruff_linter::warn_user_once; use ruff_python_ast::{PySourceType, SourceType}; -use ruff_python_formatter::{format_module_source, FormatModuleError}; +use ruff_python_formatter::{format_module_source, FormatModuleError, QuoteStyle}; use ruff_text_size::{TextLen, TextRange, TextSize}; use ruff_workspace::resolver::{ match_exclusion, python_files_in_path, PyprojectConfig, ResolvedFile, Resolver, @@ -700,53 +700,105 @@ pub(super) fn warn_incompatible_formatter_settings( { let mut incompatible_rules = Vec::new(); - for incompatible_rule in RuleTable::from_iter([ - Rule::LineTooLong, - Rule::TabIndentation, - Rule::IndentationWithInvalidMultiple, - Rule::IndentationWithInvalidMultipleComment, - Rule::OverIndented, - Rule::IndentWithSpaces, + 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( + // aaaaaaaa, bbbbbbbbb, cccccccccc, ddddddddd, eeeeeeee, ffffff, gggggggggggg, hhhh + // ): + // pass + // ``` Rule::MissingTrailingComma, - Rule::ProhibitedTrailingComma, - Rule::BadQuotesInlineString, - Rule::BadQuotesMultilineString, - Rule::BadQuotesDocstring, - Rule::AvoidableEscapedQuote, - ]) - .iter_enabled() - { - if setting.linter.rules.enabled(incompatible_rule) { - incompatible_rules.push(format!("'{}'", incompatible_rule.noqa_code())); + ] { + if setting.linter.rules.enabled(rule) { + incompatible_rules.push(rule); + } + } + + // Rules asserting for space indentation + if setting.formatter.indent_style.is_tab() { + for rule in [Rule::TabIndentation, Rule::IndentWithSpaces] { + if setting.linter.rules.enabled(rule) { + incompatible_rules.push(rule); + } + } + } + + // Rules asserting for indent-width=4 + if setting.formatter.indent_width.value() != 4 { + for rule in [ + Rule::IndentationWithInvalidMultiple, + Rule::IndentationWithInvalidMultipleComment, + ] { + if setting.linter.rules.enabled(rule) { + incompatible_rules.push(rule); + } } } if !incompatible_rules.is_empty() { - incompatible_rules.sort(); - warn!("The following rules may cause conflicts when used with the formatter: {}. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration.", incompatible_rules.join(", ")); + let mut rule_names: Vec<_> = incompatible_rules + .into_iter() + .map(|rule| format!("`{}`", rule.noqa_code())) + .collect(); + rule_names.sort(); + warn!("The following rules may cause conflicts when used with the formatter: {}. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration.", rule_names.join(", ")); + } + + // Rules with different quote styles. + if setting + .linter + .rules + .any_enabled(&[Rule::BadQuotesInlineString, Rule::AvoidableEscapedQuote]) + { + match ( + setting.linter.flake8_quotes.inline_quotes, + setting.formatter.quote_style, + ) { + (Quote::Double, QuoteStyle::Single) => { + warn!("The `flake8-quotes.inline-quotes=\"double\"` option is incompatible with the formatter's `format.quote-style=\"single\"`. 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\"`."); + } + (Quote::Single, QuoteStyle::Double) => { + warn!("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\"`."); + } + _ => {} + } + } + + if setting.linter.rules.enabled(Rule::BadQuotesMultilineString) + && setting.linter.flake8_quotes.multiline_quotes == Quote::Single + { + warn!("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\"`.`"); + } + + if setting.linter.rules.enabled(Rule::BadQuotesDocstring) + && setting.linter.flake8_quotes.docstring_quotes == Quote::Single + { + warn!("The `flake8-quotes.multiline-quotes=\"single\"` option is incompatible with the formatter. We recommend disabling `Q002` when using the formatter, which enforces double quotes for docstrings. Alternatively, set the `flake8-quotes.docstring-quotes` option to `\"double\"`.`"); } if setting.linter.rules.enabled(Rule::UnsortedImports) { // The formatter removes empty lines if the value is larger than 2 but always inserts a empty line after imports. // Two empty lines are okay because `isort` only uses this setting for top-level imports (not in nested blocks). if !matches!(setting.linter.isort.lines_after_imports, 1 | 2 | -1) { - warn!("The isort option 'isort.lines-after-imports' with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default)."); + warn!("The isort option `isort.lines-after-imports` with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default)."); } // Values larger than two get reduced to one line by the formatter if the import is in a nested block. if setting.linter.isort.lines_between_types > 1 { - warn!("The isort option 'isort.lines-between-types' with a value larger than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default)."); + warn!("The isort option `isort.lines-between-types` with a value greater than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default)."); } // isort inserts a trailing comma which the formatter preserves, but only if `skip-magic-trailing-comma` isn't false. if setting.formatter.magic_trailing_comma.is_ignore() { if setting.linter.isort.force_wrap_aliases { - warn!("The isort option 'isort.force-wrap-aliases' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.force-wrap-aliases=false' or 'format.skip-magic-trailing-comma=false'."); + warn!("The isort option `isort.force-wrap-aliases` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.force-wrap-aliases=false` or `format.skip-magic-trailing-comma=false`."); } if setting.linter.isort.split_on_trailing_comma { - warn!("The isort option 'isort.split-on-trailing-comma' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.split-on-trailing-comma=false' or 'format.skip-magic-trailing-comma=false'."); + warn!("The isort option `isort.split-on-trailing-comma` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.split-on-trailing-comma=false` or `format.skip-magic-trailing-comma=false`."); } } } diff --git a/crates/ruff_cli/tests/format.rs b/crates/ruff_cli/tests/format.rs index bb1f547a24f586..56efebcc64b4a5 100644 --- a/crates/ruff_cli/tests/format.rs +++ b/crates/ruff_cli/tests/format.rs @@ -230,7 +230,9 @@ fn format_option_inheritance() -> Result<()> { &ruff_toml, r#" extend = "base.toml" -extend-select = ["Q000"] + +[lint] +extend-select = ["COM812"] [format] quote-style = "single" @@ -273,7 +275,7 @@ if condition: print('Should change quotes') ----- stderr ----- - warning: The following rules may cause conflicts when used with the formatter: 'Q000'. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. + warning: The following rules may cause conflicts when used with the formatter: `COM812`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. "###); Ok(()) } @@ -359,18 +361,27 @@ fn conflicting_options() -> Result<()> { fs::write( &ruff_toml, r#" +indent-width = 2 + +[lint] select = ["ALL"] ignore = ["D203", "D212"] -[isort] +[lint.isort] lines-after-imports = 3 lines-between-types = 2 force-wrap-aliases = true combine-as-imports = true split-on-trailing-comma = true +[lint.flake8-quotes] +inline-quotes = "single" +docstring-quotes = "single" +multiline-quotes = "single" + [format] skip-magic-trailing-comma = true +indent-style = "tab" "#, )?; @@ -392,11 +403,14 @@ def say_hy(name: str): 1 file reformatted ----- stderr ----- - warning: The following rules may cause conflicts when used with the formatter: 'COM812', 'COM819', 'D206', 'E501', 'ISC001', 'Q000', 'Q001', 'Q002', 'Q003', 'W191'. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. - warning: The isort option 'isort.lines-after-imports' with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default). - warning: The isort option 'isort.lines-between-types' with a value larger than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default). - warning: The isort option 'isort.force-wrap-aliases' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.force-wrap-aliases=false' or 'format.skip-magic-trailing-comma=false'. - warning: The isort option 'isort.split-on-trailing-comma' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.split-on-trailing-comma=false' or 'format.skip-magic-trailing-comma=false'. + warning: The following rules may cause conflicts when used with the formatter: `COM812`, `D206`, `ISC001`, `W191`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. + 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"`.` + warning: The `flake8-quotes.multiline-quotes="single"` option is incompatible with the formatter. We recommend disabling `Q002` when using the formatter, which enforces double quotes for docstrings. Alternatively, set the `flake8-quotes.docstring-quotes` option to `"double"`.` + warning: The isort option `isort.lines-after-imports` with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default). + warning: The isort option `isort.lines-between-types` with a value greater than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default). + warning: The isort option `isort.force-wrap-aliases` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.force-wrap-aliases=false` or `format.skip-magic-trailing-comma=false`. + warning: The isort option `isort.split-on-trailing-comma` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.split-on-trailing-comma=false` or `format.skip-magic-trailing-comma=false`. "###); Ok(()) } @@ -408,18 +422,27 @@ fn conflicting_options_stdin() -> Result<()> { fs::write( &ruff_toml, r#" +indent-width = 2 + +[lint] select = ["ALL"] ignore = ["D203", "D212"] -[isort] +[lint.isort] lines-after-imports = 3 lines-between-types = 2 force-wrap-aliases = true combine-as-imports = true split-on-trailing-comma = true +[lint.flake8-quotes] +inline-quotes = "single" +docstring-quotes = "single" +multiline-quotes = "single" + [format] skip-magic-trailing-comma = true +indent-style = "tab" "#, )?; @@ -434,14 +457,17 @@ def say_hy(name: str): exit_code: 0 ----- stdout ----- def say_hy(name: str): - print(f"Hy {name}") + print(f"Hy {name}") ----- stderr ----- - warning: The following rules may cause conflicts when used with the formatter: 'COM812', 'COM819', 'D206', 'E501', 'ISC001', 'Q000', 'Q001', 'Q002', 'Q003', 'W191'. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. - warning: The isort option 'isort.lines-after-imports' with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default). - warning: The isort option 'isort.lines-between-types' with a value larger than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default). - warning: The isort option 'isort.force-wrap-aliases' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.force-wrap-aliases=false' or 'format.skip-magic-trailing-comma=false'. - warning: The isort option 'isort.split-on-trailing-comma' is incompatible with the formatter 'format.skip-magic-trailing-comma=true' option. To avoid unexpected behavior, we recommend either setting 'isort.split-on-trailing-comma=false' or 'format.skip-magic-trailing-comma=false'. + warning: The following rules may cause conflicts when used with the formatter: `COM812`, `D206`, `ISC001`, `W191`. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. + 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"`.` + warning: The `flake8-quotes.multiline-quotes="single"` option is incompatible with the formatter. We recommend disabling `Q002` when using the formatter, which enforces double quotes for docstrings. Alternatively, set the `flake8-quotes.docstring-quotes` option to `"double"`.` + warning: The isort option `isort.lines-after-imports` with a value other than `-1`, `1` or `2` is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `2`, `1`, or `-1` (default). + warning: The isort option `isort.lines-between-types` with a value greater than 1 is incompatible with the formatter. To avoid unexpected behavior, we recommend setting the option to one of: `1` or `0` (default). + warning: The isort option `isort.force-wrap-aliases` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.force-wrap-aliases=false` or `format.skip-magic-trailing-comma=false`. + warning: The isort option `isort.split-on-trailing-comma` is incompatible with the formatter `format.skip-magic-trailing-comma=true` option. To avoid unexpected behavior, we recommend either setting `isort.split-on-trailing-comma=false` or `format.skip-magic-trailing-comma=false`. "###); Ok(()) } @@ -453,18 +479,60 @@ fn valid_linter_options() -> Result<()> { fs::write( &ruff_toml, r#" +[lint] select = ["ALL"] -ignore = ["D203", "D212"] +ignore = ["D203", "D212", "COM812", "ISC001"] -[isort] +[lint.isort] lines-after-imports = 2 lines-between-types = 1 force-wrap-aliases = true combine-as-imports = true split-on-trailing-comma = true +[lint.flake8-quotes] +inline-quotes = "single" +docstring-quotes = "double" +multiline-quotes = "double" + [format] skip-magic-trailing-comma = false +quote-style = "single" +"#, + )?; + + let test_path = tempdir.path().join("test.py"); + fs::write( + &test_path, + r#" +def say_hy(name: str): + print(f"Hy {name}")"#, + )?; + + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) + .args(["format", "--no-cache", "--config"]) + .arg(&ruff_toml) + .arg(test_path), @r###" + success: true + exit_code: 0 + ----- stdout ----- + 1 file reformatted + + ----- stderr ----- + "###); + Ok(()) +} + +#[test] +fn all_rules_default_options() -> Result<()> { + let tempdir = TempDir::new()?; + let ruff_toml = tempdir.path().join("ruff.toml"); + + fs::write( + &ruff_toml, + r#" +[lint] +select = ["ALL"] "#, )?; @@ -486,10 +554,13 @@ def say_hy(name: str): 1 file reformatted ----- stderr ----- - warning: The following rules may cause conflicts when used with the formatter: 'COM812', 'COM819', 'D206', 'E501', 'ISC001', 'Q000', 'Q001', 'Q002', 'Q003', 'W191'. To avoid unexpected behavior, we recommend disabling these rules, either by removing them from the `select` or `extend-select` configuration, or adding then to the `ignore` configuration. + warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. Ignoring `one-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 then to the `ignore` configuration. "###); Ok(()) } + #[test] fn test_diff() { let args = ["format", "--no-cache", "--isolated", "--diff"]; diff --git a/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs b/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs index 8dae048b12aeec..ab4f2ef42ebbee 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/rules/avoidable_escaped_quote.rs @@ -27,6 +27,13 @@ use crate::settings::LinterSettings; /// ```python /// foo = "bar's" /// ``` +/// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter automatically removes unnecessary escapes, making the rule +/// redundant. +/// +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct AvoidableEscapedQuote; diff --git a/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs b/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs index 641a7eea571582..45a845fc6459a3 100644 --- a/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs +++ b/crates/ruff_linter/src/rules/flake8_quotes/rules/check_string_quotes.rs @@ -32,6 +32,13 @@ use super::super::settings::Quote; /// /// ## Options /// - `flake8-quotes.inline-quotes` +/// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent quotes for inline strings, making the rule +/// redundant. +/// +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct BadQuotesInlineString { preferred_quote: Quote, @@ -81,6 +88,13 @@ impl AlwaysFixableViolation for BadQuotesInlineString { /// /// ## Options /// - `flake8-quotes.multiline-quotes` +/// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces double quotes for multiline strings, making the rule +/// redundant. +/// +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct BadQuotesMultilineString { preferred_quote: Quote, @@ -129,6 +143,13 @@ impl AlwaysFixableViolation for BadQuotesMultilineString { /// /// ## Options /// - `flake8-quotes.docstring-quotes` +/// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces double quotes for docstrings, making the rule +/// redundant. +/// +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct BadQuotesDocstring { preferred_quote: Quote, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/indentation.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/indentation.rs index ea4f5928810230..ccad94347577b8 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/indentation.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/logical_lines/indentation.rs @@ -23,7 +23,16 @@ use super::LogicalLine; /// a = 1 /// ``` /// +/// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent indentation, making the rule redundant. +/// +/// The rule is also incompatible with the [formatter] when using +/// `indent-width` with a value other than `4`. +/// /// [PEP 8]: https://peps.python.org/pep-0008/#indentation +/// [formatter]:https://docs.astral.sh/ruff/formatter/ #[violation] pub struct IndentationWithInvalidMultiple { indent_size: usize, @@ -55,7 +64,15 @@ impl Violation for IndentationWithInvalidMultiple { /// # a = 1 /// ``` /// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent indentation, making the rule redundant. +/// +/// The rule is also incompatible with the [formatter] when using +/// `indent-width` with a value other than `4`. +/// /// [PEP 8]: https://peps.python.org/pep-0008/#indentation +/// [formatter]:https://docs.astral.sh/ruff/formatter/ #[violation] pub struct IndentationWithInvalidMultipleComment { indent_size: usize, diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/tab_indentation.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/tab_indentation.rs index c710533e96792c..153caab6484563 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/tab_indentation.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/tab_indentation.rs @@ -26,7 +26,15 @@ use ruff_text_size::{TextLen, TextRange, TextSize}; /// a = 1 /// ``` /// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent indentation, making the rule redundant. +/// +/// The rule is also incompatible with the [formatter] when using +/// `format.indent-style="tab"`. +/// /// [PEP 8]: https://peps.python.org/pep-0008/#tabs-or-spaces +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct TabIndentation; diff --git a/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs b/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs index 4ec116cb0622fa..9ba7786b1475d8 100644 --- a/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs +++ b/crates/ruff_linter/src/rules/pydocstyle/rules/indent.rs @@ -14,9 +14,7 @@ use crate::registry::Rule; /// Checks for docstrings that are indented with tabs. /// /// ## Why is this bad? -/// [PEP 8](https://peps.python.org/pep-0008/#tabs-or-spaces) recommends using -/// spaces over tabs for indentation. -/// +/// [PEP 8] recommends using spaces over tabs for indentation. /// /// ## Example /// ```python @@ -38,10 +36,20 @@ use crate::registry::Rule; /// """ /// ``` /// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent indentation, making the rule redundant. +/// +/// The rule is also incompatible with the [formatter] when using +/// `format.indent-style="tab"`. +/// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) /// - [NumPy Style Guide](https://numpydoc.readthedocs.io/en/latest/format.html) /// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) +/// +/// [PEP 8]: https://peps.python.org/pep-0008/#tabs-or-spaces +/// [formatter]: https://docs.astral.sh/ruff/formatter #[violation] pub struct IndentWithSpaces; @@ -126,12 +134,17 @@ impl AlwaysFixableViolation for UnderIndentation { /// """ /// ``` /// +/// ## Formatter compatibility +/// We recommend against using this rule alongside the [formatter]. The +/// formatter enforces consistent indentation, making the rule redundant. +/// /// ## References /// - [PEP 257 – Docstring Conventions](https://peps.python.org/pep-0257/) /// - [NumPy Style Guide](https://numpydoc.readthedocs.io/en/latest/format.html) /// - [Google Python Style Guide - Docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) /// /// [PEP 257]: https://peps.python.org/pep-0257/ +/// [formatter]:https://docs.astral.sh/ruff/formatter/ #[violation] pub struct OverIndentation; diff --git a/crates/ruff_workspace/src/options.rs b/crates/ruff_workspace/src/options.rs index db953f49e535f2..4adaf5fd87ae22 100644 --- a/crates/ruff_workspace/src/options.rs +++ b/crates/ruff_workspace/src/options.rs @@ -1412,6 +1412,9 @@ impl Flake8PytestStyleOptions { pub struct Flake8QuotesOptions { /// Quote style to prefer for inline strings (either "single" or /// "double"). + /// + /// When using the formatter, ensure that `format.quote-style` is set to + /// the same preferred quote style. #[option( default = r#""double""#, value_type = r#""single" | "double""#, @@ -1423,6 +1426,9 @@ pub struct Flake8QuotesOptions { /// Quote style to prefer for multiline strings (either "single" or /// "double"). + /// + /// When using the formatter, only "double" is compatible, as the formatter + /// enforces double quotes for multiline strings. #[option( default = r#""double""#, value_type = r#""single" | "double""#, @@ -1433,6 +1439,9 @@ pub struct Flake8QuotesOptions { pub multiline_quotes: Option, /// Quote style to prefer for docstrings (either "single" or "double"). + /// + /// When using the formatter, only "double" is compatible, as the formatter + /// enforces double quotes for docstrings strings. #[option( default = r#""double""#, value_type = r#""single" | "double""#, diff --git a/ruff.schema.json b/ruff.schema.json index d7661f0e516ce2..51efee721f6523 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -1083,7 +1083,7 @@ ] }, "docstring-quotes": { - "description": "Quote style to prefer for docstrings (either \"single\" or \"double\").", + "description": "Quote style to prefer for docstrings (either \"single\" or \"double\").\n\nWhen using the formatter, only \"double\" is compatible, as the formatter enforces double quotes for docstrings strings.", "anyOf": [ { "$ref": "#/definitions/Quote" @@ -1094,7 +1094,7 @@ ] }, "inline-quotes": { - "description": "Quote style to prefer for inline strings (either \"single\" or \"double\").", + "description": "Quote style to prefer for inline strings (either \"single\" or \"double\").\n\nWhen using the formatter, ensure that `format.quote-style` is set to the same preferred quote style.", "anyOf": [ { "$ref": "#/definitions/Quote" @@ -1105,7 +1105,7 @@ ] }, "multiline-quotes": { - "description": "Quote style to prefer for multiline strings (either \"single\" or \"double\").", + "description": "Quote style to prefer for multiline strings (either \"single\" or \"double\").\n\nWhen using the formatter, only \"double\" is compatible, as the formatter enforces double quotes for multiline strings.", "anyOf": [ { "$ref": "#/definitions/Quote"