Compare commits

...

4 Commits

Author SHA1 Message Date
Charlie Marsh
0e8c237167 Bump version to 0.0.205 2022-12-31 13:44:39 -05:00
Harutaka Kawamura
960c5e2006 Use more precise error ranges for names (#1513) 2022-12-31 13:42:39 -05:00
Charlie Marsh
9ba17fbf92 Avoid flagging nested f-strings (#1516) 2022-12-31 13:41:21 -05:00
Charlie Marsh
bfdf972a5d Add code kind to Quick Fix action 2022-12-31 10:26:47 -05:00
18 changed files with 108 additions and 60 deletions

View File

@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.204
rev: v0.0.205
hooks:
- id: ruff

8
Cargo.lock generated
View File

@@ -750,7 +750,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8-to-ruff"
version = "0.0.204-dev.0"
version = "0.0.205-dev.0"
dependencies = [
"anyhow",
"clap 4.0.32",
@@ -1878,7 +1878,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.204"
version = "0.0.205"
dependencies = [
"annotate-snippets 0.9.1",
"anyhow",
@@ -1946,7 +1946,7 @@ dependencies = [
[[package]]
name = "ruff_dev"
version = "0.0.204"
version = "0.0.205"
dependencies = [
"anyhow",
"clap 4.0.32",
@@ -1967,7 +1967,7 @@ dependencies = [
[[package]]
name = "ruff_macros"
version = "0.0.204"
version = "0.0.205"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -6,7 +6,7 @@ members = [
[package]
name = "ruff"
version = "0.0.204"
version = "0.0.205"
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
edition = "2021"
rust-version = "1.65.0"
@@ -51,7 +51,7 @@ path-absolutize = { version = "3.0.14", features = ["once_cell_cache", "use_unix
quick-junit = { version = "0.3.2" }
regex = { version = "1.6.0" }
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
ruff_macros = { version = "0.0.204", path = "ruff_macros" }
ruff_macros = { version = "0.0.205", path = "ruff_macros" }
rustc-hash = { version = "1.1.0" }
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "8cb2b8292062adf13bde1b863a9b02c9f0bda3dd" }
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "8cb2b8292062adf13bde1b863a9b02c9f0bda3dd" }

View File

@@ -169,7 +169,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
```yaml
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.0.204'
rev: 'v0.0.205'
hooks:
- id: ruff
# Respect `exclude` and `extend-exclude` settings.

View File

@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "flake8_to_ruff"
version = "0.0.204"
version = "0.0.205"
dependencies = [
"anyhow",
"clap",
@@ -1975,7 +1975,7 @@ dependencies = [
[[package]]
name = "ruff"
version = "0.0.204"
version = "0.0.205"
dependencies = [
"anyhow",
"bincode",

View File

@@ -1,6 +1,6 @@
[package]
name = "flake8-to-ruff"
version = "0.0.204-dev.0"
version = "0.0.205-dev.0"
edition = "2021"
[lib]

View File

@@ -57,7 +57,7 @@ export default function SourceEditor({
.filter((check) => check.fix)
.map((check) => ({
title: check.fix
? check.fix.message ?? `Fix ${check.code}`
? `${check.code}: ${check.fix.message}` ?? `Fix ${check.code}`
: "Autofix",
id: `fix-${check.code}`,
kind: "quickfix",

View File

@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "ruff"
version = "0.0.204"
version = "0.0.205"
description = "An extremely fast Python linter, written in Rust."
authors = [
{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" },

View File

@@ -1,6 +1,8 @@
# OK
a = "abc"
b = f"ghi{'jkl'}"
# Errors
c = f"def"
d = f"def" + "ghi"
e = (
@@ -8,5 +10,17 @@ e = (
"ghi"
)
# OK
g = f"ghi{123:{45}}"
# Error
h = "x" "y" f"z"
v = 23.234234
# OK
f"{v:0.2f}"
# OK (false negatives)
f"{v:{f'0.2f'}}"
f"{f''}"

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_dev"
version = "0.0.204"
version = "0.0.205"
edition = "2021"
[dependencies]

View File

@@ -1,6 +1,6 @@
[package]
name = "ruff_macros"
version = "0.0.204"
version = "0.0.205"
edition = "2021"
[lib]

View File

@@ -5,7 +5,7 @@ use regex::Regex;
use rustc_hash::{FxHashMap, FxHashSet};
use rustpython_ast::{
Arguments, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, Keyword, KeywordData,
Location, Stmt, StmtKind,
Located, Location, Stmt, StmtKind,
};
use rustpython_parser::lexer;
use rustpython_parser::lexer::Tok;
@@ -395,6 +395,19 @@ pub fn identifier_range(stmt: &Stmt, locator: &SourceCodeLocator) -> Range {
Range::from_located(stmt)
}
// Return the ranges of `Name` tokens within a specified node.
pub fn find_names<T>(located: &Located<T>, locator: &SourceCodeLocator) -> Vec<Range> {
let contents = locator.slice_source_code_range(&Range::from_located(located));
lexer::make_tokenizer_located(&contents, located.location)
.flatten()
.filter(|(_, tok, _)| matches!(tok, Tok::Name { .. }))
.map(|(start, _, end)| Range {
location: start,
end_location: end,
})
.collect()
}
/// Return the `Range` of `name` in `Excepthandler`.
pub fn excepthandler_name_range(
handler: &Excepthandler,

View File

@@ -91,6 +91,7 @@ pub struct Checker<'a> {
in_type_definition: bool,
in_deferred_string_type_definition: bool,
in_deferred_type_definition: bool,
in_f_string: bool,
in_literal: bool,
in_subscript: bool,
seen_import_boundary: bool,
@@ -147,6 +148,7 @@ impl<'a> Checker<'a> {
in_type_definition: false,
in_deferred_string_type_definition: false,
in_deferred_type_definition: false,
in_f_string: false,
in_literal: false,
in_subscript: false,
seen_import_boundary: false,
@@ -264,16 +266,17 @@ where
match &stmt.node {
StmtKind::Global { names } => {
let scope_index = *self.scope_stack.last().expect("No current scope found");
let ranges = helpers::find_names(stmt, self.locator);
if scope_index != GLOBAL_SCOPE_INDEX {
// Add the binding to the current scope.
let scope = &mut self.scopes[scope_index];
let usage = Some((scope.id, Range::from_located(stmt)));
for name in names {
for (name, range) in names.iter().zip(ranges.iter()) {
let index = self.bindings.len();
self.bindings.push(Binding {
kind: BindingKind::Global,
used: usage,
range: Range::from_located(stmt),
range: *range,
source: Some(RefEquality(stmt)),
});
scope.values.insert(name, index);
@@ -284,8 +287,9 @@ where
self.add_checks(
names
.iter()
.filter_map(|name| {
pycodestyle::checks::ambiguous_variable_name(name, stmt)
.zip(ranges.iter())
.filter_map(|(name, range)| {
pycodestyle::checks::ambiguous_variable_name(name, *range)
})
.into_iter(),
);
@@ -293,16 +297,17 @@ where
}
StmtKind::Nonlocal { names } => {
let scope_index = *self.scope_stack.last().expect("No current scope found");
let ranges = helpers::find_names(stmt, self.locator);
if scope_index != GLOBAL_SCOPE_INDEX {
let scope = &mut self.scopes[scope_index];
let usage = Some((scope.id, Range::from_located(stmt)));
for name in names {
for (name, range) in names.iter().zip(ranges.iter()) {
// Add a binding to the current scope.
let index = self.bindings.len();
self.bindings.push(Binding {
kind: BindingKind::Nonlocal,
used: usage,
range: Range::from_located(stmt),
range: *range,
source: Some(RefEquality(stmt)),
});
scope.values.insert(name, index);
@@ -310,7 +315,7 @@ where
// Mark the binding in the defining scopes as used too. (Skip the global scope
// and the current scope.)
for name in names {
for (name, range) in names.iter().zip(ranges.iter()) {
let mut exists = false;
for index in self.scope_stack.iter().skip(1).rev().skip(1) {
if let Some(index) = self.scopes[*index].values.get(&name.as_str()) {
@@ -324,7 +329,7 @@ where
if self.settings.enabled.contains(&CheckCode::PLE0117) {
self.add_check(Check::new(
CheckKind::NonlocalWithoutBinding(name.to_string()),
Range::from_located(stmt),
*range,
));
}
}
@@ -335,8 +340,9 @@ where
self.add_checks(
names
.iter()
.filter_map(|name| {
pycodestyle::checks::ambiguous_variable_name(name, stmt)
.zip(ranges.iter())
.filter_map(|(name, range)| {
pycodestyle::checks::ambiguous_variable_name(name, *range)
})
.into_iter(),
);
@@ -1466,6 +1472,7 @@ where
self.push_expr(expr);
let prev_in_f_string = self.in_f_string;
let prev_in_literal = self.in_literal;
let prev_in_type_definition = self.in_type_definition;
@@ -1540,9 +1547,10 @@ where
}
ExprContext::Store => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) =
pycodestyle::checks::ambiguous_variable_name(id, expr)
{
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(
id,
Range::from_located(expr),
) {
self.add_check(check);
}
}
@@ -2151,7 +2159,9 @@ where
}
}
ExprKind::JoinedStr { values } => {
if self.settings.enabled.contains(&CheckCode::F541) {
// Conversion flags are parsed as f-strings without placeholders, so skip
// nested f-strings, which would lead to false positives.
if !self.in_f_string && self.settings.enabled.contains(&CheckCode::F541) {
if !values
.iter()
.any(|value| matches!(value.node, ExprKind::FormattedValue { .. }))
@@ -2654,6 +2664,11 @@ where
}
self.in_subscript = prev_in_subscript;
}
ExprKind::JoinedStr { .. } => {
self.in_f_string = true;
visitor::walk_expr(self, expr);
self.in_f_string = prev_in_f_string;
}
_ => visitor::walk_expr(self, expr),
}
@@ -2671,6 +2686,7 @@ where
self.in_type_definition = prev_in_type_definition;
self.in_literal = prev_in_literal;
self.in_f_string = prev_in_f_string;
self.pop_expr();
}
@@ -2704,9 +2720,11 @@ where
match name {
Some(name) => {
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) =
pycodestyle::checks::ambiguous_variable_name(name, excepthandler)
{
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(
name,
helpers::excepthandler_name_range(excepthandler, self.locator)
.expect("Failed to find `name` range"),
) {
self.add_check(check);
}
}
@@ -2823,7 +2841,10 @@ where
);
if self.settings.enabled.contains(&CheckCode::E741) {
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(&arg.node.arg, arg) {
if let Some(check) = pycodestyle::checks::ambiguous_variable_name(
&arg.node.arg,
Range::from_located(arg),
) {
self.add_check(check);
}
}

View File

@@ -1,7 +1,7 @@
use itertools::izip;
use once_cell::sync::Lazy;
use regex::Regex;
use rustpython_ast::{Constant, Excepthandler, Located, Location, Stmt, StmtKind};
use rustpython_ast::{Constant, Excepthandler, Location, Stmt, StmtKind};
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use crate::ast::helpers::except_range;
@@ -111,11 +111,11 @@ fn is_ambiguous_name(name: &str) -> bool {
}
/// E741
pub fn ambiguous_variable_name<T>(name: &str, located: &Located<T>) -> Option<Check> {
pub fn ambiguous_variable_name(name: &str, range: Range) -> Option<Check> {
if is_ambiguous_name(name) {
Some(Check::new(
CheckKind::AmbiguousVariableName(name.to_string()),
Range::from_located(located),
range,
))
} else {
None

View File

@@ -106,7 +106,7 @@ expression: checks
AmbiguousVariableName: l
location:
row: 25
column: 4
column: 11
end_location:
row: 25
column: 12
@@ -136,7 +136,7 @@ expression: checks
AmbiguousVariableName: l
location:
row: 33
column: 8
column: 17
end_location:
row: 33
column: 18
@@ -236,10 +236,10 @@ expression: checks
AmbiguousVariableName: l
location:
row: 71
column: 0
column: 21
end_location:
row: 72
column: 8
row: 71
column: 22
fix: ~
parent: ~
- kind:

View File

@@ -4,19 +4,10 @@ expression: checks
---
- kind: FStringMissingPlaceholders
location:
row: 4
row: 6
column: 4
end_location:
row: 4
column: 10
fix: ~
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 5
column: 4
end_location:
row: 5
row: 6
column: 10
fix: ~
parent: ~
@@ -31,10 +22,19 @@ expression: checks
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 12
row: 9
column: 4
end_location:
row: 12
row: 9
column: 10
fix: ~
parent: ~
- kind: FStringMissingPlaceholders
location:
row: 17
column: 4
end_location:
row: 17
column: 16
fix: ~
parent: ~

View File

@@ -6,7 +6,7 @@ expression: checks
NonlocalWithoutBinding: x
location:
row: 5
column: 4
column: 13
end_location:
row: 5
column: 14
@@ -16,7 +16,7 @@ expression: checks
NonlocalWithoutBinding: y
location:
row: 9
column: 4
column: 13
end_location:
row: 9
column: 14
@@ -26,7 +26,7 @@ expression: checks
NonlocalWithoutBinding: y
location:
row: 19
column: 8
column: 17
end_location:
row: 19
column: 18

View File

@@ -6,7 +6,7 @@ expression: checks
GlobalVariableNotAssigned: x
location:
row: 5
column: 4
column: 11
end_location:
row: 5
column: 12
@@ -16,7 +16,7 @@ expression: checks
GlobalVariableNotAssigned: x
location:
row: 9
column: 4
column: 11
end_location:
row: 9
column: 12