mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
[ruff
] Indented form feeds (RUF054
) (#16049)
## Summary Resolves #12321. The physical-line-based `RUF054` checks for form feed characters that are preceded by only tabs and spaces, but not any other characters, including form feeds. ## Test Plan `cargo nextest run` and `cargo insta test`.
This commit is contained in:
parent
9ae98d4a09
commit
f367aa8367
10 changed files with 157 additions and 2 deletions
32
crates/ruff_linter/resources/test/fixtures/ruff/RUF054.py
vendored
Normal file
32
crates/ruff_linter/resources/test/fixtures/ruff/RUF054.py
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
############# Warning ############
|
||||
# This file contains form feeds. #
|
||||
############# Warning ############
|
||||
|
||||
|
||||
# Errors
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _():
|
||||
pass
|
||||
|
||||
if False:
|
||||
print('F')
|
||||
print('T')
|
||||
|
||||
|
||||
# No errors
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _():
|
||||
pass
|
||||
|
||||
def f():
|
||||
pass
|
|
@ -13,6 +13,7 @@ use crate::rules::pycodestyle::rules::{
|
|||
trailing_whitespace,
|
||||
};
|
||||
use crate::rules::pylint;
|
||||
use crate::rules::ruff::rules::indented_form_feed;
|
||||
use crate::settings::LinterSettings;
|
||||
use crate::Locator;
|
||||
|
||||
|
@ -71,6 +72,12 @@ pub(crate) fn check_physical_lines(
|
|||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
|
||||
if settings.rules.enabled(Rule::IndentedFormFeed) {
|
||||
if let Some(diagnostic) = indented_form_feed(&line) {
|
||||
diagnostics.push(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if enforce_no_newline_at_end_of_file {
|
||||
|
|
|
@ -1006,6 +1006,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
|||
(Ruff, "051") => (RuleGroup::Preview, rules::ruff::rules::IfKeyInDictDel),
|
||||
(Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable),
|
||||
(Ruff, "053") => (RuleGroup::Preview, rules::ruff::rules::ClassWithMixedTypeVars),
|
||||
(Ruff, "054") => (RuleGroup::Preview, rules::ruff::rules::IndentedFormFeed),
|
||||
(Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression),
|
||||
(Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback),
|
||||
(Ruff, "057") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRound),
|
||||
|
|
|
@ -253,6 +253,7 @@ impl Rule {
|
|||
Rule::BidirectionalUnicode
|
||||
| Rule::BlankLineWithWhitespace
|
||||
| Rule::DocLineTooLong
|
||||
| Rule::IndentedFormFeed
|
||||
| Rule::LineTooLong
|
||||
| Rule::MissingCopyrightNotice
|
||||
| Rule::MissingNewlineAtEndOfFile
|
||||
|
|
|
@ -11,11 +11,10 @@ mod tests {
|
|||
|
||||
use anyhow::Result;
|
||||
use regex::Regex;
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
use rustc_hash::FxHashSet;
|
||||
use test_case::test_case;
|
||||
|
||||
use ruff_source_file::SourceFileBuilder;
|
||||
|
||||
use crate::pyproject_toml::lint_pyproject_toml;
|
||||
use crate::registry::Rule;
|
||||
use crate::settings::types::{
|
||||
|
@ -436,6 +435,7 @@ mod tests {
|
|||
#[test_case(Rule::StarmapZip, Path::new("RUF058_0.py"))]
|
||||
#[test_case(Rule::StarmapZip, Path::new("RUF058_1.py"))]
|
||||
#[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))]
|
||||
#[test_case(Rule::IndentedFormFeed, Path::new("RUF054.py"))]
|
||||
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!(
|
||||
"preview__{}_{}",
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
use memchr::memchr;
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_source_file::Line;
|
||||
use ruff_text_size::{TextRange, TextSize};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for form feed characters preceded by either a space or a tab.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// [The language reference][lexical-analysis-indentation] states:
|
||||
///
|
||||
/// > A formfeed character may be present at the start of the line;
|
||||
/// > it will be ignored for the indentation calculations above.
|
||||
/// > Formfeed characters occurring elsewhere in the leading whitespace
|
||||
/// > have an undefined effect (for instance, they may reset the space count to zero).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```python
|
||||
/// if foo():\n \fbar()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```python
|
||||
/// if foo():\n bar()
|
||||
/// ```
|
||||
///
|
||||
/// [lexical-analysis-indentation]: https://docs.python.org/3/reference/lexical_analysis.html#indentation
|
||||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct IndentedFormFeed;
|
||||
|
||||
impl Violation for IndentedFormFeed {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Indented form feed".to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Remove form feed".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
const FORM_FEED: u8 = b'\x0c';
|
||||
const SPACE: u8 = b' ';
|
||||
const TAB: u8 = b'\t';
|
||||
|
||||
/// RUF054
|
||||
pub(crate) fn indented_form_feed(line: &Line) -> Option<Diagnostic> {
|
||||
let index_relative_to_line = memchr(FORM_FEED, line.as_bytes())?;
|
||||
|
||||
if index_relative_to_line == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if line[..index_relative_to_line]
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.any(|byte| *byte != SPACE && *byte != TAB)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let relative_index = u32::try_from(index_relative_to_line).ok()?;
|
||||
let absolute_index = line.start() + TextSize::new(relative_index);
|
||||
let range = TextRange::at(absolute_index, 1.into());
|
||||
|
||||
Some(Diagnostic::new(IndentedFormFeed, range))
|
||||
}
|
|
@ -13,6 +13,7 @@ pub(crate) use function_call_in_dataclass_default::*;
|
|||
pub(crate) use if_key_in_dict_del::*;
|
||||
pub(crate) use implicit_optional::*;
|
||||
pub(crate) use incorrectly_parenthesized_tuple_in_subscript::*;
|
||||
pub(crate) use indented_form_feed::*;
|
||||
pub(crate) use invalid_assert_message_literal_argument::*;
|
||||
pub(crate) use invalid_formatter_suppression_comment::*;
|
||||
pub(crate) use invalid_index_type::*;
|
||||
|
@ -69,6 +70,7 @@ mod helpers;
|
|||
mod if_key_in_dict_del;
|
||||
mod implicit_optional;
|
||||
mod incorrectly_parenthesized_tuple_in_subscript;
|
||||
mod indented_form_feed;
|
||||
mod invalid_assert_message_literal_argument;
|
||||
mod invalid_formatter_suppression_comment;
|
||||
mod invalid_index_type;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF054.py:8:2: RUF054 Indented form feed
|
||||
|
|
||||
6 | # Errors
|
||||
7 |
|
||||
8 |
|
||||
| ^ RUF054
|
||||
|
|
||||
= help: Remove form feed
|
||||
|
||||
RUF054.py:10:3: RUF054 Indented form feed
|
||||
|
|
||||
10 |
|
||||
| ^ RUF054
|
||||
11 |
|
||||
12 | def _():
|
||||
|
|
||||
= help: Remove form feed
|
||||
|
||||
RUF054.py:13:2: RUF054 Indented form feed
|
||||
|
|
||||
12 | def _():
|
||||
13 | pass
|
||||
| ^ RUF054
|
||||
14 |
|
||||
15 | if False:
|
||||
|
|
||||
= help: Remove form feed
|
||||
|
||||
RUF054.py:17:5: RUF054 Indented form feed
|
||||
|
|
||||
15 | if False:
|
||||
16 | print('F')
|
||||
17 | print('T')
|
||||
| ^ RUF054
|
||||
|
|
||||
= help: Remove form feed
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3948,6 +3948,7 @@
|
|||
"RUF051",
|
||||
"RUF052",
|
||||
"RUF053",
|
||||
"RUF054",
|
||||
"RUF055",
|
||||
"RUF056",
|
||||
"RUF057",
|
||||
|
|
|
@ -97,6 +97,7 @@ KNOWN_FORMATTING_VIOLATIONS = [
|
|||
KNOWN_PARSE_ERRORS = [
|
||||
"blank-line-with-whitespace",
|
||||
"indentation-with-invalid-multiple-comment",
|
||||
"indented-form-feed",
|
||||
"missing-newline-at-end-of-file",
|
||||
"mixed-spaces-and-tabs",
|
||||
"no-indented-block",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue