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

[pycodestyle] Implement missing-or-outdented-indentation (E122) #11349

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dd4acf0
implement e122
augustelalande May 9, 2024
0ca8a92
update doc
augustelalande May 9, 2024
4eed378
add rule gate
augustelalande May 9, 2024
9353218
remove false positive
augustelalande May 9, 2024
afc8ce4
improve gating
augustelalande May 9, 2024
841156d
fix false positive
augustelalande May 9, 2024
c280bde
fix false positive
augustelalande May 9, 2024
d614bc5
small optimization
augustelalande May 9, 2024
feb2d0f
better gating
augustelalande May 9, 2024
67f6589
test hoel-bagard's implementation
augustelalande May 15, 2024
c911626
optimize
augustelalande May 15, 2024
2a43a2e
fix is_comment_only
augustelalande May 15, 2024
a600298
remove unused helper
augustelalande May 15, 2024
1e66c40
fix bug
augustelalande May 15, 2024
7907824
fix bug in contains_backslash
augustelalande May 16, 2024
aeeca03
clippy
augustelalande May 16, 2024
198af1e
remove parens vector
augustelalande May 16, 2024
2e8ea82
try preallocating vector
augustelalande May 16, 2024
94f12a3
try Vec::with_capacity
augustelalande May 16, 2024
17a8f6a
reduce use of vec
augustelalande May 16, 2024
d08efa7
store contains_backslash result
augustelalande May 16, 2024
0642d07
more conservative line slice
augustelalande May 16, 2024
50ba417
try bypass visual indent check
augustelalande May 16, 2024
13176b9
fix bug relative to pycodestyle implementation
augustelalande May 19, 2024
b3b6bec
fix another difference with pycodestyle
augustelalande May 19, 2024
07b3a93
clean up
augustelalande May 19, 2024
6e645e9
highlight the indent instead of the token
augustelalande May 19, 2024
0db7670
consolidate expand_indent
augustelalande May 19, 2024
7638360
optimize newline search
augustelalande May 19, 2024
4a1e41c
combine multiple ifs into one match
augustelalande May 19, 2024
2cd9402
remove some line slicing
augustelalande May 19, 2024
613d292
Merge branch 'main' into E122
augustelalande May 19, 2024
00cc680
Merge branch 'main' into E122
augustelalande May 19, 2024
c12d4b2
Merge branch 'main' into E122
augustelalande Jun 5, 2024
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
191 changes: 191 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pycodestyle/E122.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#: E122
print("E122", (
"str"))

# OK
print("E122", (
"str"))

#: E122:6:5 E122:7:5 E122:8:1
print(dedent(
'''
mkdir -p ./{build}/
mv ./build/ ./{build}/%(revision)s/
'''.format(
build='build',
# more stuff
)
))

# OK
print(dedent(
'''
mkdir -p ./{build}/
mv ./build/ ./{build}/%(revision)s/
'''.format(
build='build',
# more stuff
)
))

#: E122
if True:
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)

# OK
if True:
result = some_function_that_takes_arguments(
'a', 'b', 'c',
'd', 'e', 'f',
)

#: E122
if some_very_very_very_long_variable_name or var \
or another_very_long_variable_name:
raise Exception()

# OK
if some_very_very_very_long_variable_name or var \
or another_very_long_variable_name:
raise Exception()

#: E122
if some_very_very_very_long_variable_name or var[0] \
or another_very_long_variable_name:
raise Exception()

# OK
if some_very_very_very_long_variable_name or var[0] \
or another_very_long_variable_name:
raise Exception()

#: E122
if True:
if some_very_very_very_long_variable_name or var \
or another_very_long_variable_name:
raise Exception()

# OK
if True:
if some_very_very_very_long_variable_name or var \
or another_very_long_variable_name:
raise Exception()

#: E122
if True:
if some_very_very_very_long_variable_name or var[0] \
or another_very_long_variable_name:
raise Exception()

#: OK
if True:
if some_very_very_very_long_variable_name or var[0] \
or another_very_long_variable_name:
raise Exception()

#: E122
dictionary = {
"is": {
"nested": yes(),
},
}

# OK
dictionary = {
"is": {
"nested": yes(),
},
}

#: E122
setup('',
scripts=[''],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Developers',
])

# OK
setup('',
scripts=[''],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: Developers',
])

#: E122:2:1
if True:\
print(True)

# OK
if True:\
print(True)

# E122
def f():
x = ((
(
)
)
+ 2)

# OK
def f():
# comment
x = ((
(
)
)
+ 2)

# OK
def target(
self,
) -> Union[
Guild,
Member,
User,
]:
...

# OK
x = [
[
1
], [
1
]
]

# OK
QUERY_EXECUTION_COUNT_SQL = f"""
SELECT day_start, sum(total) AS total FROM (
SELECT
count(*) AS total,
toStartOfDay(query_start_time) AS day_start
FROM
{QUERY_LOG_SYSTEM_TABLE}
WHERE
query_start_time > now() - INTERVAL %(days)s day AND type = 2 AND is_initial_query %(conditions)s
GROUP BY day_start
)
"""

# OK
NODE_STORAGE_SQL = f"""
SELECT
hostName() node,
sum(total_space) space_used,
FROM {DISKS_SYSTEM_TABLE}
WHERE type = 'local'
"""

# OK
[21,
21,
21]
57 changes: 33 additions & 24 deletions crates/ruff_linter/src/checkers/logical_lines.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::line_width::IndentWidth;
use crate::registry::Rule;
use ruff_diagnostics::Diagnostic;
use ruff_python_codegen::Stylist;
use ruff_python_index::Indexer;
Expand All @@ -7,31 +7,16 @@ use ruff_source_file::Locator;
use ruff_text_size::{Ranged, TextRange};

use crate::registry::AsRule;
use crate::rules::pycodestyle::helpers::expand_indent;
use crate::rules::pycodestyle::rules::logical_lines::{
extraneous_whitespace, indentation, missing_whitespace, missing_whitespace_after_keyword,
missing_whitespace_around_operator, redundant_backslash, space_after_comma,
space_around_operator, whitespace_around_keywords, whitespace_around_named_parameter_equals,
whitespace_before_comment, whitespace_before_parameters, LogicalLines, TokenFlags,
continuation_lines, extraneous_whitespace, indentation, missing_whitespace,
missing_whitespace_after_keyword, missing_whitespace_around_operator, redundant_backslash,
space_after_comma, space_around_operator, whitespace_around_keywords,
whitespace_around_named_parameter_equals, whitespace_before_comment,
whitespace_before_parameters, LogicalLines, TokenFlags,
};
use crate::settings::LinterSettings;

/// Return the amount of indentation, expanding tabs to the next multiple of the settings' tab size.
pub(crate) fn expand_indent(line: &str, indent_width: IndentWidth) -> usize {
let line = line.trim_end_matches(['\n', '\r']);

let mut indent = 0;
let tab_size = indent_width.as_usize();
for c in line.bytes() {
match c {
b'\t' => indent = (indent / tab_size) * tab_size + tab_size,
b' ' => indent += 1,
_ => break,
}
}

indent
}

pub(crate) fn check_logical_lines(
tokens: &Tokens,
locator: &Locator,
Expand All @@ -45,7 +30,7 @@ pub(crate) fn check_logical_lines(
let mut prev_indent_level = None;
let indent_char = stylist.indentation().as_char();

for line in &LogicalLines::from_tokens(tokens, locator) {
for mut line in &LogicalLines::from_tokens(tokens, locator) {
if line.flags().contains(TokenFlags::OPERATOR) {
space_around_operator(&line, &mut context);
whitespace_around_named_parameter_equals(&line, &mut context);
Expand Down Expand Up @@ -74,7 +59,14 @@ pub(crate) fn check_logical_lines(

if line.flags().contains(TokenFlags::BRACKET) {
whitespace_before_parameters(&line, &mut context);
redundant_backslash(&line, locator, indexer, &mut context);
}

if settings.rules.enabled(Rule::RedundantBackslash) {
if line.flags().contains(TokenFlags::BRACKET)
&& line.contains_backslash(locator, indexer)
{
redundant_backslash(&line, locator, indexer, &mut context);
}
}

// Extract the indentation level.
Expand Down Expand Up @@ -105,6 +97,23 @@ pub(crate) fn check_logical_lines(
}
}

if settings.rules.enabled(Rule::MissingOrOutdentedIndentation) {
if line
.flags()
.contains(TokenFlags::NON_LOGICAL_NEWLINE | TokenFlags::BRACKET)
|| line.contains_backslash(locator, indexer)
{
continuation_lines(
&line,
indent_char,
settings.tab_size,
locator,
indexer,
&mut context,
);
}
}

if !line.is_comment_only() {
prev_line = Some(line);
prev_indent_level = Some(indent_level);
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 @@ -85,6 +85,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pycodestyle, "E116") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::UnexpectedIndentationComment),
#[allow(deprecated)]
(Pycodestyle, "E117") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::OverIndented),
(Pycodestyle, "E122") => (RuleGroup::Preview, rules::pycodestyle::rules::logical_lines::MissingOrOutdentedIndentation),
#[allow(deprecated)]
(Pycodestyle, "E201") => (RuleGroup::Nursery, rules::pycodestyle::rules::logical_lines::WhitespaceAfterOpenBracket),
#[allow(deprecated)]
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 @@ -304,6 +304,7 @@ impl Rule {
Rule::ImplicitNamespacePackage | Rule::InvalidModuleName => LintSource::Filesystem,
Rule::IndentationWithInvalidMultiple
| Rule::IndentationWithInvalidMultipleComment
| Rule::MissingOrOutdentedIndentation
| Rule::MissingWhitespace
| Rule::MissingWhitespaceAfterKeyword
| Rule::MissingWhitespaceAroundArithmeticOperator
Expand Down
17 changes: 17 additions & 0 deletions crates/ruff_linter/src/rules/pycodestyle/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
use crate::line_width::IndentWidth;

/// Returns `true` if the name should be considered "ambiguous".
pub(super) fn is_ambiguous_name(name: &str) -> bool {
name == "l" || name == "I" || name == "O"
}

/// Return the amount of indentation, expanding tabs to the next multiple of the settings' tab size.
pub(crate) fn expand_indent(line: &str, indent_width: IndentWidth) -> usize {
let mut indent = 0;
let tab_size = indent_width.as_usize();
for c in line.bytes() {
match c {
b' ' => indent += 1,
b'\t' => indent = (indent / tab_size) * tab_size + tab_size,
_ => break,
}
}

indent
}
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pycodestyle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ mod tests {
#[test_case(Rule::TooManyNewlinesAtEndOfFile, Path::new("W391_2.py"))]
#[test_case(Rule::TooManyNewlinesAtEndOfFile, Path::new("W391_3.py"))]
#[test_case(Rule::TooManyNewlinesAtEndOfFile, Path::new("W391_4.py"))]
#[test_case(Rule::MissingOrOutdentedIndentation, Path::new("E122.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use ruff_source_file::{Locator, UniversalNewlines};
use ruff_text_size::TextRange;
use ruff_text_size::TextSize;

use crate::checkers::logical_lines::expand_indent;
use crate::line_width::IndentWidth;
use crate::rules::pycodestyle::helpers::expand_indent;
use ruff_python_trivia::PythonWhitespace;

/// Number of blank lines around top level classes and functions.
Expand Down
Loading
Loading