mirror of https://github.com/astral-sh/ruff
Merge branch 'main' into PYI050
This commit is contained in:
commit
ddd3ededec
|
|
@ -34,6 +34,7 @@ jobs:
|
||||||
args: --out dist
|
args: --out dist
|
||||||
- name: "Test sdist"
|
- name: "Test sdist"
|
||||||
run: |
|
run: |
|
||||||
|
rustup default $(cat rust-toolchain)
|
||||||
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
|
pip install dist/${{ env.PACKAGE_NAME }}-*.tar.gz --force-reinstall
|
||||||
ruff --help
|
ruff --help
|
||||||
python -m ruff --help
|
python -m ruff --help
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,12 @@
|
||||||
fail_fast: true
|
fail_fast: true
|
||||||
|
|
||||||
|
exclude: |
|
||||||
|
(?x)^(
|
||||||
|
crates/ruff/resources/.*|
|
||||||
|
crates/ruff_python_formatter/resources/.*|
|
||||||
|
crates/ruff_python_formatter/src/snapshots/.*
|
||||||
|
)$
|
||||||
|
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/abravalheri/validate-pyproject
|
- repo: https://github.com/abravalheri/validate-pyproject
|
||||||
rev: v0.12.1
|
rev: v0.12.1
|
||||||
|
|
@ -19,7 +27,7 @@ repos:
|
||||||
- id: markdownlint-fix
|
- id: markdownlint-fix
|
||||||
|
|
||||||
- repo: https://github.com/crate-ci/typos
|
- repo: https://github.com/crate-ci/typos
|
||||||
rev: v1.14.8
|
rev: v1.14.12
|
||||||
hooks:
|
hooks:
|
||||||
- id: typos
|
- id: typos
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -691,7 +691,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flake8-to-ruff"
|
name = "flake8-to-ruff"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
@ -1725,7 +1725,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"annotate-snippets 0.9.1",
|
"annotate-snippets 0.9.1",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
@ -1818,7 +1818,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruff_cli"
|
name = "ruff_cli"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"annotate-snippets 0.9.1",
|
"annotate-snippets 0.9.1",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.0.270
|
rev: v0.0.271
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[files]
|
[files]
|
||||||
extend-exclude = ["snapshots", "black"]
|
extend-exclude = ["resources", "snapshots"]
|
||||||
|
|
||||||
[default.extend-words]
|
[default.extend-words]
|
||||||
trivias = "trivias"
|
trivias = "trivias"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "flake8-to-ruff"
|
name = "flake8-to-ruff"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,6 @@
|
||||||
"{bar}{}".format(1, bar=2, spam=3) # F522
|
"{bar}{}".format(1, bar=2, spam=3) # F522
|
||||||
"{bar:{spam}}".format(bar=2, spam=3) # No issues
|
"{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||||
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||||
|
# Not fixable
|
||||||
|
(''
|
||||||
|
.format(x=2))
|
||||||
|
|
@ -83,6 +83,11 @@ def f():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
with (Nested(m)) as (cm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
toplevel = tt = lexer.get_token()
|
toplevel = tt = lexer.get_token()
|
||||||
if not tt:
|
if not tt:
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,6 @@
|
||||||
for item in {"apples", "lemons", "water"}: # flags in-line set literals
|
for item in {"apples", "lemons", "water"}: # flags in-line set literals
|
||||||
print(f"I like {item}.")
|
print(f"I like {item}.")
|
||||||
|
|
||||||
for item in set(("apples", "lemons", "water")): # flags set() calls
|
|
||||||
print(f"I like {item}.")
|
|
||||||
|
|
||||||
for number in {i for i in range(10)}: # flags set comprehensions
|
|
||||||
print(number)
|
|
||||||
|
|
||||||
numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
||||||
|
|
||||||
numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
||||||
|
|
@ -36,3 +30,9 @@ numbers_set = {i for i in (1, 2, 3)} # tuples in comprehensions are fine
|
||||||
numbers_dict = {str(i): i for i in [1, 2, 3]} # lists in dict comprehensions are fine
|
numbers_dict = {str(i): i for i in [1, 2, 3]} # lists in dict comprehensions are fine
|
||||||
|
|
||||||
numbers_gen = (i for i in (1, 2, 3)) # tuples in generator expressions are fine
|
numbers_gen = (i for i in (1, 2, 3)) # tuples in generator expressions are fine
|
||||||
|
|
||||||
|
for item in set(("apples", "lemons", "water")): # set constructor is fine
|
||||||
|
print(f"I like {item}.")
|
||||||
|
|
||||||
|
for number in {i for i in range(10)}: # set comprehensions are fine
|
||||||
|
print(number)
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,30 @@ def f():
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
nonlocal y
|
nonlocal y
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
def g():
|
||||||
|
nonlocal x
|
||||||
|
|
||||||
|
del x
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
def g():
|
||||||
|
nonlocal x
|
||||||
|
|
||||||
|
del x
|
||||||
|
|
||||||
|
|
||||||
|
def f():
|
||||||
|
try:
|
||||||
|
pass
|
||||||
|
except Exception as x:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def g():
|
||||||
|
nonlocal x
|
||||||
|
x = 2
|
||||||
|
|
|
||||||
|
|
@ -307,9 +307,18 @@ where
|
||||||
stmt.range(),
|
stmt.range(),
|
||||||
ExecutionContext::Runtime,
|
ExecutionContext::Runtime,
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
// Ensure that every nonlocal has an existing binding from a parent scope.
|
|
||||||
if self.enabled(Rule::NonlocalWithoutBinding) {
|
// Ensure that every nonlocal has an existing binding from a parent scope.
|
||||||
|
if self.enabled(Rule::NonlocalWithoutBinding) {
|
||||||
|
if self
|
||||||
|
.semantic_model
|
||||||
|
.scopes
|
||||||
|
.ancestors(self.semantic_model.scope_id)
|
||||||
|
.skip(1)
|
||||||
|
.take_while(|scope| !scope.kind.is_module())
|
||||||
|
.all(|scope| !scope.declares(name.as_str()))
|
||||||
|
{
|
||||||
self.diagnostics.push(Diagnostic::new(
|
self.diagnostics.push(Diagnostic::new(
|
||||||
pylint::rules::NonlocalWithoutBinding {
|
pylint::rules::NonlocalWithoutBinding {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
|
@ -4042,7 +4051,7 @@ where
|
||||||
let name_range =
|
let name_range =
|
||||||
helpers::excepthandler_name_range(excepthandler, self.locator).unwrap();
|
helpers::excepthandler_name_range(excepthandler, self.locator).unwrap();
|
||||||
|
|
||||||
if self.semantic_model.scope().defines(name) {
|
if self.semantic_model.scope().has(name) {
|
||||||
self.handle_node_store(
|
self.handle_node_store(
|
||||||
name,
|
name,
|
||||||
&Expr::Name(ast::ExprName {
|
&Expr::Name(ast::ExprName {
|
||||||
|
|
@ -4067,7 +4076,7 @@ where
|
||||||
|
|
||||||
if let Some(binding_id) = {
|
if let Some(binding_id) = {
|
||||||
let scope = self.semantic_model.scope_mut();
|
let scope = self.semantic_model.scope_mut();
|
||||||
scope.remove(name)
|
scope.delete(name)
|
||||||
} {
|
} {
|
||||||
if !self.semantic_model.is_used(binding_id) {
|
if !self.semantic_model.is_used(binding_id) {
|
||||||
if self.enabled(Rule::UnusedVariable) {
|
if self.enabled(Rule::UnusedVariable) {
|
||||||
|
|
@ -4697,19 +4706,16 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = self.semantic_model.scope_mut();
|
let scope = self.semantic_model.scope_mut();
|
||||||
if scope.remove(id.as_str()).is_some() {
|
if scope.delete(id.as_str()).is_none() {
|
||||||
return;
|
if self.enabled(Rule::UndefinedName) {
|
||||||
|
self.diagnostics.push(Diagnostic::new(
|
||||||
|
pyflakes::rules::UndefinedName {
|
||||||
|
name: id.to_string(),
|
||||||
|
},
|
||||||
|
expr.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if !self.enabled(Rule::UndefinedName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.diagnostics.push(Diagnostic::new(
|
|
||||||
pyflakes::rules::UndefinedName {
|
|
||||||
name: id.to_string(),
|
|
||||||
},
|
|
||||||
expr.range(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_deferred_future_type_definitions(&mut self) {
|
fn check_deferred_future_type_definitions(&mut self) {
|
||||||
|
|
@ -4977,7 +4983,7 @@ impl<'a> Checker<'a> {
|
||||||
.collect();
|
.collect();
|
||||||
if !sources.is_empty() {
|
if !sources.is_empty() {
|
||||||
for (name, range) in &exports {
|
for (name, range) in &exports {
|
||||||
if !scope.defines(name) {
|
if !scope.has(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
pyflakes::rules::UndefinedLocalWithImportStarUsage {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,9 @@ pub struct StringDotFormatExtraNamedArguments {
|
||||||
missing: Vec<String>,
|
missing: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AlwaysAutofixableViolation for StringDotFormatExtraNamedArguments {
|
impl Violation for StringDotFormatExtraNamedArguments {
|
||||||
|
const AUTOFIX: AutofixKind = AutofixKind::Sometimes;
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||||
|
|
@ -394,10 +396,10 @@ impl AlwaysAutofixableViolation for StringDotFormatExtraNamedArguments {
|
||||||
format!("`.format` call has unused named argument(s): {message}")
|
format!("`.format` call has unused named argument(s): {message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn autofix_title(&self) -> String {
|
fn autofix_title(&self) -> Option<String> {
|
||||||
let StringDotFormatExtraNamedArguments { missing } = self;
|
let StringDotFormatExtraNamedArguments { missing } = self;
|
||||||
let message = missing.join(", ");
|
let message = missing.join(", ");
|
||||||
format!("Remove extra named arguments: {message}")
|
Some(format!("Remove extra named arguments: {message}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ impl Violation for UndefinedExport {
|
||||||
pub(crate) fn undefined_export(name: &str, range: TextRange, scope: &Scope) -> Vec<Diagnostic> {
|
pub(crate) fn undefined_export(name: &str, range: TextRange, scope: &Scope) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
if !scope.uses_star_imports() {
|
if !scope.uses_star_imports() {
|
||||||
if !scope.defines(name) {
|
if !scope.has(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
UndefinedExport {
|
UndefinedExport {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ impl Violation for UndefinedLocal {
|
||||||
pub(crate) fn undefined_local(checker: &mut Checker, name: &str) {
|
pub(crate) fn undefined_local(checker: &mut Checker, name: &str) {
|
||||||
// If the name hasn't already been defined in the current scope...
|
// If the name hasn't already been defined in the current scope...
|
||||||
let current = checker.semantic_model().scope();
|
let current = checker.semantic_model().scope();
|
||||||
if !current.kind.is_any_function() || current.defines(name) {
|
if !current.kind.is_any_function() || current.has(name) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
use rustpython_parser::ast::{self, Ranged, Stmt};
|
use rustpython_parser::ast::{self, Ranged, Stmt};
|
||||||
use rustpython_parser::{lexer, Mode, Tok};
|
use rustpython_parser::{lexer, Mode, Tok};
|
||||||
|
|
||||||
|
|
@ -61,21 +61,37 @@ impl Violation for UnusedVariable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`TextRange`] of the token after the next match of
|
/// Return the [`TextRange`] of the token before the next match of the predicate
|
||||||
/// the predicate, skipping over any bracketed expressions.
|
fn match_token_before<F>(location: TextSize, locator: &Locator, f: F) -> Option<TextRange>
|
||||||
fn match_token_after<F, T>(located: &T, locator: &Locator, f: F) -> TextRange
|
|
||||||
where
|
where
|
||||||
F: Fn(Tok) -> bool,
|
F: Fn(Tok) -> bool,
|
||||||
T: Ranged,
|
|
||||||
{
|
{
|
||||||
let contents = locator.after(located.start());
|
let contents = locator.after(location);
|
||||||
|
for ((_, range), (tok, _)) in lexer::lex_starts_at(contents, Mode::Module, location)
|
||||||
|
.flatten()
|
||||||
|
.tuple_windows()
|
||||||
|
{
|
||||||
|
if f(tok) {
|
||||||
|
return Some(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the [`TextRange`] of the token after the next match of the predicate, skipping over
|
||||||
|
/// any bracketed expressions.
|
||||||
|
fn match_token_after<F>(location: TextSize, locator: &Locator, f: F) -> Option<TextRange>
|
||||||
|
where
|
||||||
|
F: Fn(Tok) -> bool,
|
||||||
|
{
|
||||||
|
let contents = locator.after(location);
|
||||||
|
|
||||||
// Track the bracket depth.
|
// Track the bracket depth.
|
||||||
let mut par_count = 0u32;
|
let mut par_count = 0u32;
|
||||||
let mut sqb_count = 0u32;
|
let mut sqb_count = 0u32;
|
||||||
let mut brace_count = 0u32;
|
let mut brace_count = 0u32;
|
||||||
|
|
||||||
for ((tok, _), (_, range)) in lexer::lex_starts_at(contents, Mode::Module, located.start())
|
for ((tok, _), (_, range)) in lexer::lex_starts_at(contents, Mode::Module, location)
|
||||||
.flatten()
|
.flatten()
|
||||||
.tuple_windows()
|
.tuple_windows()
|
||||||
{
|
{
|
||||||
|
|
@ -91,97 +107,83 @@ where
|
||||||
}
|
}
|
||||||
Tok::Rpar => {
|
Tok::Rpar => {
|
||||||
par_count = par_count.saturating_sub(1);
|
par_count = par_count.saturating_sub(1);
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if par_count == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Tok::Rsqb => {
|
Tok::Rsqb => {
|
||||||
sqb_count = sqb_count.saturating_sub(1);
|
sqb_count = sqb_count.saturating_sub(1);
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if sqb_count == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Tok::Rbrace => {
|
Tok::Rbrace => {
|
||||||
brace_count = brace_count.saturating_sub(1);
|
brace_count = brace_count.saturating_sub(1);
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if brace_count == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in nested brackets, continue.
|
// If we're in nested brackets, continue.
|
||||||
if par_count > 0 || sqb_count > 0 || brace_count > 0 {
|
if par_count > 0 || sqb_count > 0 || brace_count > 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if f(tok) {
|
if f(tok) {
|
||||||
return range;
|
return Some(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!("No token after matched");
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [`TextRange`] of the token matching the predicate,
|
/// Return the [`TextRange`] of the token matching the predicate or the first mismatched
|
||||||
/// skipping over any bracketed expressions.
|
/// bracket, skipping over any bracketed expressions.
|
||||||
fn match_token<F, T>(located: &T, locator: &Locator, f: F) -> TextRange
|
fn match_token_or_closing_brace<F>(location: TextSize, locator: &Locator, f: F) -> Option<TextRange>
|
||||||
where
|
where
|
||||||
F: Fn(Tok) -> bool,
|
F: Fn(Tok) -> bool,
|
||||||
T: Ranged,
|
|
||||||
{
|
{
|
||||||
let contents = locator.after(located.start());
|
let contents = locator.after(location);
|
||||||
|
|
||||||
// Track the bracket depth.
|
// Track the bracket depth.
|
||||||
let mut par_count = 0;
|
let mut par_count = 0u32;
|
||||||
let mut sqb_count = 0;
|
let mut sqb_count = 0u32;
|
||||||
let mut brace_count = 0;
|
let mut brace_count = 0u32;
|
||||||
|
|
||||||
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, located.start()).flatten() {
|
for (tok, range) in lexer::lex_starts_at(contents, Mode::Module, location).flatten() {
|
||||||
match tok {
|
match tok {
|
||||||
Tok::Lpar => {
|
Tok::Lpar => {
|
||||||
par_count += 1;
|
par_count = par_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
Tok::Lsqb => {
|
Tok::Lsqb => {
|
||||||
sqb_count += 1;
|
sqb_count = sqb_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
Tok::Lbrace => {
|
Tok::Lbrace => {
|
||||||
brace_count += 1;
|
brace_count = brace_count.saturating_add(1);
|
||||||
}
|
}
|
||||||
Tok::Rpar => {
|
Tok::Rpar => {
|
||||||
par_count -= 1;
|
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if par_count == 0 {
|
if par_count == 0 {
|
||||||
continue;
|
return Some(range);
|
||||||
}
|
}
|
||||||
|
par_count = par_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
Tok::Rsqb => {
|
Tok::Rsqb => {
|
||||||
sqb_count -= 1;
|
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if sqb_count == 0 {
|
if sqb_count == 0 {
|
||||||
continue;
|
return Some(range);
|
||||||
}
|
}
|
||||||
|
sqb_count = sqb_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
Tok::Rbrace => {
|
Tok::Rbrace => {
|
||||||
brace_count -= 1;
|
|
||||||
// If this is a closing bracket, continue.
|
|
||||||
if brace_count == 0 {
|
if brace_count == 0 {
|
||||||
continue;
|
return Some(range);
|
||||||
}
|
}
|
||||||
|
brace_count = brace_count.saturating_sub(1);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're in nested brackets, continue.
|
// If we're in nested brackets, continue.
|
||||||
if par_count > 0 || sqb_count > 0 || brace_count > 0 {
|
if par_count > 0 || sqb_count > 0 || brace_count > 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if f(tok) {
|
if f(tok) {
|
||||||
return range;
|
return Some(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unreachable!("No token after matched");
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a [`Edit`] to remove an unused variable assignment, given the
|
/// Generate a [`Edit`] to remove an unused variable assignment, given the
|
||||||
|
|
@ -201,10 +203,10 @@ fn remove_unused_variable(
|
||||||
{
|
{
|
||||||
// If the expression is complex (`x = foo()`), remove the assignment,
|
// If the expression is complex (`x = foo()`), remove the assignment,
|
||||||
// but preserve the right-hand side.
|
// but preserve the right-hand side.
|
||||||
let edit = Edit::deletion(
|
let start = target.start();
|
||||||
target.start(),
|
let end =
|
||||||
match_token_after(target, checker.locator, |tok| tok == Tok::Equal).start(),
|
match_token_after(start, checker.locator, |tok| tok == Tok::Equal)?.start();
|
||||||
);
|
let edit = Edit::deletion(start, end);
|
||||||
Some(Fix::suggested(edit))
|
Some(Fix::suggested(edit))
|
||||||
} else {
|
} else {
|
||||||
// If (e.g.) assigning to a constant (`x = 1`), delete the entire statement.
|
// If (e.g.) assigning to a constant (`x = 1`), delete the entire statement.
|
||||||
|
|
@ -232,10 +234,10 @@ fn remove_unused_variable(
|
||||||
return if contains_effect(value, |id| checker.semantic_model().is_builtin(id)) {
|
return if contains_effect(value, |id| checker.semantic_model().is_builtin(id)) {
|
||||||
// If the expression is complex (`x = foo()`), remove the assignment,
|
// If the expression is complex (`x = foo()`), remove the assignment,
|
||||||
// but preserve the right-hand side.
|
// but preserve the right-hand side.
|
||||||
let edit = Edit::deletion(
|
let start = stmt.start();
|
||||||
stmt.start(),
|
let end =
|
||||||
match_token_after(stmt, checker.locator, |tok| tok == Tok::Equal).start(),
|
match_token_after(start, checker.locator, |tok| tok == Tok::Equal)?.start();
|
||||||
);
|
let edit = Edit::deletion(start, end);
|
||||||
Some(Fix::suggested(edit))
|
Some(Fix::suggested(edit))
|
||||||
} else {
|
} else {
|
||||||
// If (e.g.) assigning to a constant (`x = 1`), delete the entire statement.
|
// If (e.g.) assigning to a constant (`x = 1`), delete the entire statement.
|
||||||
|
|
@ -258,15 +260,20 @@ fn remove_unused_variable(
|
||||||
for item in items {
|
for item in items {
|
||||||
if let Some(optional_vars) = &item.optional_vars {
|
if let Some(optional_vars) = &item.optional_vars {
|
||||||
if optional_vars.range() == range {
|
if optional_vars.range() == range {
|
||||||
let edit = Edit::deletion(
|
// Find the first token before the `as` keyword.
|
||||||
item.context_expr.end(),
|
let start =
|
||||||
// The end of the `Withitem` is the colon, comma, or closing
|
match_token_before(item.context_expr.start(), checker.locator, |tok| {
|
||||||
// parenthesis following the `optional_vars`.
|
tok == Tok::As
|
||||||
match_token(&item.context_expr, checker.locator, |tok| {
|
})?
|
||||||
tok == Tok::Colon || tok == Tok::Comma || tok == Tok::Rpar
|
.end();
|
||||||
})
|
|
||||||
.start(),
|
// Find the first colon, comma, or closing bracket after the `as` keyword.
|
||||||
);
|
let end = match_token_or_closing_brace(start, checker.locator, |tok| {
|
||||||
|
tok == Tok::Colon || tok == Tok::Comma
|
||||||
|
})?
|
||||||
|
.start();
|
||||||
|
|
||||||
|
let edit = Edit::deletion(start, end);
|
||||||
return Some(Fix::suggested(edit));
|
return Some(Fix::suggested(edit));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ F522.py:2:1: F522 [*] `.format` call has unused named argument(s): spam
|
||||||
2 |+"{bar}{}".format(1, bar=2, ) # F522
|
2 |+"{bar}{}".format(1, bar=2, ) # F522
|
||||||
3 3 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
3 3 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||||
4 4 | "{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
4 4 | "{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||||
|
5 5 | # Not fixable
|
||||||
|
|
||||||
F522.py:4:1: F522 [*] `.format` call has unused named argument(s): eggs, ham
|
F522.py:4:1: F522 [*] `.format` call has unused named argument(s): eggs, ham
|
||||||
|
|
|
|
||||||
|
|
@ -40,6 +41,8 @@ F522.py:4:1: F522 [*] `.format` call has unused named argument(s): eggs, ham
|
||||||
5 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
5 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||||
6 | "{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
6 | "{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F522
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ F522
|
||||||
|
7 | # Not fixable
|
||||||
|
8 | (''
|
||||||
|
|
|
|
||||||
= help: Remove extra named arguments: eggs, ham
|
= help: Remove extra named arguments: eggs, ham
|
||||||
|
|
||||||
|
|
@ -49,5 +52,19 @@ F522.py:4:1: F522 [*] `.format` call has unused named argument(s): eggs, ham
|
||||||
3 3 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
3 3 | "{bar:{spam}}".format(bar=2, spam=3) # No issues
|
||||||
4 |-"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
4 |-"{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||||
4 |+"{bar:{spam}}".format(bar=2, spam=3, ) # F522
|
4 |+"{bar:{spam}}".format(bar=2, spam=3, ) # F522
|
||||||
|
5 5 | # Not fixable
|
||||||
|
6 6 | (''
|
||||||
|
7 7 | .format(x=2))
|
||||||
|
|
||||||
|
F522.py:6:2: F522 `.format` call has unused named argument(s): x
|
||||||
|
|
|
||||||
|
6 | "{bar:{spam}}".format(bar=2, spam=3, eggs=4, ham=5) # F522
|
||||||
|
7 | # Not fixable
|
||||||
|
8 | (''
|
||||||
|
| __^
|
||||||
|
9 | | .format(x=2))
|
||||||
|
| |_____________^ F522
|
||||||
|
|
|
||||||
|
= help: Remove extra named arguments: x
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -377,126 +377,145 @@ F841_3.py:77:25: F841 [*] Local variable `cm` is assigned to but never used
|
||||||
79 79 |
|
79 79 |
|
||||||
80 80 |
|
80 80 |
|
||||||
|
|
||||||
F841_3.py:87:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
F841_3.py:87:26: F841 [*] Local variable `cm` is assigned to but never used
|
||||||
|
|
|
|
||||||
87 | def f():
|
87 | def f():
|
||||||
88 | toplevel = tt = lexer.get_token()
|
88 | with (Nested(m)) as (cm):
|
||||||
| ^^^^^^^^ F841
|
| ^^ F841
|
||||||
89 | if not tt:
|
89 | pass
|
||||||
90 | break
|
|
||||||
|
|
|
|
||||||
= help: Remove assignment to unused variable `toplevel`
|
= help: Remove assignment to unused variable `cm`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
84 84 |
|
84 84 |
|
||||||
85 85 |
|
85 85 |
|
||||||
86 86 | def f():
|
86 86 | def f():
|
||||||
87 |- toplevel = tt = lexer.get_token()
|
87 |- with (Nested(m)) as (cm):
|
||||||
87 |+ tt = lexer.get_token()
|
87 |+ with (Nested(m)):
|
||||||
88 88 | if not tt:
|
88 88 | pass
|
||||||
89 89 | break
|
89 89 |
|
||||||
90 90 |
|
90 90 |
|
||||||
|
|
||||||
F841_3.py:93:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
F841_3.py:92:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
||||||
|
|
|
|
||||||
93 | def f():
|
92 | def f():
|
||||||
94 | toplevel = tt = lexer.get_token()
|
93 | toplevel = tt = lexer.get_token()
|
||||||
|
| ^^^^^^^^ F841
|
||||||
|
94 | if not tt:
|
||||||
|
95 | break
|
||||||
|
|
|
||||||
|
= help: Remove assignment to unused variable `toplevel`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
89 89 |
|
||||||
|
90 90 |
|
||||||
|
91 91 | def f():
|
||||||
|
92 |- toplevel = tt = lexer.get_token()
|
||||||
|
92 |+ tt = lexer.get_token()
|
||||||
|
93 93 | if not tt:
|
||||||
|
94 94 | break
|
||||||
|
95 95 |
|
||||||
|
|
||||||
|
F841_3.py:98:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
||||||
|
|
|
||||||
|
98 | def f():
|
||||||
|
99 | toplevel = tt = lexer.get_token()
|
||||||
| ^^^^^^^^ F841
|
| ^^^^^^^^ F841
|
||||||
|
|
|
|
||||||
= help: Remove assignment to unused variable `toplevel`
|
= help: Remove assignment to unused variable `toplevel`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
90 90 |
|
|
||||||
91 91 |
|
|
||||||
92 92 | def f():
|
|
||||||
93 |- toplevel = tt = lexer.get_token()
|
|
||||||
93 |+ tt = lexer.get_token()
|
|
||||||
94 94 |
|
|
||||||
95 95 |
|
95 95 |
|
||||||
96 96 | def f():
|
96 96 |
|
||||||
|
97 97 | def f():
|
||||||
|
98 |- toplevel = tt = lexer.get_token()
|
||||||
|
98 |+ tt = lexer.get_token()
|
||||||
|
99 99 |
|
||||||
|
100 100 |
|
||||||
|
101 101 | def f():
|
||||||
|
|
||||||
F841_3.py:93:16: F841 [*] Local variable `tt` is assigned to but never used
|
F841_3.py:98:16: F841 [*] Local variable `tt` is assigned to but never used
|
||||||
|
|
|
|
||||||
93 | def f():
|
98 | def f():
|
||||||
94 | toplevel = tt = lexer.get_token()
|
99 | toplevel = tt = lexer.get_token()
|
||||||
| ^^ F841
|
| ^^ F841
|
||||||
|
|
|
|
||||||
= help: Remove assignment to unused variable `tt`
|
= help: Remove assignment to unused variable `tt`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
90 90 |
|
|
||||||
91 91 |
|
|
||||||
92 92 | def f():
|
|
||||||
93 |- toplevel = tt = lexer.get_token()
|
|
||||||
93 |+ toplevel = lexer.get_token()
|
|
||||||
94 94 |
|
|
||||||
95 95 |
|
95 95 |
|
||||||
96 96 | def f():
|
96 96 |
|
||||||
|
97 97 | def f():
|
||||||
F841_3.py:97:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
98 |- toplevel = tt = lexer.get_token()
|
||||||
|
|
98 |+ toplevel = lexer.get_token()
|
||||||
97 | def f():
|
|
||||||
98 | toplevel = (a, b) = lexer.get_token()
|
|
||||||
| ^^^^^^^^ F841
|
|
||||||
|
|
|
||||||
= help: Remove assignment to unused variable `toplevel`
|
|
||||||
|
|
||||||
ℹ Suggested fix
|
|
||||||
94 94 |
|
|
||||||
95 95 |
|
|
||||||
96 96 | def f():
|
|
||||||
97 |- toplevel = (a, b) = lexer.get_token()
|
|
||||||
97 |+ (a, b) = lexer.get_token()
|
|
||||||
98 98 |
|
|
||||||
99 99 |
|
99 99 |
|
||||||
100 100 | def f():
|
100 100 |
|
||||||
|
101 101 | def f():
|
||||||
|
|
||||||
F841_3.py:101:14: F841 [*] Local variable `toplevel` is assigned to but never used
|
F841_3.py:102:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
||||||
|
|
|
|
||||||
101 | def f():
|
102 | def f():
|
||||||
102 | (a, b) = toplevel = lexer.get_token()
|
103 | toplevel = (a, b) = lexer.get_token()
|
||||||
| ^^^^^^^^ F841
|
|
||||||
|
|
|
||||||
= help: Remove assignment to unused variable `toplevel`
|
|
||||||
|
|
||||||
ℹ Suggested fix
|
|
||||||
98 98 |
|
|
||||||
99 99 |
|
|
||||||
100 100 | def f():
|
|
||||||
101 |- (a, b) = toplevel = lexer.get_token()
|
|
||||||
101 |+ (a, b) = lexer.get_token()
|
|
||||||
102 102 |
|
|
||||||
103 103 |
|
|
||||||
104 104 | def f():
|
|
||||||
|
|
||||||
F841_3.py:105:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
|
||||||
|
|
|
||||||
105 | def f():
|
|
||||||
106 | toplevel = tt = 1
|
|
||||||
| ^^^^^^^^ F841
|
| ^^^^^^^^ F841
|
||||||
|
|
|
|
||||||
= help: Remove assignment to unused variable `toplevel`
|
= help: Remove assignment to unused variable `toplevel`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
102 102 |
|
99 99 |
|
||||||
|
100 100 |
|
||||||
|
101 101 | def f():
|
||||||
|
102 |- toplevel = (a, b) = lexer.get_token()
|
||||||
|
102 |+ (a, b) = lexer.get_token()
|
||||||
103 103 |
|
103 103 |
|
||||||
104 104 | def f():
|
104 104 |
|
||||||
105 |- toplevel = tt = 1
|
105 105 | def f():
|
||||||
105 |+ tt = 1
|
|
||||||
|
|
||||||
F841_3.py:105:16: F841 [*] Local variable `tt` is assigned to but never used
|
F841_3.py:106:14: F841 [*] Local variable `toplevel` is assigned to but never used
|
||||||
|
|
|
|
||||||
105 | def f():
|
106 | def f():
|
||||||
106 | toplevel = tt = 1
|
107 | (a, b) = toplevel = lexer.get_token()
|
||||||
|
| ^^^^^^^^ F841
|
||||||
|
|
|
||||||
|
= help: Remove assignment to unused variable `toplevel`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
103 103 |
|
||||||
|
104 104 |
|
||||||
|
105 105 | def f():
|
||||||
|
106 |- (a, b) = toplevel = lexer.get_token()
|
||||||
|
106 |+ (a, b) = lexer.get_token()
|
||||||
|
107 107 |
|
||||||
|
108 108 |
|
||||||
|
109 109 | def f():
|
||||||
|
|
||||||
|
F841_3.py:110:5: F841 [*] Local variable `toplevel` is assigned to but never used
|
||||||
|
|
|
||||||
|
110 | def f():
|
||||||
|
111 | toplevel = tt = 1
|
||||||
|
| ^^^^^^^^ F841
|
||||||
|
|
|
||||||
|
= help: Remove assignment to unused variable `toplevel`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
107 107 |
|
||||||
|
108 108 |
|
||||||
|
109 109 | def f():
|
||||||
|
110 |- toplevel = tt = 1
|
||||||
|
110 |+ tt = 1
|
||||||
|
|
||||||
|
F841_3.py:110:16: F841 [*] Local variable `tt` is assigned to but never used
|
||||||
|
|
|
||||||
|
110 | def f():
|
||||||
|
111 | toplevel = tt = 1
|
||||||
| ^^ F841
|
| ^^ F841
|
||||||
|
|
|
|
||||||
= help: Remove assignment to unused variable `tt`
|
= help: Remove assignment to unused variable `tt`
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
102 102 |
|
107 107 |
|
||||||
103 103 |
|
108 108 |
|
||||||
104 104 | def f():
|
109 109 | def f():
|
||||||
105 |- toplevel = tt = 1
|
110 |- toplevel = tt = 1
|
||||||
105 |+ toplevel = 1
|
110 |+ toplevel = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use rustpython_parser::ast::{Expr, ExprName, Ranged};
|
use rustpython_parser::ast::{Expr, Ranged};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
|
@ -6,7 +6,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for iterations over `set` literals and comprehensions.
|
/// Checks for iterations over `set` literals.
|
||||||
///
|
///
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Iterating over a `set` is less efficient than iterating over a sequence
|
/// Iterating over a `set` is less efficient than iterating over a sequence
|
||||||
|
|
@ -38,23 +38,7 @@ impl Violation for IterationOverSet {
|
||||||
|
|
||||||
/// PLC0208
|
/// PLC0208
|
||||||
pub(crate) fn iteration_over_set(checker: &mut Checker, expr: &Expr) {
|
pub(crate) fn iteration_over_set(checker: &mut Checker, expr: &Expr) {
|
||||||
let is_set = match expr {
|
if expr.is_set_expr() {
|
||||||
// Ex) `for i in {1, 2, 3}`
|
|
||||||
Expr::Set(_) => true,
|
|
||||||
// Ex)` for i in {n for n in range(1, 4)}`
|
|
||||||
Expr::SetComp(_) => true,
|
|
||||||
// Ex) `for i in set(1, 2, 3)`
|
|
||||||
Expr::Call(call) => {
|
|
||||||
if let Expr::Name(ExprName { id, .. }) = call.func.as_ref() {
|
|
||||||
id.as_str() == "set" && checker.semantic_model().is_builtin("set")
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_set {
|
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(IterationOverSet, expr.range()));
|
.push(Diagnostic::new(IterationOverSet, expr.range()));
|
||||||
|
|
|
||||||
|
|
@ -10,62 +10,44 @@ iteration_over_set.py:3:13: PLC0208 Use a sequence type instead of a `set` when
|
||||||
6 | print(f"I like {item}.")
|
6 | print(f"I like {item}.")
|
||||||
|
|
|
|
||||||
|
|
||||||
iteration_over_set.py:6:13: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
iteration_over_set.py:6:28: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
||||||
|
|
|
||||||
6 | print(f"I like {item}.")
|
|
||||||
7 |
|
|
||||||
8 | for item in set(("apples", "lemons", "water")): # flags set() calls
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC0208
|
|
||||||
9 | print(f"I like {item}.")
|
|
||||||
|
|
|
||||||
|
|
||||||
iteration_over_set.py:9:15: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
|
||||||
|
|
|
|
||||||
9 | print(f"I like {item}.")
|
6 | print(f"I like {item}.")
|
||||||
10 |
|
7 |
|
||||||
11 | for number in {i for i in range(10)}: # flags set comprehensions
|
8 | numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ PLC0208
|
|
||||||
12 | print(number)
|
|
||||||
|
|
|
||||||
|
|
||||||
iteration_over_set.py:12:28: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
|
||||||
|
|
|
||||||
12 | print(number)
|
|
||||||
13 |
|
|
||||||
14 | numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
|
||||||
| ^^^^^^^^^ PLC0208
|
| ^^^^^^^^^ PLC0208
|
||||||
15 |
|
9 |
|
||||||
16 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
10 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
||||||
|
|
|
|
||||||
|
|
||||||
iteration_over_set.py:14:27: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
iteration_over_set.py:8:27: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
||||||
|
|
|
|
||||||
14 | numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
8 | numbers_list = [i for i in {1, 2, 3}] # flags sets in list comprehensions
|
||||||
15 |
|
9 |
|
||||||
16 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
10 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
||||||
| ^^^^^^^^^ PLC0208
|
| ^^^^^^^^^ PLC0208
|
||||||
17 |
|
11 |
|
||||||
18 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
12 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
||||||
|
|
|
|
||||||
|
|
||||||
iteration_over_set.py:16:36: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
iteration_over_set.py:10:36: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
||||||
|
|
|
|
||||||
16 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
10 | numbers_set = {i for i in {1, 2, 3}} # flags sets in set comprehensions
|
||||||
17 |
|
11 |
|
||||||
18 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
12 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
||||||
| ^^^^^^^^^ PLC0208
|
| ^^^^^^^^^ PLC0208
|
||||||
19 |
|
13 |
|
||||||
20 | numbers_gen = (i for i in {1, 2, 3}) # flags sets in generator expressions
|
14 | numbers_gen = (i for i in {1, 2, 3}) # flags sets in generator expressions
|
||||||
|
|
|
|
||||||
|
|
||||||
iteration_over_set.py:18:27: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
iteration_over_set.py:12:27: PLC0208 Use a sequence type instead of a `set` when iterating over values
|
||||||
|
|
|
|
||||||
18 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
12 | numbers_dict = {str(i): i for i in {1, 2, 3}} # flags sets in dict comprehensions
|
||||||
19 |
|
13 |
|
||||||
20 | numbers_gen = (i for i in {1, 2, 3}) # flags sets in generator expressions
|
14 | numbers_gen = (i for i in {1, 2, 3}) # flags sets in generator expressions
|
||||||
| ^^^^^^^^^ PLC0208
|
| ^^^^^^^^^ PLC0208
|
||||||
21 |
|
15 |
|
||||||
22 | # Non-errors
|
16 | # Non-errors
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ruff_cli"
|
name = "ruff_cli"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
authors = ["Charlie Marsh <charlie.r.marsh@gmail.com>"]
|
||||||
edition = { workspace = true }
|
edition = { workspace = true }
|
||||||
rust-version = { workspace = true }
|
rust-version = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
use crate::node::AnyNodeRef;
|
||||||
|
use ruff_text_size::TextRange;
|
||||||
|
use rustpython_ast::{
|
||||||
|
Arguments, Expr, Identifier, Ranged, StmtAsyncFunctionDef, StmtFunctionDef, Suite,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Enum that represents any python function definition.
|
||||||
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
pub enum AnyFunctionDefinition<'a> {
|
||||||
|
FunctionDefinition(&'a StmtFunctionDef),
|
||||||
|
AsyncFunctionDefinition(&'a StmtAsyncFunctionDef),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AnyFunctionDefinition<'a> {
|
||||||
|
pub const fn cast_ref(reference: AnyNodeRef<'a>) -> Option<Self> {
|
||||||
|
match reference {
|
||||||
|
AnyNodeRef::StmtAsyncFunctionDef(definition) => {
|
||||||
|
Some(Self::AsyncFunctionDefinition(definition))
|
||||||
|
}
|
||||||
|
AnyNodeRef::StmtFunctionDef(definition) => Some(Self::FunctionDefinition(definition)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some` if this is a [`StmtFunctionDef`] and `None` otherwise.
|
||||||
|
pub const fn as_function_definition(self) -> Option<&'a StmtFunctionDef> {
|
||||||
|
if let Self::FunctionDefinition(definition) = self {
|
||||||
|
Some(definition)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `Some` if this is a [`StmtAsyncFunctionDef`] and `None` otherwise.
|
||||||
|
pub const fn as_async_function_definition(self) -> Option<&'a StmtAsyncFunctionDef> {
|
||||||
|
if let Self::AsyncFunctionDefinition(definition) = self {
|
||||||
|
Some(definition)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the function's name
|
||||||
|
pub const fn name(self) -> &'a Identifier {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => &definition.name,
|
||||||
|
Self::AsyncFunctionDefinition(definition) => &definition.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the function arguments (parameters).
|
||||||
|
pub fn arguments(self) -> &'a Arguments {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => definition.args.as_ref(),
|
||||||
|
Self::AsyncFunctionDefinition(definition) => definition.args.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the function's body
|
||||||
|
pub const fn body(self) -> &'a Suite {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => &definition.body,
|
||||||
|
Self::AsyncFunctionDefinition(definition) => &definition.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the decorators attributing the function.
|
||||||
|
pub fn decorators(self) -> &'a [Expr] {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => &definition.decorator_list,
|
||||||
|
Self::AsyncFunctionDefinition(definition) => &definition.decorator_list,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn returns(self) -> Option<&'a Expr> {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => definition.returns.as_deref(),
|
||||||
|
Self::AsyncFunctionDefinition(definition) => definition.returns.as_deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_comments(self) -> Option<&'a str> {
|
||||||
|
match self {
|
||||||
|
Self::FunctionDefinition(definition) => definition.type_comment.as_deref(),
|
||||||
|
Self::AsyncFunctionDefinition(definition) => definition.type_comment.as_deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this is [`Self::AsyncFunctionDefinition`]
|
||||||
|
pub const fn is_async(self) -> bool {
|
||||||
|
matches!(self, Self::AsyncFunctionDefinition(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ranged for AnyFunctionDefinition<'_> {
|
||||||
|
fn range(&self) -> TextRange {
|
||||||
|
match self {
|
||||||
|
AnyFunctionDefinition::FunctionDefinition(definition) => definition.range(),
|
||||||
|
AnyFunctionDefinition::AsyncFunctionDefinition(definition) => definition.range(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a StmtFunctionDef> for AnyFunctionDefinition<'a> {
|
||||||
|
fn from(value: &'a StmtFunctionDef) -> Self {
|
||||||
|
Self::FunctionDefinition(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a StmtAsyncFunctionDef> for AnyFunctionDefinition<'a> {
|
||||||
|
fn from(value: &'a StmtAsyncFunctionDef) -> Self {
|
||||||
|
Self::AsyncFunctionDefinition(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ pub mod all;
|
||||||
pub mod call_path;
|
pub mod call_path;
|
||||||
pub mod cast;
|
pub mod cast;
|
||||||
pub mod comparable;
|
pub mod comparable;
|
||||||
|
pub mod function;
|
||||||
pub mod hashable;
|
pub mod hashable;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod imports;
|
pub mod imports;
|
||||||
|
|
|
||||||
43
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary_expression.py
vendored
Normal file
43
crates/ruff_python_formatter/resources/test/fixtures/ruff/expression/binary_expression.py
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
(aaaaaaaa
|
||||||
|
+ # trailing operator comment
|
||||||
|
b # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
aaaaaaaaaaaaaa + caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
aaaaaaaaaaaaaa + [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee]
|
||||||
|
aaaaaaaaaaaaaa + (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
aaaaaaaaaaaaaa + { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
aaaaaaaaaaaaaa + (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eee
|
||||||
|
] # comment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
[a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c ]
|
||||||
|
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content + b
|
||||||
|
)
|
||||||
|
|
@ -12,7 +12,7 @@ pub(crate) trait PyFormatterExtensions<'ast, 'buf> {
|
||||||
///
|
///
|
||||||
/// * [`NodeLevel::Module`]: Up to two empty lines
|
/// * [`NodeLevel::Module`]: Up to two empty lines
|
||||||
/// * [`NodeLevel::CompoundStatement`]: Up to one empty line
|
/// * [`NodeLevel::CompoundStatement`]: Up to one empty line
|
||||||
/// * [`NodeLevel::Parenthesized`]: No empty lines
|
/// * [`NodeLevel::Expression`]: No empty lines
|
||||||
fn join_nodes<'fmt>(&'fmt mut self, level: NodeLevel) -> JoinNodesBuilder<'fmt, 'ast, 'buf>;
|
fn join_nodes<'fmt>(&'fmt mut self, level: NodeLevel) -> JoinNodesBuilder<'fmt, 'ast, 'buf>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,18 +48,18 @@ impl<'fmt, 'ast, 'buf> JoinNodesBuilder<'fmt, 'ast, 'buf> {
|
||||||
{
|
{
|
||||||
let node_level = self.node_level;
|
let node_level = self.node_level;
|
||||||
let separator = format_with(|f: &mut PyFormatter| match node_level {
|
let separator = format_with(|f: &mut PyFormatter| match node_level {
|
||||||
NodeLevel::TopLevel => match lines_before(f.context().contents(), node.start()) {
|
NodeLevel::TopLevel => match lines_before(node.start(), f.context().contents()) {
|
||||||
0 | 1 => hard_line_break().fmt(f),
|
0 | 1 => hard_line_break().fmt(f),
|
||||||
2 => empty_line().fmt(f),
|
2 => empty_line().fmt(f),
|
||||||
_ => write!(f, [empty_line(), empty_line()]),
|
_ => write!(f, [empty_line(), empty_line()]),
|
||||||
},
|
},
|
||||||
NodeLevel::CompoundStatement => {
|
NodeLevel::CompoundStatement => {
|
||||||
match lines_before(f.context().contents(), node.start()) {
|
match lines_before(node.start(), f.context().contents()) {
|
||||||
0 | 1 => hard_line_break().fmt(f),
|
0 | 1 => hard_line_break().fmt(f),
|
||||||
_ => empty_line().fmt(f),
|
_ => empty_line().fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeLevel::Parenthesized => hard_line_break().fmt(f),
|
NodeLevel::Expression => hard_line_break().fmt(f),
|
||||||
});
|
});
|
||||||
|
|
||||||
self.entry_with_separator(&separator, content);
|
self.entry_with_separator(&separator, content);
|
||||||
|
|
@ -200,7 +200,7 @@ no_leading_newline = 30"#
|
||||||
// Removes all empty lines
|
// Removes all empty lines
|
||||||
#[test]
|
#[test]
|
||||||
fn ranged_builder_parenthesized_level() {
|
fn ranged_builder_parenthesized_level() {
|
||||||
let printed = format_ranged(NodeLevel::Parenthesized);
|
let printed = format_ranged(NodeLevel::Expression);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&printed,
|
&printed,
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingComments<'_> {
|
||||||
for comment in leading_comments {
|
for comment in leading_comments {
|
||||||
let slice = comment.slice();
|
let slice = comment.slice();
|
||||||
|
|
||||||
let lines_after_comment = lines_after(f.context().contents(), slice.end());
|
let lines_after_comment = lines_after(slice.end(), f.context().contents());
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
[format_comment(comment), empty_lines(lines_after_comment)]
|
[format_comment(comment), empty_lines(lines_after_comment)]
|
||||||
|
|
@ -80,7 +80,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {
|
||||||
if let Some(first_leading) = self.comments.first() {
|
if let Some(first_leading) = self.comments.first() {
|
||||||
// Leading comments only preserves the lines after the comment but not before.
|
// Leading comments only preserves the lines after the comment but not before.
|
||||||
// Insert the necessary lines.
|
// Insert the necessary lines.
|
||||||
if lines_before(f.context().contents(), first_leading.slice().start()) > 1 {
|
if lines_before(first_leading.slice().start(), f.context().contents()) > 1 {
|
||||||
write!(f, [empty_line()])?;
|
write!(f, [empty_line()])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ impl Format<PyFormatContext<'_>> for FormatLeadingAlternateBranchComments<'_> {
|
||||||
} else if let Some(last_preceding) = self.last_node {
|
} else if let Some(last_preceding) = self.last_node {
|
||||||
// The leading comments formatting ensures that it preserves the right amount of lines after
|
// The leading comments formatting ensures that it preserves the right amount of lines after
|
||||||
// We need to take care of this ourselves, if there's no leading `else` comment.
|
// We need to take care of this ourselves, if there's no leading `else` comment.
|
||||||
if lines_after(f.context().contents(), last_preceding.end()) > 1 {
|
if lines_after(last_preceding.end(), f.context().contents()) > 1 {
|
||||||
write!(f, [empty_line()])?;
|
write!(f, [empty_line()])?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +132,7 @@ impl Format<PyFormatContext<'_>> for FormatTrailingComments<'_> {
|
||||||
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
has_trailing_own_line_comment |= trailing.position().is_own_line();
|
||||||
|
|
||||||
if has_trailing_own_line_comment {
|
if has_trailing_own_line_comment {
|
||||||
let lines_before_comment = lines_before(f.context().contents(), slice.start());
|
let lines_before_comment = lines_before(slice.start(), f.context().contents());
|
||||||
|
|
||||||
// A trailing comment at the end of a body or list
|
// A trailing comment at the end of a body or list
|
||||||
// ```python
|
// ```python
|
||||||
|
|
@ -175,20 +175,26 @@ pub(crate) fn dangling_node_comments<T>(node: &T) -> FormatDanglingComments
|
||||||
where
|
where
|
||||||
T: AstNode,
|
T: AstNode,
|
||||||
{
|
{
|
||||||
FormatDanglingComments {
|
FormatDanglingComments::Node(node.as_any_node_ref())
|
||||||
node: node.as_any_node_ref(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FormatDanglingComments<'a> {
|
pub(crate) fn dangling_comments(comments: &[SourceComment]) -> FormatDanglingComments {
|
||||||
node: AnyNodeRef<'a>,
|
FormatDanglingComments::Comments(comments)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum FormatDanglingComments<'a> {
|
||||||
|
Node(AnyNodeRef<'a>),
|
||||||
|
Comments(&'a [SourceComment]),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
|
fn fmt(&self, f: &mut Formatter<PyFormatContext>) -> FormatResult<()> {
|
||||||
let comments = f.context().comments().clone();
|
let comments = f.context().comments().clone();
|
||||||
|
|
||||||
let dangling_comments = comments.dangling_comments(self.node);
|
let dangling_comments = match self {
|
||||||
|
Self::Comments(comments) => comments,
|
||||||
|
Self::Node(node) => comments.dangling_comments(*node),
|
||||||
|
};
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for comment in dangling_comments {
|
for comment in dangling_comments {
|
||||||
|
|
@ -200,7 +206,7 @@ impl Format<PyFormatContext<'_>> for FormatDanglingComments<'_> {
|
||||||
f,
|
f,
|
||||||
[
|
[
|
||||||
format_comment(comment),
|
format_comment(comment),
|
||||||
empty_lines(lines_after(f.context().contents(), comment.slice().end()))
|
empty_lines(lines_after(comment.slice().end(), f.context().contents()))
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
@ -301,7 +307,7 @@ impl Format<PyFormatContext<'_>> for FormatEmptyLines {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove all whitespace in parenthesized expressions
|
// Remove all whitespace in parenthesized expressions
|
||||||
NodeLevel::Parenthesized => write!(f, [hard_line_break()]),
|
NodeLevel::Expression => write!(f, [hard_line_break()]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ use crate::comments::map::MultiMap;
|
||||||
use crate::comments::node_key::NodeRefEqualityKey;
|
use crate::comments::node_key::NodeRefEqualityKey;
|
||||||
use crate::comments::visitor::CommentsVisitor;
|
use crate::comments::visitor::CommentsVisitor;
|
||||||
pub(crate) use format::{
|
pub(crate) use format::{
|
||||||
dangling_node_comments, leading_alternate_branch_comments, leading_node_comments,
|
dangling_comments, dangling_node_comments, leading_alternate_branch_comments,
|
||||||
trailing_comments, trailing_node_comments,
|
leading_node_comments, trailing_comments, trailing_node_comments,
|
||||||
};
|
};
|
||||||
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
use ruff_formatter::{SourceCode, SourceCodeSlice};
|
||||||
use ruff_python_ast::node::AnyNodeRef;
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
|
|
||||||
|
|
@ -520,8 +520,8 @@ fn handle_trailing_end_of_line_condition_comment<'a>(
|
||||||
if preceding.ptr_eq(last_before_colon) {
|
if preceding.ptr_eq(last_before_colon) {
|
||||||
let mut start = preceding.end();
|
let mut start = preceding.end();
|
||||||
while let Some((offset, c)) = find_first_non_trivia_character_in_range(
|
while let Some((offset, c)) = find_first_non_trivia_character_in_range(
|
||||||
locator.contents(),
|
|
||||||
TextRange::new(start, following.start()),
|
TextRange::new(start, following.start()),
|
||||||
|
locator.contents(),
|
||||||
) {
|
) {
|
||||||
match c {
|
match c {
|
||||||
':' => {
|
':' => {
|
||||||
|
|
@ -655,7 +655,7 @@ fn handle_trailing_binary_expression_left_or_operator_comment<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let operator_offset = loop {
|
let operator_offset = loop {
|
||||||
match find_first_non_trivia_character_in_range(locator.contents(), between_operands_range) {
|
match find_first_non_trivia_character_in_range(between_operands_range, locator.contents()) {
|
||||||
// Skip over closing parens
|
// Skip over closing parens
|
||||||
Some((offset, ')')) => {
|
Some((offset, ')')) => {
|
||||||
between_operands_range =
|
between_operands_range =
|
||||||
|
|
@ -733,17 +733,17 @@ fn find_pos_only_slash_offset(
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
) -> Option<TextSize> {
|
) -> Option<TextSize> {
|
||||||
// First find the comma separating the two arguments
|
// First find the comma separating the two arguments
|
||||||
find_first_non_trivia_character_in_range(locator.contents(), between_arguments_range).and_then(
|
find_first_non_trivia_character_in_range(between_arguments_range, locator.contents()).and_then(
|
||||||
|(comma_offset, comma)| {
|
|(comma_offset, comma)| {
|
||||||
debug_assert_eq!(comma, ',');
|
debug_assert_eq!(comma, ',');
|
||||||
|
|
||||||
// Then find the position of the `/` operator
|
// Then find the position of the `/` operator
|
||||||
find_first_non_trivia_character_in_range(
|
find_first_non_trivia_character_in_range(
|
||||||
locator.contents(),
|
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
comma_offset + TextSize::new(1),
|
comma_offset + TextSize::new(1),
|
||||||
between_arguments_range.end(),
|
between_arguments_range.end(),
|
||||||
),
|
),
|
||||||
|
locator.contents(),
|
||||||
)
|
)
|
||||||
.map(|(offset, c)| {
|
.map(|(offset, c)| {
|
||||||
debug_assert_eq!(c, '/');
|
debug_assert_eq!(c, '/');
|
||||||
|
|
|
||||||
|
|
@ -82,5 +82,5 @@ pub(crate) enum NodeLevel {
|
||||||
CompoundStatement,
|
CompoundStatement,
|
||||||
|
|
||||||
/// Formatting nodes that are enclosed in a parenthesized expression.
|
/// Formatting nodes that are enclosed in a parenthesized expression.
|
||||||
Parenthesized,
|
Expression,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprAttribute;
|
use rustpython_parser::ast::ExprAttribute;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAttribute> for FormatExprAttribute {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprAttribute {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprAwait;
|
use rustpython_parser::ast::ExprAwait;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprAwait> for FormatExprAwait {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprAwait {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,215 @@
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::comments::trailing_comments;
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use crate::expression::parentheses::{
|
||||||
use rustpython_parser::ast::ExprBinOp;
|
default_expression_needs_parentheses, NeedsParentheses, Parenthesize,
|
||||||
|
};
|
||||||
|
use crate::expression::Parentheses;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::FormatNodeRule;
|
||||||
|
use ruff_formatter::{
|
||||||
|
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRuleWithOptions,
|
||||||
|
};
|
||||||
|
use ruff_python_ast::node::AstNode;
|
||||||
|
use rustpython_parser::ast::{
|
||||||
|
Constant, Expr, ExprAttribute, ExprBinOp, ExprConstant, ExprUnaryOp, Operator, Unaryop,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExprBinOp;
|
pub struct FormatExprBinOp {
|
||||||
|
parentheses: Option<Parentheses>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRuleWithOptions<ExprBinOp, PyFormatContext<'_>> for FormatExprBinOp {
|
||||||
|
type Options = Option<Parentheses>;
|
||||||
|
|
||||||
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
|
self.parentheses = options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
impl FormatNodeRule<ExprBinOp> for FormatExprBinOp {
|
||||||
fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprBinOp, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
write!(f, [verbatim_text(item.range)])
|
let ExprBinOp {
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
op,
|
||||||
|
range: _,
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
let should_break_right = self.parentheses == Some(Parentheses::Custom);
|
||||||
|
|
||||||
|
if should_break_right {
|
||||||
|
let left = left.format().memoized();
|
||||||
|
let right = right.format().memoized();
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[best_fitting![
|
||||||
|
// The whole expression on a single line
|
||||||
|
format_args![left, space(), op.format(), space(), right],
|
||||||
|
// Break the right, but keep the left flat
|
||||||
|
format_args![
|
||||||
|
left,
|
||||||
|
space(),
|
||||||
|
op.format(),
|
||||||
|
space(),
|
||||||
|
group(&right).should_expand(true),
|
||||||
|
],
|
||||||
|
// Break after the operator, try to keep the right flat, otherwise expand it
|
||||||
|
format_args![
|
||||||
|
text("("),
|
||||||
|
block_indent(&format_args![
|
||||||
|
left,
|
||||||
|
hard_line_break(),
|
||||||
|
op.format(),
|
||||||
|
space(),
|
||||||
|
group(&right),
|
||||||
|
]),
|
||||||
|
text(")")
|
||||||
|
],
|
||||||
|
]]
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let operator_comments = comments.dangling_comments(item.as_any_node_ref());
|
||||||
|
let needs_space = !is_simple_power_expression(item);
|
||||||
|
|
||||||
|
let before_operator_space = if needs_space {
|
||||||
|
soft_line_break_or_space()
|
||||||
|
} else {
|
||||||
|
soft_line_break()
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[
|
||||||
|
left.format(),
|
||||||
|
before_operator_space,
|
||||||
|
op.format(),
|
||||||
|
trailing_comments(operator_comments),
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Format the operator on its own line if the operator has trailing comments and the right side has leading comments.
|
||||||
|
if !operator_comments.is_empty() && comments.has_leading_comments(right.as_ref().into())
|
||||||
|
{
|
||||||
|
write!(f, [hard_line_break()])?;
|
||||||
|
} else if needs_space {
|
||||||
|
write!(f, [space()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(f, [group(&right.format())])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dangling_comments(&self, _node: &ExprBinOp, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// Handled inside of `fmt_fields`
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn is_simple_power_expression(expr: &ExprBinOp) -> bool {
|
||||||
|
expr.op.is_pow() && is_simple_power_operand(&expr.left) && is_simple_power_operand(&expr.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return `true` if an [`Expr`] adheres to [Black's definition](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#line-breaks-binary-operators)
|
||||||
|
/// of a non-complex expression, in the context of a power operation.
|
||||||
|
const fn is_simple_power_operand(expr: &Expr) -> bool {
|
||||||
|
match expr {
|
||||||
|
Expr::UnaryOp(ExprUnaryOp {
|
||||||
|
op: Unaryop::Not, ..
|
||||||
|
}) => false,
|
||||||
|
Expr::Constant(ExprConstant {
|
||||||
|
value: Constant::Complex { .. } | Constant::Float(_) | Constant::Int(_),
|
||||||
|
..
|
||||||
|
}) => true,
|
||||||
|
Expr::Name(_) => true,
|
||||||
|
Expr::UnaryOp(ExprUnaryOp { operand, .. }) => is_simple_power_operand(operand),
|
||||||
|
Expr::Attribute(ExprAttribute { value, .. }) => is_simple_power_operand(value),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct FormatOperator;
|
||||||
|
|
||||||
|
impl<'ast> AsFormat<PyFormatContext<'ast>> for Operator {
|
||||||
|
type Format<'a> = FormatRefWithRule<'a, Operator, FormatOperator, PyFormatContext<'ast>>;
|
||||||
|
|
||||||
|
fn format(&self) -> Self::Format<'_> {
|
||||||
|
FormatRefWithRule::new(self, FormatOperator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ast> IntoFormat<PyFormatContext<'ast>> for Operator {
|
||||||
|
type Format = FormatOwnedWithRule<Operator, FormatOperator, PyFormatContext<'ast>>;
|
||||||
|
fn into_format(self) -> Self::Format {
|
||||||
|
FormatOwnedWithRule::new(self, FormatOperator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRule<Operator, PyFormatContext<'_>> for FormatOperator {
|
||||||
|
fn fmt(&self, item: &Operator, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
||||||
|
let operator = match item {
|
||||||
|
Operator::Add => "+",
|
||||||
|
Operator::Sub => "-",
|
||||||
|
Operator::Mult => "*",
|
||||||
|
Operator::MatMult => "@",
|
||||||
|
Operator::Div => "/",
|
||||||
|
Operator::Mod => "%",
|
||||||
|
Operator::Pow => "**",
|
||||||
|
Operator::LShift => "<<",
|
||||||
|
Operator::RShift => ">>",
|
||||||
|
Operator::BitOr => "|",
|
||||||
|
Operator::BitXor => "^",
|
||||||
|
Operator::BitAnd => "&",
|
||||||
|
Operator::FloorDiv => "//",
|
||||||
|
};
|
||||||
|
|
||||||
|
text(operator).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprBinOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => {
|
||||||
|
if should_binary_break_right_side_first(self) {
|
||||||
|
Parentheses::Custom
|
||||||
|
} else {
|
||||||
|
Parentheses::Optional
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn should_binary_break_right_side_first(expr: &ExprBinOp) -> bool {
|
||||||
|
use ruff_python_ast::prelude::*;
|
||||||
|
|
||||||
|
if expr.left.is_bin_op_expr() {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
match expr.right.as_ref() {
|
||||||
|
Expr::Tuple(ExprTuple {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::List(ExprList {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::Set(ExprSet {
|
||||||
|
elts: expressions, ..
|
||||||
|
})
|
||||||
|
| Expr::Dict(ExprDict {
|
||||||
|
values: expressions,
|
||||||
|
..
|
||||||
|
}) => !expressions.is_empty(),
|
||||||
|
Expr::Call(ExprCall { args, keywords, .. }) => !args.is_empty() && !keywords.is_empty(),
|
||||||
|
Expr::ListComp(_) | Expr::SetComp(_) | Expr::DictComp(_) | Expr::GeneratorExp(_) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprBoolOp;
|
use rustpython_parser::ast::ExprBoolOp;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprBoolOp> for FormatExprBoolOp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprBoolOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprCall;
|
use rustpython_parser::ast::ExprCall;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprCall> for FormatExprCall {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprCall {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprCompare;
|
use rustpython_parser::ast::ExprCompare;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprCompare> for FormatExprCompare {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprCompare {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprConstant;
|
use rustpython_parser::ast::ExprConstant;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprConstant> for FormatExprConstant {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprConstant {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprDict;
|
use rustpython_parser::ast::ExprDict;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDict> for FormatExprDict {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprDict {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprDictComp;
|
use rustpython_parser::ast::ExprDictComp;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprDictComp> for FormatExprDictComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprDictComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprFormattedValue;
|
use rustpython_parser::ast::ExprFormattedValue;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprFormattedValue> for FormatExprFormattedValue {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprFormattedValue {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprGeneratorExp;
|
use rustpython_parser::ast::ExprGeneratorExp;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprGeneratorExp> for FormatExprGeneratorExp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprGeneratorExp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprIfExp;
|
use rustpython_parser::ast::ExprIfExp;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprIfExp> for FormatExprIfExp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprIfExp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprJoinedStr;
|
use rustpython_parser::ast::ExprJoinedStr;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprJoinedStr> for FormatExprJoinedStr {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprJoinedStr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprLambda;
|
use rustpython_parser::ast::ExprLambda;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprLambda> for FormatExprLambda {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprLambda {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::comments::dangling_comments;
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::FormatNodeRule;
|
||||||
|
use ruff_formatter::{format_args, write};
|
||||||
use rustpython_parser::ast::ExprList;
|
use rustpython_parser::ast::ExprList;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -7,6 +12,55 @@ pub struct FormatExprList;
|
||||||
|
|
||||||
impl FormatNodeRule<ExprList> for FormatExprList {
|
impl FormatNodeRule<ExprList> for FormatExprList {
|
||||||
fn fmt_fields(&self, item: &ExprList, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &ExprList, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
write!(f, [verbatim_text(item.range)])
|
let ExprList {
|
||||||
|
range: _,
|
||||||
|
elts,
|
||||||
|
ctx: _,
|
||||||
|
} = item;
|
||||||
|
|
||||||
|
let items = format_with(|f| {
|
||||||
|
let mut iter = elts.iter();
|
||||||
|
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
write!(f, [first.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in iter {
|
||||||
|
write!(f, [text(","), soft_line_break_or_space(), item.format()])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !elts.is_empty() {
|
||||||
|
write!(f, [if_group_breaks(&text(","))])?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
let comments = f.context().comments().clone();
|
||||||
|
let dangling = comments.dangling_comments(item.into());
|
||||||
|
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
text("["),
|
||||||
|
dangling_comments(dangling),
|
||||||
|
soft_block_indent(&items),
|
||||||
|
text("]")
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_dangling_comments(&self, _node: &ExprList, _f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
|
// Handled as part of `fmt_fields`
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprList {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprListComp;
|
use rustpython_parser::ast::ExprListComp;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprListComp> for FormatExprListComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprListComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use ruff_formatter::{write, FormatContext};
|
use ruff_formatter::{write, FormatContext};
|
||||||
|
|
@ -22,6 +25,12 @@ impl FormatNodeRule<ExprName> for FormatExprName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprName {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ruff_text_size::{TextRange, TextSize};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprNamedExpr;
|
use rustpython_parser::ast::ExprNamedExpr;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprNamedExpr> for FormatExprNamedExpr {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprNamedExpr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSet;
|
use rustpython_parser::ast::ExprSet;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSet> for FormatExprSet {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSet {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSetComp;
|
use rustpython_parser::ast::ExprSetComp;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprSetComp> for FormatExprSetComp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSetComp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSlice;
|
use rustpython_parser::ast::ExprSlice;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSlice> for FormatExprSlice {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSlice {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprStarred;
|
use rustpython_parser::ast::ExprStarred;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprStarred> for FormatExprStarred {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprStarred {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprSubscript;
|
use rustpython_parser::ast::ExprSubscript;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprSubscript> for FormatExprSubscript {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprSubscript {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprTuple;
|
use rustpython_parser::ast::ExprTuple;
|
||||||
|
|
@ -10,3 +13,12 @@ impl FormatNodeRule<ExprTuple> for FormatExprTuple {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprTuple {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match default_expression_needs_parentheses(self.into(), parenthesize, source) {
|
||||||
|
Parentheses::Optional => Parentheses::Never,
|
||||||
|
parentheses => parentheses,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprUnaryOp;
|
use rustpython_parser::ast::ExprUnaryOp;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprUnaryOp> for FormatExprUnaryOp {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprUnaryOp {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprYield;
|
use rustpython_parser::ast::ExprYield;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYield> for FormatExprYield {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprYield {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use crate::expression::parentheses::{
|
||||||
|
default_expression_needs_parentheses, NeedsParentheses, Parentheses, Parenthesize,
|
||||||
|
};
|
||||||
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
use crate::{verbatim_text, FormatNodeRule, PyFormatter};
|
||||||
use ruff_formatter::{write, Buffer, FormatResult};
|
use ruff_formatter::{write, Buffer, FormatResult};
|
||||||
use rustpython_parser::ast::ExprYieldFrom;
|
use rustpython_parser::ast::ExprYieldFrom;
|
||||||
|
|
@ -10,3 +13,9 @@ impl FormatNodeRule<ExprYieldFrom> for FormatExprYieldFrom {
|
||||||
write!(f, [verbatim_text(item.range)])
|
write!(f, [verbatim_text(item.range)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for ExprYieldFrom {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
default_expression_needs_parentheses(self.into(), parenthesize, source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
use crate::context::NodeLevel;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use ruff_formatter::{format_args, write};
|
|
||||||
use rustpython_parser::ast::Expr;
|
|
||||||
|
|
||||||
/// Formats the passed expression. Adds parentheses if the expression doesn't fit on a line.
|
|
||||||
pub(crate) const fn maybe_parenthesize(expression: &Expr) -> MaybeParenthesize {
|
|
||||||
MaybeParenthesize { expression }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct MaybeParenthesize<'a> {
|
|
||||||
expression: &'a Expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Format<PyFormatContext<'_>> for MaybeParenthesize<'_> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<PyFormatContext<'_>>) -> FormatResult<()> {
|
|
||||||
let saved_level = f.context().node_level();
|
|
||||||
f.context_mut().set_node_level(NodeLevel::Parenthesized);
|
|
||||||
|
|
||||||
let result = if needs_parentheses(self.expression) {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
[group(&format_args![
|
|
||||||
if_group_breaks(&text("(")),
|
|
||||||
soft_block_indent(&self.expression.format()),
|
|
||||||
if_group_breaks(&text(")"))
|
|
||||||
])]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Don't add parentheses around expressions that have parentheses on their own (e.g. list, dict, tuple, call expression)
|
|
||||||
self.expression.format().fmt(f)
|
|
||||||
};
|
|
||||||
|
|
||||||
f.context_mut().set_node_level(saved_level);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn needs_parentheses(expr: &Expr) -> bool {
|
|
||||||
!matches!(
|
|
||||||
expr,
|
|
||||||
Expr::Tuple(_)
|
|
||||||
| Expr::List(_)
|
|
||||||
| Expr::Set(_)
|
|
||||||
| Expr::Dict(_)
|
|
||||||
| Expr::ListComp(_)
|
|
||||||
| Expr::SetComp(_)
|
|
||||||
| Expr::DictComp(_)
|
|
||||||
| Expr::GeneratorExp(_)
|
|
||||||
| Expr::Call(_)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
|
use crate::context::NodeLevel;
|
||||||
|
use crate::expression::parentheses::{NeedsParentheses, Parentheses, Parenthesize};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use ruff_formatter::{FormatOwnedWithRule, FormatRefWithRule, FormatRule};
|
use ruff_formatter::{
|
||||||
|
format_args, write, FormatOwnedWithRule, FormatRefWithRule, FormatRule, FormatRuleWithOptions,
|
||||||
|
};
|
||||||
use rustpython_parser::ast::Expr;
|
use rustpython_parser::ast::Expr;
|
||||||
|
|
||||||
pub(crate) mod expr_attribute;
|
pub(crate) mod expr_attribute;
|
||||||
|
|
@ -29,17 +33,30 @@ pub(crate) mod expr_tuple;
|
||||||
pub(crate) mod expr_unary_op;
|
pub(crate) mod expr_unary_op;
|
||||||
pub(crate) mod expr_yield;
|
pub(crate) mod expr_yield;
|
||||||
pub(crate) mod expr_yield_from;
|
pub(crate) mod expr_yield_from;
|
||||||
pub(crate) mod maybe_parenthesize;
|
pub(crate) mod parentheses;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatExpr;
|
pub struct FormatExpr {
|
||||||
|
parenthesize: Parenthesize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatRuleWithOptions<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
|
type Options = Parenthesize;
|
||||||
|
|
||||||
|
fn with_options(mut self, options: Self::Options) -> Self {
|
||||||
|
self.parenthesize = options;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
fn fmt(&self, item: &Expr, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt(&self, item: &Expr, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
match item {
|
let parentheses = item.needs_parentheses(self.parenthesize, f.context().contents());
|
||||||
|
|
||||||
|
let format_expr = format_with(|f| match item {
|
||||||
Expr::BoolOp(expr) => expr.format().fmt(f),
|
Expr::BoolOp(expr) => expr.format().fmt(f),
|
||||||
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
Expr::NamedExpr(expr) => expr.format().fmt(f),
|
||||||
Expr::BinOp(expr) => expr.format().fmt(f),
|
Expr::BinOp(expr) => expr.format().with_options(Some(parentheses)).fmt(f),
|
||||||
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
Expr::UnaryOp(expr) => expr.format().fmt(f),
|
||||||
Expr::Lambda(expr) => expr.format().fmt(f),
|
Expr::Lambda(expr) => expr.format().fmt(f),
|
||||||
Expr::IfExp(expr) => expr.format().fmt(f),
|
Expr::IfExp(expr) => expr.format().fmt(f),
|
||||||
|
|
@ -64,6 +81,72 @@ impl FormatRule<Expr, PyFormatContext<'_>> for FormatExpr {
|
||||||
Expr::List(expr) => expr.format().fmt(f),
|
Expr::List(expr) => expr.format().fmt(f),
|
||||||
Expr::Tuple(expr) => expr.format().fmt(f),
|
Expr::Tuple(expr) => expr.format().fmt(f),
|
||||||
Expr::Slice(expr) => expr.format().fmt(f),
|
Expr::Slice(expr) => expr.format().fmt(f),
|
||||||
|
});
|
||||||
|
|
||||||
|
let saved_level = f.context().node_level();
|
||||||
|
f.context_mut().set_node_level(NodeLevel::Expression);
|
||||||
|
|
||||||
|
let result = match parentheses {
|
||||||
|
Parentheses::Always => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
text("("),
|
||||||
|
soft_block_indent(&format_expr),
|
||||||
|
text(")")
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Add optional parentheses. Ignore if the item renders parentheses itself.
|
||||||
|
Parentheses::Optional => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
[group(&format_args![
|
||||||
|
if_group_breaks(&text("(")),
|
||||||
|
soft_block_indent(&format_expr),
|
||||||
|
if_group_breaks(&text(")"))
|
||||||
|
])]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Parentheses::Custom | Parentheses::Never => Format::fmt(&format_expr, f),
|
||||||
|
};
|
||||||
|
|
||||||
|
f.context_mut().set_node_level(saved_level);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NeedsParentheses for Expr {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses {
|
||||||
|
match self {
|
||||||
|
Expr::BoolOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::NamedExpr(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::BinOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::UnaryOp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Lambda(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::IfExp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Dict(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Set(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::ListComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::SetComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::DictComp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::GeneratorExp(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Await(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Yield(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::YieldFrom(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Compare(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Call(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::FormattedValue(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::JoinedStr(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Constant(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Attribute(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Subscript(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Starred(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Name(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::List(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Tuple(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
|
Expr::Slice(expr) => expr.needs_parentheses(parenthesize, source),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
use crate::trivia::{
|
||||||
|
find_first_non_trivia_character_after, find_first_non_trivia_character_before,
|
||||||
|
};
|
||||||
|
use ruff_python_ast::node::AnyNodeRef;
|
||||||
|
|
||||||
|
pub(crate) trait NeedsParentheses {
|
||||||
|
fn needs_parentheses(&self, parenthesize: Parenthesize, source: &str) -> Parentheses;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn default_expression_needs_parentheses(
|
||||||
|
node: AnyNodeRef,
|
||||||
|
parenthesize: Parenthesize,
|
||||||
|
source: &str,
|
||||||
|
) -> Parentheses {
|
||||||
|
debug_assert!(
|
||||||
|
node.is_expression(),
|
||||||
|
"Should only be called for expressions"
|
||||||
|
);
|
||||||
|
|
||||||
|
// `Optional` or `Preserve` and expression has parentheses in source code.
|
||||||
|
if !parenthesize.is_if_breaks() && is_expression_parenthesized(node, source) {
|
||||||
|
Parentheses::Always
|
||||||
|
}
|
||||||
|
// `Optional` or `IfBreaks`: Add parentheses if the expression doesn't fit on a line
|
||||||
|
else if !parenthesize.is_preserve() {
|
||||||
|
Parentheses::Optional
|
||||||
|
} else {
|
||||||
|
//`Preserve` and expression has no parentheses in the source code
|
||||||
|
Parentheses::Never
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures if the expression should be parenthesized.
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub enum Parenthesize {
|
||||||
|
/// Parenthesize the expression if it has parenthesis in the source.
|
||||||
|
#[default]
|
||||||
|
Preserve,
|
||||||
|
|
||||||
|
/// Parenthesizes the expression if it doesn't fit on a line OR if the expression is parenthesized in the source code.
|
||||||
|
Optional,
|
||||||
|
|
||||||
|
/// Parenthesizes the expression only if it doesn't fit on a line.
|
||||||
|
IfBreaks,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parenthesize {
|
||||||
|
const fn is_if_breaks(self) -> bool {
|
||||||
|
matches!(self, Parenthesize::IfBreaks)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn is_preserve(self) -> bool {
|
||||||
|
matches!(self, Parenthesize::Preserve)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether it is necessary to add parentheses around an expression.
|
||||||
|
/// This is different from [`Parenthesize`] in that it is the resolved representation: It takes into account
|
||||||
|
/// whether there are parentheses in the source code or not.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Parentheses {
|
||||||
|
/// Always create parentheses
|
||||||
|
Always,
|
||||||
|
|
||||||
|
/// Only add parentheses when necessary because the expression breaks over multiple lines.
|
||||||
|
Optional,
|
||||||
|
|
||||||
|
/// Custom handling by the node's formatter implementation
|
||||||
|
Custom,
|
||||||
|
|
||||||
|
/// Never add parentheses
|
||||||
|
Never,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_expression_parenthesized(expr: AnyNodeRef, contents: &str) -> bool {
|
||||||
|
use rustpython_parser::ast::Ranged;
|
||||||
|
|
||||||
|
debug_assert!(
|
||||||
|
expr.is_expression(),
|
||||||
|
"Should only be called for expressions"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Search backwards to avoid ambiguity with `(a, )` and because it's faster
|
||||||
|
matches!(
|
||||||
|
find_first_non_trivia_character_after(expr.end(), contents),
|
||||||
|
Some((_, ')'))
|
||||||
|
)
|
||||||
|
// Search forwards to confirm that this is not a nested expression `(5 + d * 3)`
|
||||||
|
&& matches!(
|
||||||
|
find_first_non_trivia_character_before(expr.start(), contents),
|
||||||
|
Some((_, '('))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ where
|
||||||
self.fmt_node(node, f)?;
|
self.fmt_node(node, f)?;
|
||||||
self.fmt_dangling_comments(node, f)?;
|
self.fmt_dangling_comments(node, f)?;
|
||||||
self.fmt_trailing_comments(node, f)?;
|
self.fmt_trailing_comments(node, f)?;
|
||||||
write!(f, [source_position(node.start())])
|
write!(f, [source_position(node.end())])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats the node without comments. Ignores any suppression comments.
|
/// Formats the node without comments. Ignores any suppression comments.
|
||||||
|
|
@ -225,8 +225,11 @@ if True:
|
||||||
|
|
||||||
let formatted_code = printed.as_code();
|
let formatted_code = printed.as_code();
|
||||||
|
|
||||||
let reformatted =
|
let reformatted = format_module(formatted_code).unwrap_or_else(|err| {
|
||||||
format_module(formatted_code).expect("Expected formatted code to be valid syntax");
|
panic!(
|
||||||
|
"Formatted code resulted introduced a syntax error {err:#?}. Code:\n{formatted_code}"
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
if reformatted.as_code() != formatted_code {
|
if reformatted.as_code() != formatted_code {
|
||||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||||
|
|
@ -314,7 +317,7 @@ Formatted twice:
|
||||||
let formatted_code = printed.as_code();
|
let formatted_code = printed.as_code();
|
||||||
|
|
||||||
let reformatted =
|
let reformatted =
|
||||||
format_module(formatted_code).expect("Expected formatted code to be valid syntax");
|
format_module(formatted_code).unwrap_or_else(|err| panic!("Expected formatted code to be valid syntax but it contains syntax errors: {err}\n{formatted_code}"));
|
||||||
|
|
||||||
if reformatted.as_code() != formatted_code {
|
if reformatted.as_code() != formatted_code {
|
||||||
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
let diff = TextDiff::from_lines(formatted_code, reformatted.as_code())
|
||||||
|
|
@ -378,7 +381,9 @@ other
|
||||||
// Uncomment the `dbg` to print the IR.
|
// Uncomment the `dbg` to print the IR.
|
||||||
// Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR
|
// Use `dbg_write!(f, []) instead of `write!(f, [])` in your formatting code to print some IR
|
||||||
// inside of a `Format` implementation
|
// inside of a `Format` implementation
|
||||||
// dbg!(formatted.document());
|
// dbg!(formatted
|
||||||
|
// .document()
|
||||||
|
// .display(formatted.context().source_code()));
|
||||||
|
|
||||||
let printed = formatted.print().unwrap();
|
let printed = formatted.print().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ if True:
|
||||||
- 2,
|
- 2,
|
||||||
- 3,
|
- 3,
|
||||||
-]
|
-]
|
||||||
+[1, 2, 3,]
|
+[1, 2, 3]
|
||||||
|
|
||||||
-division_result_tuple = (6 / 2,)
|
-division_result_tuple = (6 / 2,)
|
||||||
+division_result_tuple = (6/2,)
|
+division_result_tuple = (6/2,)
|
||||||
|
|
@ -250,7 +250,7 @@ for x in (1,):
|
||||||
for (x,) in (1,), (2,), (3,):
|
for (x,) in (1,), (2,), (3,):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
[1, 2, 3,]
|
[1, 2, 3]
|
||||||
|
|
||||||
division_result_tuple = (6/2,)
|
division_result_tuple = (6/2,)
|
||||||
print("foo %r", (foo.bar,))
|
print("foo %r", (foo.bar,))
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,21 @@ async def wat():
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -19,14 +19,9 @@
|
@@ -4,10 +4,12 @@
|
||||||
|
#
|
||||||
|
# Has many lines. Many, many lines.
|
||||||
|
# Many, many, many lines.
|
||||||
|
-"""Module docstring.
|
||||||
|
+(
|
||||||
|
+ """Module docstring.
|
||||||
|
|
||||||
|
Possibly also many, many lines.
|
||||||
|
"""
|
||||||
|
+)
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
@@ -19,9 +21,6 @@
|
||||||
import fast
|
import fast
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import slow as fast
|
import slow as fast
|
||||||
|
|
@ -117,16 +131,9 @@ async def wat():
|
||||||
-
|
-
|
||||||
-# Some comment before a function.
|
-# Some comment before a function.
|
||||||
y = 1
|
y = 1
|
||||||
-(
|
(
|
||||||
- # some strings
|
# some strings
|
||||||
- y # type: ignore
|
@@ -93,4 +92,4 @@
|
||||||
-)
|
|
||||||
+# some strings
|
|
||||||
+y # type: ignore
|
|
||||||
|
|
||||||
|
|
||||||
def function(default=None):
|
|
||||||
@@ -93,4 +88,4 @@
|
|
||||||
|
|
||||||
# Some closing comments.
|
# Some closing comments.
|
||||||
# Maybe Vim or Emacs directives for formatting.
|
# Maybe Vim or Emacs directives for formatting.
|
||||||
|
|
@ -144,10 +151,12 @@ async def wat():
|
||||||
#
|
#
|
||||||
# Has many lines. Many, many lines.
|
# Has many lines. Many, many lines.
|
||||||
# Many, many, many lines.
|
# Many, many, many lines.
|
||||||
"""Module docstring.
|
(
|
||||||
|
"""Module docstring.
|
||||||
|
|
||||||
Possibly also many, many lines.
|
Possibly also many, many lines.
|
||||||
"""
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -160,8 +169,10 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import slow as fast
|
import slow as fast
|
||||||
y = 1
|
y = 1
|
||||||
# some strings
|
(
|
||||||
y # type: ignore
|
# some strings
|
||||||
|
y # type: ignore
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def function(default=None):
|
def function(default=None):
|
||||||
|
|
|
||||||
|
|
@ -276,17 +276,7 @@ last_call()
|
||||||
Name
|
Name
|
||||||
None
|
None
|
||||||
True
|
True
|
||||||
@@ -22,119 +23,83 @@
|
@@ -30,56 +31,39 @@
|
||||||
v1 << 2
|
|
||||||
1 >> v2
|
|
||||||
1 % finished
|
|
||||||
-1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
|
||||||
-((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
|
||||||
+1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8
|
|
||||||
+((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)
|
|
||||||
not great
|
|
||||||
~great
|
|
||||||
+value
|
|
||||||
-1
|
-1
|
||||||
~int and not v1 ^ 123 + v2 | True
|
~int and not v1 ^ 123 + v2 | True
|
||||||
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
(~int) and (not ((v1 ^ (123 + v2)) | True))
|
||||||
|
|
@ -312,14 +302,14 @@ last_call()
|
||||||
(str or None) if True else (str or bytes or None)
|
(str or None) if True else (str or bytes or None)
|
||||||
str or None if (1 if True else 2) else str or bytes or None
|
str or None if (1 if True else 2) else str or bytes or None
|
||||||
(str or None) if (1 if True else 2) else (str or bytes or None)
|
(str or None) if (1 if True else 2) else (str or bytes or None)
|
||||||
-(
|
(
|
||||||
- (super_long_variable_name or None)
|
- (super_long_variable_name or None)
|
||||||
- if (1 if super_long_test_name else 2)
|
- if (1 if super_long_test_name else 2)
|
||||||
- else (str or bytes or None)
|
- else (str or bytes or None)
|
||||||
-)
|
+ (super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
||||||
|
)
|
||||||
-{"2.7": dead, "3.7": (long_live or die_hard)}
|
-{"2.7": dead, "3.7": (long_live or die_hard)}
|
||||||
-{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}}
|
-{"2.7": dead, "3.7": (long_live or die_hard), **{"3.6": verygood}}
|
||||||
+(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
|
||||||
+{'2.7': dead, '3.7': (long_live or die_hard)}
|
+{'2.7': dead, '3.7': (long_live or die_hard)}
|
||||||
+{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
+{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
||||||
{**a, **b, **c}
|
{**a, **b, **c}
|
||||||
|
|
@ -338,7 +328,7 @@ last_call()
|
||||||
- 2,
|
- 2,
|
||||||
- 3,
|
- 3,
|
||||||
-]
|
-]
|
||||||
+[1, 2, 3,]
|
+[1, 2, 3]
|
||||||
[*a]
|
[*a]
|
||||||
[*range(10)]
|
[*range(10)]
|
||||||
-[
|
-[
|
||||||
|
|
@ -351,15 +341,14 @@ last_call()
|
||||||
- *a,
|
- *a,
|
||||||
- 5,
|
- 5,
|
||||||
-]
|
-]
|
||||||
-[
|
+[*a, 4, 5]
|
||||||
- this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
+[4, *a, 5]
|
||||||
- element,
|
[
|
||||||
- another,
|
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||||
- *more,
|
element,
|
||||||
-]
|
@@ -87,54 +71,44 @@
|
||||||
+[*a, 4, 5,]
|
*more,
|
||||||
+[4, *a, 5,]
|
]
|
||||||
+[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
|
||||||
{i for i in (1, 2, 3)}
|
{i for i in (1, 2, 3)}
|
||||||
-{(i**2) for i in (1, 2, 3)}
|
-{(i**2) for i in (1, 2, 3)}
|
||||||
-{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))}
|
-{(i**2) for i, _ in ((1, "a"), (2, "b"), (3, "c"))}
|
||||||
|
|
@ -417,18 +406,21 @@ last_call()
|
||||||
dict[str, int]
|
dict[str, int]
|
||||||
tuple[str, ...]
|
tuple[str, ...]
|
||||||
-tuple[str, int, float, dict[str, int]]
|
-tuple[str, int, float, dict[str, int]]
|
||||||
tuple[
|
-tuple[
|
||||||
- str,
|
- str,
|
||||||
- int,
|
- int,
|
||||||
- float,
|
- float,
|
||||||
- dict[str, int],
|
- dict[str, int],
|
||||||
|
+(
|
||||||
|
+ tuple[
|
||||||
+ str, int, float, dict[str, int]
|
+ str, int, float, dict[str, int]
|
||||||
]
|
]
|
||||||
|
+)
|
||||||
+tuple[str, int, float, dict[str, int],]
|
+tuple[str, int, float, dict[str, int],]
|
||||||
very_long_variable_name_filters: t.List[
|
very_long_variable_name_filters: t.List[
|
||||||
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
||||||
]
|
]
|
||||||
@@ -144,9 +109,9 @@
|
@@ -144,9 +118,9 @@
|
||||||
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
|
xxxx_xxx_xxxx_xxxxx_xxxx_xxx: Callable[..., List[SomeClass]] = classmethod( # type: ignore
|
||||||
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
sync(async_xxxx_xxx_xxxx_xxxxx_xxxx_xxx.__func__)
|
||||||
)
|
)
|
||||||
|
|
@ -441,7 +433,7 @@ last_call()
|
||||||
slice[0]
|
slice[0]
|
||||||
slice[0:1]
|
slice[0:1]
|
||||||
slice[0:1:2]
|
slice[0:1:2]
|
||||||
@@ -174,57 +139,29 @@
|
@@ -174,57 +148,29 @@
|
||||||
numpy[:, ::-1]
|
numpy[:, ::-1]
|
||||||
numpy[np.newaxis, :]
|
numpy[np.newaxis, :]
|
||||||
(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
|
(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)
|
||||||
|
|
@ -450,9 +442,8 @@ last_call()
|
||||||
+{'2.7': dead, '3.7': long_live or die_hard}
|
+{'2.7': dead, '3.7': long_live or die_hard}
|
||||||
+{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
+{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||||
-(SomeName)
|
(SomeName)
|
||||||
SomeName
|
SomeName
|
||||||
+SomeName
|
|
||||||
(Good, Bad, Ugly)
|
(Good, Bad, Ugly)
|
||||||
(i for i in (1, 2, 3))
|
(i for i in (1, 2, 3))
|
||||||
-((i**2) for i in (1, 2, 3))
|
-((i**2) for i in (1, 2, 3))
|
||||||
|
|
@ -511,7 +502,7 @@ last_call()
|
||||||
Ø = set()
|
Ø = set()
|
||||||
authors.łukasz.say_thanks()
|
authors.łukasz.say_thanks()
|
||||||
mapping = {
|
mapping = {
|
||||||
@@ -237,134 +174,86 @@
|
@@ -237,114 +183,80 @@
|
||||||
|
|
||||||
def gen():
|
def gen():
|
||||||
yield from outside_of_generator
|
yield from outside_of_generator
|
||||||
|
|
@ -655,30 +646,7 @@ last_call()
|
||||||
+ ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
+ ~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
-(
|
(
|
||||||
- aaaaaaaaaaaaaaaa
|
|
||||||
- + aaaaaaaaaaaaaaaa
|
|
||||||
- - aaaaaaaaaaaaaaaa
|
|
||||||
- * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
- / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaa * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa) / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
|
||||||
-(
|
|
||||||
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
bbbb >> bbbb * bbbb
|
|
||||||
-(
|
|
||||||
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- ^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
- ^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
-)
|
|
||||||
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
|
||||||
last_call()
|
|
||||||
# standalone comment at ENDMARKER
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
|
@ -709,8 +677,8 @@ Name1 or Name2 and Name3 or Name4
|
||||||
v1 << 2
|
v1 << 2
|
||||||
1 >> v2
|
1 >> v2
|
||||||
1 % finished
|
1 % finished
|
||||||
1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8
|
1 + v2 - v3 * 4 ^ 5**v6 / 7 // 8
|
||||||
((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)
|
((1 + v2) - (v3 * 4)) ^ (((5**v6) / 7) // 8)
|
||||||
not great
|
not great
|
||||||
~great
|
~great
|
||||||
+value
|
+value
|
||||||
|
|
@ -731,7 +699,9 @@ str or None if True else str or bytes or None
|
||||||
(str or None) if True else (str or bytes or None)
|
(str or None) if True else (str or bytes or None)
|
||||||
str or None if (1 if True else 2) else str or bytes or None
|
str or None if (1 if True else 2) else str or bytes or None
|
||||||
(str or None) if (1 if True else 2) else (str or bytes or None)
|
(str or None) if (1 if True else 2) else (str or bytes or None)
|
||||||
(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
(
|
||||||
|
(super_long_variable_name or None) if (1 if super_long_test_name else 2) else (str or bytes or None)
|
||||||
|
)
|
||||||
{'2.7': dead, '3.7': (long_live or die_hard)}
|
{'2.7': dead, '3.7': (long_live or die_hard)}
|
||||||
{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}
|
||||||
{**a, **b, **c}
|
{**a, **b, **c}
|
||||||
|
|
@ -743,12 +713,17 @@ str or None if (1 if True else 2) else str or bytes or None
|
||||||
(1, 2, 3)
|
(1, 2, 3)
|
||||||
[]
|
[]
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]
|
||||||
[1, 2, 3,]
|
[1, 2, 3]
|
||||||
[*a]
|
[*a]
|
||||||
[*range(10)]
|
[*range(10)]
|
||||||
[*a, 4, 5,]
|
[*a, 4, 5]
|
||||||
[4, *a, 5,]
|
[4, *a, 5]
|
||||||
[this_is_a_very_long_variable_which_will_force_a_delimiter_split, element, another, *more]
|
[
|
||||||
|
this_is_a_very_long_variable_which_will_force_a_delimiter_split,
|
||||||
|
element,
|
||||||
|
another,
|
||||||
|
*more,
|
||||||
|
]
|
||||||
{i for i in (1, 2, 3)}
|
{i for i in (1, 2, 3)}
|
||||||
{(i ** 2) for i in (1, 2, 3)}
|
{(i ** 2) for i in (1, 2, 3)}
|
||||||
{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
|
{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}
|
||||||
|
|
@ -782,9 +757,11 @@ call.me(maybe)
|
||||||
list[str]
|
list[str]
|
||||||
dict[str, int]
|
dict[str, int]
|
||||||
tuple[str, ...]
|
tuple[str, ...]
|
||||||
tuple[
|
(
|
||||||
|
tuple[
|
||||||
str, int, float, dict[str, int]
|
str, int, float, dict[str, int]
|
||||||
]
|
]
|
||||||
|
)
|
||||||
tuple[str, int, float, dict[str, int],]
|
tuple[str, int, float, dict[str, int],]
|
||||||
very_long_variable_name_filters: t.List[
|
very_long_variable_name_filters: t.List[
|
||||||
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
t.Tuple[str, t.Union[str, t.List[t.Optional[str]]]],
|
||||||
|
|
@ -828,7 +805,7 @@ numpy[np.newaxis, :]
|
||||||
{'2.7': dead, '3.7': long_live or die_hard}
|
{'2.7': dead, '3.7': long_live or die_hard}
|
||||||
{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}
|
||||||
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]
|
||||||
SomeName
|
(SomeName)
|
||||||
SomeName
|
SomeName
|
||||||
(Good, Bad, Ugly)
|
(Good, Bad, Ugly)
|
||||||
(i for i in (1, 2, 3))
|
(i for i in (1, 2, 3))
|
||||||
|
|
@ -936,11 +913,25 @@ if (
|
||||||
~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
~ aaaaaaaaaaaaaaaa.a + aaaaaaaaaaaaaaaa.b - aaaaaaaaaaaaaaaa.c * aaaaaaaaaaaaaaaa.d @ aaaaaaaaaaaaaaaa.e | aaaaaaaaaaaaaaaa.f & aaaaaaaaaaaaaaaa.g % aaaaaaaaaaaaaaaa.h ^ aaaaaaaaaaaaaaaa.i << aaaaaaaaaaaaaaaa.k >> aaaaaaaaaaaaaaaa.l ** aaaaaaaaaaaaaaaa.m // aaaaaaaaaaaaaaaa.n
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa - aaaaaaaaaaaaaaaa * (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa) / (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
(
|
||||||
|
aaaaaaaaaaaaaaaa
|
||||||
|
+ aaaaaaaaaaaaaaaa
|
||||||
|
- aaaaaaaaaaaaaaaa
|
||||||
|
* (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
||||||
|
/ (aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa)
|
||||||
|
)
|
||||||
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
aaaaaaaaaaaaaaaa + aaaaaaaaaaaaaaaa
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa << aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
<< aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
)
|
||||||
bbbb >> bbbb * bbbb
|
bbbb >> bbbb * bbbb
|
||||||
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ^bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
^ bbbb.a & aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
^ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
)
|
||||||
last_call()
|
last_call()
|
||||||
# standalone comment at ENDMARKER
|
# standalone comment at ENDMARKER
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -76,16 +76,51 @@ x[
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -56,4 +56,8 @@
|
@@ -31,14 +31,17 @@
|
||||||
|
ham[lower + offset : upper + offset]
|
||||||
|
|
||||||
|
slice[::, ::]
|
||||||
|
-slice[
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
:
|
||||||
|
# B
|
||||||
|
:
|
||||||
|
# C
|
||||||
|
]
|
||||||
|
-slice[
|
||||||
|
+)
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
1:
|
||||||
|
# B
|
||||||
|
@@ -46,8 +49,10 @@
|
||||||
|
# C
|
||||||
|
3
|
||||||
|
]
|
||||||
|
+)
|
||||||
|
|
||||||
|
-slice[
|
||||||
|
+(
|
||||||
|
+ slice[
|
||||||
|
# A
|
||||||
|
1
|
||||||
|
+ 2 :
|
||||||
|
@@ -56,4 +61,11 @@
|
||||||
# C
|
# C
|
||||||
4
|
4
|
||||||
]
|
]
|
||||||
-x[1:2:3] # A # B # C
|
-x[1:2:3] # A # B # C
|
||||||
+x[
|
+)
|
||||||
|
+(
|
||||||
|
+ x[
|
||||||
+ 1: # A
|
+ 1: # A
|
||||||
+ 2: # B
|
+ 2: # B
|
||||||
+ 3 # C
|
+ 3 # C
|
||||||
+]
|
+]
|
||||||
|
+)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Ruff Output
|
## Ruff Output
|
||||||
|
|
@ -124,14 +159,17 @@ ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
|
||||||
ham[lower + offset : upper + offset]
|
ham[lower + offset : upper + offset]
|
||||||
|
|
||||||
slice[::, ::]
|
slice[::, ::]
|
||||||
slice[
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
:
|
:
|
||||||
# B
|
# B
|
||||||
:
|
:
|
||||||
# C
|
# C
|
||||||
]
|
]
|
||||||
slice[
|
)
|
||||||
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
1:
|
1:
|
||||||
# B
|
# B
|
||||||
|
|
@ -139,8 +177,10 @@ slice[
|
||||||
# C
|
# C
|
||||||
3
|
3
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
slice[
|
(
|
||||||
|
slice[
|
||||||
# A
|
# A
|
||||||
1
|
1
|
||||||
+ 2 :
|
+ 2 :
|
||||||
|
|
@ -149,11 +189,14 @@ slice[
|
||||||
# C
|
# C
|
||||||
4
|
4
|
||||||
]
|
]
|
||||||
x[
|
)
|
||||||
|
(
|
||||||
|
x[
|
||||||
1: # A
|
1: # A
|
||||||
2: # B
|
2: # B
|
||||||
3 # C
|
3 # C
|
||||||
]
|
]
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Black Output
|
## Black Output
|
||||||
|
|
|
||||||
|
|
@ -42,14 +42,13 @@ assert (
|
||||||
```diff
|
```diff
|
||||||
--- Black
|
--- Black
|
||||||
+++ Ruff
|
+++ Ruff
|
||||||
@@ -1,58 +1,33 @@
|
@@ -2,18 +2,13 @@
|
||||||
importA
|
(
|
||||||
-(
|
()
|
||||||
- ()
|
<< 0
|
||||||
- << 0
|
|
||||||
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
- ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
-) #
|
+ **101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
+() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
) #
|
||||||
|
|
||||||
assert sort_by_dependency(
|
assert sort_by_dependency(
|
||||||
{
|
{
|
||||||
|
|
@ -65,12 +64,7 @@ assert (
|
||||||
}
|
}
|
||||||
) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
|
) == ["2a", "2b", "2", "3a", "3b", "3", "1"]
|
||||||
|
|
||||||
importA
|
@@ -25,34 +20,18 @@
|
||||||
0
|
|
||||||
-0 ^ 0 #
|
|
||||||
+0^0 #
|
|
||||||
|
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def foo(self):
|
def foo(self):
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
|
|
@ -120,7 +114,11 @@ assert (
|
||||||
|
|
||||||
```py
|
```py
|
||||||
importA
|
importA
|
||||||
() << 0 ** 101234234242352525425252352352525234890264906820496920680926538059059209922523523525 #
|
(
|
||||||
|
()
|
||||||
|
<< 0
|
||||||
|
**101234234242352525425252352352525234890264906820496920680926538059059209922523523525
|
||||||
|
) #
|
||||||
|
|
||||||
assert sort_by_dependency(
|
assert sort_by_dependency(
|
||||||
{
|
{
|
||||||
|
|
@ -131,7 +129,7 @@ assert sort_by_dependency(
|
||||||
|
|
||||||
importA
|
importA
|
||||||
0
|
0
|
||||||
0^0 #
|
0 ^ 0 #
|
||||||
|
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_formatter/src/lib.rs
|
||||||
|
expression: snapshot
|
||||||
|
---
|
||||||
|
## Input
|
||||||
|
```py
|
||||||
|
(aaaaaaaa
|
||||||
|
+ # trailing operator comment
|
||||||
|
b # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
aaaaaaaaaaaaaa + caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
aaaaaaaaaaaaaa + [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee]
|
||||||
|
aaaaaaaaaaaaaa + (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
aaaaaaaaaaaaaa + { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
aaaaaaaaaaaaaa + [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
aaaaaaaaaaaaaa + (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eee
|
||||||
|
] # comment
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
[a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c ]
|
||||||
|
|
||||||
|
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content + b
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Output
|
||||||
|
```py
|
||||||
|
(
|
||||||
|
aaaaaaaa
|
||||||
|
+ b # trailing operator comment # trailing right comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
(
|
||||||
|
aaaaaaaa # trailing left comment
|
||||||
|
+ # trailing operator comment
|
||||||
|
# leading right comment
|
||||||
|
b
|
||||||
|
)
|
||||||
|
# Black breaks the right side first for the following expressions:
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ caaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaal(argument1, argument2, argument3)
|
||||||
|
)
|
||||||
|
aaaaaaaaaaaaaa + [
|
||||||
|
bbbbbbbbbbbbbbbbbbbbbb,
|
||||||
|
ccccccccccccccccccccc,
|
||||||
|
dddddddddddddddd,
|
||||||
|
eeeeeee,
|
||||||
|
]
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ (bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ { key1:bbbbbbbbbbbbbbbbbbbbbb, key2: ccccccccccccccccccccc, key3: dddddddddddddddd, key4: eeeeeee }
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ { bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eeeeeee }
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ [a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ]
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ (a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb )
|
||||||
|
)
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaa
|
||||||
|
+ {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb}
|
||||||
|
)
|
||||||
|
# Wraps it in parentheses if it needs to break both left and right
|
||||||
|
(
|
||||||
|
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||||
|
+ [bbbbbbbbbbbbbbbbbbbbbb, ccccccccccccccccccccc, dddddddddddddddd, eee]
|
||||||
|
) # comment
|
||||||
|
# But only for expressions that have a statement parent.
|
||||||
|
(
|
||||||
|
not (aaaaaaaaaaaaaa + {a for x in bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb})
|
||||||
|
)
|
||||||
|
[
|
||||||
|
a + [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb] in c,
|
||||||
|
]
|
||||||
|
# leading comment
|
||||||
|
(
|
||||||
|
# comment
|
||||||
|
content
|
||||||
|
+ b
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use rustpython_parser::ast::StmtExpr;
|
use rustpython_parser::ast::StmtExpr;
|
||||||
|
|
@ -9,6 +10,6 @@ impl FormatNodeRule<StmtExpr> for FormatStmtExpr {
|
||||||
fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &StmtExpr, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
let StmtExpr { value, .. } = item;
|
let StmtExpr { value, .. } = item;
|
||||||
|
|
||||||
value.format().fmt(f)
|
value.format().with_options(Parenthesize::Optional).fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::comments::{leading_alternate_branch_comments, trailing_comments};
|
use crate::comments::{leading_alternate_branch_comments, trailing_comments};
|
||||||
use crate::expression::maybe_parenthesize::maybe_parenthesize;
|
use crate::expression::parentheses::Parenthesize;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::FormatNodeRule;
|
use crate::FormatNodeRule;
|
||||||
use ruff_formatter::write;
|
use ruff_formatter::write;
|
||||||
|
|
@ -33,7 +33,7 @@ impl FormatNodeRule<StmtWhile> for FormatStmtWhile {
|
||||||
[
|
[
|
||||||
text("while"),
|
text("while"),
|
||||||
space(),
|
space(),
|
||||||
maybe_parenthesize(test),
|
test.format().with_options(Parenthesize::IfBreaks),
|
||||||
text(":"),
|
text(":"),
|
||||||
trailing_comments(trailing_condition_comments),
|
trailing_comments(trailing_condition_comments),
|
||||||
block_indent(&body.format())
|
block_indent(&body.format())
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use ruff_text_size::{TextLen, TextRange, TextSize};
|
||||||
///
|
///
|
||||||
/// Returns `None` if the range is empty or only contains trivia (whitespace or comments).
|
/// Returns `None` if the range is empty or only contains trivia (whitespace or comments).
|
||||||
pub(crate) fn find_first_non_trivia_character_in_range(
|
pub(crate) fn find_first_non_trivia_character_in_range(
|
||||||
code: &str,
|
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
|
code: &str,
|
||||||
) -> Option<(TextSize, char)> {
|
) -> Option<(TextSize, char)> {
|
||||||
let rest = &code[range];
|
let rest = &code[range];
|
||||||
let mut char_iter = rest.chars();
|
let mut char_iter = rest.chars();
|
||||||
|
|
@ -40,8 +40,63 @@ pub(crate) fn find_first_non_trivia_character_in_range(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_first_non_trivia_character_after(
|
||||||
|
offset: TextSize,
|
||||||
|
code: &str,
|
||||||
|
) -> Option<(TextSize, char)> {
|
||||||
|
find_first_non_trivia_character_in_range(TextRange::new(offset, code.text_len()), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_first_non_trivia_character_before(
|
||||||
|
offset: TextSize,
|
||||||
|
code: &str,
|
||||||
|
) -> Option<(TextSize, char)> {
|
||||||
|
let head = &code[TextRange::up_to(offset)];
|
||||||
|
let mut char_iter = head.chars();
|
||||||
|
|
||||||
|
while let Some(c) = char_iter.next_back() {
|
||||||
|
match c {
|
||||||
|
c if is_python_whitespace(c) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty comment
|
||||||
|
'#' => continue,
|
||||||
|
|
||||||
|
non_trivia_character => {
|
||||||
|
// Non trivia character but we don't know if it is a comment or not. Consume all characters
|
||||||
|
// until the start of the line and track if the last non-whitespace character was a `#`.
|
||||||
|
let mut is_comment = false;
|
||||||
|
|
||||||
|
let first_non_trivia_offset = char_iter.as_str().text_len();
|
||||||
|
|
||||||
|
while let Some(c) = char_iter.next_back() {
|
||||||
|
match c {
|
||||||
|
'#' => {
|
||||||
|
is_comment = true;
|
||||||
|
}
|
||||||
|
'\n' | '\r' => {
|
||||||
|
if !is_comment {
|
||||||
|
return Some((first_non_trivia_offset, non_trivia_character));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c => {
|
||||||
|
if !is_python_whitespace(c) {
|
||||||
|
is_comment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the number of newlines between `offset` and the first non whitespace character in the source code.
|
/// Returns the number of newlines between `offset` and the first non whitespace character in the source code.
|
||||||
pub(crate) fn lines_before(code: &str, offset: TextSize) -> u32 {
|
pub(crate) fn lines_before(offset: TextSize, code: &str) -> u32 {
|
||||||
let head = &code[TextRange::up_to(offset)];
|
let head = &code[TextRange::up_to(offset)];
|
||||||
let mut newlines = 0u32;
|
let mut newlines = 0u32;
|
||||||
|
|
||||||
|
|
@ -68,7 +123,7 @@ pub(crate) fn lines_before(code: &str, offset: TextSize) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Counts the empty lines between `offset` and the first non-whitespace character.
|
/// Counts the empty lines between `offset` and the first non-whitespace character.
|
||||||
pub(crate) fn lines_after(code: &str, offset: TextSize) -> u32 {
|
pub(crate) fn lines_after(offset: TextSize, code: &str) -> u32 {
|
||||||
let rest = &code[usize::from(offset)..];
|
let rest = &code[usize::from(offset)..];
|
||||||
let mut newlines = 0;
|
let mut newlines = 0;
|
||||||
|
|
||||||
|
|
@ -98,33 +153,33 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_empty_string() {
|
fn lines_before_empty_string() {
|
||||||
assert_eq!(lines_before("", TextSize::new(0)), 0);
|
assert_eq!(lines_before(TextSize::new(0), ""), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_in_the_middle_of_a_line() {
|
fn lines_before_in_the_middle_of_a_line() {
|
||||||
assert_eq!(lines_before("a = 20", TextSize::new(4)), 0);
|
assert_eq!(lines_before(TextSize::new(4), "a = 20"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_on_a_new_line() {
|
fn lines_before_on_a_new_line() {
|
||||||
assert_eq!(lines_before("a = 20\nb = 10", TextSize::new(7)), 1);
|
assert_eq!(lines_before(TextSize::new(7), "a = 20\nb = 10"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_multiple_leading_newlines() {
|
fn lines_before_multiple_leading_newlines() {
|
||||||
assert_eq!(lines_before("a = 20\n\r\nb = 10", TextSize::new(9)), 2);
|
assert_eq!(lines_before(TextSize::new(9), "a = 20\n\r\nb = 10"), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_comment_offset() {
|
fn lines_before_with_comment_offset() {
|
||||||
assert_eq!(lines_before("a = 20\n# a comment", TextSize::new(8)), 0);
|
assert_eq!(lines_before(TextSize::new(8), "a = 20\n# a comment"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_trailing_comment() {
|
fn lines_before_with_trailing_comment() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_before("a = 20 # some comment\nb = 10", TextSize::new(22)),
|
lines_before(TextSize::new(22), "a = 20 # some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -132,40 +187,40 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_before_with_comment_only_line() {
|
fn lines_before_with_comment_only_line() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_before("a = 20\n# some comment\nb = 10", TextSize::new(22)),
|
lines_before(TextSize::new(22), "a = 20\n# some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_empty_string() {
|
fn lines_after_empty_string() {
|
||||||
assert_eq!(lines_after("", TextSize::new(0)), 0);
|
assert_eq!(lines_after(TextSize::new(0), ""), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_in_the_middle_of_a_line() {
|
fn lines_after_in_the_middle_of_a_line() {
|
||||||
assert_eq!(lines_after("a = 20", TextSize::new(4)), 0);
|
assert_eq!(lines_after(TextSize::new(4), "a = 20"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_before_a_new_line() {
|
fn lines_after_before_a_new_line() {
|
||||||
assert_eq!(lines_after("a = 20\nb = 10", TextSize::new(6)), 1);
|
assert_eq!(lines_after(TextSize::new(6), "a = 20\nb = 10"), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_multiple_newlines() {
|
fn lines_after_multiple_newlines() {
|
||||||
assert_eq!(lines_after("a = 20\n\r\nb = 10", TextSize::new(6)), 2);
|
assert_eq!(lines_after(TextSize::new(6), "a = 20\n\r\nb = 10"), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_before_comment_offset() {
|
fn lines_after_before_comment_offset() {
|
||||||
assert_eq!(lines_after("a = 20 # a comment\n", TextSize::new(7)), 0);
|
assert_eq!(lines_after(TextSize::new(7), "a = 20 # a comment\n"), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lines_after_with_comment_only_line() {
|
fn lines_after_with_comment_only_line() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lines_after("a = 20\n# some comment\nb = 10", TextSize::new(6)),
|
lines_after(TextSize::new(6), "a = 20\n# some comment\nb = 10"),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ pub struct Scope<'a> {
|
||||||
bindings: FxHashMap<&'a str, BindingId>,
|
bindings: FxHashMap<&'a str, BindingId>,
|
||||||
/// A map from binding ID to binding ID that it shadows.
|
/// A map from binding ID to binding ID that it shadows.
|
||||||
shadowed_bindings: HashMap<BindingId, BindingId, BuildNoHashHasher<BindingId>>,
|
shadowed_bindings: HashMap<BindingId, BindingId, BuildNoHashHasher<BindingId>>,
|
||||||
|
/// A list of all names that have been deleted in this scope.
|
||||||
|
deleted_symbols: Vec<&'a str>,
|
||||||
/// Index into the globals arena, if the scope contains any globally-declared symbols.
|
/// Index into the globals arena, if the scope contains any globally-declared symbols.
|
||||||
globals_id: Option<GlobalsId>,
|
globals_id: Option<GlobalsId>,
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +38,7 @@ impl<'a> Scope<'a> {
|
||||||
star_imports: Vec::default(),
|
star_imports: Vec::default(),
|
||||||
bindings: FxHashMap::default(),
|
bindings: FxHashMap::default(),
|
||||||
shadowed_bindings: IntMap::default(),
|
shadowed_bindings: IntMap::default(),
|
||||||
|
deleted_symbols: Vec::default(),
|
||||||
globals_id: None,
|
globals_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,6 +51,7 @@ impl<'a> Scope<'a> {
|
||||||
star_imports: Vec::default(),
|
star_imports: Vec::default(),
|
||||||
bindings: FxHashMap::default(),
|
bindings: FxHashMap::default(),
|
||||||
shadowed_bindings: IntMap::default(),
|
shadowed_bindings: IntMap::default(),
|
||||||
|
deleted_symbols: Vec::default(),
|
||||||
globals_id: None,
|
globals_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,14 +71,23 @@ impl<'a> Scope<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if this scope defines a binding with the given name.
|
/// Removes the binding with the given name.
|
||||||
pub fn defines(&self, name: &str) -> bool {
|
pub fn delete(&mut self, name: &'a str) -> Option<BindingId> {
|
||||||
|
self.deleted_symbols.push(name);
|
||||||
|
self.bindings.remove(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this scope has a binding with the given name.
|
||||||
|
pub fn has(&self, name: &str) -> bool {
|
||||||
self.bindings.contains_key(name)
|
self.bindings.contains_key(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the binding with the given name
|
/// Returns `true` if the scope declares a symbol with the given name.
|
||||||
pub fn remove(&mut self, name: &str) -> Option<BindingId> {
|
///
|
||||||
self.bindings.remove(name)
|
/// Unlike [`Scope::has`], the name may no longer be bound to a value (e.g., it could be
|
||||||
|
/// deleted).
|
||||||
|
pub fn declares(&self, name: &str) -> bool {
|
||||||
|
self.has(name) || self.deleted_symbols.contains(&name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the ids of all bindings defined in this scope.
|
/// Returns the ids of all bindings defined in this scope.
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ This tutorial has focused on Ruff's command-line interface, but Ruff can also be
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.0.270
|
rev: v0.0.271
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ Ruff can also be used as a [pre-commit](https://pre-commit.com) hook:
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.0.270
|
rev: v0.0.271
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
```
|
```
|
||||||
|
|
@ -32,7 +32,7 @@ Or, to enable autofix:
|
||||||
```yaml
|
```yaml
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.0.270
|
rev: v0.0.271
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: [ --fix, --exit-non-zero-on-fix ]
|
args: [ --fix, --exit-non-zero-on-fix ]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ build-backend = "maturin"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "ruff"
|
name = "ruff"
|
||||||
version = "0.0.270"
|
version = "0.0.271"
|
||||||
description = "An extremely fast Python linter, written in Rust."
|
description = "An extremely fast Python linter, written in Rust."
|
||||||
authors = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
authors = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
||||||
maintainers = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
maintainers = [{ name = "Charlie Marsh", email = "charlie.r.marsh@gmail.com" }]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue