Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 32 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/ruff/RUF054.py
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions crates/ruff_linter/src/checkers/physical_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ impl Rule {
Rule::BidirectionalUnicode
| Rule::BlankLineWithWhitespace
| Rule::DocLineTooLong
| Rule::IndentedFormFeed
| Rule::LineTooLong
| Rule::MissingCopyrightNotice
| Rule::MissingNewlineAtEndOfFile
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff_linter/src/rules/ruff/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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__{}_{}",
Expand Down
71 changes: 71 additions & 0 deletions crates/ruff_linter/src/rules/ruff/rules/indented_form_feed.rs
Original file line number Diff line number Diff line change
@@ -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()
Comment thread
InSyncWithFoo marked this conversation as resolved.
/// ```
///
/// 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;
}
Comment thread
InSyncWithFoo marked this conversation as resolved.

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))
}
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/ruff/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions scripts/check_docs_formatted.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
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",
Expand Down