mirror of https://github.com/astral-sh/ruff
Merge branch 'main' into dcreager/breakest-the-cycle
This commit is contained in:
commit
3651068fce
|
|
@ -298,7 +298,7 @@ jobs:
|
||||||
# sync, not just public items. Eventually we should do this for all
|
# sync, not just public items. Eventually we should do this for all
|
||||||
# crates; for now add crates here as they are warning-clean to prevent
|
# crates; for now add crates here as they are warning-clean to prevent
|
||||||
# regression.
|
# regression.
|
||||||
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db --document-private-items
|
- run: cargo doc --no-deps -p ty_python_semantic -p ty -p ty_test -p ruff_db -p ruff_python_formatter --document-private-items
|
||||||
env:
|
env:
|
||||||
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
# Setting RUSTDOCFLAGS because `cargo doc --check` isn't yet implemented (https://github.com/rust-lang/cargo/issues/10025).
|
||||||
RUSTDOCFLAGS: "-D warnings"
|
RUSTDOCFLAGS: "-D warnings"
|
||||||
|
|
|
||||||
|
|
@ -3349,6 +3349,7 @@ dependencies = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"get-size2",
|
"get-size2",
|
||||||
"insta",
|
"insta",
|
||||||
|
"itertools 0.14.0",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ruff_annotate_snippets",
|
"ruff_annotate_snippets",
|
||||||
"ruff_python_ast",
|
"ruff_python_ast",
|
||||||
|
|
|
||||||
|
|
@ -437,6 +437,15 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`Tokens`] for the parsed source file.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Unlike [`Self::tokens`], this method always returns
|
||||||
|
/// the tokens for the current file, even when within a parsed type annotation.
|
||||||
|
pub(crate) fn source_tokens(&self) -> &'a Tokens {
|
||||||
|
self.parsed.tokens()
|
||||||
|
}
|
||||||
|
|
||||||
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
/// The [`Locator`] for the current file, which enables extraction of source code from byte
|
||||||
/// offsets.
|
/// offsets.
|
||||||
pub(crate) const fn locator(&self) -> &'a Locator<'a> {
|
pub(crate) const fn locator(&self) -> &'a Locator<'a> {
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,13 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{self, Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, ExceptHandler, Expr, ExprList, Parameters, Stmt};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_index::Indexer;
|
use ruff_python_index::Indexer;
|
||||||
use ruff_python_trivia::textwrap::dedent_to;
|
use ruff_python_trivia::textwrap::dedent_to;
|
||||||
use ruff_python_trivia::{
|
use ruff_python_trivia::{
|
||||||
CommentRanges, PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content,
|
PythonWhitespace, SimpleTokenKind, SimpleTokenizer, has_leading_content, is_python_whitespace,
|
||||||
is_python_whitespace,
|
|
||||||
};
|
};
|
||||||
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
|
use ruff_source_file::{LineRanges, NewlineWithTrailingNewline, UniversalNewlines};
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
@ -209,7 +208,7 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
arguments: &Arguments,
|
arguments: &Arguments,
|
||||||
parentheses: Parentheses,
|
parentheses: Parentheses,
|
||||||
source: &str,
|
source: &str,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Edit> {
|
) -> Result<Edit> {
|
||||||
// Partition into arguments before and after the argument to remove.
|
// Partition into arguments before and after the argument to remove.
|
||||||
let (before, after): (Vec<_>, Vec<_>) = arguments
|
let (before, after): (Vec<_>, Vec<_>) = arguments
|
||||||
|
|
@ -224,7 +223,7 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
.context("Unable to find argument")?;
|
.context("Unable to find argument")?;
|
||||||
|
|
||||||
let parenthesized_range =
|
let parenthesized_range =
|
||||||
parenthesized_range(arg.value().into(), arguments.into(), comment_ranges, source)
|
token::parenthesized_range(arg.value().into(), arguments.into(), tokens)
|
||||||
.unwrap_or(arg.range());
|
.unwrap_or(arg.range());
|
||||||
|
|
||||||
if !after.is_empty() {
|
if !after.is_empty() {
|
||||||
|
|
@ -270,25 +269,14 @@ pub(crate) fn remove_argument<T: Ranged>(
|
||||||
///
|
///
|
||||||
/// The new argument will be inserted before the first existing keyword argument in `arguments`, if
|
/// The new argument will be inserted before the first existing keyword argument in `arguments`, if
|
||||||
/// there are any present. Otherwise, the new argument is added to the end of the argument list.
|
/// there are any present. Otherwise, the new argument is added to the end of the argument list.
|
||||||
pub(crate) fn add_argument(
|
pub(crate) fn add_argument(argument: &str, arguments: &Arguments, tokens: &Tokens) -> Edit {
|
||||||
argument: &str,
|
|
||||||
arguments: &Arguments,
|
|
||||||
comment_ranges: &CommentRanges,
|
|
||||||
source: &str,
|
|
||||||
) -> Edit {
|
|
||||||
if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() {
|
if let Some(ast::Keyword { range, value, .. }) = arguments.keywords.first() {
|
||||||
let keyword = parenthesized_range(value.into(), arguments.into(), comment_ranges, source)
|
let keyword = parenthesized_range(value.into(), arguments.into(), tokens).unwrap_or(*range);
|
||||||
.unwrap_or(*range);
|
|
||||||
Edit::insertion(format!("{argument}, "), keyword.start())
|
Edit::insertion(format!("{argument}, "), keyword.start())
|
||||||
} else if let Some(last) = arguments.arguments_source_order().last() {
|
} else if let Some(last) = arguments.arguments_source_order().last() {
|
||||||
// Case 1: existing arguments, so append after the last argument.
|
// Case 1: existing arguments, so append after the last argument.
|
||||||
let last = parenthesized_range(
|
let last = parenthesized_range(last.value().into(), arguments.into(), tokens)
|
||||||
last.value().into(),
|
.unwrap_or(last.range());
|
||||||
arguments.into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
.unwrap_or(last.range());
|
|
||||||
Edit::insertion(format!(", {argument}"), last.end())
|
Edit::insertion(format!(", {argument}"), last.end())
|
||||||
} else {
|
} else {
|
||||||
// Case 2: no arguments. Add argument, without any trailing comma.
|
// Case 2: no arguments. Add argument, without any trailing comma.
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,8 @@ pub(crate) fn fastapi_redundant_response_model(checker: &Checker, function_def:
|
||||||
response_model_arg,
|
response_model_arg,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::unsafe_edit)
|
.map(Fix::unsafe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,7 @@ pub(crate) fn map_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
|
||||||
checker
|
checker
|
||||||
.report_diagnostic(MapWithoutExplicitStrict, call.range())
|
.report_diagnostic(MapWithoutExplicitStrict, call.range())
|
||||||
.set_fix(Fix::applicable_edit(
|
.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("strict=False", &call.arguments, checker.tokens()),
|
||||||
"strict=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
Applicability::Unsafe,
|
Applicability::Unsafe,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::fmt::Write;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::name::QualifiedName;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault};
|
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::function_type::is_stub;
|
use ruff_python_semantic::analyze::function_type::is_stub;
|
||||||
|
|
@ -166,12 +166,7 @@ fn move_initialization(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = match parenthesized_range(
|
let range = match parenthesized_range(default.into(), parameter.into(), checker.tokens()) {
|
||||||
default.into(),
|
|
||||||
parameter.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
) {
|
|
||||||
Some(range) => range,
|
Some(range) => range,
|
||||||
None => default.range(),
|
None => default.range(),
|
||||||
};
|
};
|
||||||
|
|
@ -194,13 +189,8 @@ fn move_initialization(
|
||||||
"{} = {}",
|
"{} = {}",
|
||||||
parameter.parameter.name(),
|
parameter.parameter.name(),
|
||||||
locator.slice(
|
locator.slice(
|
||||||
parenthesized_range(
|
parenthesized_range(default.into(), parameter.into(), checker.tokens())
|
||||||
default.into(),
|
.unwrap_or(default.range())
|
||||||
parameter.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source()
|
|
||||||
)
|
|
||||||
.unwrap_or(default.range())
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,7 @@ pub(crate) fn no_explicit_stacklevel(checker: &Checker, call: &ast::ExprCall) {
|
||||||
}
|
}
|
||||||
let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range());
|
let mut diagnostic = checker.report_diagnostic(NoExplicitStacklevel, call.func.range());
|
||||||
|
|
||||||
let edit = add_argument(
|
let edit = add_argument("stacklevel=2", &call.arguments, checker.tokens());
|
||||||
"stacklevel=2",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
);
|
|
||||||
|
|
||||||
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
diagnostic.set_fix(Fix::unsafe_edit(edit));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,12 +70,7 @@ pub(crate) fn zip_without_explicit_strict(checker: &Checker, call: &ast::ExprCal
|
||||||
checker
|
checker
|
||||||
.report_diagnostic(ZipWithoutExplicitStrict, call.range())
|
.report_diagnostic(ZipWithoutExplicitStrict, call.range())
|
||||||
.set_fix(Fix::applicable_edit(
|
.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("strict=False", &call.arguments, checker.tokens()),
|
||||||
"strict=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
Applicability::Unsafe,
|
Applicability::Unsafe,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -142,13 +142,9 @@ pub(crate) fn unnecessary_generator_list(checker: &Checker, call: &ast::ExprCall
|
||||||
if *parenthesized {
|
if *parenthesized {
|
||||||
// The generator's range will include the innermost parentheses, but it could be
|
// The generator's range will include the innermost parentheses, but it could be
|
||||||
// surrounded by additional parentheses.
|
// surrounded by additional parentheses.
|
||||||
let range = parenthesized_range(
|
let range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or(argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(argument.range());
|
|
||||||
|
|
||||||
// The generator always parenthesizes the expression; trim the parentheses.
|
// The generator always parenthesizes the expression; trim the parentheses.
|
||||||
let generator = checker.generator().expr(argument);
|
let generator = checker.generator().expr(argument);
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::ExprGenerator;
|
use ruff_python_ast::ExprGenerator;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -147,13 +147,9 @@ pub(crate) fn unnecessary_generator_set(checker: &Checker, call: &ast::ExprCall)
|
||||||
if *parenthesized {
|
if *parenthesized {
|
||||||
// The generator's range will include the innermost parentheses, but it could be
|
// The generator's range will include the innermost parentheses, but it could be
|
||||||
// surrounded by additional parentheses.
|
// surrounded by additional parentheses.
|
||||||
let range = parenthesized_range(
|
let range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or(argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(argument.range());
|
|
||||||
|
|
||||||
// The generator always parenthesizes the expression; trim the parentheses.
|
// The generator always parenthesizes the expression; trim the parentheses.
|
||||||
let generator = checker.generator().expr(argument);
|
let generator = checker.generator().expr(argument);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -89,13 +89,9 @@ pub(crate) fn unnecessary_list_comprehension_set(checker: &Checker, call: &ast::
|
||||||
|
|
||||||
// If the list comprehension is parenthesized, remove the parentheses in addition to
|
// If the list comprehension is parenthesized, remove the parentheses in addition to
|
||||||
// removing the brackets.
|
// removing the brackets.
|
||||||
let replacement_range = parenthesized_range(
|
let replacement_range =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), checker.tokens())
|
||||||
(&call.arguments).into(),
|
.unwrap_or_else(|| argument.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| argument.range());
|
|
||||||
|
|
||||||
let span = argument.range().add_start(one).sub_end(one);
|
let span = argument.range().add_start(one).sub_end(one);
|
||||||
let replacement =
|
let replacement =
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
use ruff_python_trivia::is_python_whitespace;
|
use ruff_python_trivia::is_python_whitespace;
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
|
|
@ -88,13 +88,7 @@ pub(crate) fn explicit(checker: &Checker, expr: &Expr) {
|
||||||
checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
|
checker.report_diagnostic(ExplicitStringConcatenation, expr.range());
|
||||||
|
|
||||||
let is_parenthesized = |expr: &Expr| {
|
let is_parenthesized = |expr: &Expr| {
|
||||||
parenthesized_range(
|
parenthesized_range(expr.into(), bin_op.into(), checker.tokens()).is_some()
|
||||||
expr.into(),
|
|
||||||
bin_op.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
};
|
};
|
||||||
// If either `left` or `right` is parenthesized, generating
|
// If either `left` or `right` is parenthesized, generating
|
||||||
// a fix would be too involved. Just report the diagnostic.
|
// a fix would be too involved. Just report the diagnostic.
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,6 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
|
||||||
}
|
}
|
||||||
|
|
||||||
let arguments = &call.arguments;
|
let arguments = &call.arguments;
|
||||||
let source = checker.source();
|
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range);
|
let mut diagnostic = checker.report_diagnostic(ExcInfoOutsideExceptHandler, exc_info.range);
|
||||||
|
|
||||||
|
|
@ -120,8 +119,8 @@ pub(crate) fn exc_info_outside_except_handler(checker: &Checker, call: &ExprCall
|
||||||
exc_info,
|
exc_info,
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::unsafe_edit(edit))
|
Ok(Fix::unsafe_edit(edit))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_stdlib::identifiers::is_identifier;
|
use ruff_python_stdlib::identifiers::is_identifier;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
@ -129,8 +129,8 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||||
keyword,
|
keyword,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -158,8 +158,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &Checker, call: &ast::ExprCall) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
value.into(),
|
value.into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens()
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(value.range())
|
.unwrap_or(value.range())
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@ pub(crate) fn unnecessary_range_start(checker: &Checker, call: &ast::ExprCall) {
|
||||||
let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range());
|
let mut diagnostic = checker.report_diagnostic(UnnecessaryRangeStart, start.range());
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic.try_set_fix(|| {
|
||||||
remove_argument(
|
remove_argument(
|
||||||
&start,
|
start,
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -160,20 +160,16 @@ fn generate_fix(
|
||||||
) -> anyhow::Result<Fix> {
|
) -> anyhow::Result<Fix> {
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let source = locator.contents();
|
let source = locator.contents();
|
||||||
|
let tokens = checker.tokens();
|
||||||
|
|
||||||
let deletion = remove_argument(
|
let deletion = remove_argument(
|
||||||
generic_base,
|
generic_base,
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
source,
|
||||||
checker.comment_ranges(),
|
tokens,
|
||||||
)?;
|
)?;
|
||||||
let insertion = add_argument(
|
let insertion = add_argument(locator.slice(generic_base), arguments, tokens);
|
||||||
locator.slice(generic_base),
|
|
||||||
arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
source,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Fix::unsafe_edits(deletion, [insertion]))
|
Ok(Fix::unsafe_edits(deletion, [insertion]))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use ruff_python_ast::{
|
||||||
helpers::{pep_604_union, typing_optional},
|
helpers::{pep_604_union, typing_optional},
|
||||||
name::Name,
|
name::Name,
|
||||||
operator_precedence::OperatorPrecedence,
|
operator_precedence::OperatorPrecedence,
|
||||||
parenthesize::parenthesized_range,
|
token::{Tokens, parenthesized_range},
|
||||||
};
|
};
|
||||||
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
|
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -243,16 +243,12 @@ fn create_fix(
|
||||||
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
|
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
|
||||||
|
|
||||||
// Check if we need parentheses to preserve operator precedence
|
// Check if we need parentheses to preserve operator precedence
|
||||||
let content = if needs_parentheses_for_precedence(
|
let content =
|
||||||
semantic,
|
if needs_parentheses_for_precedence(semantic, literal_expr, checker.tokens()) {
|
||||||
literal_expr,
|
format!("({})", checker.generator().expr(&union_expr))
|
||||||
checker.comment_ranges(),
|
} else {
|
||||||
checker.source(),
|
checker.generator().expr(&union_expr)
|
||||||
) {
|
};
|
||||||
format!("({})", checker.generator().expr(&union_expr))
|
|
||||||
} else {
|
|
||||||
checker.generator().expr(&union_expr)
|
|
||||||
};
|
|
||||||
|
|
||||||
let union_edit = Edit::range_replacement(content, literal_expr.range());
|
let union_edit = Edit::range_replacement(content, literal_expr.range());
|
||||||
Fix::applicable_edit(union_edit, applicability)
|
Fix::applicable_edit(union_edit, applicability)
|
||||||
|
|
@ -278,8 +274,7 @@ enum UnionKind {
|
||||||
fn needs_parentheses_for_precedence(
|
fn needs_parentheses_for_precedence(
|
||||||
semantic: &ruff_python_semantic::SemanticModel,
|
semantic: &ruff_python_semantic::SemanticModel,
|
||||||
literal_expr: &Expr,
|
literal_expr: &Expr,
|
||||||
comment_ranges: &ruff_python_trivia::CommentRanges,
|
tokens: &Tokens,
|
||||||
source: &str,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// Get the parent expression to check if we're in a context that needs parentheses
|
// Get the parent expression to check if we're in a context that needs parentheses
|
||||||
let Some(parent_expr) = semantic.current_expression_parent() else {
|
let Some(parent_expr) = semantic.current_expression_parent() else {
|
||||||
|
|
@ -287,14 +282,7 @@ fn needs_parentheses_for_precedence(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the literal expression is already parenthesized
|
// Check if the literal expression is already parenthesized
|
||||||
if parenthesized_range(
|
if parenthesized_range(literal_expr.into(), parent_expr.into(), tokens).is_some() {
|
||||||
literal_expr.into(),
|
|
||||||
parent_expr.into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
return false; // Already parenthesized, don't add more
|
return false; // Already parenthesized, don't add more
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use libcst_native::{
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::Truthiness;
|
use ruff_python_ast::helpers::Truthiness;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp,
|
self as ast, AnyNodeRef, Arguments, BoolOp, ExceptHandler, Expr, Keyword, Stmt, UnaryOp,
|
||||||
|
|
@ -303,8 +303,7 @@ pub(crate) fn unittest_assertion(
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
expr.into(),
|
expr.into(),
|
||||||
checker.semantic().current_statement().into(),
|
checker.semantic().current_statement().into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(expr.range()),
|
.unwrap_or(expr.range()),
|
||||||
)));
|
)));
|
||||||
|
|
|
||||||
|
|
@ -768,8 +768,8 @@ fn check_fixture_decorator(checker: &Checker, func_name: &str, decorator: &Decor
|
||||||
keyword,
|
keyword,
|
||||||
arguments,
|
arguments,
|
||||||
edits::Parentheses::Preserve,
|
edits::Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::unsafe_edit)
|
.map(Fix::unsafe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@ use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags};
|
use ruff_python_ast::{self as ast, Expr, ExprCall, ExprContext, StringLiteralFlags};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
|
|
@ -322,18 +321,8 @@ fn elts_to_csv(elts: &[Expr], generator: Generator, flags: StringLiteralFlags) -
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This method assumes that the first argument is a string.
|
/// This method assumes that the first argument is a string.
|
||||||
fn get_parametrize_name_range(
|
fn get_parametrize_name_range(call: &ExprCall, expr: &Expr, tokens: &Tokens) -> Option<TextRange> {
|
||||||
call: &ExprCall,
|
parenthesized_range(expr.into(), (&call.arguments).into(), tokens)
|
||||||
expr: &Expr,
|
|
||||||
comment_ranges: &CommentRanges,
|
|
||||||
source: &str,
|
|
||||||
) -> Option<TextRange> {
|
|
||||||
parenthesized_range(
|
|
||||||
expr.into(),
|
|
||||||
(&call.arguments).into(),
|
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PT006
|
/// PT006
|
||||||
|
|
@ -349,13 +338,8 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
|
||||||
if names.len() > 1 {
|
if names.len() > 1 {
|
||||||
match names_type {
|
match names_type {
|
||||||
types::ParametrizeNameType::Tuple => {
|
types::ParametrizeNameType::Tuple => {
|
||||||
let name_range = get_parametrize_name_range(
|
let name_range = get_parametrize_name_range(call, expr, checker.tokens())
|
||||||
call,
|
.unwrap_or(expr.range());
|
||||||
expr,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(expr.range());
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
PytestParametrizeNamesWrongType {
|
PytestParametrizeNamesWrongType {
|
||||||
single_argument: false,
|
single_argument: false,
|
||||||
|
|
@ -386,13 +370,8 @@ fn check_names(checker: &Checker, call: &ExprCall, expr: &Expr, argvalues: &Expr
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
types::ParametrizeNameType::List => {
|
types::ParametrizeNameType::List => {
|
||||||
let name_range = get_parametrize_name_range(
|
let name_range = get_parametrize_name_range(call, expr, checker.tokens())
|
||||||
call,
|
.unwrap_or(expr.range());
|
||||||
expr,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(expr.range());
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
PytestParametrizeNamesWrongType {
|
PytestParametrizeNamesWrongType {
|
||||||
single_argument: false,
|
single_argument: false,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::helpers::{Truthiness, contains_effect};
|
use ruff_python_ast::helpers::{Truthiness, contains_effect};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
|
||||||
|
|
@ -800,14 +800,9 @@ fn is_short_circuit(
|
||||||
edit = Some(get_short_circuit_edit(
|
edit = Some(get_short_circuit_edit(
|
||||||
value,
|
value,
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
parenthesized_range(
|
parenthesized_range(furthest.into(), expr.into(), checker.tokens())
|
||||||
furthest.into(),
|
.unwrap_or(furthest.range())
|
||||||
expr.into(),
|
.start(),
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(furthest.range())
|
|
||||||
.start(),
|
|
||||||
expr.end(),
|
expr.end(),
|
||||||
),
|
),
|
||||||
short_circuit_truthiness,
|
short_circuit_truthiness,
|
||||||
|
|
@ -828,14 +823,9 @@ fn is_short_circuit(
|
||||||
edit = Some(get_short_circuit_edit(
|
edit = Some(get_short_circuit_edit(
|
||||||
next_value,
|
next_value,
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
parenthesized_range(
|
parenthesized_range(furthest.into(), expr.into(), checker.tokens())
|
||||||
furthest.into(),
|
.unwrap_or(furthest.range())
|
||||||
expr.into(),
|
.start(),
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(furthest.range())
|
|
||||||
.start(),
|
|
||||||
expr.end(),
|
expr.end(),
|
||||||
),
|
),
|
||||||
short_circuit_truthiness,
|
short_circuit_truthiness,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use ruff_text_size::{Ranged, TextRange};
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
use ruff_python_ast::helpers::{is_const_false, is_const_true};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
||||||
|
|
@ -171,13 +171,8 @@ pub(crate) fn if_expr_with_true_false(
|
||||||
checker
|
checker
|
||||||
.locator()
|
.locator()
|
||||||
.slice(
|
.slice(
|
||||||
parenthesized_range(
|
parenthesized_range(test.into(), expr.into(), checker.tokens())
|
||||||
test.into(),
|
.unwrap_or(test.range()),
|
||||||
expr.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(test.range()),
|
|
||||||
)
|
)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use anyhow::Result;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableStmt;
|
use ruff_python_ast::comparable::ComparableStmt;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches};
|
use ruff_python_ast::stmt_if::{IfElifBranch, if_elif_branches};
|
||||||
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub(crate) fn if_with_same_arms(checker: &Checker, stmt_if: &ast::StmtIf) {
|
||||||
¤t_branch,
|
¤t_branch,
|
||||||
following_branch,
|
following_branch,
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ fn merge_branches(
|
||||||
current_branch: &IfElifBranch,
|
current_branch: &IfElifBranch,
|
||||||
following_branch: &IfElifBranch,
|
following_branch: &IfElifBranch,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &ruff_python_ast::token::Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
// Identify the colon (`:`) at the end of the current branch's test.
|
// Identify the colon (`:`) at the end of the current branch's test.
|
||||||
let Some(current_branch_colon) =
|
let Some(current_branch_colon) =
|
||||||
|
|
@ -127,12 +127,9 @@ fn merge_branches(
|
||||||
);
|
);
|
||||||
|
|
||||||
// If the following test isn't parenthesized, consider parenthesizing it.
|
// If the following test isn't parenthesized, consider parenthesizing it.
|
||||||
let following_branch_test = if let Some(range) = parenthesized_range(
|
let following_branch_test = if let Some(range) =
|
||||||
following_branch.test.into(),
|
parenthesized_range(following_branch.test.into(), stmt_if.into(), tokens)
|
||||||
stmt_if.into(),
|
{
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
) {
|
|
||||||
Cow::Borrowed(locator.slice(range))
|
Cow::Borrowed(locator.slice(range))
|
||||||
} else if matches!(
|
} else if matches!(
|
||||||
following_branch.test,
|
following_branch.test,
|
||||||
|
|
@ -153,24 +150,19 @@ fn merge_branches(
|
||||||
//
|
//
|
||||||
// For example, if the current test is `x if x else y`, we should parenthesize it to
|
// For example, if the current test is `x if x else y`, we should parenthesize it to
|
||||||
// `(x if x else y) or ...`.
|
// `(x if x else y) or ...`.
|
||||||
let parenthesize_edit = if matches!(
|
let parenthesize_edit =
|
||||||
current_branch.test,
|
if matches!(
|
||||||
Expr::Lambda(_) | Expr::Named(_) | Expr::If(_)
|
current_branch.test,
|
||||||
) && parenthesized_range(
|
Expr::Lambda(_) | Expr::Named(_) | Expr::If(_)
|
||||||
current_branch.test.into(),
|
) && parenthesized_range(current_branch.test.into(), stmt_if.into(), tokens).is_none()
|
||||||
stmt_if.into(),
|
{
|
||||||
comment_ranges,
|
Some(Edit::range_replacement(
|
||||||
locator.contents(),
|
format!("({})", locator.slice(current_branch.test)),
|
||||||
)
|
current_branch.test.range(),
|
||||||
.is_none()
|
))
|
||||||
{
|
} else {
|
||||||
Some(Edit::range_replacement(
|
None
|
||||||
format!("({})", locator.slice(current_branch.test)),
|
};
|
||||||
current_branch.test.range(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Fix::safe_edits(
|
Ok(Fix::safe_edits(
|
||||||
deletion_edit,
|
deletion_edit,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
|
use ruff_python_ast::{self as ast, Arguments, CmpOp, Comprehension, Expr};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
|
@ -90,20 +90,10 @@ fn key_in_dict(checker: &Checker, left: &Expr, right: &Expr, operator: CmpOp, pa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the exact range of the left and right expressions.
|
// Extract the exact range of the left and right expressions.
|
||||||
let left_range = parenthesized_range(
|
let left_range =
|
||||||
left.into(),
|
parenthesized_range(left.into(), parent, checker.tokens()).unwrap_or(left.range());
|
||||||
parent,
|
let right_range =
|
||||||
checker.comment_ranges(),
|
parenthesized_range(right.into(), parent, checker.tokens()).unwrap_or(right.range());
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(left.range());
|
|
||||||
let right_range = parenthesized_range(
|
|
||||||
right.into(),
|
|
||||||
parent,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(right.range());
|
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(
|
let mut diagnostic = checker.report_diagnostic(
|
||||||
InDictKeys {
|
InDictKeys {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use crate::registry::Rule;
|
||||||
use crate::rules::flake8_type_checking::helpers::quote_type_expression;
|
use crate::rules::flake8_type_checking::helpers::quote_type_expression;
|
||||||
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
use crate::{AlwaysFixableViolation, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks if [PEP 613] explicit type aliases contain references to
|
/// Checks if [PEP 613] explicit type aliases contain references to
|
||||||
|
|
@ -295,21 +295,20 @@ pub(crate) fn quoted_type_alias(
|
||||||
let range = annotation_expr.range();
|
let range = annotation_expr.range();
|
||||||
let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range);
|
let mut diagnostic = checker.report_diagnostic(QuotedTypeAlias, range);
|
||||||
let fix_string = annotation_expr.value.to_string();
|
let fix_string = annotation_expr.value.to_string();
|
||||||
|
|
||||||
let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r'))
|
let fix_string = if (fix_string.contains('\n') || fix_string.contains('\r'))
|
||||||
&& parenthesized_range(
|
&& parenthesized_range(
|
||||||
// Check for parenthesis outside string ("""...""")
|
// Check for parentheses outside the string ("""...""")
|
||||||
annotation_expr.into(),
|
annotation_expr.into(),
|
||||||
checker.semantic().current_statement().into(),
|
checker.semantic().current_statement().into(),
|
||||||
checker.comment_ranges(),
|
checker.source_tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
&& parenthesized_range(
|
&& parenthesized_range(
|
||||||
// Check for parenthesis inside string """(...)"""
|
// Check for parentheses inside the string """(...)"""
|
||||||
expr.into(),
|
expr.into(),
|
||||||
annotation_expr.into(),
|
annotation_expr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator};
|
use ruff_python_ast::{Expr, ExprBinOp, ExprCall, Operator};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -89,11 +88,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
|
|
||||||
let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range());
|
let mut diagnostic = checker.report_diagnostic(PathConstructorCurrentDirectory, arg.range());
|
||||||
|
|
||||||
match parent_and_next_path_fragment_range(
|
match parent_and_next_path_fragment_range(checker.semantic(), checker.tokens()) {
|
||||||
checker.semantic(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
) {
|
|
||||||
Some((parent_range, next_fragment_range)) => {
|
Some((parent_range, next_fragment_range)) => {
|
||||||
let next_fragment_expr = checker.locator().slice(next_fragment_range);
|
let next_fragment_expr = checker.locator().slice(next_fragment_range);
|
||||||
let call_expr = checker.locator().slice(call.range());
|
let call_expr = checker.locator().slice(call.range());
|
||||||
|
|
@ -116,7 +111,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::applicable_edit(edit, applicability(call.range())))
|
Ok(Fix::applicable_edit(edit, applicability(call.range())))
|
||||||
}),
|
}),
|
||||||
|
|
@ -125,8 +120,7 @@ pub(crate) fn path_constructor_current_directory(
|
||||||
|
|
||||||
fn parent_and_next_path_fragment_range(
|
fn parent_and_next_path_fragment_range(
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &ruff_python_ast::token::Tokens,
|
||||||
source: &str,
|
|
||||||
) -> Option<(TextRange, TextRange)> {
|
) -> Option<(TextRange, TextRange)> {
|
||||||
let parent = semantic.current_expression_parent()?;
|
let parent = semantic.current_expression_parent()?;
|
||||||
|
|
||||||
|
|
@ -142,6 +136,6 @@ fn parent_and_next_path_fragment_range(
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
parent.range(),
|
parent.range(),
|
||||||
parenthesized_range(right.into(), parent.into(), comment_ranges, source).unwrap_or(range),
|
parenthesized_range(right.into(), parent.into(), tokens).unwrap_or(range),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_const_true;
|
use ruff_python_ast::helpers::is_const_true;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{self as ast, Keyword, Stmt};
|
use ruff_python_ast::{self as ast, Keyword, Stmt};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -91,7 +90,7 @@ pub(crate) fn inplace_argument(checker: &Checker, call: &ast::ExprCall) {
|
||||||
call,
|
call,
|
||||||
keyword,
|
keyword,
|
||||||
statement,
|
statement,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
) {
|
) {
|
||||||
diagnostic.set_fix(fix);
|
diagnostic.set_fix(fix);
|
||||||
|
|
@ -111,21 +110,16 @@ fn convert_inplace_argument_to_assignment(
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
keyword: &Keyword,
|
keyword: &Keyword,
|
||||||
statement: &Stmt,
|
statement: &Stmt,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
) -> Option<Fix> {
|
) -> Option<Fix> {
|
||||||
// Add the assignment.
|
// Add the assignment.
|
||||||
let attr = call.func.as_attribute_expr()?;
|
let attr = call.func.as_attribute_expr()?;
|
||||||
let insert_assignment = Edit::insertion(
|
let insert_assignment = Edit::insertion(
|
||||||
format!("{name} = ", name = locator.slice(attr.value.range())),
|
format!("{name} = ", name = locator.slice(attr.value.range())),
|
||||||
parenthesized_range(
|
parenthesized_range(call.into(), statement.into(), tokens)
|
||||||
call.into(),
|
.unwrap_or(call.range())
|
||||||
statement.into(),
|
.start(),
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(call.range())
|
|
||||||
.start(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Remove the `inplace` argument.
|
// Remove the `inplace` argument.
|
||||||
|
|
@ -134,7 +128,7 @@ fn convert_inplace_argument_to_assignment(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
locator.contents(),
|
locator.contents(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
)
|
)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter,
|
self as ast, Expr, ExprEllipsisLiteral, ExprLambda, Identifier, Parameter,
|
||||||
ParameterWithDefault, Parameters, Stmt,
|
ParameterWithDefault, Parameters, Stmt,
|
||||||
|
|
@ -265,29 +265,19 @@ fn replace_trailing_ellipsis_with_original_expr(
|
||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
checker: &Checker,
|
checker: &Checker,
|
||||||
) -> String {
|
) -> String {
|
||||||
let original_expr_range = parenthesized_range(
|
let original_expr_range =
|
||||||
(&lambda.body).into(),
|
parenthesized_range((&lambda.body).into(), lambda.into(), checker.tokens())
|
||||||
lambda.into(),
|
.unwrap_or(lambda.body.range());
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(lambda.body.range());
|
|
||||||
|
|
||||||
// This prevents the autofix of introducing a syntax error if the lambda's body is an
|
// This prevents the autofix of introducing a syntax error if the lambda's body is an
|
||||||
// expression spanned across multiple lines. To avoid the syntax error we preserve
|
// expression spanned across multiple lines. To avoid the syntax error we preserve
|
||||||
// the parenthesis around the body.
|
// the parenthesis around the body.
|
||||||
let original_expr_in_source = if parenthesized_range(
|
let original_expr_in_source =
|
||||||
lambda.into(),
|
if parenthesized_range(lambda.into(), stmt.into(), checker.tokens()).is_some() {
|
||||||
stmt.into(),
|
format!("({})", checker.locator().slice(original_expr_range))
|
||||||
checker.comment_ranges(),
|
} else {
|
||||||
checker.source(),
|
checker.locator().slice(original_expr_range).to_string()
|
||||||
)
|
};
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
format!("({})", checker.locator().slice(original_expr_range))
|
|
||||||
} else {
|
|
||||||
checker.locator().slice(original_expr_range).to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let placeholder_ellipsis_start = generated.rfind("...").unwrap();
|
let placeholder_ellipsis_start = generated.rfind("...").unwrap();
|
||||||
let placeholder_ellipsis_end = placeholder_ellipsis_start + "...".len();
|
let placeholder_ellipsis_end = placeholder_ellipsis_start + "...".len();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
|
@ -179,15 +179,14 @@ fn is_redundant_boolean_comparison(op: CmpOp, comparator: &Expr) -> Option<bool>
|
||||||
|
|
||||||
fn generate_redundant_comparison(
|
fn generate_redundant_comparison(
|
||||||
compare: &ast::ExprCompare,
|
compare: &ast::ExprCompare,
|
||||||
comment_ranges: &ruff_python_trivia::CommentRanges,
|
tokens: &Tokens,
|
||||||
source: &str,
|
source: &str,
|
||||||
comparator: &Expr,
|
comparator: &Expr,
|
||||||
kind: bool,
|
kind: bool,
|
||||||
needs_wrap: bool,
|
needs_wrap: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
let comparator_range =
|
let comparator_range = parenthesized_range(comparator.into(), compare.into(), tokens)
|
||||||
parenthesized_range(comparator.into(), compare.into(), comment_ranges, source)
|
.unwrap_or(comparator.range());
|
||||||
.unwrap_or(comparator.range());
|
|
||||||
|
|
||||||
let comparator_str = &source[comparator_range];
|
let comparator_str = &source[comparator_range];
|
||||||
|
|
||||||
|
|
@ -379,7 +378,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
.copied()
|
.copied()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
let source = checker.source();
|
let source = checker.source();
|
||||||
|
|
||||||
let content = match (&*compare.ops, &*compare.comparators) {
|
let content = match (&*compare.ops, &*compare.comparators) {
|
||||||
|
|
@ -387,18 +386,13 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) {
|
if let Some(kind) = is_redundant_boolean_comparison(*op, &compare.left) {
|
||||||
let needs_wrap = compare.left.range().start() != compare.range().start();
|
let needs_wrap = compare.left.range().start() != compare.range().start();
|
||||||
generate_redundant_comparison(
|
generate_redundant_comparison(
|
||||||
compare,
|
compare, tokens, source, comparator, kind, needs_wrap,
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
comparator,
|
|
||||||
kind,
|
|
||||||
needs_wrap,
|
|
||||||
)
|
)
|
||||||
} else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) {
|
} else if let Some(kind) = is_redundant_boolean_comparison(*op, comparator) {
|
||||||
let needs_wrap = comparator.range().end() != compare.range().end();
|
let needs_wrap = comparator.range().end() != compare.range().end();
|
||||||
generate_redundant_comparison(
|
generate_redundant_comparison(
|
||||||
compare,
|
compare,
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
&compare.left,
|
&compare.left,
|
||||||
kind,
|
kind,
|
||||||
|
|
@ -410,7 +404,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
&ops,
|
&ops,
|
||||||
&compare.comparators,
|
&compare.comparators,
|
||||||
compare.into(),
|
compare.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -420,7 +414,7 @@ pub(crate) fn literal_comparisons(checker: &Checker, compare: &ast::ExprCompare)
|
||||||
&ops,
|
&ops,
|
||||||
&compare.comparators,
|
&compare.comparators,
|
||||||
compare.into(),
|
compare.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
source,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
|
||||||
&[CmpOp::NotIn],
|
&[CmpOp::NotIn],
|
||||||
comparators,
|
comparators,
|
||||||
unary_op.into(),
|
unary_op.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
unary_op.range(),
|
unary_op.range(),
|
||||||
|
|
@ -127,7 +127,7 @@ pub(crate) fn not_tests(checker: &Checker, unary_op: &ast::ExprUnaryOp) {
|
||||||
&[CmpOp::IsNot],
|
&[CmpOp::IsNot],
|
||||||
comparators,
|
comparators,
|
||||||
unary_op.into(),
|
unary_op.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
unary_op.range(),
|
unary_op.range(),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::{ComparableExpr, HashableExpr};
|
use ruff_python_ast::comparable::{ComparableExpr, HashableExpr};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -193,16 +193,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i - 1).into(),
|
dict.value(i - 1).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i - 1).range())
|
.unwrap_or_else(|| dict.value(i - 1).range())
|
||||||
.end(),
|
.end(),
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i).into(),
|
dict.value(i).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i).range())
|
.unwrap_or_else(|| dict.value(i).range())
|
||||||
.end(),
|
.end(),
|
||||||
|
|
@ -224,16 +222,14 @@ pub(crate) fn repeated_keys(checker: &Checker, dict: &ast::ExprDict) {
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i - 1).into(),
|
dict.value(i - 1).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i - 1).range())
|
.unwrap_or_else(|| dict.value(i - 1).range())
|
||||||
.end(),
|
.end(),
|
||||||
parenthesized_range(
|
parenthesized_range(
|
||||||
dict.value(i).into(),
|
dict.value(i).into(),
|
||||||
dict.into(),
|
dict.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|| dict.value(i).range())
|
.unwrap_or_else(|| dict.value(i).range())
|
||||||
.end(),
|
.end(),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::token::{TokenKind, Tokens};
|
use ruff_python_ast::token::{TokenKind, Tokens};
|
||||||
use ruff_python_ast::{self as ast, Stmt};
|
use ruff_python_ast::{self as ast, Stmt};
|
||||||
use ruff_python_semantic::Binding;
|
use ruff_python_semantic::Binding;
|
||||||
|
|
@ -172,14 +172,10 @@ fn remove_unused_variable(binding: &Binding, checker: &Checker) -> Option<Fix> {
|
||||||
{
|
{
|
||||||
// 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 start = parenthesized_range(
|
let start =
|
||||||
target.into(),
|
parenthesized_range(target.into(), statement.into(), checker.tokens())
|
||||||
statement.into(),
|
.unwrap_or(target.range())
|
||||||
checker.comment_ranges(),
|
.start();
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(target.range())
|
|
||||||
.start();
|
|
||||||
let end = match_token_after(checker.tokens(), target.end(), |token| {
|
let end = match_token_after(checker.tokens(), target.end(), |token| {
|
||||||
token == TokenKind::Equal
|
token == TokenKind::Equal
|
||||||
})?
|
})?
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare,
|
BoolOp, CmpOp, Expr, ExprBoolOp, ExprCompare,
|
||||||
parenthesize::{parentheses_iterator, parenthesized_range},
|
token::{parentheses_iterator, parenthesized_range},
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -62,7 +62,7 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
|
||||||
}
|
}
|
||||||
|
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
|
|
||||||
// retrieve all compare expressions from boolean expression
|
// retrieve all compare expressions from boolean expression
|
||||||
let compare_expressions = expr_bool_op
|
let compare_expressions = expr_bool_op
|
||||||
|
|
@ -89,40 +89,22 @@ pub(crate) fn boolean_chained_comparison(checker: &Checker, expr_bool_op: &ExprB
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_paren_count = parentheses_iterator(
|
let left_paren_count =
|
||||||
left_compare.into(),
|
parentheses_iterator(left_compare.into(), Some(expr_bool_op.into()), tokens).count();
|
||||||
Some(expr_bool_op.into()),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
let right_paren_count = parentheses_iterator(
|
let right_paren_count =
|
||||||
right_compare.into(),
|
parentheses_iterator(right_compare.into(), Some(expr_bool_op.into()), tokens).count();
|
||||||
Some(expr_bool_op.into()),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
// Create the edit that removes the comparison operator
|
// Create the edit that removes the comparison operator
|
||||||
|
|
||||||
// In `a<(b) and ((b))<c`, we need to handle the
|
// In `a<(b) and ((b))<c`, we need to handle the
|
||||||
// parentheses when specifying the fix range.
|
// parentheses when specifying the fix range.
|
||||||
let left_compare_right_range = parenthesized_range(
|
let left_compare_right_range =
|
||||||
left_compare_right.into(),
|
parenthesized_range(left_compare_right.into(), left_compare.into(), tokens)
|
||||||
left_compare.into(),
|
.unwrap_or(left_compare_right.range());
|
||||||
comment_ranges,
|
let right_compare_left_range =
|
||||||
locator.contents(),
|
parenthesized_range(right_compare_left.into(), right_compare.into(), tokens)
|
||||||
)
|
.unwrap_or(right_compare_left.range());
|
||||||
.unwrap_or(left_compare_right.range());
|
|
||||||
let right_compare_left_range = parenthesized_range(
|
|
||||||
right_compare_left.into(),
|
|
||||||
right_compare.into(),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(right_compare_left.range());
|
|
||||||
let edit = Edit::range_replacement(
|
let edit = Edit::range_replacement(
|
||||||
locator.slice(left_compare_right_range).to_string(),
|
locator.slice(left_compare_right_range).to_string(),
|
||||||
TextRange::new(
|
TextRange::new(
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ pub(crate) fn duplicate_bases(checker: &Checker, name: &str, arguments: Option<&
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(|edit| {
|
.map(|edit| {
|
||||||
Fix::applicable_edit(
|
Fix::applicable_edit(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Stmt};
|
use ruff_python_ast::{self as ast, CmpOp, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -166,13 +166,8 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
|
||||||
let replacement = format!(
|
let replacement = format!(
|
||||||
"{} = {min_max}({}, {})",
|
"{} = {min_max}({}, {})",
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(body_target.into(), body.into(), checker.tokens())
|
||||||
body_target.into(),
|
.unwrap_or(body_target.range())
|
||||||
body.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents()
|
|
||||||
)
|
|
||||||
.unwrap_or(body_target.range())
|
|
||||||
),
|
),
|
||||||
checker.locator().slice(arg1),
|
checker.locator().slice(arg1),
|
||||||
checker.locator().slice(arg2),
|
checker.locator().slice(arg2),
|
||||||
|
|
|
||||||
|
|
@ -174,12 +174,8 @@ pub(crate) fn missing_maxsplit_arg(checker: &Checker, value: &Expr, slice: &Expr
|
||||||
SliceBoundary::Last => "rsplit",
|
SliceBoundary::Last => "rsplit",
|
||||||
};
|
};
|
||||||
|
|
||||||
let maxsplit_argument_edit = fix::edits::add_argument(
|
let maxsplit_argument_edit =
|
||||||
"maxsplit=1",
|
fix::edits::add_argument("maxsplit=1", arguments, checker.tokens());
|
||||||
arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Only change `actual_split_type` if it doesn't match `suggested_split_type`
|
// Only change `actual_split_type` if it doesn't match `suggested_split_type`
|
||||||
let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type {
|
let split_type_edit: Option<Edit> = if actual_split_type == suggested_split_type {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ast::Expr;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{ExprBinOp, ExprRef, Operator};
|
use ruff_python_ast::{ExprBinOp, ExprRef, Operator};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
|
@ -150,12 +150,10 @@ fn augmented_assignment(
|
||||||
|
|
||||||
let right_operand_ref = ExprRef::from(right_operand);
|
let right_operand_ref = ExprRef::from(right_operand);
|
||||||
let parent = original_expr.into();
|
let parent = original_expr.into();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let tokens = checker.tokens();
|
||||||
let source = checker.source();
|
|
||||||
|
|
||||||
let right_operand_range =
|
let right_operand_range =
|
||||||
parenthesized_range(right_operand_ref, parent, comment_ranges, source)
|
parenthesized_range(right_operand_ref, parent, tokens).unwrap_or(right_operand.range());
|
||||||
.unwrap_or(right_operand.range());
|
|
||||||
let right_operand_expr = locator.slice(right_operand_range);
|
let right_operand_expr = locator.slice(right_operand_range);
|
||||||
|
|
||||||
let target_expr = locator.slice(target);
|
let target_expr = locator.slice(target);
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,7 @@ pub(crate) fn subprocess_run_without_check(checker: &Checker, call: &ast::ExprCa
|
||||||
let mut diagnostic =
|
let mut diagnostic =
|
||||||
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
|
checker.report_diagnostic(SubprocessRunWithoutCheck, call.func.range());
|
||||||
diagnostic.set_fix(Fix::applicable_edit(
|
diagnostic.set_fix(Fix::applicable_edit(
|
||||||
add_argument(
|
add_argument("check=False", &call.arguments, checker.tokens()),
|
||||||
"check=False",
|
|
||||||
&call.arguments,
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
),
|
|
||||||
// If the function call contains `**kwargs`, mark the fix as unsafe.
|
// If the function call contains `**kwargs`, mark the fix as unsafe.
|
||||||
if call
|
if call
|
||||||
.arguments
|
.arguments
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::{self as ast, Expr, name::QualifiedName};
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -193,8 +192,7 @@ fn generate_keyword_fix(checker: &Checker, call: &ast::ExprCall) -> Fix {
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.locator().contents(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Fix::unsafe_edits(
|
Ok(Fix::unsafe_edits(
|
||||||
Edit::insertion(type_params.to_string(), name.end()),
|
Edit::insertion(type_params.to_string(), name.end()),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::name::Name;
|
use ruff_python_ast::name::Name;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef};
|
use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssign, StmtRef};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -261,11 +261,11 @@ fn create_diagnostic(
|
||||||
type_alias_kind: TypeAliasKind,
|
type_alias_kind: TypeAliasKind,
|
||||||
) {
|
) {
|
||||||
let source = checker.source();
|
let source = checker.source();
|
||||||
|
let tokens = checker.tokens();
|
||||||
let comment_ranges = checker.comment_ranges();
|
let comment_ranges = checker.comment_ranges();
|
||||||
|
|
||||||
let range_with_parentheses =
|
let range_with_parentheses =
|
||||||
parenthesized_range(value.into(), stmt.into(), comment_ranges, source)
|
parenthesized_range(value.into(), stmt.into(), tokens).unwrap_or(value.range());
|
||||||
.unwrap_or(value.range());
|
|
||||||
|
|
||||||
let content = format!(
|
let content = format!(
|
||||||
"type {name}{type_params} = {value}",
|
"type {name}{type_params} = {value}",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::{self as ast, Keyword};
|
use ruff_python_ast::{self as ast, Keyword, token::Tokens};
|
||||||
use ruff_python_semantic::Modules;
|
use ruff_python_semantic::Modules;
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -104,7 +103,7 @@ pub(crate) fn replace_stdout_stderr(checker: &Checker, call: &ast::ExprCall) {
|
||||||
stderr,
|
stderr,
|
||||||
call,
|
call,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +116,7 @@ fn generate_fix(
|
||||||
stderr: &Keyword,
|
stderr: &Keyword,
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
source: &str,
|
source: &str,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
let (first, second) = if stdout.start() < stderr.start() {
|
let (first, second) = if stdout.start() < stderr.start() {
|
||||||
(stdout, stderr)
|
(stdout, stderr)
|
||||||
|
|
@ -132,7 +131,7 @@ fn generate_fix(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
source,
|
source,
|
||||||
comment_ranges,
|
tokens,
|
||||||
)?],
|
)?],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ pub(crate) fn replace_universal_newlines(checker: &Checker, call: &ast::ExprCall
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -206,7 +206,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -231,7 +231,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
@ -249,7 +249,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &Checker, call: &ast::ExprCall) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(Fix::safe_edit)
|
.map(Fix::safe_edit)
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ pub(crate) fn useless_class_metaclass_type(checker: &Checker, class_def: &StmtCl
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let range = edit.range();
|
let range = edit.range();
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ pub(crate) fn useless_object_inheritance(checker: &Checker, class_def: &ast::Stm
|
||||||
arguments,
|
arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let range = edit.range();
|
let range = edit.range();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -139,13 +139,8 @@ pub(crate) fn yield_in_for_loop(checker: &Checker, stmt_for: &ast::StmtFor) {
|
||||||
let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range());
|
let mut diagnostic = checker.report_diagnostic(YieldInForLoop, stmt_for.range());
|
||||||
|
|
||||||
let contents = checker.locator().slice(
|
let contents = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(iter.as_ref().into(), stmt_for.into(), checker.tokens())
|
||||||
iter.as_ref().into(),
|
.unwrap_or(iter.range()),
|
||||||
stmt_for.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iter.range()),
|
|
||||||
);
|
);
|
||||||
let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) {
|
let contents = if iter.as_tuple_expr().is_some_and(|it| !it.parenthesized) {
|
||||||
format!("yield from ({contents})")
|
format!("yield from ({contents})")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::{self as ast, Expr, name::Name, parenthesize::parenthesized_range};
|
use ruff_python_ast::{self as ast, Expr, name::Name, token::parenthesized_range};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
|
use ruff_python_semantic::{BindingId, ResolvedReference, SemanticModel};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
@ -330,12 +330,8 @@ pub(super) fn parenthesize_loop_iter_if_necessary<'a>(
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let iter = for_stmt.iter.as_ref();
|
let iter = for_stmt.iter.as_ref();
|
||||||
|
|
||||||
let original_parenthesized_range = parenthesized_range(
|
let original_parenthesized_range =
|
||||||
iter.into(),
|
parenthesized_range(iter.into(), for_stmt.into(), checker.tokens());
|
||||||
for_stmt.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(range) = original_parenthesized_range {
|
if let Some(range) = original_parenthesized_range {
|
||||||
return Cow::Borrowed(locator.slice(range));
|
return Cow::Borrowed(locator.slice(range));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
|
Expr, ExprAttribute, ExprBinOp, ExprCall, ExprStringLiteral, ExprSubscript, ExprUnaryOp,
|
||||||
Number, Operator, PythonVersion, UnaryOp,
|
Number, Operator, PythonVersion, UnaryOp,
|
||||||
|
|
@ -112,8 +112,7 @@ pub(crate) fn fromisoformat_replace_z(checker: &Checker, call: &ExprCall) {
|
||||||
let value_full_range = parenthesized_range(
|
let value_full_range = parenthesized_range(
|
||||||
replace_time_zone.date.into(),
|
replace_time_zone.date.into(),
|
||||||
replace_time_zone.parent.into(),
|
replace_time_zone.parent.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(replace_time_zone.date.range());
|
.unwrap_or(replace_time_zone.date.range());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::Expr;
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -76,8 +75,8 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
|
||||||
Edit::range_replacement(
|
Edit::range_replacement(
|
||||||
format!(
|
format!(
|
||||||
"{} or {}",
|
"{} or {}",
|
||||||
parenthesize_test(test, if_expr, checker.comment_ranges(), checker.locator()),
|
parenthesize_test(test, if_expr, checker.tokens(), checker.locator()),
|
||||||
parenthesize_test(orelse, if_expr, checker.comment_ranges(), checker.locator()),
|
parenthesize_test(orelse, if_expr, checker.tokens(), checker.locator()),
|
||||||
),
|
),
|
||||||
if_expr.range(),
|
if_expr.range(),
|
||||||
),
|
),
|
||||||
|
|
@ -99,15 +98,10 @@ pub(crate) fn if_exp_instead_of_or_operator(checker: &Checker, if_expr: &ast::Ex
|
||||||
fn parenthesize_test<'a>(
|
fn parenthesize_test<'a>(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
if_expr: &ast::ExprIf,
|
if_expr: &ast::ExprIf,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
locator: &Locator<'a>,
|
locator: &Locator<'a>,
|
||||||
) -> Cow<'a, str> {
|
) -> Cow<'a, str> {
|
||||||
if let Some(range) = parenthesized_range(
|
if let Some(range) = parenthesized_range(expr.into(), if_expr.into(), tokens) {
|
||||||
expr.into(),
|
|
||||||
if_expr.into(),
|
|
||||||
comment_ranges,
|
|
||||||
locator.contents(),
|
|
||||||
) {
|
|
||||||
Cow::Borrowed(locator.slice(range))
|
Cow::Borrowed(locator.slice(range))
|
||||||
} else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) {
|
} else if matches!(expr, Expr::If(_) | Expr::Lambda(_) | Expr::Named(_)) {
|
||||||
Cow::Owned(format!("({})", locator.slice(expr.range())))
|
Cow::Owned(format!("({})", locator.slice(expr.range())))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::Applicability;
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{Comprehension, Expr, StmtFor};
|
use ruff_python_ast::{Comprehension, Expr, StmtFor};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_python_semantic::analyze::typing::is_io_base_expr;
|
use ruff_python_semantic::analyze::typing::is_io_base_expr;
|
||||||
|
|
@ -104,8 +104,7 @@ fn readlines_in_iter(checker: &Checker, iter_expr: &Expr) {
|
||||||
let deletion_range = if let Some(parenthesized_range) = parenthesized_range(
|
let deletion_range = if let Some(parenthesized_range) = parenthesized_range(
|
||||||
expr_attr.value.as_ref().into(),
|
expr_attr.value.as_ref().into(),
|
||||||
expr_attr.into(),
|
expr_attr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
) {
|
) {
|
||||||
expr_call.range().add_start(parenthesized_range.len())
|
expr_call.range().add_start(parenthesized_range.len())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ruff_diagnostics::Applicability;
|
use ruff_diagnostics::Applicability;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr, Number};
|
use ruff_python_ast::{self as ast, Expr, Number};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -152,13 +152,8 @@ fn generate_fix(checker: &Checker, call: &ast::ExprCall, base: Base, arg: &Expr)
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let arg_range = parenthesized_range(
|
let arg_range =
|
||||||
arg.into(),
|
parenthesized_range(arg.into(), call.into(), checker.tokens()).unwrap_or(arg.range());
|
||||||
call.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(arg.range());
|
|
||||||
let arg_str = checker.locator().slice(arg_range);
|
let arg_str = checker.locator().slice(arg_range);
|
||||||
|
|
||||||
Ok(Fix::applicable_edits(
|
Ok(Fix::applicable_edits(
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ pub(crate) fn single_item_membership_test(
|
||||||
&[membership_test.replacement_op()],
|
&[membership_test.replacement_op()],
|
||||||
std::slice::from_ref(item),
|
std::slice::from_ref(item),
|
||||||
expr.into(),
|
expr.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
),
|
),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ fn convert_type_vars(
|
||||||
class_arguments,
|
class_arguments,
|
||||||
Parentheses::Remove,
|
Parentheses::Remove,
|
||||||
source,
|
source,
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?;
|
)?;
|
||||||
let replace_type_params =
|
let replace_type_params =
|
||||||
Edit::range_replacement(new_type_params.to_string(), type_params.range);
|
Edit::range_replacement(new_type_params.to_string(), type_params.range);
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ use anyhow::Result;
|
||||||
use ast::Keyword;
|
use ast::Keyword;
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::is_constant;
|
use ruff_python_ast::helpers::is_constant;
|
||||||
|
use ruff_python_ast::token::Tokens;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_trivia::CommentRanges;
|
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::Locator;
|
use crate::Locator;
|
||||||
|
|
@ -108,9 +108,8 @@ pub(crate) fn default_factory_kwarg(checker: &Checker, call: &ast::ExprCall) {
|
||||||
},
|
},
|
||||||
call.range(),
|
call.range(),
|
||||||
);
|
);
|
||||||
diagnostic.try_set_fix(|| {
|
diagnostic
|
||||||
convert_to_positional(call, keyword, checker.locator(), checker.comment_ranges())
|
.try_set_fix(|| convert_to_positional(call, keyword, checker.locator(), checker.tokens()));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`).
|
/// Returns `true` if a value is definitively not callable (e.g., `1` or `[]`).
|
||||||
|
|
@ -136,7 +135,7 @@ fn convert_to_positional(
|
||||||
call: &ast::ExprCall,
|
call: &ast::ExprCall,
|
||||||
default_factory: &Keyword,
|
default_factory: &Keyword,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
) -> Result<Fix> {
|
) -> Result<Fix> {
|
||||||
if call.arguments.len() == 1 {
|
if call.arguments.len() == 1 {
|
||||||
// Ex) `defaultdict(default_factory=list)`
|
// Ex) `defaultdict(default_factory=list)`
|
||||||
|
|
@ -153,7 +152,7 @@ fn convert_to_positional(
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
locator.contents(),
|
locator.contents(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Second, insert the value as the first positional argument.
|
// Second, insert the value as the first positional argument.
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ pub(crate) fn falsy_dict_get_fallback(checker: &Checker, expr: &Expr) {
|
||||||
&call.arguments,
|
&call.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.locator().contents(),
|
checker.locator().contents(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)
|
)
|
||||||
.map(|edit| Fix::applicable_edit(edit, applicability))
|
.map(|edit| Fix::applicable_edit(edit, applicability))
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -77,14 +77,7 @@ pub(crate) fn parenthesize_chained_logical_operators(checker: &Checker, expr: &a
|
||||||
) => {
|
) => {
|
||||||
let locator = checker.locator();
|
let locator = checker.locator();
|
||||||
let source_range = bool_op.range();
|
let source_range = bool_op.range();
|
||||||
if parenthesized_range(
|
if parenthesized_range(bool_op.into(), expr.into(), checker.tokens()).is_none() {
|
||||||
bool_op.into(),
|
|
||||||
expr.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
locator.contents(),
|
|
||||||
)
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
let new_source = format!("({})", locator.slice(source_range));
|
let new_source = format!("({})", locator.slice(source_range));
|
||||||
let edit = Edit::range_replacement(new_source, source_range);
|
let edit = Edit::range_replacement(new_source, source_range);
|
||||||
checker
|
checker
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Context;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_semantic::{Scope, ScopeKind};
|
use ruff_python_semantic::{Scope, ScopeKind};
|
||||||
use ruff_python_trivia::{indentation_at_offset, textwrap};
|
use ruff_python_trivia::{indentation_at_offset, textwrap};
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
|
|
@ -159,8 +159,7 @@ fn use_initvar(
|
||||||
let default_loc = parenthesized_range(
|
let default_loc = parenthesized_range(
|
||||||
default.into(),
|
default.into(),
|
||||||
parameter_with_default.into(),
|
parameter_with_default.into(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
checker.source(),
|
|
||||||
)
|
)
|
||||||
.unwrap_or(default.range());
|
.unwrap_or(default.range());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Arguments, Expr};
|
use ruff_python_ast::{self as ast, Arguments, Expr};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
@ -116,13 +116,8 @@ fn convert_to_reduce(iterable: &Expr, call: &ast::ExprCall, checker: &Checker) -
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let iterable = checker.locator().slice(
|
let iterable = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(iterable.into(), (&call.arguments).into(), checker.tokens())
|
||||||
iterable.into(),
|
.unwrap_or(iterable.range()),
|
||||||
(&call.arguments).into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iterable.range()),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Fix::unsafe_edits(
|
Ok(Fix::unsafe_edits(
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::PythonVersion;
|
use ruff_python_ast::PythonVersion;
|
||||||
use ruff_python_ast::token::TokenKind;
|
use ruff_python_ast::token::TokenKind;
|
||||||
use ruff_python_ast::{Expr, ExprCall, parenthesize::parenthesized_range};
|
use ruff_python_ast::{Expr, ExprCall, token::parenthesized_range};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -124,13 +124,8 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
|
||||||
|
|
||||||
let mut remove_zip = vec![];
|
let mut remove_zip = vec![];
|
||||||
|
|
||||||
let full_zip_range = parenthesized_range(
|
let full_zip_range =
|
||||||
zip.into(),
|
parenthesized_range(zip.into(), starmap.into(), checker.tokens()).unwrap_or(zip.range());
|
||||||
starmap.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(zip.range());
|
|
||||||
|
|
||||||
// Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple.
|
// Delete any parentheses around the `zip` call to prevent that the argument turns into a tuple.
|
||||||
remove_zip.push(Edit::range_deletion(TextRange::new(
|
remove_zip.push(Edit::range_deletion(TextRange::new(
|
||||||
|
|
@ -138,13 +133,8 @@ fn replace_with_map(starmap: &ExprCall, zip: &ExprCall, checker: &Checker) -> Op
|
||||||
zip.start(),
|
zip.start(),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
let full_zip_func_range = parenthesized_range(
|
let full_zip_func_range = parenthesized_range((&zip.func).into(), zip.into(), checker.tokens())
|
||||||
(&zip.func).into(),
|
.unwrap_or(zip.func.range());
|
||||||
zip.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(zip.func.range());
|
|
||||||
|
|
||||||
// Delete the `zip` callee
|
// Delete the `zip` callee
|
||||||
remove_zip.push(Edit::range_deletion(full_zip_func_range));
|
remove_zip.push(Edit::range_deletion(full_zip_func_range));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::{Tokens, parenthesized_range};
|
||||||
use ruff_python_ast::{Arguments, Expr, ExprCall};
|
use ruff_python_ast::{Arguments, Expr, ExprCall};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
use ruff_python_semantic::analyze::type_inference::{NumberLike, PythonType, ResolvedPythonType};
|
||||||
|
|
@ -86,6 +86,7 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
|
||||||
applicability,
|
applicability,
|
||||||
checker.semantic(),
|
checker.semantic(),
|
||||||
checker.locator(),
|
checker.locator(),
|
||||||
|
checker.tokens(),
|
||||||
checker.comment_ranges(),
|
checker.comment_ranges(),
|
||||||
checker.source(),
|
checker.source(),
|
||||||
);
|
);
|
||||||
|
|
@ -95,27 +96,26 @@ pub(crate) fn unnecessary_cast_to_int(checker: &Checker, call: &ExprCall) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a fix that replaces `int(expression)` with `expression`.
|
/// Creates a fix that replaces `int(expression)` with `expression`.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn unwrap_int_expression(
|
fn unwrap_int_expression(
|
||||||
call: &ExprCall,
|
call: &ExprCall,
|
||||||
argument: &Expr,
|
argument: &Expr,
|
||||||
applicability: Applicability,
|
applicability: Applicability,
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
locator: &Locator,
|
locator: &Locator,
|
||||||
|
tokens: &Tokens,
|
||||||
comment_ranges: &CommentRanges,
|
comment_ranges: &CommentRanges,
|
||||||
source: &str,
|
source: &str,
|
||||||
) -> Fix {
|
) -> Fix {
|
||||||
let content = if let Some(range) = parenthesized_range(
|
let content = if let Some(range) =
|
||||||
argument.into(),
|
parenthesized_range(argument.into(), (&call.arguments).into(), tokens)
|
||||||
(&call.arguments).into(),
|
{
|
||||||
comment_ranges,
|
|
||||||
source,
|
|
||||||
) {
|
|
||||||
locator.slice(range).to_string()
|
locator.slice(range).to_string()
|
||||||
} else {
|
} else {
|
||||||
let parenthesize = semantic.current_expression_parent().is_some()
|
let parenthesize = semantic.current_expression_parent().is_some()
|
||||||
|| argument.is_named_expr()
|
|| argument.is_named_expr()
|
||||||
|| locator.count_lines(argument.range()) > 0;
|
|| locator.count_lines(argument.range()) > 0;
|
||||||
if parenthesize && !has_own_parentheses(argument, comment_ranges, source) {
|
if parenthesize && !has_own_parentheses(argument, tokens, source) {
|
||||||
format!("({})", locator.slice(argument.range()))
|
format!("({})", locator.slice(argument.range()))
|
||||||
} else {
|
} else {
|
||||||
locator.slice(argument.range()).to_string()
|
locator.slice(argument.range()).to_string()
|
||||||
|
|
@ -255,7 +255,7 @@ fn round_applicability(arguments: &Arguments, semantic: &SemanticModel) -> Optio
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
|
/// Returns `true` if the given [`Expr`] has its own parentheses (e.g., `()`, `[]`, `{}`).
|
||||||
fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str) -> bool {
|
fn has_own_parentheses(expr: &Expr, tokens: &Tokens, source: &str) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::ListComp(_)
|
Expr::ListComp(_)
|
||||||
| Expr::SetComp(_)
|
| Expr::SetComp(_)
|
||||||
|
|
@ -276,14 +276,10 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
|
||||||
// f
|
// f
|
||||||
// (10)
|
// (10)
|
||||||
// ```
|
// ```
|
||||||
let func_end = parenthesized_range(
|
let func_end =
|
||||||
call_expr.func.as_ref().into(),
|
parenthesized_range(call_expr.func.as_ref().into(), call_expr.into(), tokens)
|
||||||
call_expr.into(),
|
.unwrap_or(call_expr.func.range())
|
||||||
comment_ranges,
|
.end();
|
||||||
source,
|
|
||||||
)
|
|
||||||
.unwrap_or(call_expr.func.range())
|
|
||||||
.end();
|
|
||||||
lines_after_ignoring_trivia(func_end, source) == 0
|
lines_after_ignoring_trivia(func_end, source) == 0
|
||||||
}
|
}
|
||||||
Expr::Subscript(subscript_expr) => {
|
Expr::Subscript(subscript_expr) => {
|
||||||
|
|
@ -291,8 +287,7 @@ fn has_own_parentheses(expr: &Expr, comment_ranges: &CommentRanges, source: &str
|
||||||
let subscript_end = parenthesized_range(
|
let subscript_end = parenthesized_range(
|
||||||
subscript_expr.value.as_ref().into(),
|
subscript_expr.value.as_ref().into(),
|
||||||
subscript_expr.into(),
|
subscript_expr.into(),
|
||||||
comment_ranges,
|
tokens,
|
||||||
source,
|
|
||||||
)
|
)
|
||||||
.unwrap_or(subscript_expr.value.range())
|
.unwrap_or(subscript_expr.value.range())
|
||||||
.end();
|
.end();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use ruff_python_ast::{self as ast, BoolOp, CmpOp, Expr};
|
||||||
|
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
@ -108,22 +108,12 @@ pub(crate) fn unnecessary_key_check(checker: &Checker, expr: &Expr) {
|
||||||
format!(
|
format!(
|
||||||
"{}.get({})",
|
"{}.get({})",
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(obj_right.into(), right.into(), checker.tokens(),)
|
||||||
obj_right.into(),
|
.unwrap_or(obj_right.range())
|
||||||
right.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(obj_right.range())
|
|
||||||
),
|
),
|
||||||
checker.locator().slice(
|
checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(key_right.into(), right.into(), checker.tokens(),)
|
||||||
key_right.into(),
|
.unwrap_or(key_right.range())
|
||||||
right.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.locator().contents(),
|
|
||||||
)
|
|
||||||
.unwrap_or(key_right.range())
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
expr.range(),
|
expr.range(),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ruff_diagnostics::{Applicability, Edit};
|
||||||
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
use ruff_macros::{ViolationMetadata, derive_message_formats};
|
||||||
|
|
||||||
use ruff_python_ast::helpers::is_empty_f_string;
|
use ruff_python_ast::helpers::is_empty_f_string;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::token::parenthesized_range;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
@ -140,31 +140,19 @@ fn fix_unnecessary_literal_in_deque(
|
||||||
// call. otherwise, we only delete the `iterable` argument and leave the others untouched.
|
// call. otherwise, we only delete the `iterable` argument and leave the others untouched.
|
||||||
let edit = if let Some(maxlen) = maxlen {
|
let edit = if let Some(maxlen) = maxlen {
|
||||||
let deque_name = checker.locator().slice(
|
let deque_name = checker.locator().slice(
|
||||||
parenthesized_range(
|
parenthesized_range(deque.func.as_ref().into(), deque.into(), checker.tokens())
|
||||||
deque.func.as_ref().into(),
|
.unwrap_or(deque.func.range()),
|
||||||
deque.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(deque.func.range()),
|
|
||||||
);
|
);
|
||||||
let len_str = checker.locator().slice(maxlen);
|
let len_str = checker.locator().slice(maxlen);
|
||||||
let deque_str = format!("{deque_name}(maxlen={len_str})");
|
let deque_str = format!("{deque_name}(maxlen={len_str})");
|
||||||
Edit::range_replacement(deque_str, deque.range)
|
Edit::range_replacement(deque_str, deque.range)
|
||||||
} else {
|
} else {
|
||||||
let range = parenthesized_range(
|
|
||||||
iterable.value().into(),
|
|
||||||
(&deque.arguments).into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
)
|
|
||||||
.unwrap_or(iterable.range());
|
|
||||||
remove_argument(
|
remove_argument(
|
||||||
&range,
|
&iterable,
|
||||||
&deque.arguments,
|
&deque.arguments,
|
||||||
Parentheses::Preserve,
|
Parentheses::Preserve,
|
||||||
checker.source(),
|
checker.source(),
|
||||||
checker.comment_ranges(),
|
checker.tokens(),
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
let has_comments = checker.comment_ranges().intersects(edit.range());
|
let has_comments = checker.comment_ranges().intersects(edit.range());
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,14 @@ use std::path::Path;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use ruff_python_trivia::{CommentRanges, SimpleTokenKind, SimpleTokenizer, indentation_at_offset};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer, indentation_at_offset};
|
||||||
use ruff_source_file::LineRanges;
|
use ruff_source_file::LineRanges;
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::name::{Name, QualifiedName, QualifiedNameBuilder};
|
use crate::name::{Name, QualifiedName, QualifiedNameBuilder};
|
||||||
use crate::parenthesize::parenthesized_range;
|
|
||||||
use crate::statement_visitor::StatementVisitor;
|
use crate::statement_visitor::StatementVisitor;
|
||||||
|
use crate::token::Tokens;
|
||||||
|
use crate::token::parenthesized_range;
|
||||||
use crate::visitor::Visitor;
|
use crate::visitor::Visitor;
|
||||||
use crate::{
|
use crate::{
|
||||||
self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
|
self as ast, Arguments, AtomicNodeIndex, CmpOp, DictItem, ExceptHandler, Expr, ExprNoneLiteral,
|
||||||
|
|
@ -1474,7 +1475,7 @@ pub fn generate_comparison(
|
||||||
ops: &[CmpOp],
|
ops: &[CmpOp],
|
||||||
comparators: &[Expr],
|
comparators: &[Expr],
|
||||||
parent: AnyNodeRef,
|
parent: AnyNodeRef,
|
||||||
comment_ranges: &CommentRanges,
|
tokens: &Tokens,
|
||||||
source: &str,
|
source: &str,
|
||||||
) -> String {
|
) -> String {
|
||||||
let start = left.start();
|
let start = left.start();
|
||||||
|
|
@ -1483,8 +1484,7 @@ pub fn generate_comparison(
|
||||||
|
|
||||||
// Add the left side of the comparison.
|
// Add the left side of the comparison.
|
||||||
contents.push_str(
|
contents.push_str(
|
||||||
&source[parenthesized_range(left.into(), parent, comment_ranges, source)
|
&source[parenthesized_range(left.into(), parent, tokens).unwrap_or(left.range())],
|
||||||
.unwrap_or(left.range())],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (op, comparator) in ops.iter().zip(comparators) {
|
for (op, comparator) in ops.iter().zip(comparators) {
|
||||||
|
|
@ -1504,7 +1504,7 @@ pub fn generate_comparison(
|
||||||
|
|
||||||
// Add the right side of the comparison.
|
// Add the right side of the comparison.
|
||||||
contents.push_str(
|
contents.push_str(
|
||||||
&source[parenthesized_range(comparator.into(), parent, comment_ranges, source)
|
&source[parenthesized_range(comparator.into(), parent, tokens)
|
||||||
.unwrap_or(comparator.range())],
|
.unwrap_or(comparator.range())],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl Debug for DebugComment<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pretty-printed debug representation of [`Comments`].
|
/// Pretty-printed debug representation of [`Comments`](super::Comments).
|
||||||
pub(crate) struct DebugComments<'a> {
|
pub(crate) struct DebugComments<'a> {
|
||||||
comments: &'a CommentsMap<'a>,
|
comments: &'a CommentsMap<'a>,
|
||||||
source_code: SourceCode<'a>,
|
source_code: SourceCode<'a>,
|
||||||
|
|
|
||||||
|
|
@ -504,7 +504,7 @@ impl InOrderEntry {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
struct OutOfOrderEntry {
|
struct OutOfOrderEntry {
|
||||||
/// Index into the [`MultiMap::out_of_order`] vector at which offset the leading vec is stored.
|
/// Index into the [`MultiMap::out_of_order_parts`] vector at which offset the leading vec is stored.
|
||||||
leading_index: usize,
|
leading_index: usize,
|
||||||
_count: Count<OutOfOrderEntry>,
|
_count: Count<OutOfOrderEntry>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use ruff_python_ast::AnyNodeRef;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// Used as key into the [`MultiMap`] storing the comments per node by [`Comments`].
|
/// Used as key into the [`MultiMap`](super::MultiMap) storing the comments per node by
|
||||||
|
/// [`Comments`](super::Comments).
|
||||||
///
|
///
|
||||||
/// Implements equality and hashing based on the address of the [`AnyNodeRef`] to get fast and cheap
|
/// Implements equality and hashing based on the address of the [`AnyNodeRef`] to get fast and cheap
|
||||||
/// hashing/equality comparison.
|
/// hashing/equality comparison.
|
||||||
|
|
|
||||||
|
|
@ -1974,8 +1974,8 @@ fn handle_unary_op_comment<'a>(
|
||||||
/// )
|
/// )
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The comment will be attached to the [`Arguments`] node as a dangling comment, to ensure
|
/// The comment will be attached to the [`Arguments`](ast::Arguments) node as a dangling comment, to
|
||||||
/// that it remains on the same line as open parenthesis.
|
/// ensure that it remains on the same line as open parenthesis.
|
||||||
///
|
///
|
||||||
/// Similarly, given:
|
/// Similarly, given:
|
||||||
/// ```python
|
/// ```python
|
||||||
|
|
@ -1984,8 +1984,8 @@ fn handle_unary_op_comment<'a>(
|
||||||
/// ] = ...
|
/// ] = ...
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// The comment will be attached to the [`TypeParams`] node as a dangling comment, to ensure
|
/// The comment will be attached to the [`TypeParams`](ast::TypeParams) node as a dangling comment,
|
||||||
/// that it remains on the same line as open bracket.
|
/// to ensure that it remains on the same line as open bracket.
|
||||||
fn handle_bracketed_end_of_line_comment<'a>(
|
fn handle_bracketed_end_of_line_comment<'a>(
|
||||||
comment: DecoratedComment<'a>,
|
comment: DecoratedComment<'a>,
|
||||||
source: &str,
|
source: &str,
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,8 @@ impl<'ast> SourceOrderVisitor<'ast> for CommentsVisitor<'ast, '_> {
|
||||||
|
|
||||||
/// A comment decorated with additional information about its surrounding context in the source document.
|
/// A comment decorated with additional information about its surrounding context in the source document.
|
||||||
///
|
///
|
||||||
/// Used by [`CommentStyle::place_comment`] to determine if this should become a [leading](self#leading-comments), [dangling](self#dangling-comments), or [trailing](self#trailing-comments) comment.
|
/// Used by [`place_comment`] to determine if this should become a [leading](self#leading-comments),
|
||||||
|
/// [dangling](self#dangling-comments), or [trailing](self#trailing-comments) comment.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct DecoratedComment<'a> {
|
pub(crate) struct DecoratedComment<'a> {
|
||||||
enclosing: AnyNodeRef<'a>,
|
enclosing: AnyNodeRef<'a>,
|
||||||
|
|
@ -465,7 +466,7 @@ pub(super) enum CommentPlacement<'a> {
|
||||||
///
|
///
|
||||||
/// [`preceding_node`]: DecoratedComment::preceding_node
|
/// [`preceding_node`]: DecoratedComment::preceding_node
|
||||||
/// [`following_node`]: DecoratedComment::following_node
|
/// [`following_node`]: DecoratedComment::following_node
|
||||||
/// [`enclosing_node`]: DecoratedComment::enclosing_node_id
|
/// [`enclosing_node`]: DecoratedComment::enclosing_node
|
||||||
/// [trailing comment]: self#trailing-comments
|
/// [trailing comment]: self#trailing-comments
|
||||||
/// [leading comment]: self#leading-comments
|
/// [leading comment]: self#leading-comments
|
||||||
/// [dangling comment]: self#dangling-comments
|
/// [dangling comment]: self#dangling-comments
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ impl InterpolatedStringState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the interpolated string state is [`NestedInterpolatedElement`].
|
/// Returns `true` if the interpolated string state is [`Self::NestedInterpolatedElement`].
|
||||||
pub(crate) fn is_nested(self) -> bool {
|
pub(crate) fn is_nested(self) -> bool {
|
||||||
matches!(self, Self::NestedInterpolatedElement(..))
|
matches!(self, Self::NestedInterpolatedElement(..))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1095,9 +1095,9 @@ impl OperandIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the index of the operand's right operator. The method always returns an index
|
/// Returns the index of the operand's right operator. The method always returns an index even
|
||||||
/// even if the operand has no right operator. Use [`BinaryCallChain::get_operator`] to test if
|
/// if the operand has no right operator. Use [`FlatBinaryExpressionSlice::get_operator`] to
|
||||||
/// the operand has a right operator.
|
/// test if the operand has a right operator.
|
||||||
fn right_operator(self) -> OperatorIndex {
|
fn right_operator(self) -> OperatorIndex {
|
||||||
OperatorIndex::new(self.0 + 1)
|
OperatorIndex::new(self.0 + 1)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,18 +56,20 @@ pub(crate) enum Parenthesize {
|
||||||
/// Adding parentheses is desired to prevent the comments from wandering.
|
/// Adding parentheses is desired to prevent the comments from wandering.
|
||||||
IfRequired,
|
IfRequired,
|
||||||
|
|
||||||
/// Same as [`Self::IfBreaks`] except that it uses [`parenthesize_if_expands`] for expressions
|
/// Same as [`Self::IfBreaks`] except that it uses
|
||||||
/// with the layout [`NeedsParentheses::BestFit`] which is used by non-splittable
|
/// [`parenthesize_if_expands`](crate::builders::parenthesize_if_expands) for expressions with
|
||||||
/// expressions like literals, name, and strings.
|
/// the layout [`OptionalParentheses::BestFit`] which is used by non-splittable expressions like
|
||||||
|
/// literals, name, and strings.
|
||||||
///
|
///
|
||||||
/// Use this layout over `IfBreaks` when there's a sequence of `maybe_parenthesize_expression`
|
/// Use this layout over `IfBreaks` when there's a sequence of `maybe_parenthesize_expression`
|
||||||
/// in a single logical-line and you want to break from right-to-left. Use `IfBreaks` for the
|
/// in a single logical-line and you want to break from right-to-left. Use `IfBreaks` for the
|
||||||
/// first expression and `IfBreaksParenthesized` for the rest.
|
/// first expression and `IfBreaksParenthesized` for the rest.
|
||||||
IfBreaksParenthesized,
|
IfBreaksParenthesized,
|
||||||
|
|
||||||
/// Same as [`Self::IfBreaksParenthesized`] but uses [`parenthesize_if_expands`] for nested
|
/// Same as [`Self::IfBreaksParenthesized`] but uses
|
||||||
/// [`maybe_parenthesized_expression`] calls unlike other layouts that always omit parentheses
|
/// [`parenthesize_if_expands`](crate::builders::parenthesize_if_expands) for nested
|
||||||
/// when outer parentheses are present.
|
/// [`maybe_parenthesized_expression`](crate::expression::maybe_parenthesize_expression) calls
|
||||||
|
/// unlike other layouts that always omit parentheses when outer parentheses are present.
|
||||||
IfBreaksParenthesizedNested,
|
IfBreaksParenthesizedNested,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -214,8 +214,9 @@ impl Format<PyFormatContext<'_>> for MaybeParenthesizePattern<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is very similar to [`can_omit_optional_parentheses`] with the only difference that it is for patterns
|
/// This function is very similar to
|
||||||
/// and not expressions.
|
/// [`can_omit_optional_parentheses`](crate::expression::can_omit_optional_parentheses)
|
||||||
|
/// with the only difference that it is for patterns and not expressions.
|
||||||
///
|
///
|
||||||
/// The base idea of the omit optional parentheses layout is to prefer using parentheses of sub-patterns
|
/// The base idea of the omit optional parentheses layout is to prefer using parentheses of sub-patterns
|
||||||
/// when splitting the pattern over introducing new patterns. For example, prefer splitting the sequence pattern in
|
/// when splitting the pattern over introducing new patterns. For example, prefer splitting the sequence pattern in
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,9 @@ impl FormatNodeRule<PatternArguments> for FormatPatternArguments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the pattern (which is the only argument to a [`PatternMatchClass`]) is
|
/// Returns `true` if the pattern (which is the only argument to a
|
||||||
/// parenthesized. Used to avoid falsely assuming that `x` is parenthesized in cases like:
|
/// [`PatternMatchClass`](ruff_python_ast::PatternMatchClass)) is parenthesized.
|
||||||
|
/// Used to avoid falsely assuming that `x` is parenthesized in cases like:
|
||||||
/// ```python
|
/// ```python
|
||||||
/// case Point2D(x): ...
|
/// case Point2D(x): ...
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ use crate::{FormatModuleError, PyFormatOptions, format_module_source};
|
||||||
///
|
///
|
||||||
/// The returned formatted range guarantees to cover at least `range` (excluding whitespace), but the range might be larger.
|
/// The returned formatted range guarantees to cover at least `range` (excluding whitespace), but the range might be larger.
|
||||||
/// Some cases in which the returned range is larger than `range` are:
|
/// Some cases in which the returned range is larger than `range` are:
|
||||||
/// * The logical lines in `range` use a indentation different from the configured [`IndentStyle`] and [`IndentWidth`].
|
/// * The logical lines in `range` use a indentation different from the configured [`IndentStyle`]
|
||||||
|
/// and [`IndentWidth`](ruff_formatter::IndentWidth).
|
||||||
/// * `range` is smaller than a logical lines and the formatter needs to format the entire logical line.
|
/// * `range` is smaller than a logical lines and the formatter needs to format the entire logical line.
|
||||||
/// * `range` falls on a single line body.
|
/// * `range` falls on a single line body.
|
||||||
///
|
///
|
||||||
|
|
@ -129,16 +130,19 @@ pub fn format_range(
|
||||||
/// b) formatting a sub-expression has fewer split points than formatting the entire expressions.
|
/// b) formatting a sub-expression has fewer split points than formatting the entire expressions.
|
||||||
///
|
///
|
||||||
/// ### Possible docstrings
|
/// ### Possible docstrings
|
||||||
/// Strings that are suspected to be docstrings are excluded from the search to format the enclosing suite instead
|
/// Strings that are suspected to be docstrings are excluded from the search to format the enclosing
|
||||||
/// so that the formatter's docstring detection in [`FormatSuite`] correctly detects and formats the docstrings.
|
/// suite instead so that the formatter's docstring detection in
|
||||||
|
/// [`FormatSuite`](crate::statement::suite::FormatSuite) correctly detects and formats the
|
||||||
|
/// docstrings.
|
||||||
///
|
///
|
||||||
/// ### Compound statements with a simple statement body
|
/// ### Compound statements with a simple statement body
|
||||||
/// Don't include simple-statement bodies of compound statements `if True: pass` because the formatter
|
/// Don't include simple-statement bodies of compound statements `if True: pass` because the formatter
|
||||||
/// must run [`FormatClauseBody`] to determine if the body should be collapsed or not.
|
/// must run `FormatClauseBody` to determine if the body should be collapsed or not.
|
||||||
///
|
///
|
||||||
/// ### Incorrectly indented code
|
/// ### Incorrectly indented code
|
||||||
/// Code that uses indentations that don't match the configured [`IndentStyle`] and [`IndentWidth`] are excluded from the search,
|
/// Code that uses indentations that don't match the configured [`IndentStyle`] and
|
||||||
/// because formatting such nodes on their own can lead to indentation mismatch with its sibling nodes.
|
/// [`IndentWidth`](ruff_formatter::IndentWidth) are excluded from the search, because formatting
|
||||||
|
/// such nodes on their own can lead to indentation mismatch with its sibling nodes.
|
||||||
///
|
///
|
||||||
/// ## Suppression comments
|
/// ## Suppression comments
|
||||||
/// The search ends when `range` falls into a suppressed range because there's nothing to format. It also avoids that the
|
/// The search ends when `range` falls into a suppressed range because there's nothing to format. It also avoids that the
|
||||||
|
|
@ -279,13 +283,15 @@ enum EnclosingNode<'a> {
|
||||||
///
|
///
|
||||||
/// ## Compound statements with simple statement bodies
|
/// ## Compound statements with simple statement bodies
|
||||||
/// Similar to [`find_enclosing_node`], exclude the compound statement's body if it is a simple statement (not a suite) from the search to format the entire clause header
|
/// Similar to [`find_enclosing_node`], exclude the compound statement's body if it is a simple statement (not a suite) from the search to format the entire clause header
|
||||||
/// with the body. This ensures that the formatter runs [`FormatClauseBody`] that determines if the body should be indented.s
|
/// with the body. This ensures that the formatter runs `FormatClauseBody` that determines if the body should be indented.
|
||||||
///
|
///
|
||||||
/// ## Non-standard indentation
|
/// ## Non-standard indentation
|
||||||
/// Node's that use an indentation that doesn't match the configured [`IndentStyle`] and [`IndentWidth`] are excluded from the search.
|
/// Nodes that use an indentation that doesn't match the configured [`IndentStyle`] and
|
||||||
/// This is because the formatter always uses the configured [`IndentStyle`] and [`IndentWidth`], resulting in the
|
/// [`IndentWidth`](ruff_formatter::IndentWidth) are excluded from the search. This is because the
|
||||||
/// formatted nodes using a different indentation than the unformatted sibling nodes. This would be tolerable
|
/// formatter always uses the configured [`IndentStyle`] and
|
||||||
/// in non whitespace sensitive languages like JavaScript but results in lexical errors in Python.
|
/// [`IndentWidth`](ruff_formatter::IndentWidth), resulting in the formatted nodes using a different
|
||||||
|
/// indentation than the unformatted sibling nodes. This would be tolerable in non whitespace
|
||||||
|
/// sensitive languages like JavaScript but results in lexical errors in Python.
|
||||||
///
|
///
|
||||||
/// ## Implementation
|
/// ## Implementation
|
||||||
/// It would probably be possible to merge this visitor with [`FindEnclosingNode`] but they are separate because
|
/// It would probably be possible to merge this visitor with [`FindEnclosingNode`] but they are separate because
|
||||||
|
|
@ -713,9 +719,11 @@ impl Format<PyFormatContext<'_>> for FormatEnclosingNode<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the level of indentation for `indentation` when using the configured [`IndentStyle`] and [`IndentWidth`].
|
/// Computes the level of indentation for `indentation` when using the configured [`IndentStyle`]
|
||||||
|
/// and [`IndentWidth`](ruff_formatter::IndentWidth).
|
||||||
///
|
///
|
||||||
/// Returns `None` if the indentation doesn't conform to the configured [`IndentStyle`] and [`IndentWidth`].
|
/// Returns `None` if the indentation doesn't conform to the configured [`IndentStyle`] and
|
||||||
|
/// [`IndentWidth`](ruff_formatter::IndentWidth).
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// If `offset` is outside of `source`.
|
/// If `offset` is outside of `source`.
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ impl Format<PyFormatContext<'_>> for FormatTargetWithEqualOperator<'_> {
|
||||||
/// No parentheses are added for `short` because it fits into the configured line length, regardless of whether
|
/// No parentheses are added for `short` because it fits into the configured line length, regardless of whether
|
||||||
/// the comment exceeds the line width or not.
|
/// the comment exceeds the line width or not.
|
||||||
///
|
///
|
||||||
/// This logic isn't implemented in [`place_comment`] by associating trailing statement comments to the expression because
|
/// This logic isn't implemented in `place_comment` by associating trailing statement comments to the expression because
|
||||||
/// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement.
|
/// doing so breaks the suite empty lines formatting that relies on trailing comments to be stored on the statement.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum FormatStatementsLastExpression<'a> {
|
pub(super) enum FormatStatementsLastExpression<'a> {
|
||||||
|
|
@ -202,8 +202,8 @@ pub(super) enum FormatStatementsLastExpression<'a> {
|
||||||
/// ] = some_long_value
|
/// ] = some_long_value
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// This layout is preferred over [`RightToLeft`] if the left is unsplittable (single keyword like `return` or a Name)
|
/// This layout is preferred over [`Self::RightToLeft`] if the left is unsplittable (single
|
||||||
/// because it has better performance characteristics.
|
/// keyword like `return` or a Name) because it has better performance characteristics.
|
||||||
LeftToRight {
|
LeftToRight {
|
||||||
/// The right side of an assignment or the value returned in a return statement.
|
/// The right side of an assignment or the value returned in a return statement.
|
||||||
value: &'a Expr,
|
value: &'a Expr,
|
||||||
|
|
@ -1083,11 +1083,10 @@ impl Format<PyFormatContext<'_>> for InterpolatedString<'_> {
|
||||||
/// For legibility, we discuss only the case of f-strings below, but the
|
/// For legibility, we discuss only the case of f-strings below, but the
|
||||||
/// same comments apply to t-strings.
|
/// same comments apply to t-strings.
|
||||||
///
|
///
|
||||||
/// This is just a wrapper around [`FormatFString`] while considering a special
|
/// This is just a wrapper around [`FormatFString`](crate::other::f_string::FormatFString) while
|
||||||
/// case when the f-string is at an assignment statement's value position.
|
/// considering a special case when the f-string is at an assignment statement's value position.
|
||||||
/// This is necessary to prevent an instability where an f-string contains a
|
/// This is necessary to prevent an instability where an f-string contains a multiline expression
|
||||||
/// multiline expression and the f-string fits on the line, but only when it's
|
/// and the f-string fits on the line, but only when it's surrounded by parentheses.
|
||||||
/// surrounded by parentheses.
|
|
||||||
///
|
///
|
||||||
/// ```python
|
/// ```python
|
||||||
/// aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
/// aaaaaaaaaaaaaaaaaa = f"testeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{
|
||||||
|
|
|
||||||
|
|
@ -177,8 +177,10 @@ enum WithItemsLayout<'a> {
|
||||||
/// ...
|
/// ...
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// In this case, use [`maybe_parenthesize_expression`] to format the context expression
|
/// In this case, use
|
||||||
/// to get the exact same formatting as when formatting an expression in any other clause header.
|
/// [`maybe_parenthesize_expression`](crate::expression::maybe_parenthesize_expression) to
|
||||||
|
/// format the context expression to get the exact same formatting as when formatting an
|
||||||
|
/// expression in any other clause header.
|
||||||
///
|
///
|
||||||
/// Only used for Python 3.9+
|
/// Only used for Python 3.9+
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -783,7 +783,7 @@ enum CodeExampleKind<'src> {
|
||||||
///
|
///
|
||||||
/// Documentation describing doctests and how they're recognized can be
|
/// Documentation describing doctests and how they're recognized can be
|
||||||
/// found as part of the Python standard library:
|
/// found as part of the Python standard library:
|
||||||
/// https://docs.python.org/3/library/doctest.html.
|
/// <https://docs.python.org/3/library/doctest.html>.
|
||||||
///
|
///
|
||||||
/// (You'll likely need to read the [regex matching] used internally by the
|
/// (You'll likely need to read the [regex matching] used internally by the
|
||||||
/// doctest module to determine more precisely how it works.)
|
/// doctest module to determine more precisely how it works.)
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,9 @@ impl<'a, 'src> StringNormalizer<'a, 'src> {
|
||||||
/// it can't because the string contains the preferred quotes OR
|
/// it can't because the string contains the preferred quotes OR
|
||||||
/// it leads to more escaping.
|
/// it leads to more escaping.
|
||||||
///
|
///
|
||||||
/// Note: If you add more cases here where we return `QuoteStyle::Preserve`,
|
/// Note: If you add more cases here where we return `QuoteStyle::Preserve`, make sure to also
|
||||||
/// make sure to also add them to [`FormatImplicitConcatenatedStringFlat::new`].
|
/// add them to
|
||||||
|
/// [`FormatImplicitConcatenatedStringFlat::new`](crate::string::implicit::FormatImplicitConcatenatedStringFlat::new).
|
||||||
pub(super) fn preferred_quote_style(&self, string: StringLikePart) -> QuoteStyle {
|
pub(super) fn preferred_quote_style(&self, string: StringLikePart) -> QuoteStyle {
|
||||||
let preferred_quote_style = self
|
let preferred_quote_style = self
|
||||||
.preferred_quote_style
|
.preferred_quote_style
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use crate::prelude::*;
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FormatTypeParams;
|
pub struct FormatTypeParams;
|
||||||
|
|
||||||
/// Formats a sequence of [`TypeParam`] nodes.
|
/// Formats a sequence of [`TypeParam`](ruff_python_ast::TypeParam) nodes.
|
||||||
impl FormatNodeRule<TypeParams> for FormatTypeParams {
|
impl FormatNodeRule<TypeParams> for FormatTypeParams {
|
||||||
fn fmt_fields(&self, item: &TypeParams, f: &mut PyFormatter) -> FormatResult<()> {
|
fn fmt_fields(&self, item: &TypeParams, f: &mut PyFormatter) -> FormatResult<()> {
|
||||||
// A dangling comment indicates a comment on the same line as the opening bracket, e.g.:
|
// A dangling comment indicates a comment on the same line as the opening bracket, e.g.:
|
||||||
|
|
|
||||||
|
|
@ -679,8 +679,9 @@ impl Indentation {
|
||||||
|
|
||||||
/// Returns `true` for a space or tab character.
|
/// Returns `true` for a space or tab character.
|
||||||
///
|
///
|
||||||
/// This is different than [`is_python_whitespace`] in that it returns `false` for a form feed character.
|
/// This is different than [`is_python_whitespace`](ruff_python_trivia::is_python_whitespace) in
|
||||||
/// Form feed characters are excluded because they should be preserved in the suppressed output.
|
/// that it returns `false` for a form feed character. Form feed characters are excluded because
|
||||||
|
/// they should be preserved in the suppressed output.
|
||||||
const fn is_indent_whitespace(c: char) -> bool {
|
const fn is_indent_whitespace(c: char) -> bool {
|
||||||
matches!(c, ' ' | '\t')
|
matches!(c, ' ' | '\t')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ ruff_source_file = { workspace = true }
|
||||||
|
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
insta = { workspace = true, features = ["glob"] }
|
insta = { workspace = true, features = ["glob"] }
|
||||||
|
itertools = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
walkdir = { workspace = true }
|
walkdir = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
(c: int = 1,f"""{d=[
|
||||||
|
def a(
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
@ -67,26 +67,59 @@ impl<'src> TokenSource<'src> {
|
||||||
///
|
///
|
||||||
/// [`re_lex_logical_token`]: Lexer::re_lex_logical_token
|
/// [`re_lex_logical_token`]: Lexer::re_lex_logical_token
|
||||||
pub(crate) fn re_lex_logical_token(&mut self) {
|
pub(crate) fn re_lex_logical_token(&mut self) {
|
||||||
let mut non_logical_newline_start = None;
|
let mut non_logical_newline = None;
|
||||||
for token in self.tokens.iter().rev() {
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
let last_non_trivia_end_before = {
|
||||||
|
self.tokens
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|tok| !tok.kind().is_trivia())
|
||||||
|
.map(ruff_text_size::Ranged::end)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, token) in self.tokens.iter().enumerate().rev() {
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
TokenKind::NonLogicalNewline => {
|
TokenKind::NonLogicalNewline => {
|
||||||
non_logical_newline_start = Some(token.start());
|
non_logical_newline = Some((index, token.start()));
|
||||||
}
|
}
|
||||||
TokenKind::Comment => continue,
|
TokenKind::Comment => continue,
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.lexer.re_lex_logical_token(non_logical_newline_start) {
|
if !self
|
||||||
let current_start = self.current_range().start();
|
.lexer
|
||||||
while self
|
.re_lex_logical_token(non_logical_newline.map(|(_, start)| start))
|
||||||
.tokens
|
{
|
||||||
.last()
|
return;
|
||||||
.is_some_and(|last| last.start() >= current_start)
|
}
|
||||||
{
|
|
||||||
self.tokens.pop();
|
let non_logical_line_index = non_logical_newline
|
||||||
}
|
.expect(
|
||||||
|
"`re_lex_logical_token` should only return `true` if `non_logical_line` is `Some`",
|
||||||
|
)
|
||||||
|
.0;
|
||||||
|
|
||||||
|
// Trim the already bumped logical line token (and comments coming after it) as it might now have become a logical line token
|
||||||
|
self.tokens.truncate(non_logical_line_index);
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
let last_non_trivia_end_now = {
|
||||||
|
self.tokens
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|tok| !tok.kind().is_trivia())
|
||||||
|
.map(ruff_text_size::Ranged::end)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(last_non_trivia_end_before, last_non_trivia_end_now);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure `current` is positioned at a non-trivia token.
|
||||||
|
if self.current_kind().is_trivia() {
|
||||||
|
self.bump(self.current_kind());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,16 @@ use std::fmt::{Formatter, Write};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||||||
use ruff_python_ast::token::Token;
|
use ruff_python_ast::token::{Token, Tokens};
|
||||||
use ruff_python_ast::visitor::Visitor;
|
use ruff_python_ast::visitor::Visitor;
|
||||||
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal, walk_module};
|
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, TraversalSignal, walk_module};
|
||||||
use ruff_python_ast::{self as ast, AnyNodeRef, Mod, PythonVersion};
|
use ruff_python_ast::{self as ast, AnyNodeRef, Mod, PythonVersion};
|
||||||
use ruff_python_parser::semantic_errors::{
|
use ruff_python_parser::semantic_errors::{
|
||||||
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError,
|
SemanticSyntaxChecker, SemanticSyntaxContext, SemanticSyntaxError,
|
||||||
};
|
};
|
||||||
use ruff_python_parser::{Mode, ParseErrorType, ParseOptions, parse_unchecked};
|
use ruff_python_parser::{Mode, ParseErrorType, ParseOptions, Parsed, parse_unchecked};
|
||||||
use ruff_source_file::{LineIndex, OneIndexed, SourceCode};
|
use ruff_source_file::{LineIndex, OneIndexed, SourceCode};
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||||
|
|
||||||
|
|
@ -81,7 +82,7 @@ fn test_valid_syntax(input_path: &Path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
validate_tokens(parsed.tokens(), source.text_len(), input_path);
|
validate_tokens(parsed.tokens(), source.text_len(), input_path);
|
||||||
validate_ast(parsed.syntax(), source.text_len(), input_path);
|
validate_ast(&parsed, source.text_len(), input_path);
|
||||||
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
writeln!(&mut output, "## AST").unwrap();
|
writeln!(&mut output, "## AST").unwrap();
|
||||||
|
|
@ -139,7 +140,7 @@ fn test_invalid_syntax(input_path: &Path) {
|
||||||
let parsed = parse_unchecked(&source, options.clone());
|
let parsed = parse_unchecked(&source, options.clone());
|
||||||
|
|
||||||
validate_tokens(parsed.tokens(), source.text_len(), input_path);
|
validate_tokens(parsed.tokens(), source.text_len(), input_path);
|
||||||
validate_ast(parsed.syntax(), source.text_len(), input_path);
|
validate_ast(&parsed, source.text_len(), input_path);
|
||||||
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
writeln!(&mut output, "## AST").unwrap();
|
writeln!(&mut output, "## AST").unwrap();
|
||||||
|
|
@ -402,12 +403,16 @@ Tokens: {tokens:#?}
|
||||||
/// * the range of the parent node fully encloses all its child nodes
|
/// * the range of the parent node fully encloses all its child nodes
|
||||||
/// * the ranges are strictly increasing when traversing the nodes in pre-order.
|
/// * the ranges are strictly increasing when traversing the nodes in pre-order.
|
||||||
/// * all ranges are within the length of the source code.
|
/// * all ranges are within the length of the source code.
|
||||||
fn validate_ast(root: &Mod, source_len: TextSize, test_path: &Path) {
|
fn validate_ast(parsed: &Parsed<Mod>, source_len: TextSize, test_path: &Path) {
|
||||||
walk_module(&mut ValidateAstVisitor::new(source_len, test_path), root);
|
walk_module(
|
||||||
|
&mut ValidateAstVisitor::new(parsed.tokens(), source_len, test_path),
|
||||||
|
parsed.syntax(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ValidateAstVisitor<'a> {
|
struct ValidateAstVisitor<'a> {
|
||||||
|
tokens: std::iter::Peekable<std::slice::Iter<'a, Token>>,
|
||||||
parents: Vec<AnyNodeRef<'a>>,
|
parents: Vec<AnyNodeRef<'a>>,
|
||||||
previous: Option<AnyNodeRef<'a>>,
|
previous: Option<AnyNodeRef<'a>>,
|
||||||
source_length: TextSize,
|
source_length: TextSize,
|
||||||
|
|
@ -415,8 +420,9 @@ struct ValidateAstVisitor<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ValidateAstVisitor<'a> {
|
impl<'a> ValidateAstVisitor<'a> {
|
||||||
fn new(source_length: TextSize, test_path: &'a Path) -> Self {
|
fn new(tokens: &'a Tokens, source_length: TextSize, test_path: &'a Path) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
tokens: tokens.iter().peekable(),
|
||||||
parents: Vec::new(),
|
parents: Vec::new(),
|
||||||
previous: None,
|
previous: None,
|
||||||
source_length,
|
source_length,
|
||||||
|
|
@ -425,6 +431,47 @@ impl<'a> ValidateAstVisitor<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ValidateAstVisitor<'_> {
|
||||||
|
/// Check that the node's start doesn't fall within a token.
|
||||||
|
/// Called in `enter_node` before visiting children.
|
||||||
|
fn assert_start_boundary(&mut self, node: AnyNodeRef<'_>) {
|
||||||
|
// Skip tokens that end at or before the node starts.
|
||||||
|
self.tokens
|
||||||
|
.peeking_take_while(|t| t.end() <= node.start())
|
||||||
|
.last();
|
||||||
|
|
||||||
|
if let Some(next) = self.tokens.peek() {
|
||||||
|
// At this point, next_token.end() > node.start()
|
||||||
|
assert!(
|
||||||
|
next.start() >= node.start(),
|
||||||
|
"{path}: The start of the node falls within a token.\nNode: {node:#?}\n\nToken: {next:#?}\n\nRoot: {root:#?}",
|
||||||
|
path = self.test_path.display(),
|
||||||
|
root = self.parents.first()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check that the node's end doesn't fall within a token.
|
||||||
|
/// Called in `leave_node` after visiting children, so all tokens
|
||||||
|
/// within the node have been consumed.
|
||||||
|
fn assert_end_boundary(&mut self, node: AnyNodeRef<'_>) {
|
||||||
|
// Skip tokens that end at or before the node ends.
|
||||||
|
self.tokens
|
||||||
|
.peeking_take_while(|t| t.end() <= node.end())
|
||||||
|
.last();
|
||||||
|
|
||||||
|
if let Some(next) = self.tokens.peek() {
|
||||||
|
// At this point, `next_token.end() > node.end()`
|
||||||
|
assert!(
|
||||||
|
next.start() >= node.end(),
|
||||||
|
"{path}: The end of the node falls within a token.\nNode: {node:#?}\n\nToken: {next:#?}\n\nRoot: {root:#?}",
|
||||||
|
path = self.test_path.display(),
|
||||||
|
root = self.parents.first()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
|
impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
|
||||||
fn enter_node(&mut self, node: AnyNodeRef<'ast>) -> TraversalSignal {
|
fn enter_node(&mut self, node: AnyNodeRef<'ast>) -> TraversalSignal {
|
||||||
assert!(
|
assert!(
|
||||||
|
|
@ -452,12 +499,16 @@ impl<'ast> SourceOrderVisitor<'ast> for ValidateAstVisitor<'ast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.assert_start_boundary(node);
|
||||||
|
|
||||||
self.parents.push(node);
|
self.parents.push(node);
|
||||||
|
|
||||||
TraversalSignal::Traverse
|
TraversalSignal::Traverse
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leave_node(&mut self, node: AnyNodeRef<'ast>) {
|
fn leave_node(&mut self, node: AnyNodeRef<'ast>) {
|
||||||
|
self.assert_end_boundary(node);
|
||||||
|
|
||||||
self.parents.pop().expect("Expected tree to be balanced");
|
self.parents.pop().expect("Expected tree to be balanced");
|
||||||
|
|
||||||
self.previous = Some(node);
|
self.previous = Some(node);
|
||||||
|
|
|
||||||
|
|
@ -296,7 +296,7 @@ Module(
|
||||||
test: Call(
|
test: Call(
|
||||||
ExprCall {
|
ExprCall {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
range: 456..472,
|
range: 456..471,
|
||||||
func: Name(
|
func: Name(
|
||||||
ExprName {
|
ExprName {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
|
|
@ -306,7 +306,7 @@ Module(
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
range: 460..472,
|
range: 460..471,
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
args: [
|
args: [
|
||||||
Name(
|
Name(
|
||||||
|
|
@ -581,7 +581,7 @@ Module(
|
||||||
test: Call(
|
test: Call(
|
||||||
ExprCall {
|
ExprCall {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
range: 890..906,
|
range: 890..905,
|
||||||
func: Name(
|
func: Name(
|
||||||
ExprName {
|
ExprName {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
|
|
@ -591,7 +591,7 @@ Module(
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
range: 894..906,
|
range: 894..905,
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
args: [
|
args: [
|
||||||
FString(
|
FString(
|
||||||
|
|
@ -832,7 +832,16 @@ Module(
|
||||||
|
|
|
|
||||||
28 | # The lexer is nested with multiple levels of parentheses
|
28 | # The lexer is nested with multiple levels of parentheses
|
||||||
29 | if call(foo, [a, b
|
29 | if call(foo, [a, b
|
||||||
| ^ Syntax Error: Expected `]`, found NonLogicalNewline
|
30 | def bar():
|
||||||
|
| ^^^ Syntax Error: Expected `]`, found `def`
|
||||||
|
31 | pass
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
28 | # The lexer is nested with multiple levels of parentheses
|
||||||
|
29 | if call(foo, [a, b
|
||||||
|
| ^ Syntax Error: Expected `)`, found newline
|
||||||
30 | def bar():
|
30 | def bar():
|
||||||
31 | pass
|
31 | pass
|
||||||
|
|
|
|
||||||
|
|
@ -857,11 +866,10 @@ Module(
|
||||||
|
|
||||||
|
|
||||||
|
|
|
|
||||||
41 | # test is to make sure it emits a `NonLogicalNewline` token after `b`.
|
|
||||||
42 | if call(foo, [a,
|
42 | if call(foo, [a,
|
||||||
43 | b
|
43 | b
|
||||||
| ^ Syntax Error: Expected `]`, found NonLogicalNewline
|
|
||||||
44 | )
|
44 | )
|
||||||
|
| ^ Syntax Error: Expected `]`, found `)`
|
||||||
45 | def bar():
|
45 | def bar():
|
||||||
46 | pass
|
46 | pass
|
||||||
|
|
|
|
||||||
|
|
@ -898,7 +906,7 @@ Module(
|
||||||
|
|
|
|
||||||
49 | # F-strings uses normal list parsing, so test those as well
|
49 | # F-strings uses normal list parsing, so test those as well
|
||||||
50 | if call(f"hello {x
|
50 | if call(f"hello {x
|
||||||
| ^ Syntax Error: Expected FStringEnd, found NonLogicalNewline
|
| ^ Syntax Error: Expected `)`, found newline
|
||||||
51 | def bar():
|
51 | def bar():
|
||||||
52 | pass
|
52 | pass
|
||||||
|
|
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Module(
|
||||||
test: Call(
|
test: Call(
|
||||||
ExprCall {
|
ExprCall {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
range: 3..19,
|
range: 3..18,
|
||||||
func: Name(
|
func: Name(
|
||||||
ExprName {
|
ExprName {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
|
|
@ -27,7 +27,7 @@ Module(
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
range: 7..19,
|
range: 7..18,
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
args: [
|
args: [
|
||||||
Name(
|
Name(
|
||||||
|
|
@ -113,5 +113,11 @@ Module(
|
||||||
|
|
||||||
|
|
|
|
||||||
1 | if call(foo, [a, b
def bar():
pass
|
1 | if call(foo, [a, b
def bar():
pass
|
||||||
| ^ Syntax Error: Expected `]`, found NonLogicalNewline
|
| ^^^ Syntax Error: Expected `]`, found `def`
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | if call(foo, [a, b
def bar():
pass
|
||||||
|
| ^ Syntax Error: Expected `)`, found newline
|
||||||
|
|
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Module(
|
||||||
test: Call(
|
test: Call(
|
||||||
ExprCall {
|
ExprCall {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
range: 3..20,
|
range: 3..18,
|
||||||
func: Name(
|
func: Name(
|
||||||
ExprName {
|
ExprName {
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
|
|
@ -27,7 +27,7 @@ Module(
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
arguments: Arguments {
|
arguments: Arguments {
|
||||||
range: 7..20,
|
range: 7..18,
|
||||||
node_index: NodeIndex(None),
|
node_index: NodeIndex(None),
|
||||||
args: [
|
args: [
|
||||||
Name(
|
Name(
|
||||||
|
|
@ -113,7 +113,15 @@ Module(
|
||||||
|
|
||||||
|
|
|
|
||||||
1 | if call(foo, [a, b
|
1 | if call(foo, [a, b
|
||||||
| ^ Syntax Error: Expected `]`, found NonLogicalNewline
|
2 | def bar():
|
||||||
|
| ^^^ Syntax Error: Expected `]`, found `def`
|
||||||
|
3 | pass
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | if call(foo, [a, b
|
||||||
|
| ^ Syntax Error: Expected `)`, found newline
|
||||||
2 | def bar():
|
2 | def bar():
|
||||||
3 | pass
|
3 | pass
|
||||||
|
|
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/invalid/re_lexing/ty_1828.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 0..112,
|
||||||
|
body: [
|
||||||
|
AnnAssign(
|
||||||
|
StmtAnnAssign {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 66..93,
|
||||||
|
target: Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 67..68,
|
||||||
|
id: Name("c"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
annotation: Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 70..73,
|
||||||
|
id: Name("int"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
value: Some(
|
||||||
|
Tuple(
|
||||||
|
ExprTuple {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 76..93,
|
||||||
|
elts: [
|
||||||
|
NumberLiteral(
|
||||||
|
ExprNumberLiteral {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 76..77,
|
||||||
|
value: Int(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Subscript(
|
||||||
|
ExprSubscript {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 78..90,
|
||||||
|
value: FString(
|
||||||
|
ExprFString {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 78..85,
|
||||||
|
value: FStringValue {
|
||||||
|
inner: Single(
|
||||||
|
FString(
|
||||||
|
FString {
|
||||||
|
range: 78..85,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
elements: [
|
||||||
|
Interpolation(
|
||||||
|
InterpolatedElement {
|
||||||
|
range: 82..85,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
expression: Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 83..84,
|
||||||
|
id: Name("d"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
debug_text: Some(
|
||||||
|
DebugText {
|
||||||
|
leading: "",
|
||||||
|
trailing: "=",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
conversion: None,
|
||||||
|
format_spec: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
flags: FStringFlags {
|
||||||
|
quote_style: Double,
|
||||||
|
prefix: Regular,
|
||||||
|
triple_quoted: true,
|
||||||
|
unclosed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
slice: Slice(
|
||||||
|
ExprSlice {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 87..90,
|
||||||
|
lower: None,
|
||||||
|
upper: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 87..90,
|
||||||
|
id: Name("def"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
step: None,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Call(
|
||||||
|
ExprCall {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 91..93,
|
||||||
|
func: Name(
|
||||||
|
ExprName {
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
range: 91..92,
|
||||||
|
id: Name("a"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arguments: Arguments {
|
||||||
|
range: 92..93,
|
||||||
|
node_index: NodeIndex(None),
|
||||||
|
args: [],
|
||||||
|
keywords: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ctx: Load,
|
||||||
|
parenthesized: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
simple: false,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
| ^ Syntax Error: Expected `)`, found `:`
|
||||||
|
3 | def a(
|
||||||
|
4 | class A:
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
| ^ Syntax Error: f-string: expecting `}`
|
||||||
|
3 | def a(
|
||||||
|
4 | class A:
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
3 | def a(
|
||||||
|
| ^^^ Syntax Error: Expected `:`, found `def`
|
||||||
|
4 | class A:
|
||||||
|
5 | pass
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
3 | def a(
|
||||||
|
| ^ Syntax Error: Expected `]`, found name
|
||||||
|
4 | class A:
|
||||||
|
5 | pass
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
3 | def a(
|
||||||
|
| _______^
|
||||||
|
4 | | class A:
|
||||||
|
5 | | pass
|
||||||
|
| |_________^ Syntax Error: f-string: unterminated triple-quoted string
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
3 | def a(
|
||||||
|
4 | class A:
|
||||||
|
| ^^^^^ Syntax Error: Expected `)`, found `class`
|
||||||
|
5 | pass
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # Regression test for https://github.com/astral-sh/ty/issues/1828
|
||||||
|
2 | (c: int = 1,f"""{d=[
|
||||||
|
3 | def a(
|
||||||
|
| _______^
|
||||||
|
4 | | class A:
|
||||||
|
5 | | pass
|
||||||
|
| |_________^ Syntax Error: Expected a statement
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
4 | class A:
|
||||||
|
5 | pass
|
||||||
|
| ^ Syntax Error: unexpected EOF while parsing
|
||||||
|
|
|
||||||
|
|
@ -6433,6 +6433,155 @@ collabc<CURSOR>
|
||||||
assert_snapshot!(snapshot, @"collections.abc");
|
assert_snapshot!(snapshot, @"collections.abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn local_function_variable_with_return() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
variable_global = 1
|
||||||
|
def foo():
|
||||||
|
variable_local = 1
|
||||||
|
variable_<CURSOR>
|
||||||
|
return
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().build().snapshot(),
|
||||||
|
@r"
|
||||||
|
variable_global
|
||||||
|
variable_local
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn nested_scopes_with_return() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
variable_1 = 1
|
||||||
|
def fun1():
|
||||||
|
variable_2 = 1
|
||||||
|
def fun2():
|
||||||
|
variable_3 = 1
|
||||||
|
def fun3():
|
||||||
|
variable_4 = 1
|
||||||
|
variable_<CURSOR>
|
||||||
|
return
|
||||||
|
return
|
||||||
|
return
|
||||||
|
",
|
||||||
|
);
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().build().snapshot(),
|
||||||
|
@r"
|
||||||
|
variable_1
|
||||||
|
variable_2
|
||||||
|
variable_3
|
||||||
|
variable_4
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_declarations_global_scope1() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
zqzqzq: int = 1
|
||||||
|
zqzqzq: str = 'foo'
|
||||||
|
zqzq<CURSOR>
|
||||||
|
",
|
||||||
|
);
|
||||||
|
// The type for `zqzqzq` *should* be `str`, but we consider all
|
||||||
|
// reachable declarations and bindings, which means we get a
|
||||||
|
// union of `int` and `str` here even though the `int` binding
|
||||||
|
// isn't live at the cursor position.
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().type_signatures().build().snapshot(),
|
||||||
|
@"zqzqzq :: int | str",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_declarations_global_scope2() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
zqzqzq: int = 1
|
||||||
|
zqzq<CURSOR>
|
||||||
|
zqzqzq: str = 'foo'
|
||||||
|
",
|
||||||
|
);
|
||||||
|
// The type for `zqzqzq` *should* be `int`, but we consider all
|
||||||
|
// reachable declarations and bindings, which means we get a
|
||||||
|
// union of `int` and `str` here even though the `str` binding
|
||||||
|
// doesn't exist at the cursor position.
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().type_signatures().build().snapshot(),
|
||||||
|
@"zqzqzq :: int | str",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_declarations_function_scope1() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
def foo():
|
||||||
|
zqzqzq: int = 1
|
||||||
|
zqzqzq: str = 'foo'
|
||||||
|
zqzq<CURSOR>
|
||||||
|
return
|
||||||
|
",
|
||||||
|
);
|
||||||
|
// The type for `zqzqzq` *should* be `str`, but we consider all
|
||||||
|
// reachable declarations and bindings, which means we get a
|
||||||
|
// union of `int` and `str` here even though the `int` binding
|
||||||
|
// isn't live at the cursor position.
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().type_signatures().build().snapshot(),
|
||||||
|
@"zqzqzq :: int | str",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_declarations_function_scope2() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
def foo():
|
||||||
|
zqzqzq: int = 1
|
||||||
|
zqzq<CURSOR>
|
||||||
|
zqzqzq: str = 'foo'
|
||||||
|
return
|
||||||
|
",
|
||||||
|
);
|
||||||
|
// The type for `zqzqzq` *should* be `int`, but we consider all
|
||||||
|
// reachable declarations and bindings, which means we get a
|
||||||
|
// union of `int` and `str` here even though the `str` binding
|
||||||
|
// doesn't exist at the cursor position.
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().type_signatures().build().snapshot(),
|
||||||
|
@"zqzqzq :: int | str",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn multiple_declarations_function_parameter() {
|
||||||
|
let builder = completion_test_builder(
|
||||||
|
"\
|
||||||
|
from pathlib import Path
|
||||||
|
def f(zqzqzq: str):
|
||||||
|
zqzqzq: Path = Path(zqzqzq)
|
||||||
|
zqzq<CURSOR>
|
||||||
|
return
|
||||||
|
",
|
||||||
|
);
|
||||||
|
// The type for `zqzqzq` *should* be `Path`, but we consider all
|
||||||
|
// reachable declarations and bindings, which means we get a
|
||||||
|
// union of `str` and `Path` here even though the `str` binding
|
||||||
|
// isn't live at the cursor position.
|
||||||
|
assert_snapshot!(
|
||||||
|
builder.skip_auto_import().type_signatures().build().snapshot(),
|
||||||
|
@"zqzqzq :: str | Path",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// A way to create a simple single-file (named `main.py`) completion test
|
/// A way to create a simple single-file (named `main.py`) completion test
|
||||||
/// builder.
|
/// builder.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -295,7 +295,7 @@ impl<'db> Definitions<'db> {
|
||||||
|
|
||||||
impl GotoTarget<'_> {
|
impl GotoTarget<'_> {
|
||||||
pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
let ty = match self {
|
match self {
|
||||||
GotoTarget::Expression(expression) => expression.inferred_type(model),
|
GotoTarget::Expression(expression) => expression.inferred_type(model),
|
||||||
GotoTarget::FunctionDef(function) => function.inferred_type(model),
|
GotoTarget::FunctionDef(function) => function.inferred_type(model),
|
||||||
GotoTarget::ClassDef(class) => class.inferred_type(model),
|
GotoTarget::ClassDef(class) => class.inferred_type(model),
|
||||||
|
|
@ -317,7 +317,7 @@ impl GotoTarget<'_> {
|
||||||
} => {
|
} => {
|
||||||
// We don't currently support hovering the bare `.` so there is always a name
|
// We don't currently support hovering the bare `.` so there is always a name
|
||||||
let module = import_name(module_name, *component_index);
|
let module = import_name(module_name, *component_index);
|
||||||
model.resolve_module_type(Some(module), *level)?
|
model.resolve_module_type(Some(module), *level)
|
||||||
}
|
}
|
||||||
GotoTarget::StringAnnotationSubexpr {
|
GotoTarget::StringAnnotationSubexpr {
|
||||||
string_expr,
|
string_expr,
|
||||||
|
|
@ -334,16 +334,16 @@ impl GotoTarget<'_> {
|
||||||
} else {
|
} else {
|
||||||
// TODO: force the typechecker to tell us its secrets
|
// TODO: force the typechecker to tell us its secrets
|
||||||
// (it computes but then immediately discards these types)
|
// (it computes but then immediately discards these types)
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GotoTarget::BinOp { expression, .. } => {
|
GotoTarget::BinOp { expression, .. } => {
|
||||||
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
|
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
|
||||||
ty
|
Some(ty)
|
||||||
}
|
}
|
||||||
GotoTarget::UnaryOp { expression, .. } => {
|
GotoTarget::UnaryOp { expression, .. } => {
|
||||||
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
||||||
ty
|
Some(ty)
|
||||||
}
|
}
|
||||||
// TODO: Support identifier targets
|
// TODO: Support identifier targets
|
||||||
GotoTarget::PatternMatchRest(_)
|
GotoTarget::PatternMatchRest(_)
|
||||||
|
|
@ -353,10 +353,8 @@ impl GotoTarget<'_> {
|
||||||
| GotoTarget::TypeParamParamSpecName(_)
|
| GotoTarget::TypeParamParamSpecName(_)
|
||||||
| GotoTarget::TypeParamTypeVarTupleName(_)
|
| GotoTarget::TypeParamTypeVarTupleName(_)
|
||||||
| GotoTarget::NonLocal { .. }
|
| GotoTarget::NonLocal { .. }
|
||||||
| GotoTarget::Globals { .. } => return None,
|
| GotoTarget::Globals { .. } => None,
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(ty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to get a simplified display of this callable type by resolving overloads
|
/// Try to get a simplified display of this callable type by resolving overloads
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -3610,6 +3610,20 @@ def function():
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_tuple_assignment_target() {
|
||||||
|
let test = CursorTest::builder()
|
||||||
|
.source(
|
||||||
|
"test.py",
|
||||||
|
r#"
|
||||||
|
(x, y)<CURSOR> = "test", 10
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn hover(&self) -> String {
|
fn hover(&self) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
||||||
|
|
@ -362,8 +362,9 @@ impl<'a> SourceOrderVisitor<'a> for InlayHintVisitor<'a, '_> {
|
||||||
Expr::Name(name) => {
|
Expr::Name(name) => {
|
||||||
if let Some(rhs) = self.assignment_rhs {
|
if let Some(rhs) = self.assignment_rhs {
|
||||||
if name.ctx.is_store() {
|
if name.ctx.is_store() {
|
||||||
let ty = expr.inferred_type(&self.model);
|
if let Some(ty) = expr.inferred_type(&self.model) {
|
||||||
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source_order::walk_expr(self, expr);
|
source_order::walk_expr(self, expr);
|
||||||
|
|
@ -371,8 +372,9 @@ impl<'a> SourceOrderVisitor<'a> for InlayHintVisitor<'a, '_> {
|
||||||
Expr::Attribute(attribute) => {
|
Expr::Attribute(attribute) => {
|
||||||
if let Some(rhs) = self.assignment_rhs {
|
if let Some(rhs) = self.assignment_rhs {
|
||||||
if attribute.ctx.is_store() {
|
if attribute.ctx.is_store() {
|
||||||
let ty = expr.inferred_type(&self.model);
|
if let Some(ty) = expr.inferred_type(&self.model) {
|
||||||
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source_order::walk_expr(self, expr);
|
source_order::walk_expr(self, expr);
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,11 @@ impl NavigationTargets {
|
||||||
fn is_empty(&self) -> bool {
|
fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.0.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for NavigationTargets {
|
impl IntoIterator for NavigationTargets {
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,9 @@ mod tests {
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn prepare_rename(&self) -> String {
|
fn prepare_rename(&self) -> String {
|
||||||
let Some(range) = can_rename(&self.db, self.cursor.file, self.cursor.offset) else {
|
let Some(range) = salsa::attach(&self.db, || {
|
||||||
|
can_rename(&self.db, self.cursor.file, self.cursor.offset)
|
||||||
|
}) else {
|
||||||
return "Cannot rename".to_string();
|
return "Cannot rename".to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -106,13 +108,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename(&self, new_name: &str) -> String {
|
fn rename(&self, new_name: &str) -> String {
|
||||||
let Some(_) = can_rename(&self.db, self.cursor.file, self.cursor.offset) else {
|
let rename_results = salsa::attach(&self.db, || {
|
||||||
return "Cannot rename".to_string();
|
can_rename(&self.db, self.cursor.file, self.cursor.offset)?;
|
||||||
};
|
|
||||||
|
|
||||||
let Some(rename_results) =
|
|
||||||
rename(&self.db, self.cursor.file, self.cursor.offset, new_name)
|
rename(&self.db, self.cursor.file, self.cursor.offset, new_name)
|
||||||
else {
|
});
|
||||||
|
|
||||||
|
let Some(rename_results) = rename_results else {
|
||||||
return "Cannot rename".to_string();
|
return "Cannot rename".to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue