mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 02:38:25 +00:00
Split Constant
to individual literal nodes (#8064)
## Summary This PR splits the `Constant` enum as individual literal nodes. It introduces the following new nodes for each variant: * `ExprStringLiteral` * `ExprBytesLiteral` * `ExprNumberLiteral` * `ExprBooleanLiteral` * `ExprNoneLiteral` * `ExprEllipsisLiteral` The main motivation behind this refactor is to introduce the new AST node for implicit string concatenation in the coming PR. The elements of that node will be either a string literal, bytes literal or a f-string which can be implemented using an enum. This means that a string or bytes literal cannot be represented by `Constant::Str` / `Constant::Bytes` which creates an inconsistency. This PR avoids that inconsistency by splitting the constant nodes into it's own literal nodes, literal being the more appropriate naming convention from a static analysis tool perspective. This also makes working with literals in the linter and formatter much more ergonomic like, for example, if one would want to check if this is a string literal, it can be done easily using `Expr::is_string_literal_expr` or matching against `Expr::StringLiteral` as oppose to matching against the `ExprConstant` and enum `Constant`. A few AST helper methods can be simplified as well which will be done in a follow-up PR. This introduces a new `Expr::is_literal_expr` method which is the same as `Expr::is_constant_expr`. There are also intermediary changes related to implicit string concatenation which are quiet less. This is done so as to avoid having a huge PR which this already is. ## Test Plan 1. Verify and update all of the existing snapshots (parser, visitor) 2. Verify that the ecosystem check output remains **unchanged** for both the linter and formatter ### Formatter ecosystem check #### `main` | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75803 | 1799 | 1647 | | django | 0.99983 | 2772 | 34 | | home-assistant | 0.99953 | 10596 | 186 | | poetry | 0.99891 | 317 | 17 | | transformers | 0.99966 | 2657 | 330 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99978 | 3669 | 20 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 | #### `dhruv/constant-to-literal` | project | similarity index | total files | changed files | |----------------|------------------:|------------------:|------------------:| | cpython | 0.75803 | 1799 | 1647 | | django | 0.99983 | 2772 | 34 | | home-assistant | 0.99953 | 10596 | 186 | | poetry | 0.99891 | 317 | 17 | | transformers | 0.99966 | 2657 | 330 | | twine | 1.00000 | 33 | 0 | | typeshed | 0.99978 | 3669 | 20 | | warehouse | 0.99977 | 654 | 13 | | zulip | 0.99970 | 1459 | 22 |
This commit is contained in:
parent
78bbf6d403
commit
230c9ce236
268 changed files with 6663 additions and 6741 deletions
|
@ -170,7 +170,7 @@ pub(crate) fn definitions(checker: &mut Checker) {
|
||||||
expr.start(),
|
expr.start(),
|
||||||
));
|
));
|
||||||
|
|
||||||
if pydocstyle::helpers::should_ignore_docstring(expr) {
|
if expr.implicit_concatenated {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let location = checker.locator.compute_source_location(expr.start());
|
let location = checker.locator.compute_source_location(expr.start());
|
||||||
warn_user!(
|
warn_user!(
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, ExprContext, Operator};
|
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Operator};
|
||||||
use ruff_python_literal::cformat::{CFormatError, CFormatErrorType};
|
use ruff_python_literal::cformat::{CFormatError, CFormatErrorType};
|
||||||
|
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
|
@ -363,20 +363,18 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
]) {
|
]) {
|
||||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
||||||
let attr = attr.as_str();
|
let attr = attr.as_str();
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) =
|
||||||
value: Constant::Str(val),
|
value.as_ref()
|
||||||
..
|
|
||||||
}) = value.as_ref()
|
|
||||||
{
|
{
|
||||||
if attr == "join" {
|
if attr == "join" {
|
||||||
// "...".join(...) call
|
// "...".join(...) call
|
||||||
if checker.enabled(Rule::StaticJoinToFString) {
|
if checker.enabled(Rule::StaticJoinToFString) {
|
||||||
flynt::rules::static_join_to_fstring(checker, expr, val);
|
flynt::rules::static_join_to_fstring(checker, expr, string);
|
||||||
}
|
}
|
||||||
} else if attr == "format" {
|
} else if attr == "format" {
|
||||||
// "...".format(...) call
|
// "...".format(...) call
|
||||||
let location = expr.range();
|
let location = expr.range();
|
||||||
match pyflakes::format::FormatSummary::try_from(val.as_ref()) {
|
match pyflakes::format::FormatSummary::try_from(string.as_ref()) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
|
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
@ -421,7 +419,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
|
|
||||||
if checker.enabled(Rule::BadStringFormatCharacter) {
|
if checker.enabled(Rule::BadStringFormatCharacter) {
|
||||||
pylint::rules::bad_string_format_character::call(
|
pylint::rules::bad_string_format_character::call(
|
||||||
checker, val, location,
|
checker, string, location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -993,11 +991,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
right,
|
right,
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = left.as_ref() {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = left.as_ref()
|
|
||||||
{
|
|
||||||
if checker.any_enabled(&[
|
if checker.any_enabled(&[
|
||||||
Rule::PercentFormatInvalidFormat,
|
Rule::PercentFormatInvalidFormat,
|
||||||
Rule::PercentFormatExpectedMapping,
|
Rule::PercentFormatExpectedMapping,
|
||||||
|
@ -1234,38 +1228,29 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
|
||||||
refurb::rules::single_item_membership_test(checker, expr, left, ops, comparators);
|
refurb::rules::single_item_membership_test(checker, expr, left, ops, comparators);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(_) => {
|
||||||
value: Constant::Int(_) | Constant::Float(_) | Constant::Complex { .. },
|
|
||||||
range: _,
|
|
||||||
}) => {
|
|
||||||
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
|
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
|
||||||
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
|
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::BytesLiteral(_) => {
|
||||||
value: Constant::Bytes(_),
|
|
||||||
range: _,
|
|
||||||
}) => {
|
|
||||||
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
|
||||||
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(string) => {
|
||||||
value: Constant::Str(value),
|
|
||||||
range: _,
|
|
||||||
}) => {
|
|
||||||
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
|
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
|
||||||
if let Some(diagnostic) =
|
if let Some(diagnostic) =
|
||||||
flake8_bandit::rules::hardcoded_bind_all_interfaces(value, expr.range())
|
flake8_bandit::rules::hardcoded_bind_all_interfaces(string)
|
||||||
{
|
{
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::HardcodedTempFile) {
|
if checker.enabled(Rule::HardcodedTempFile) {
|
||||||
flake8_bandit::rules::hardcoded_tmp_directory(checker, expr, value);
|
flake8_bandit::rules::hardcoded_tmp_directory(checker, string);
|
||||||
}
|
}
|
||||||
if checker.enabled(Rule::UnicodeKindPrefix) {
|
if checker.enabled(Rule::UnicodeKindPrefix) {
|
||||||
pyupgrade::rules::unicode_kind_prefix(checker, expr, value.unicode);
|
pyupgrade::rules::unicode_kind_prefix(checker, string);
|
||||||
}
|
}
|
||||||
if checker.source_type.is_stub() {
|
if checker.source_type.is_stub() {
|
||||||
if checker.enabled(Rule::StringOrBytesTooLong) {
|
if checker.enabled(Rule::StringOrBytesTooLong) {
|
||||||
|
|
|
@ -31,9 +31,8 @@ use std::path::Path;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Arguments, Comprehension, Constant, ElifElseClause, ExceptHandler, Expr,
|
self as ast, Arguments, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext,
|
||||||
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt,
|
Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt, Suite, UnaryOp,
|
||||||
Suite, UnaryOp,
|
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -787,11 +786,7 @@ where
|
||||||
&& self.semantic.in_type_definition()
|
&& self.semantic.in_type_definition()
|
||||||
&& self.semantic.future_annotations()
|
&& self.semantic.future_annotations()
|
||||||
{
|
{
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
}) = expr
|
|
||||||
{
|
|
||||||
self.deferred.string_type_definitions.push((
|
self.deferred.string_type_definitions.push((
|
||||||
expr.range(),
|
expr.range(),
|
||||||
value,
|
value,
|
||||||
|
@ -1186,10 +1181,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
value: Constant::Str(value),
|
|
||||||
range: _,
|
|
||||||
}) => {
|
|
||||||
if self.semantic.in_type_definition()
|
if self.semantic.in_type_definition()
|
||||||
&& !self.semantic.in_literal()
|
&& !self.semantic.in_literal()
|
||||||
&& !self.semantic.in_f_string()
|
&& !self.semantic.in_f_string()
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
use std::iter::FusedIterator;
|
use std::iter::FusedIterator;
|
||||||
|
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt, Suite};
|
use ruff_python_ast::{self as ast, Stmt, Suite};
|
||||||
use ruff_python_parser::lexer::LexResult;
|
use ruff_python_parser::lexer::LexResult;
|
||||||
use ruff_python_parser::Tok;
|
use ruff_python_parser::Tok;
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
|
@ -69,15 +69,15 @@ struct StringLinesVisitor<'a> {
|
||||||
|
|
||||||
impl StatementVisitor<'_> for StringLinesVisitor<'_> {
|
impl StatementVisitor<'_> for StringLinesVisitor<'_> {
|
||||||
fn visit_stmt(&mut self, stmt: &Stmt) {
|
fn visit_stmt(&mut self, stmt: &Stmt) {
|
||||||
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
|
if let Stmt::Expr(ast::StmtExpr {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
value: expr,
|
||||||
value: Constant::Str(..),
|
range: _,
|
||||||
..
|
}) = stmt
|
||||||
}) = value.as_ref()
|
{
|
||||||
{
|
if expr.is_string_literal_expr() {
|
||||||
for line in UniversalNewlineIterator::with_offset(
|
for line in UniversalNewlineIterator::with_offset(
|
||||||
self.locator.slice(value.as_ref()),
|
self.locator.slice(expr.as_ref()),
|
||||||
value.start(),
|
expr.start(),
|
||||||
) {
|
) {
|
||||||
self.string_lines.push(line.start());
|
self.string_lines.push(line.start());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,23 @@
|
||||||
//! Extract docstrings from an AST.
|
//! Extract docstrings from an AST.
|
||||||
|
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Stmt};
|
||||||
use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, MemberKind};
|
use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, MemberKind};
|
||||||
|
|
||||||
/// Extract a docstring from a function or class body.
|
/// Extract a docstring from a function or class body.
|
||||||
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
|
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&ast::ExprStringLiteral> {
|
||||||
let stmt = suite.first()?;
|
let stmt = suite.first()?;
|
||||||
// Require the docstring to be a standalone expression.
|
// Require the docstring to be a standalone expression.
|
||||||
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
|
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
// Only match strings.
|
// Only match strings.
|
||||||
if !matches!(
|
value.as_string_literal_expr()
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a docstring from a `Definition`.
|
/// Extract a docstring from a `Definition`.
|
||||||
pub(crate) fn extract_docstring<'a>(definition: &'a Definition<'a>) -> Option<&'a Expr> {
|
pub(crate) fn extract_docstring<'a>(
|
||||||
|
definition: &'a Definition<'a>,
|
||||||
|
) -> Option<&'a ast::ExprStringLiteral> {
|
||||||
match definition {
|
match definition {
|
||||||
Definition::Module(module) => docstring_from(module.python_ast),
|
Definition::Module(module) => docstring_from(module.python_ast),
|
||||||
Definition::Member(member) => docstring_from(member.body()),
|
Definition::Member(member) => docstring_from(member.body()),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::ExprStringLiteral;
|
||||||
use ruff_python_semantic::Definition;
|
use ruff_python_semantic::Definition;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
@ -14,7 +14,8 @@ pub(crate) mod styles;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct Docstring<'a> {
|
pub(crate) struct Docstring<'a> {
|
||||||
pub(crate) definition: &'a Definition<'a>,
|
pub(crate) definition: &'a Definition<'a>,
|
||||||
pub(crate) expr: &'a Expr,
|
/// The literal AST node representing the docstring.
|
||||||
|
pub(crate) expr: &'a ExprStringLiteral,
|
||||||
/// The content of the docstring, including the leading and trailing quotes.
|
/// The content of the docstring, including the leading and trailing quotes.
|
||||||
pub(crate) contents: &'a str,
|
pub(crate) contents: &'a str,
|
||||||
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].
|
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::Constant;
|
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::Expr;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
@ -79,13 +78,7 @@ pub(crate) fn variable_name_task_id(
|
||||||
let keyword = arguments.find_keyword("task_id")?;
|
let keyword = arguments.find_keyword("task_id")?;
|
||||||
|
|
||||||
// If the keyword argument is not a string, we can't do anything.
|
// If the keyword argument is not a string, we can't do anything.
|
||||||
let task_id = match &keyword.value {
|
let ast::ExprStringLiteral { value: task_id, .. } = keyword.value.as_string_literal_expr()?;
|
||||||
Expr::Constant(constant) => match &constant.value {
|
|
||||||
Constant::Str(ast::StringConstant { value, .. }) => value,
|
|
||||||
_ => return None,
|
|
||||||
},
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// If the target name is the same as the task_id, no violation.
|
// If the target name is the same as the task_id, no violation.
|
||||||
if id == task_id {
|
if id == task_id {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr};
|
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -230,16 +230,16 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
|
||||||
Expr::Subscript(ast::ExprSubscript { value, slice, .. })
|
Expr::Subscript(ast::ExprSubscript { value, slice, .. })
|
||||||
if is_sys(value, "version_info", checker.semantic()) =>
|
if is_sys(value, "version_info", checker.semantic()) =>
|
||||||
{
|
{
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(i),
|
value: ast::Number::Int(i),
|
||||||
..
|
..
|
||||||
}) = slice.as_ref()
|
}) = slice.as_ref()
|
||||||
{
|
{
|
||||||
if *i == 0 {
|
if *i == 0 {
|
||||||
if let (
|
if let (
|
||||||
[CmpOp::Eq | CmpOp::NotEq],
|
[CmpOp::Eq | CmpOp::NotEq],
|
||||||
[Expr::Constant(ast::ExprConstant {
|
[Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(n),
|
value: ast::Number::Int(n),
|
||||||
..
|
..
|
||||||
})],
|
})],
|
||||||
) = (ops, comparators)
|
) = (ops, comparators)
|
||||||
|
@ -253,8 +253,8 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
|
||||||
} else if *i == 1 {
|
} else if *i == 1 {
|
||||||
if let (
|
if let (
|
||||||
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
||||||
[Expr::Constant(ast::ExprConstant {
|
[Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(_),
|
value: ast::Number::Int(_),
|
||||||
..
|
..
|
||||||
})],
|
})],
|
||||||
) = (ops, comparators)
|
) = (ops, comparators)
|
||||||
|
@ -274,8 +274,8 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
|
||||||
{
|
{
|
||||||
if let (
|
if let (
|
||||||
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
||||||
[Expr::Constant(ast::ExprConstant {
|
[Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(_),
|
value: ast::Number::Int(_),
|
||||||
..
|
..
|
||||||
})],
|
})],
|
||||||
) = (ops, comparators)
|
) = (ops, comparators)
|
||||||
|
@ -294,13 +294,10 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
|
||||||
if is_sys(left, "version", checker.semantic()) {
|
if is_sys(left, "version", checker.semantic()) {
|
||||||
if let (
|
if let (
|
||||||
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
|
||||||
[Expr::Constant(ast::ExprConstant {
|
[Expr::StringLiteral(ast::ExprStringLiteral { value, .. })],
|
||||||
value: Constant::Str(s),
|
|
||||||
..
|
|
||||||
})],
|
|
||||||
) = (ops, comparators)
|
) = (ops, comparators)
|
||||||
{
|
{
|
||||||
if s.len() == 1 {
|
if value.len() == 1 {
|
||||||
if checker.enabled(Rule::SysVersionCmpStr10) {
|
if checker.enabled(Rule::SysVersionCmpStr10) {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -177,8 +177,8 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
|
||||||
step: None,
|
step: None,
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(i),
|
value: ast::Number::Int(i),
|
||||||
..
|
..
|
||||||
}) = upper.as_ref()
|
}) = upper.as_ref()
|
||||||
{
|
{
|
||||||
|
@ -194,8 +194,8 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(i),
|
value: ast::Number::Int(i),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if *i == 2 && checker.enabled(Rule::SysVersion2) {
|
if *i == 2 && checker.enabled(Rule::SysVersion2) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::ReturnStatementVisitor;
|
use ruff_python_ast::helpers::ReturnStatementVisitor;
|
||||||
use ruff_python_ast::identifier::Identifier;
|
use ruff_python_ast::identifier::Identifier;
|
||||||
use ruff_python_ast::statement_visitor::StatementVisitor;
|
use ruff_python_ast::statement_visitor::StatementVisitor;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, ParameterWithDefault, Stmt};
|
use ruff_python_ast::{self as ast, Expr, ParameterWithDefault, Stmt};
|
||||||
use ruff_python_parser::typing::parse_type_annotation;
|
use ruff_python_parser::typing::parse_type_annotation;
|
||||||
use ruff_python_semantic::analyze::visibility;
|
use ruff_python_semantic::analyze::visibility;
|
||||||
use ruff_python_semantic::Definition;
|
use ruff_python_semantic::Definition;
|
||||||
|
@ -431,10 +431,7 @@ fn is_none_returning(body: &[Stmt]) -> bool {
|
||||||
visitor.visit_body(body);
|
visitor.visit_body(body);
|
||||||
for stmt in visitor.returns {
|
for stmt in visitor.returns {
|
||||||
if let Some(value) = stmt.value.as_deref() {
|
if let Some(value) = stmt.value.as_deref() {
|
||||||
if !matches!(
|
if !value.is_none_literal_expr() {
|
||||||
value,
|
|
||||||
Expr::Constant(constant) if constant.value.is_none()
|
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,9 +448,10 @@ fn check_dynamically_typed<F>(
|
||||||
) where
|
) where
|
||||||
F: FnOnce() -> String,
|
F: FnOnce() -> String,
|
||||||
{
|
{
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
range,
|
range,
|
||||||
value: Constant::Str(string),
|
value: string,
|
||||||
|
..
|
||||||
}) = annotation
|
}) = annotation
|
||||||
{
|
{
|
||||||
// Quoted annotations
|
// Quoted annotations
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
|
||||||
|
@ -10,10 +10,7 @@ static PASSWORD_CANDIDATE_REGEX: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
|
||||||
pub(super) fn string_literal(expr: &Expr) -> Option<&str> {
|
pub(super) fn string_literal(expr: &Expr) -> Option<&str> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => Some(value),
|
||||||
value: Constant::Str(string),
|
|
||||||
..
|
|
||||||
}) => Some(string),
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use anyhow::Result;
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::call_path::CallPath;
|
use ruff_python_ast::call_path::CallPath;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
@ -144,8 +144,8 @@ fn py_stat(call_path: &CallPath) -> Option<u16> {
|
||||||
/// an integer value, but that value is out of range.
|
/// an integer value, but that value is out of range.
|
||||||
fn parse_mask(expr: &Expr, semantic: &SemanticModel) -> Result<Option<u16>> {
|
fn parse_mask(expr: &Expr, semantic: &SemanticModel) -> Result<Option<u16>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(int),
|
value: ast::Number::Int(int),
|
||||||
..
|
..
|
||||||
}) => match int.as_u16() {
|
}) => match int.as_u16() {
|
||||||
Some(value) => Ok(Some(value)),
|
Some(value) => Ok(Some(value)),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use ruff_text_size::TextRange;
|
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::ExprStringLiteral;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for hardcoded bindings to all network interfaces (`0.0.0.0`).
|
/// Checks for hardcoded bindings to all network interfaces (`0.0.0.0`).
|
||||||
|
@ -35,9 +34,9 @@ impl Violation for HardcodedBindAllInterfaces {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// S104
|
/// S104
|
||||||
pub(crate) fn hardcoded_bind_all_interfaces(value: &str, range: TextRange) -> Option<Diagnostic> {
|
pub(crate) fn hardcoded_bind_all_interfaces(string: &ExprStringLiteral) -> Option<Diagnostic> {
|
||||||
if value == "0.0.0.0" {
|
if string.value == "0.0.0.0" {
|
||||||
Some(Diagnostic::new(HardcodedBindAllInterfaces, range))
|
Some(Diagnostic::new(HardcodedBindAllInterfaces, string.range))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -55,10 +55,7 @@ fn password_target(target: &Expr) -> Option<&str> {
|
||||||
Expr::Name(ast::ExprName { id, .. }) => id.as_str(),
|
Expr::Name(ast::ExprName { id, .. }) => id.as_str(),
|
||||||
// d["password"] = "s3cr3t"
|
// d["password"] = "s3cr3t"
|
||||||
Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
|
Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value,
|
||||||
value: Constant::Str(string),
|
|
||||||
..
|
|
||||||
}) => string,
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
// obj.password = "s3cr3t"
|
// obj.password = "s3cr3t"
|
||||||
|
|
|
@ -2,7 +2,6 @@ use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_text_size::Ranged;
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -52,13 +51,13 @@ impl Violation for HardcodedTempFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// S108
|
/// S108
|
||||||
pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, expr: &Expr, value: &str) {
|
pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, string: &ast::ExprStringLiteral) {
|
||||||
if !checker
|
if !checker
|
||||||
.settings
|
.settings
|
||||||
.flake8_bandit
|
.flake8_bandit
|
||||||
.hardcoded_tmp_directory
|
.hardcoded_tmp_directory
|
||||||
.iter()
|
.iter()
|
||||||
.any(|prefix| value.starts_with(prefix))
|
.any(|prefix| string.value.starts_with(prefix))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -77,8 +76,8 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, expr: &Expr, value:
|
||||||
|
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
HardcodedTempFile {
|
HardcodedTempFile {
|
||||||
string: value.to_string(),
|
string: string.value.clone(),
|
||||||
},
|
},
|
||||||
expr.range(),
|
string.range,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -66,10 +66,7 @@ pub(crate) fn jinja2_autoescape_false(checker: &mut Checker, call: &ast::ExprCal
|
||||||
{
|
{
|
||||||
if let Some(keyword) = call.arguments.find_keyword("autoescape") {
|
if let Some(keyword) = call.arguments.find_keyword("autoescape") {
|
||||||
match &keyword.value {
|
match &keyword.value {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: true, .. }) => (),
|
||||||
value: Constant::Bool(true),
|
|
||||||
..
|
|
||||||
}) => (),
|
|
||||||
Expr::Call(ast::ExprCall { func, .. }) => {
|
Expr::Call(ast::ExprCall { func, .. }) => {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
|
||||||
if id != "select_autoescape" {
|
if id != "select_autoescape" {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::Truthiness;
|
use ruff_python_ast::helpers::Truthiness;
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, Keyword};
|
use ruff_python_ast::{self as ast, Arguments, Expr, Keyword};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
@ -456,13 +456,7 @@ fn find_shell_keyword<'a>(
|
||||||
/// Return `true` if the value provided to the `shell` call seems safe. This is based on Bandit's
|
/// Return `true` if the value provided to the `shell` call seems safe. This is based on Bandit's
|
||||||
/// definition: string literals are considered okay, but dynamically-computed values are not.
|
/// definition: string literals are considered okay, but dynamically-computed values are not.
|
||||||
fn shell_call_seems_safe(arg: &Expr) -> bool {
|
fn shell_call_seems_safe(arg: &Expr) -> bool {
|
||||||
matches!(
|
arg.is_string_literal_expr()
|
||||||
arg,
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if the string appears to be a full file path.
|
/// Return `true` if the string appears to be a full file path.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Int};
|
use ruff_python_ast::{self as ast, Expr, Int};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -52,8 +52,8 @@ pub(crate) fn snmp_insecure_version(checker: &mut Checker, call: &ast::ExprCall)
|
||||||
if let Some(keyword) = call.arguments.find_keyword("mpModel") {
|
if let Some(keyword) = call.arguments.find_keyword("mpModel") {
|
||||||
if matches!(
|
if matches!(
|
||||||
keyword.value,
|
keyword.value,
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(Int::ZERO | Int::ONE),
|
value: ast::Number::Int(Int::ZERO | Int::ONE),
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -854,7 +854,7 @@ pub(crate) fn suspicious_function_call(checker: &mut Checker, call: &ExprCall) {
|
||||||
["six", "moves", "urllib", "request", "urlopen" | "urlretrieve" | "Request"] => {
|
["six", "moves", "urllib", "request", "urlopen" | "urlretrieve" | "Request"] => {
|
||||||
// If the `url` argument is a string literal, allow `http` and `https` schemes.
|
// If the `url` argument is a string literal, allow `http` and `https` schemes.
|
||||||
if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) {
|
if call.arguments.args.iter().all(|arg| !arg.is_starred_expr()) && call.arguments.keywords.iter().all(|keyword| keyword.arg.is_some()) {
|
||||||
if let Some(Expr::Constant(ast::ExprConstant { value: ast::Constant::Str(url), .. })) = &call.arguments.find_argument("url", 0) {
|
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value: url, .. })) = &call.arguments.find_argument("url", 0) {
|
||||||
let url = url.trim_start();
|
let url = url.trim_start();
|
||||||
if url.starts_with("http://") || url.starts_with("https://") {
|
if url.starts_with("http://") || url.starts_with("https://") {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, ExprAttribute, ExprCall};
|
use ruff_python_ast::{self as ast, Expr, ExprAttribute, ExprCall};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -151,8 +151,8 @@ fn extract_cryptographic_key(
|
||||||
|
|
||||||
fn extract_int_argument(call: &ExprCall, name: &str, position: usize) -> Option<(u16, TextRange)> {
|
fn extract_int_argument(call: &ExprCall, name: &str, position: usize) -> Option<(u16, TextRange)> {
|
||||||
let argument = call.arguments.find_argument(name, position)?;
|
let argument = call.arguments.find_argument(name, position)?;
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(i),
|
value: ast::Number::Int(i),
|
||||||
..
|
..
|
||||||
}) = argument
|
}) = argument
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
/// Returns `true` if a function call is allowed to use a boolean trap.
|
/// Returns `true` if a function call is allowed to use a boolean trap.
|
||||||
pub(super) fn is_allowed_func_call(name: &str) -> bool {
|
pub(super) fn is_allowed_func_call(name: &str) -> bool {
|
||||||
|
@ -62,14 +62,3 @@ pub(super) fn allow_boolean_trap(func: &Expr) -> bool {
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if an expression is a boolean literal.
|
|
||||||
pub(super) const fn is_boolean(expr: &Expr) -> bool {
|
|
||||||
matches!(
|
|
||||||
&expr,
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Bool(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ruff_python_ast::{Decorator, ParameterWithDefault, Parameters};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::rules::flake8_boolean_trap::helpers::{is_allowed_func_def, is_boolean};
|
use crate::rules::flake8_boolean_trap::helpers::is_allowed_func_def;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for the use of boolean positional arguments in function definitions,
|
/// Checks for the use of boolean positional arguments in function definitions,
|
||||||
|
@ -117,7 +117,10 @@ pub(crate) fn boolean_default_value_positional_argument(
|
||||||
range: _,
|
range: _,
|
||||||
} in parameters.posonlyargs.iter().chain(¶meters.args)
|
} in parameters.posonlyargs.iter().chain(¶meters.args)
|
||||||
{
|
{
|
||||||
if default.as_ref().is_some_and(|default| is_boolean(default)) {
|
if default
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|default| default.is_boolean_literal_expr())
|
||||||
|
{
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
BooleanDefaultValuePositionalArgument,
|
BooleanDefaultValuePositionalArgument,
|
||||||
parameter.name.range(),
|
parameter.name.range(),
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ruff_python_ast::Expr;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::rules::flake8_boolean_trap::helpers::{allow_boolean_trap, is_boolean};
|
use crate::rules::flake8_boolean_trap::helpers::allow_boolean_trap;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for boolean positional arguments in function calls.
|
/// Checks for boolean positional arguments in function calls.
|
||||||
|
@ -49,7 +49,7 @@ pub(crate) fn boolean_positional_value_in_call(checker: &mut Checker, args: &[Ex
|
||||||
if allow_boolean_trap(func) {
|
if allow_boolean_trap(func) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for arg in args.iter().filter(|arg| is_boolean(arg)) {
|
for arg in args.iter().filter(|arg| arg.is_boolean_literal_expr()) {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(BooleanPositionalValueInCall, arg.range()));
|
.push(Diagnostic::new(BooleanPositionalValueInCall, arg.range()));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Decorator, Expr, ParameterWithDefault, Parameters};
|
use ruff_python_ast::{self as ast, Decorator, Expr, ParameterWithDefault, Parameters};
|
||||||
|
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
|
@ -126,10 +126,7 @@ pub(crate) fn boolean_type_hint_positional_argument(
|
||||||
// check for both bool (python class) and 'bool' (string annotation)
|
// check for both bool (python class) and 'bool' (string annotation)
|
||||||
let hint = match annotation.as_ref() {
|
let hint = match annotation.as_ref() {
|
||||||
Expr::Name(name) => &name.id == "bool",
|
Expr::Name(name) => &name.id == "bool",
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) => value == "bool",
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if !hint || !checker.semantic().is_builtin("bool") {
|
if !hint || !checker.semantic().is_builtin("bool") {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, Keyword, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, Expr, Keyword, Stmt};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -121,12 +121,12 @@ fn is_abc_class(bases: &[Expr], keywords: &[Keyword], semantic: &SemanticModel)
|
||||||
fn is_empty_body(body: &[Stmt]) -> bool {
|
fn is_empty_body(body: &[Stmt]) -> bool {
|
||||||
body.iter().all(|stmt| match stmt {
|
body.iter().all(|stmt| match stmt {
|
||||||
Stmt::Pass(_) => true,
|
Stmt::Pass(_) => true,
|
||||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => match value.as_ref() {
|
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => {
|
matches!(
|
||||||
matches!(value, Constant::Str(..) | Constant::Ellipsis)
|
value.as_ref(),
|
||||||
}
|
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||||
_ => false,
|
)
|
||||||
},
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::Expr;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
@ -42,13 +42,13 @@ impl Violation for DuplicateValue {
|
||||||
pub(crate) fn duplicate_value(checker: &mut Checker, elts: &Vec<Expr>) {
|
pub(crate) fn duplicate_value(checker: &mut Checker, elts: &Vec<Expr>) {
|
||||||
let mut seen_values: FxHashSet<ComparableExpr> = FxHashSet::default();
|
let mut seen_values: FxHashSet<ComparableExpr> = FxHashSet::default();
|
||||||
for elt in elts {
|
for elt in elts {
|
||||||
if let Expr::Constant(ast::ExprConstant { value, .. }) = elt {
|
if elt.is_literal_expr() {
|
||||||
let comparable_value: ComparableExpr = elt.into();
|
let comparable_value: ComparableExpr = elt.into();
|
||||||
|
|
||||||
if !seen_values.insert(comparable_value) {
|
if !seen_values.insert(comparable_value) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
DuplicateValue {
|
DuplicateValue {
|
||||||
value: checker.generator().constant(value),
|
value: checker.generator().expr(elt),
|
||||||
},
|
},
|
||||||
elt.range(),
|
elt.range(),
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::fix::edits::pad;
|
use crate::fix::edits::pad;
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
use ruff_python_stdlib::identifiers::{is_identifier, is_mangled_private};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
@ -66,11 +66,7 @@ pub(crate) fn getattr_with_constant(
|
||||||
if obj.is_starred_expr() {
|
if obj.is_starred_expr() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = arg else {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = arg
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !is_identifier(value) {
|
if !is_identifier(value) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl Violation for RaiseLiteral {
|
||||||
|
|
||||||
/// B016
|
/// B016
|
||||||
pub(crate) fn raise_literal(checker: &mut Checker, expr: &Expr) {
|
pub(crate) fn raise_literal(checker: &mut Checker, expr: &Expr) {
|
||||||
if expr.is_constant_expr() {
|
if expr.is_literal_expr() {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(RaiseLiteral, expr.range()));
|
.push(Diagnostic::new(RaiseLiteral, expr.range()));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, ExprContext, Identifier, Stmt};
|
use ruff_python_ast::{self as ast, Expr, ExprContext, Identifier, Stmt};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
|
@ -80,11 +80,7 @@ pub(crate) fn setattr_with_constant(
|
||||||
if obj.is_starred_expr() {
|
if obj.is_starred_expr() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::StringLiteral(ast::ExprStringLiteral { value: name, .. }) = name else {
|
||||||
value: Constant::Str(name),
|
|
||||||
..
|
|
||||||
}) = name
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !is_identifier(name) {
|
if !is_identifier(name) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -68,11 +68,7 @@ pub(crate) fn strip_with_multi_characters(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [Expr::Constant(ast::ExprConstant {
|
let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] = args else {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
})] = args
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -67,11 +67,7 @@ pub(crate) fn unreliable_callable_check(
|
||||||
let [obj, attr, ..] = args else {
|
let [obj, attr, ..] = args else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = attr else {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = attr
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if value != "__call__" {
|
if value != "__call__" {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::Expr;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -54,11 +54,7 @@ pub(crate) fn useless_expression(checker: &mut Checker, value: &Expr) {
|
||||||
// Ignore strings, to avoid false positives with docstrings.
|
// Ignore strings, to avoid false positives with docstrings.
|
||||||
if matches!(
|
if matches!(
|
||||||
value,
|
value,
|
||||||
Expr::FString(_)
|
Expr::FString(_) | Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
|
||||||
| Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(..) | Constant::Ellipsis,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, UnaryOp};
|
use ruff_python_ast::{self as ast, Expr, UnaryOp};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -79,8 +79,8 @@ pub(crate) fn unnecessary_subscript_reversal(checker: &mut Checker, call: &ast::
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(val),
|
value: ast::Number::Int(val),
|
||||||
..
|
..
|
||||||
}) = operand.as_ref()
|
}) = operand.as_ref()
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -75,10 +75,8 @@ pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: &
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does the `strptime` call contain a format string with a timezone specifier?
|
// Does the `strptime` call contain a format string with a timezone specifier?
|
||||||
if let Some(Expr::Constant(ast::ExprConstant {
|
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value: format, .. })) =
|
||||||
value: Constant::Str(format),
|
call.arguments.args.get(1).as_ref()
|
||||||
range: _,
|
|
||||||
})) = call.arguments.args.get(1).as_ref()
|
|
||||||
{
|
{
|
||||||
if format.contains("%z") {
|
if format.contains("%z") {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, Expr, Stmt};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -80,16 +80,13 @@ pub(crate) fn all_with_model_form(
|
||||||
if id != "fields" {
|
if id != "fields" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() else {
|
match value.as_ref() {
|
||||||
continue;
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
};
|
|
||||||
match value {
|
|
||||||
Constant::Str(ast::StringConstant { value, .. }) => {
|
|
||||||
if value == "__all__" {
|
if value == "__all__" {
|
||||||
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));
|
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Constant::Bytes(ast::BytesConstant { value, .. }) => {
|
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
|
||||||
if value == "__all__".as_bytes() {
|
if value == "__all__".as_bytes() {
|
||||||
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));
|
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, ExprContext, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Stmt};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
|
@ -182,10 +182,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||||
if let Some(first) = args.first() {
|
if let Some(first) = args.first() {
|
||||||
match first {
|
match first {
|
||||||
// Check for string literals.
|
// Check for string literals.
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
|
||||||
value: Constant::Str(string),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if checker.enabled(Rule::RawStringInException) {
|
if checker.enabled(Rule::RawStringInException) {
|
||||||
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
|
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
|
||||||
let mut diagnostic =
|
let mut diagnostic =
|
||||||
|
@ -232,7 +229,7 @@ pub(crate) fn string_in_exception(checker: &mut Checker, stmt: &Stmt, exc: &Expr
|
||||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
|
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
|
||||||
func.as_ref()
|
func.as_ref()
|
||||||
{
|
{
|
||||||
if attr == "format" && value.is_constant_expr() {
|
if attr == "format" && value.is_literal_expr() {
|
||||||
let mut diagnostic =
|
let mut diagnostic =
|
||||||
Diagnostic::new(DotFormatInException, first.range());
|
Diagnostic::new(DotFormatInException, first.range());
|
||||||
if let Some(indentation) =
|
if let Some(indentation) =
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
@ -58,11 +58,7 @@ pub(crate) fn printf_in_gettext_func_call(checker: &mut Checker, args: &[Expr])
|
||||||
..
|
..
|
||||||
}) = &first
|
}) = &first
|
||||||
{
|
{
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if left.is_string_literal_expr() {
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
}) = left.as_ref()
|
|
||||||
{
|
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(PrintfInGetTextFuncCall {}, first.range()));
|
.push(Diagnostic::new(PrintfInGetTextFuncCall {}, first.range()));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -51,18 +51,10 @@ pub(crate) fn explicit(expr: &Expr, locator: &Locator) -> Option<Diagnostic> {
|
||||||
if matches!(op, Operator::Add) {
|
if matches!(op, Operator::Add) {
|
||||||
if matches!(
|
if matches!(
|
||||||
left.as_ref(),
|
left.as_ref(),
|
||||||
Expr::FString(_)
|
Expr::FString(_) | Expr::StringLiteral(_) | Expr::BytesLiteral(_)
|
||||||
| Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(..) | Constant::Bytes(..),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) && matches!(
|
) && matches!(
|
||||||
right.as_ref(),
|
right.as_ref(),
|
||||||
Expr::FString(_)
|
Expr::FString(_) | Expr::StringLiteral(_) | Expr::BytesLiteral(_)
|
||||||
| Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(..) | Constant::Bytes(..),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) && locator.contains_line_break(*range)
|
) && locator.contains_line_break(*range)
|
||||||
{
|
{
|
||||||
return Some(Diagnostic::new(ExplicitStringConcatenation, expr.range()));
|
return Some(Diagnostic::new(ExplicitStringConcatenation, expr.range()));
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix};
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, Keyword, Operator};
|
use ruff_python_ast::{self as ast, Arguments, Expr, Keyword, Operator};
|
||||||
use ruff_python_semantic::analyze::logging;
|
use ruff_python_semantic::analyze::logging;
|
||||||
use ruff_python_stdlib::logging::LoggingLevel;
|
use ruff_python_stdlib::logging::LoggingLevel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -74,7 +74,7 @@ fn check_msg(checker: &mut Checker, msg: &Expr) {
|
||||||
Expr::Call(ast::ExprCall { func, .. }) => {
|
Expr::Call(ast::ExprCall { func, .. }) => {
|
||||||
if checker.enabled(Rule::LoggingStringFormat) {
|
if checker.enabled(Rule::LoggingStringFormat) {
|
||||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func.as_ref() {
|
||||||
if attr == "format" && value.is_constant_expr() {
|
if attr == "format" && value.is_literal_expr() {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(LoggingStringFormat, msg.range()));
|
.push(Diagnostic::new(LoggingStringFormat, msg.range()));
|
||||||
|
@ -92,11 +92,7 @@ fn check_log_record_attr_clash(checker: &mut Checker, extra: &Keyword) {
|
||||||
Expr::Dict(ast::ExprDict { keys, .. }) => {
|
Expr::Dict(ast::ExprDict { keys, .. }) => {
|
||||||
for key in keys {
|
for key in keys {
|
||||||
if let Some(key) = &key {
|
if let Some(key) = &key {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value: attr, .. }) = key {
|
||||||
value: Constant::Str(attr),
|
|
||||||
..
|
|
||||||
}) = key
|
|
||||||
{
|
|
||||||
if is_reserved_attr(attr) {
|
if is_reserved_attr(attr) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
LoggingExtraAttrClash(attr.to_string()),
|
LoggingExtraAttrClash(attr.to_string()),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Keyword};
|
use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||||
|
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -96,7 +96,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
||||||
.iter()
|
.iter()
|
||||||
.zip(values.iter())
|
.zip(values.iter())
|
||||||
.map(|(kwarg, value)| {
|
.map(|(kwarg, value)| {
|
||||||
format!("{}={}", kwarg.value, checker.locator().slice(value.range()))
|
format!("{}={}", kwarg, checker.locator().slice(value.range()))
|
||||||
})
|
})
|
||||||
.join(", "),
|
.join(", "),
|
||||||
kw.range(),
|
kw.range(),
|
||||||
|
@ -108,12 +108,8 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `Some` if a key is a valid keyword argument name, or `None` otherwise.
|
/// Return `Some` if a key is a valid keyword argument name, or `None` otherwise.
|
||||||
fn as_kwarg(key: &Expr) -> Option<&ast::StringConstant> {
|
fn as_kwarg(key: &Expr) -> Option<&str> {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = key {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
}) = key
|
|
||||||
{
|
|
||||||
if is_identifier(value) {
|
if is_identifier(value) {
|
||||||
return Some(value);
|
return Some(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -65,8 +65,8 @@ pub(crate) fn unnecessary_range_start(checker: &mut Checker, call: &ast::ExprCal
|
||||||
};
|
};
|
||||||
|
|
||||||
// Verify that the `start` argument is the literal `0`.
|
// Verify that the `start` argument is the literal `0`.
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(value),
|
value: ast::Number::Int(value),
|
||||||
..
|
..
|
||||||
}) = start
|
}) = start
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::ExprStringLiteral;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -36,8 +36,8 @@ impl Violation for DocstringInStub {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PYI021
|
/// PYI021
|
||||||
pub(crate) fn docstring_in_stubs(checker: &mut Checker, docstring: Option<&Expr>) {
|
pub(crate) fn docstring_in_stubs(checker: &mut Checker, docstring: Option<&ExprStringLiteral>) {
|
||||||
if let Some(docstr) = &docstring {
|
if let Some(docstr) = docstring {
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(DocstringInStub, docstr.range()));
|
.push(Diagnostic::new(DocstringInStub, docstr.range()));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{Constant, Expr, ExprConstant, Stmt, StmtExpr};
|
use ruff_python_ast::{Stmt, StmtExpr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -54,13 +54,7 @@ pub(crate) fn ellipsis_in_non_empty_class_body(checker: &mut Checker, body: &[St
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
if value.is_ellipsis_literal_expr() {
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ExprConstant {
|
|
||||||
value: Constant::Ellipsis,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
let mut diagnostic = Diagnostic::new(EllipsisInNonEmptyClassBody, stmt.range());
|
let mut diagnostic = Diagnostic::new(EllipsisInNonEmptyClassBody, stmt.range());
|
||||||
let edit =
|
let edit =
|
||||||
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -60,10 +60,8 @@ pub(crate) fn non_empty_stub_body(checker: &mut Checker, body: &[Stmt]) {
|
||||||
|
|
||||||
// Ignore `...` (the desired case).
|
// Ignore `...` (the desired case).
|
||||||
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
|
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
|
||||||
if let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() {
|
if value.is_ellipsis_literal_expr() {
|
||||||
if value.is_ellipsis() {
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use ast::Constant;
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
@ -147,14 +146,15 @@ fn match_builtin_type(expr: &Expr, semantic: &SemanticModel) -> Option<ExprType>
|
||||||
/// Return the [`ExprType`] of an [`Expr]` if it is a constant (e.g., an `int`, like `1`, or a
|
/// Return the [`ExprType`] of an [`Expr]` if it is a constant (e.g., an `int`, like `1`, or a
|
||||||
/// `bool`, like `True`).
|
/// `bool`, like `True`).
|
||||||
fn match_constant_type(expr: &Expr) -> Option<ExprType> {
|
fn match_constant_type(expr: &Expr) -> Option<ExprType> {
|
||||||
let constant = expr.as_constant_expr()?;
|
let result = match expr {
|
||||||
let result = match constant.value {
|
Expr::BooleanLiteral(_) => ExprType::Bool,
|
||||||
Constant::Bool(_) => ExprType::Bool,
|
Expr::StringLiteral(_) => ExprType::Str,
|
||||||
Constant::Str(_) => ExprType::Str,
|
Expr::BytesLiteral(_) => ExprType::Bytes,
|
||||||
Constant::Bytes(_) => ExprType::Bytes,
|
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
|
||||||
Constant::Int(_) => ExprType::Int,
|
ast::Number::Int(_) => ExprType::Int,
|
||||||
Constant::Float(_) => ExprType::Float,
|
ast::Number::Float(_) => ExprType::Float,
|
||||||
Constant::Complex { .. } => ExprType::Complex,
|
ast::Number::Complex { .. } => ExprType::Complex,
|
||||||
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(result)
|
Some(result)
|
||||||
|
|
|
@ -2,8 +2,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix, Violation}
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::call_path::CallPath;
|
use ruff_python_ast::call_path::CallPath;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Arguments, Constant, Expr, Operator, ParameterWithDefault, Parameters, Stmt,
|
self as ast, Arguments, Expr, Operator, ParameterWithDefault, Parameters, Stmt, UnaryOp,
|
||||||
UnaryOp,
|
|
||||||
};
|
};
|
||||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
||||||
use ruff_source_file::Locator;
|
use ruff_source_file::Locator;
|
||||||
|
@ -282,7 +281,12 @@ fn is_valid_default_value_with_annotation(
|
||||||
semantic: &SemanticModel,
|
semantic: &SemanticModel,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match default {
|
match default {
|
||||||
Expr::Constant(_) => {
|
Expr::StringLiteral(_)
|
||||||
|
| Expr::BytesLiteral(_)
|
||||||
|
| Expr::NumberLiteral(_)
|
||||||
|
| Expr::BooleanLiteral(_)
|
||||||
|
| Expr::NoneLiteral(_)
|
||||||
|
| Expr::EllipsisLiteral(_) => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Expr::List(ast::ExprList { elts, .. })
|
Expr::List(ast::ExprList { elts, .. })
|
||||||
|
@ -314,10 +318,7 @@ fn is_valid_default_value_with_annotation(
|
||||||
}) => {
|
}) => {
|
||||||
match operand.as_ref() {
|
match operand.as_ref() {
|
||||||
// Ex) `-1`, `-3.14`, `2j`
|
// Ex) `-1`, `-3.14`, `2j`
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(_) => return true,
|
||||||
value: Constant::Int(..) | Constant::Float(..) | Constant::Complex { .. },
|
|
||||||
..
|
|
||||||
}) => return true,
|
|
||||||
// Ex) `-math.inf`, `-math.pi`, etc.
|
// Ex) `-math.inf`, `-math.pi`, etc.
|
||||||
Expr::Attribute(_) => {
|
Expr::Attribute(_) => {
|
||||||
if semantic
|
if semantic
|
||||||
|
@ -338,14 +339,14 @@ fn is_valid_default_value_with_annotation(
|
||||||
range: _,
|
range: _,
|
||||||
}) => {
|
}) => {
|
||||||
// Ex) `1 + 2j`, `1 - 2j`, `-1 - 2j`, `-1 + 2j`
|
// Ex) `1 + 2j`, `1 - 2j`, `-1 - 2j`, `-1 + 2j`
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Complex { .. },
|
value: ast::Number::Complex { .. },
|
||||||
..
|
..
|
||||||
}) = right.as_ref()
|
}) = right.as_ref()
|
||||||
{
|
{
|
||||||
// Ex) `1 + 2j`, `1 - 2j`
|
// Ex) `1 + 2j`, `1 - 2j`
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(..) | Constant::Float(..),
|
value: ast::Number::Int(..) | ast::Number::Float(..),
|
||||||
..
|
..
|
||||||
}) = left.as_ref()
|
}) = left.as_ref()
|
||||||
{
|
{
|
||||||
|
@ -357,8 +358,8 @@ fn is_valid_default_value_with_annotation(
|
||||||
}) = left.as_ref()
|
}) = left.as_ref()
|
||||||
{
|
{
|
||||||
// Ex) `-1 + 2j`, `-1 - 2j`
|
// Ex) `-1 + 2j`, `-1 - 2j`
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(..) | Constant::Float(..),
|
value: ast::Number::Int(..) | ast::Number::Float(..),
|
||||||
..
|
..
|
||||||
}) = operand.as_ref()
|
}) = operand.as_ref()
|
||||||
{
|
{
|
||||||
|
@ -393,13 +394,7 @@ fn is_valid_pep_604_union(annotation: &Expr) -> bool {
|
||||||
right,
|
right,
|
||||||
range: _,
|
range: _,
|
||||||
}) => is_valid_pep_604_union_member(left) && is_valid_pep_604_union_member(right),
|
}) => is_valid_pep_604_union_member(left) && is_valid_pep_604_union_member(right),
|
||||||
Expr::Name(_)
|
Expr::Name(_) | Expr::Subscript(_) | Expr::Attribute(_) | Expr::NoneLiteral(_) => true,
|
||||||
| Expr::Subscript(_)
|
|
||||||
| Expr::Attribute(_)
|
|
||||||
| Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::None,
|
|
||||||
..
|
|
||||||
}) => true,
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,10 +422,8 @@ fn is_valid_default_value_without_annotation(default: &Expr) -> bool {
|
||||||
| Expr::Name(_)
|
| Expr::Name(_)
|
||||||
| Expr::Attribute(_)
|
| Expr::Attribute(_)
|
||||||
| Expr::Subscript(_)
|
| Expr::Subscript(_)
|
||||||
| Expr::Constant(ast::ExprConstant {
|
| Expr::EllipsisLiteral(_)
|
||||||
value: Constant::Ellipsis | Constant::None,
|
| Expr::NoneLiteral(_)
|
||||||
..
|
|
||||||
})
|
|
||||||
) || is_valid_pep_604_union(default)
|
) || is_valid_pep_604_union(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,14 +492,8 @@ fn is_enum(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool {
|
||||||
/// valid type alias. In particular, this function checks for uses of `typing.Any`, `None`,
|
/// valid type alias. In particular, this function checks for uses of `typing.Any`, `None`,
|
||||||
/// parameterized generics, and PEP 604-style unions.
|
/// parameterized generics, and PEP 604-style unions.
|
||||||
fn is_annotatable_type_alias(value: &Expr, semantic: &SemanticModel) -> bool {
|
fn is_annotatable_type_alias(value: &Expr, semantic: &SemanticModel) -> bool {
|
||||||
matches!(
|
matches!(value, Expr::Subscript(_) | Expr::NoneLiteral(_))
|
||||||
value,
|
|| is_valid_pep_604_union(value)
|
||||||
Expr::Subscript(_)
|
|
||||||
| Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::None,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) || is_valid_pep_604_union(value)
|
|
||||||
|| semantic.match_typing_expr(value, "Any")
|
|| semantic.match_typing_expr(value, "Any")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -51,14 +51,8 @@ pub(crate) fn string_or_bytes_too_long(checker: &mut Checker, expr: &Expr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = match expr {
|
let length = match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.chars().count(),
|
||||||
value: Constant::Str(s),
|
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.len(),
|
||||||
..
|
|
||||||
}) => s.chars().count(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Bytes(bytes),
|
|
||||||
..
|
|
||||||
}) => bytes.len(),
|
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
if length <= 50 {
|
if length <= 50 {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr};
|
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -123,11 +123,7 @@ pub(crate) fn unrecognized_platform(checker: &mut Checker, test: &Expr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = right {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = right
|
|
||||||
{
|
|
||||||
// Other values are possible but we don't need them right now.
|
// Other values are possible but we don't need them right now.
|
||||||
// This protects against typos.
|
// This protects against typos.
|
||||||
if checker.enabled(Rule::UnrecognizedPlatformName) {
|
if checker.enabled(Rule::UnrecognizedPlatformName) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::map_subscript;
|
use ruff_python_ast::helpers::map_subscript;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr, Int};
|
use ruff_python_ast::{self as ast, CmpOp, Expr, Int};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -241,8 +241,8 @@ impl ExpectedComparator {
|
||||||
step: None,
|
step: None,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(upper),
|
value: ast::Number::Int(upper),
|
||||||
..
|
..
|
||||||
}) = upper.as_ref()
|
}) = upper.as_ref()
|
||||||
{
|
{
|
||||||
|
@ -254,8 +254,8 @@ impl ExpectedComparator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(Int::ZERO),
|
value: ast::Number::Int(Int::ZERO),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
return Some(ExpectedComparator::MajorDigit);
|
return Some(ExpectedComparator::MajorDigit);
|
||||||
|
@ -271,8 +271,8 @@ impl ExpectedComparator {
|
||||||
fn is_int_constant(expr: &Expr) -> bool {
|
fn is_int_constant(expr: &Expr) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
expr,
|
expr,
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: ast::Constant::Int(_),
|
value: ast::Number::Int(_),
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Decorator, Expr, Keyword};
|
use ruff_python_ast::{self as ast, Decorator, Expr, Keyword};
|
||||||
|
|
||||||
use ruff_python_ast::call_path::collect_call_path;
|
use ruff_python_ast::call_path::collect_call_path;
|
||||||
use ruff_python_ast::helpers::map_callable;
|
use ruff_python_ast::helpers::map_callable;
|
||||||
|
@ -44,11 +44,7 @@ pub(super) fn is_pytest_parametrize(decorator: &Decorator, semantic: &SemanticMo
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn keyword_is_literal(keyword: &Keyword, literal: &str) -> bool {
|
pub(super) fn keyword_is_literal(keyword: &Keyword, literal: &str) -> bool {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &keyword.value {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = &keyword.value
|
|
||||||
{
|
|
||||||
value == literal
|
value == literal
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -57,11 +53,8 @@ pub(super) fn keyword_is_literal(keyword: &Keyword, literal: &str) -> bool {
|
||||||
|
|
||||||
pub(super) fn is_empty_or_null_string(expr: &Expr) -> bool {
|
pub(super) fn is_empty_or_null_string(expr: &Expr) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
|
||||||
value: Constant::Str(string),
|
Expr::NoneLiteral(_) => true,
|
||||||
..
|
|
||||||
}) => string.is_empty(),
|
|
||||||
Expr::Constant(constant) if constant.value.is_none() => true,
|
|
||||||
Expr::FString(ast::ExprFString { values, .. }) => {
|
Expr::FString(ast::ExprFString { values, .. }) => {
|
||||||
values.iter().all(is_empty_or_null_string)
|
values.iter().all(is_empty_or_null_string)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::comparable::ComparableExpr;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||||
use ruff_python_ast::AstNode;
|
use ruff_python_ast::AstNode;
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Decorator, Expr, ExprContext};
|
use ruff_python_ast::{self as ast, Arguments, Decorator, Expr, ExprContext};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_trivia::CommentRanges;
|
use ruff_python_trivia::CommentRanges;
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
@ -248,37 +248,22 @@ impl Violation for PytestDuplicateParametrizeTestCases {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn elts_to_csv(elts: &[Expr], generator: Generator) -> Option<String> {
|
fn elts_to_csv(elts: &[Expr], generator: Generator) -> Option<String> {
|
||||||
let all_literals = elts.iter().all(|expr| {
|
if !elts.iter().all(Expr::is_string_literal_expr) {
|
||||||
matches!(
|
|
||||||
expr,
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
if !all_literals {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = Expr::Constant(ast::ExprConstant {
|
let node = Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value: elts
|
value: elts.iter().fold(String::new(), |mut acc, elt| {
|
||||||
.iter()
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = elt {
|
||||||
.fold(String::new(), |mut acc, elt| {
|
if !acc.is_empty() {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
acc.push(',');
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = elt
|
|
||||||
{
|
|
||||||
if !acc.is_empty() {
|
|
||||||
acc.push(',');
|
|
||||||
}
|
|
||||||
acc.push_str(value.as_str());
|
|
||||||
}
|
}
|
||||||
acc
|
acc.push_str(value.as_str());
|
||||||
})
|
}
|
||||||
.into(),
|
acc
|
||||||
|
}),
|
||||||
|
unicode: false,
|
||||||
|
implicit_concatenated: false,
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
});
|
});
|
||||||
Some(generator.expr(&node))
|
Some(generator.expr(&node))
|
||||||
|
@ -317,10 +302,7 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
||||||
let names_type = checker.settings.flake8_pytest_style.parametrize_names_type;
|
let names_type = checker.settings.flake8_pytest_style.parametrize_names_type;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
|
||||||
value: Constant::Str(string),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
let names = split_names(string);
|
let names = split_names(string);
|
||||||
if names.len() > 1 {
|
if names.len() > 1 {
|
||||||
match names_type {
|
match names_type {
|
||||||
|
@ -342,8 +324,10 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
||||||
elts: names
|
elts: names
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value: (*name).to_string().into(),
|
value: (*name).to_string(),
|
||||||
|
unicode: false,
|
||||||
|
implicit_concatenated: false,
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -375,8 +359,10 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
|
||||||
elts: names
|
elts: names
|
||||||
.iter()
|
.iter()
|
||||||
.map(|name| {
|
.map(|name| {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value: (*name).to_string().into(),
|
value: (*name).to_string(),
|
||||||
|
unicode: false,
|
||||||
|
implicit_concatenated: false,
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -495,15 +481,12 @@ fn check_values(checker: &mut Checker, names: &Expr, values: &Expr) {
|
||||||
.flake8_pytest_style
|
.flake8_pytest_style
|
||||||
.parametrize_values_row_type;
|
.parametrize_values_row_type;
|
||||||
|
|
||||||
let is_multi_named = if let Expr::Constant(ast::ExprConstant {
|
let is_multi_named =
|
||||||
value: Constant::Str(string),
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) = &names {
|
||||||
..
|
split_names(string).len() > 1
|
||||||
}) = &names
|
} else {
|
||||||
{
|
true
|
||||||
split_names(string).len() > 1
|
};
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
match values {
|
match values {
|
||||||
Expr::List(ast::ExprList { elts, .. }) => {
|
Expr::List(ast::ExprList { elts, .. }) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::hash::BuildHasherDefault;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Arguments, CmpOp, Constant, Expr, ExprContext, Identifier, Keyword, Stmt, UnaryOp,
|
self as ast, Arguments, CmpOp, Expr, ExprContext, Identifier, Keyword, Stmt, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -368,8 +368,7 @@ impl UnittestAssert {
|
||||||
} else {
|
} else {
|
||||||
CmpOp::IsNot
|
CmpOp::IsNot
|
||||||
};
|
};
|
||||||
let node = Expr::Constant(ast::ExprConstant {
|
let node = Expr::NoneLiteral(ast::ExprNoneLiteral {
|
||||||
value: Constant::None,
|
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
});
|
});
|
||||||
let expr = compare(expr, cmp_op, &node);
|
let expr = compare(expr, cmp_op, &node);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::{Expr, Stmt};
|
use ruff_python_ast::Stmt;
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
|
|
||||||
use ruff_source_file::{Locator, UniversalNewlines};
|
use ruff_source_file::{Locator, UniversalNewlines};
|
||||||
|
@ -8,12 +8,9 @@ use ruff_source_file::{Locator, UniversalNewlines};
|
||||||
/// non-`None` value.
|
/// non-`None` value.
|
||||||
pub(super) fn result_exists(returns: &[&ast::StmtReturn]) -> bool {
|
pub(super) fn result_exists(returns: &[&ast::StmtReturn]) -> bool {
|
||||||
returns.iter().any(|stmt| {
|
returns.iter().any(|stmt| {
|
||||||
stmt.value.as_deref().is_some_and(|value| {
|
stmt.value
|
||||||
!matches!(
|
.as_deref()
|
||||||
value,
|
.is_some_and(|value| !value.is_none_literal_expr())
|
||||||
Expr::Constant(constant) if constant.value.is_none()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr};
|
use ruff_python_ast::{self as ast, Arguments, Expr};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::fix::snippet::SourceCodeSnippet;
|
use crate::fix::snippet::SourceCodeSnippet;
|
||||||
|
@ -139,11 +139,7 @@ pub(crate) fn use_capital_environment_variables(checker: &mut Checker, expr: &Ex
|
||||||
let Some(arg) = args.get(0) else {
|
let Some(arg) = args.get(0) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::StringLiteral(ast::ExprStringLiteral { value: env_var, .. }) = arg else {
|
||||||
value: Constant::Str(ast::StringConstant { value: env_var, .. }),
|
|
||||||
..
|
|
||||||
}) = arg
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !checker
|
if !checker
|
||||||
|
@ -195,14 +191,10 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
|
||||||
if id != "os" || attr != "environ" {
|
if id != "os" || attr != "environ" {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value:
|
value: env_var,
|
||||||
Constant::Str(ast::StringConstant {
|
unicode,
|
||||||
value: env_var,
|
..
|
||||||
unicode,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
range: _,
|
|
||||||
}) = slice.as_ref()
|
}) = slice.as_ref()
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
|
@ -224,12 +216,10 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
|
||||||
},
|
},
|
||||||
slice.range(),
|
slice.range(),
|
||||||
);
|
);
|
||||||
let node = ast::ExprConstant {
|
let node = ast::ExprStringLiteral {
|
||||||
value: ast::Constant::Str(ast::StringConstant {
|
value: capital_env_var,
|
||||||
value: capital_env_var,
|
unicode: *unicode,
|
||||||
unicode: *unicode,
|
implicit_concatenated: false,
|
||||||
implicit_concatenated: false,
|
|
||||||
}),
|
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
let new_env_var = node.into();
|
let new_env_var = node.into();
|
||||||
|
@ -265,7 +255,7 @@ pub(crate) fn dict_get_with_none_default(checker: &mut Checker, expr: &Expr) {
|
||||||
let Some(key) = args.get(0) else {
|
let Some(key) = args.get(0) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !matches!(key, Expr::Constant(_) | Expr::Name(_)) {
|
if !(key.is_literal_expr() || key.is_name_expr()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some(default) = args.get(1) else {
|
let Some(default) = args.get(1) else {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use log::error;
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::AnyNodeRef;
|
use ruff_python_ast::AnyNodeRef;
|
||||||
use ruff_python_ast::{self as ast, whitespace, Constant, ElifElseClause, Expr, Stmt};
|
use ruff_python_ast::{self as ast, whitespace, ElifElseClause, Expr, Stmt};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
||||||
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
|
||||||
|
@ -212,13 +212,7 @@ fn nested_if_body(stmt_if: &ast::StmtIf) -> Option<NestedIf> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow `if True:` and `if False:` statements.
|
// Allow `if True:` and `if False:` statements.
|
||||||
if matches!(
|
if test.is_boolean_literal_expr() {
|
||||||
test,
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Bool(..),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,10 +253,8 @@ fn is_main_check(expr: &Expr) -> bool {
|
||||||
{
|
{
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
|
||||||
if id == "__name__" {
|
if id == "__name__" {
|
||||||
if let [Expr::Constant(ast::ExprConstant {
|
if let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] =
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
comparators.as_slice()
|
||||||
..
|
|
||||||
})] = comparators.as_slice()
|
|
||||||
{
|
{
|
||||||
if value == "__main__" {
|
if value == "__main__" {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::comparable::ComparableConstant;
|
use ruff_python_ast::comparable::ComparableExpr;
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
use ruff_python_ast::helpers::contains_effect;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, ElifElseClause, Expr, Stmt};
|
use ruff_python_ast::{self as ast, CmpOp, ElifElseClause, Expr, Stmt};
|
||||||
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
||||||
|
@ -67,12 +67,12 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
if ops != &[CmpOp::Eq] {
|
if ops != &[CmpOp::Eq] {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [Expr::Constant(ast::ExprConstant {
|
let [expr] = comparators.as_slice() else {
|
||||||
value: constant, ..
|
|
||||||
})] = comparators.as_slice()
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if !expr.is_literal_expr() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
|
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -94,8 +94,9 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut constants: FxHashSet<ComparableConstant> = FxHashSet::default();
|
// The `expr` was checked to be a literal above, so this is safe.
|
||||||
constants.insert(constant.into());
|
let mut literals: FxHashSet<ComparableExpr> = FxHashSet::default();
|
||||||
|
literals.insert(expr.into());
|
||||||
|
|
||||||
for clause in elif_else_clauses {
|
for clause in elif_else_clauses {
|
||||||
let ElifElseClause { test, body, .. } = clause;
|
let ElifElseClause { test, body, .. } = clause;
|
||||||
|
@ -129,12 +130,12 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
if id != target || ops != &[CmpOp::Eq] {
|
if id != target || ops != &[CmpOp::Eq] {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let [Expr::Constant(ast::ExprConstant {
|
let [expr] = comparators.as_slice() else {
|
||||||
value: constant, ..
|
|
||||||
})] = comparators.as_slice()
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
if !expr.is_literal_expr() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if value.as_ref().is_some_and(|value| {
|
if value.as_ref().is_some_and(|value| {
|
||||||
contains_effect(value, |id| checker.semantic().is_builtin(id))
|
contains_effect(value, |id| checker.semantic().is_builtin(id))
|
||||||
|
@ -142,7 +143,8 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
constants.insert(constant.into());
|
// The `expr` was checked to be a literal above, so this is safe.
|
||||||
|
literals.insert(expr.into());
|
||||||
}
|
}
|
||||||
// Different `elif`
|
// Different `elif`
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -151,7 +153,7 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if constants.len() < 3 {
|
if literals.len() < 3 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, ElifElseClause, Expr, ExprContext, Stmt};
|
use ruff_python_ast::{self as ast, Arguments, ElifElseClause, Expr, ExprContext, Stmt};
|
||||||
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
use ruff_python_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
@ -176,10 +176,7 @@ fn is_one_line_return_bool(stmts: &[Stmt]) -> Option<Bool> {
|
||||||
let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt else {
|
let Stmt::Return(ast::StmtReturn { value, range: _ }) = stmt else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Some(Expr::Constant(ast::ExprConstant { value, .. })) = value.as_deref() else {
|
let Some(Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. })) = value.as_deref() else {
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let Constant::Bool(value) = value else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
Some((*value).into())
|
Some((*value).into())
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::any_over_expr;
|
use ruff_python_ast::helpers::any_over_expr;
|
||||||
use ruff_python_ast::traversal;
|
use ruff_python_ast::traversal;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Arguments, CmpOp, Comprehension, Constant, Expr, ExprContext, Stmt, UnaryOp,
|
self as ast, Arguments, CmpOp, Comprehension, Expr, ExprContext, Stmt, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
@ -280,11 +280,7 @@ fn match_loop(stmt: &Stmt) -> Option<Loop> {
|
||||||
let Some(value) = value else {
|
let Some(value) = value else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = value.as_ref() else {
|
||||||
value: Constant::Bool(value),
|
|
||||||
..
|
|
||||||
}) = value.as_ref()
|
|
||||||
else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -319,9 +315,8 @@ fn match_else_return(stmt: &Stmt) -> Option<Terminal> {
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::BooleanLiteral(ast::ExprBooleanLiteral {
|
||||||
value: Constant::Bool(next_value),
|
value: next_value, ..
|
||||||
..
|
|
||||||
}) = next_value.as_ref()
|
}) = next_value.as_ref()
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -362,9 +357,8 @@ fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Termina
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::BooleanLiteral(ast::ExprBooleanLiteral {
|
||||||
value: Constant::Bool(next_value),
|
value: next_value, ..
|
||||||
..
|
|
||||||
}) = next_value.as_ref()
|
}) = next_value.as_ref()
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::call_path::compose_call_path;
|
use ruff_python_ast::call_path::compose_call_path;
|
||||||
use ruff_python_ast::helpers;
|
use ruff_python_ast::helpers;
|
||||||
use ruff_python_ast::{self as ast, Constant, ExceptHandler, Expr, Stmt};
|
use ruff_python_ast::{self as ast, ExceptHandler, Stmt};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
use ruff_text_size::{TextLen, TextRange};
|
use ruff_text_size::{TextLen, TextRange};
|
||||||
|
|
||||||
|
@ -64,13 +64,7 @@ impl Violation for SuppressibleException {
|
||||||
fn is_empty(body: &[Stmt]) -> bool {
|
fn is_empty(body: &[Stmt]) -> bool {
|
||||||
match body {
|
match body {
|
||||||
[Stmt::Pass(_)] => true,
|
[Stmt::Pass(_)] => true,
|
||||||
[Stmt::Expr(ast::StmtExpr { value, range: _ })] => matches!(
|
[Stmt::Expr(ast::StmtExpr { value, range: _ })] => value.is_ellipsis_literal_expr(),
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Ellipsis,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,15 +82,14 @@ impl Violation for YodaConditions {
|
||||||
fn is_constant_like(expr: &Expr) -> bool {
|
fn is_constant_like(expr: &Expr) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Attribute(ast::ExprAttribute { attr, .. }) => str::is_cased_uppercase(attr),
|
Expr::Attribute(ast::ExprAttribute { attr, .. }) => str::is_cased_uppercase(attr),
|
||||||
Expr::Constant(_) => true,
|
|
||||||
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(is_constant_like),
|
Expr::Tuple(ast::ExprTuple { elts, .. }) => elts.iter().all(is_constant_like),
|
||||||
Expr::Name(ast::ExprName { id, .. }) => str::is_cased_uppercase(id),
|
Expr::Name(ast::ExprName { id, .. }) => str::is_cased_uppercase(id),
|
||||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||||
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
||||||
operand,
|
operand,
|
||||||
range: _,
|
range: _,
|
||||||
}) => operand.is_constant_expr(),
|
}) => operand.is_literal_expr(),
|
||||||
_ => false,
|
_ => expr.is_literal_expr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||||
|
|
||||||
use ruff_python_ast::helpers::is_docstring_stmt;
|
use ruff_python_ast::helpers::is_docstring_stmt;
|
||||||
|
|
||||||
|
@ -7,15 +7,7 @@ use ruff_python_ast::helpers::is_docstring_stmt;
|
||||||
fn is_empty_stmt(stmt: &Stmt) -> bool {
|
fn is_empty_stmt(stmt: &Stmt) -> bool {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Pass(_) => return true,
|
Stmt::Pass(_) => return true,
|
||||||
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
|
Stmt::Expr(ast::StmtExpr { value, range: _ }) => return value.is_ellipsis_literal_expr(),
|
||||||
return matches!(
|
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Ellipsis,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Stmt::Raise(ast::StmtRaise { exc, cause, .. }) => {
|
Stmt::Raise(ast::StmtRaise { exc, cause, .. }) => {
|
||||||
if cause.is_none() {
|
if cause.is_none() {
|
||||||
if let Some(exc) = exc {
|
if let Some(exc) = exc {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, ExprCall, ExprConstant};
|
use ruff_python_ast::{self as ast, Arguments, Expr, ExprCall};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -65,11 +65,7 @@ pub(crate) fn path_constructor_current_directory(checker: &mut Checker, expr: &E
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [Expr::Constant(ExprConstant {
|
let [Expr::StringLiteral(ast::ExprStringLiteral { value, range, .. })] = args.as_slice() else {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
range,
|
|
||||||
})] = args.as_slice()
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||||
use ruff_python_ast::{Constant, Expr, ExprCall, ExprConstant};
|
use ruff_python_ast::{Expr, ExprBooleanLiteral, ExprCall};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -118,24 +118,13 @@ pub(crate) fn replaceable_by_pathlib(checker: &mut Checker, call: &ExprCall) {
|
||||||
.is_some_and(|expr| {
|
.is_some_and(|expr| {
|
||||||
!matches!(
|
!matches!(
|
||||||
expr,
|
expr,
|
||||||
Expr::Constant(ExprConstant {
|
Expr::BooleanLiteral(ExprBooleanLiteral { value: true, .. })
|
||||||
value: Constant::Bool(true),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|| call
|
|| call
|
||||||
.arguments
|
.arguments
|
||||||
.find_argument("opener", 7)
|
.find_argument("opener", 7)
|
||||||
.is_some_and(|expr| {
|
.is_some_and(|expr| !expr.is_none_literal_expr())
|
||||||
!matches!(
|
|
||||||
expr,
|
|
||||||
Expr::Constant(ExprConstant {
|
|
||||||
value: Constant::None,
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, ConversionFlag, Expr};
|
use ruff_python_ast::{self as ast, Arguments, ConversionFlag, Expr};
|
||||||
use ruff_text_size::TextRange;
|
use ruff_text_size::TextRange;
|
||||||
|
|
||||||
/// Wrap an expression in a `FormattedValue` with no special formatting.
|
/// Wrap an expression in a `FormattedValue` with no special formatting.
|
||||||
|
@ -15,8 +15,10 @@ fn to_formatted_value_expr(inner: &Expr) -> Expr {
|
||||||
|
|
||||||
/// Convert a string to a constant string expression.
|
/// Convert a string to a constant string expression.
|
||||||
pub(super) fn to_constant_string(s: &str) -> Expr {
|
pub(super) fn to_constant_string(s: &str) -> Expr {
|
||||||
let node = ast::ExprConstant {
|
let node = ast::ExprStringLiteral {
|
||||||
value: s.to_owned().into(),
|
value: s.to_owned(),
|
||||||
|
unicode: false,
|
||||||
|
implicit_concatenated: false,
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
node.into()
|
node.into()
|
||||||
|
@ -54,20 +56,11 @@ fn is_simple_callee(func: &Expr) -> bool {
|
||||||
pub(super) fn to_f_string_element(expr: &Expr) -> Option<Expr> {
|
pub(super) fn to_f_string_element(expr: &Expr) -> Option<Expr> {
|
||||||
match expr {
|
match expr {
|
||||||
// These are directly handled by `unparse_f_string_element`:
|
// These are directly handled by `unparse_f_string_element`:
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(_) | Expr::FString(_) | Expr::FormattedValue(_) => Some(expr.clone()),
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| Expr::FString(_)
|
|
||||||
| Expr::FormattedValue(_) => Some(expr.clone()),
|
|
||||||
// These should be pretty safe to wrap in a formatted value.
|
// These should be pretty safe to wrap in a formatted value.
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
|
||||||
value:
|
Some(to_formatted_value_expr(expr))
|
||||||
Constant::Int(_) | Constant::Float(_) | Constant::Bool(_) | Constant::Complex { .. },
|
}
|
||||||
..
|
|
||||||
})
|
|
||||||
| Expr::Name(_)
|
|
||||||
| Expr::Attribute(_) => Some(to_formatted_value_expr(expr)),
|
|
||||||
Expr::Call(_) if is_simple_call(expr) => Some(to_formatted_value_expr(expr)),
|
Expr::Call(_) if is_simple_call(expr) => Some(to_formatted_value_expr(expr)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use itertools::Itertools;
|
||||||
use crate::fix::edits::pad;
|
use crate::fix::edits::pad;
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr};
|
use ruff_python_ast::{self as ast, Arguments, Expr};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -61,31 +61,20 @@ fn is_static_length(elts: &[Expr]) -> bool {
|
||||||
|
|
||||||
fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
|
fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
|
||||||
// If all elements are string constants, join them into a single string.
|
// If all elements are string constants, join them into a single string.
|
||||||
if joinees.iter().all(|expr| {
|
if joinees.iter().all(Expr::is_string_literal_expr) {
|
||||||
matches!(
|
let node = ast::ExprStringLiteral {
|
||||||
expr,
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
let node = ast::ExprConstant {
|
|
||||||
value: joinees
|
value: joinees
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|expr| {
|
.filter_map(|expr| {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = expr
|
|
||||||
{
|
|
||||||
Some(value.as_str())
|
Some(value.as_str())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.join(joiner)
|
.join(joiner),
|
||||||
.into(),
|
unicode: false,
|
||||||
|
implicit_concatenated: false,
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
};
|
};
|
||||||
return Some(node.into());
|
return Some(node.into());
|
||||||
|
|
|
@ -16,7 +16,12 @@ pub(super) enum Resolution {
|
||||||
/// Test an [`Expr`] for relevance to Pandas-related operations.
|
/// Test an [`Expr`] for relevance to Pandas-related operations.
|
||||||
pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resolution {
|
pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resolution {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(_)
|
Expr::StringLiteral(_)
|
||||||
|
| Expr::BytesLiteral(_)
|
||||||
|
| Expr::NumberLiteral(_)
|
||||||
|
| Expr::BooleanLiteral(_)
|
||||||
|
| Expr::NoneLiteral(_)
|
||||||
|
| Expr::EllipsisLiteral(_)
|
||||||
| Expr::Tuple(_)
|
| Expr::Tuple(_)
|
||||||
| Expr::List(_)
|
| Expr::List(_)
|
||||||
| Expr::Set(_)
|
| Expr::Set(_)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr, Int};
|
use ruff_python_ast::{self as ast, CmpOp, Expr, Int};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -80,8 +80,8 @@ pub(crate) fn nunique_constant_series_check(
|
||||||
// Right should be the integer 1.
|
// Right should be the integer 1.
|
||||||
if !matches!(
|
if !matches!(
|
||||||
right,
|
right,
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(Int::ONE),
|
value: ast::Number::Int(Int::ONE),
|
||||||
range: _,
|
range: _,
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::{Constant, Expr};
|
use ruff_python_ast::Expr;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -51,10 +51,7 @@ pub(crate) fn use_of_read_table(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
.resolve_call_path(&call.func)
|
.resolve_call_path(&call.func)
|
||||||
.is_some_and(|call_path| matches!(call_path.as_slice(), ["pandas", "read_table"]))
|
.is_some_and(|call_path| matches!(call_path.as_slice(), ["pandas", "read_table"]))
|
||||||
{
|
{
|
||||||
if let Some(Expr::Constant(ast::ExprConstant {
|
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) = call
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
})) = call
|
|
||||||
.arguments
|
.arguments
|
||||||
.find_keyword("sep")
|
.find_keyword("sep")
|
||||||
.map(|keyword| &keyword.value)
|
.map(|keyword| &keyword.value)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
self as ast, Constant, Expr, Identifier, Parameter, ParameterWithDefault, Parameters, Stmt,
|
self as ast, Expr, Identifier, Parameter, ParameterWithDefault, Parameters, Stmt,
|
||||||
};
|
};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
|
@ -164,10 +164,7 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec<Exp
|
||||||
// specification, or ellipsis.
|
// specification, or ellipsis.
|
||||||
let params = match param_types {
|
let params = match param_types {
|
||||||
Expr::List(ast::ExprList { elts, .. }) => elts.clone(),
|
Expr::List(ast::ExprList { elts, .. }) => elts.clone(),
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::EllipsisLiteral(_) => vec![],
|
||||||
value: Constant::Ellipsis,
|
|
||||||
..
|
|
||||||
}) => vec![],
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers;
|
use ruff_python_ast::helpers;
|
||||||
use ruff_python_ast::helpers::{generate_comparison, is_const_none};
|
use ruff_python_ast::helpers::{generate_comparison, is_const_none};
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr};
|
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -164,11 +164,7 @@ pub(crate) fn literal_comparisons(checker: &mut Checker, compare: &ast::ExprComp
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker.enabled(Rule::TrueFalseComparison) {
|
if checker.enabled(Rule::TrueFalseComparison) {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = comparator {
|
||||||
value: Constant::Bool(value),
|
|
||||||
range: _,
|
|
||||||
}) = comparator
|
|
||||||
{
|
|
||||||
match op {
|
match op {
|
||||||
EqCmpOp::Eq => {
|
EqCmpOp::Eq => {
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
|
@ -224,11 +220,7 @@ pub(crate) fn literal_comparisons(checker: &mut Checker, compare: &ast::ExprComp
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker.enabled(Rule::TrueFalseComparison) {
|
if checker.enabled(Rule::TrueFalseComparison) {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = next {
|
||||||
value: Constant::Bool(value),
|
|
||||||
range: _,
|
|
||||||
}) = next
|
|
||||||
{
|
|
||||||
match op {
|
match op {
|
||||||
EqCmpOp::Eq => {
|
EqCmpOp::Eq => {
|
||||||
let diagnostic =
|
let diagnostic =
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::collections::BTreeSet;
|
||||||
|
|
||||||
use ruff_python_ast::call_path::from_qualified_name;
|
use ruff_python_ast::call_path::from_qualified_name;
|
||||||
use ruff_python_ast::helpers::map_callable;
|
use ruff_python_ast::helpers::map_callable;
|
||||||
use ruff_python_ast::Expr;
|
|
||||||
use ruff_python_semantic::{Definition, SemanticModel};
|
use ruff_python_semantic::{Definition, SemanticModel};
|
||||||
use ruff_source_file::UniversalNewlines;
|
use ruff_source_file::UniversalNewlines;
|
||||||
|
|
||||||
|
@ -61,13 +60,3 @@ pub(crate) fn should_ignore_definition(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a docstring should be ignored.
|
|
||||||
pub(crate) fn should_ignore_docstring(docstring: &Expr) -> bool {
|
|
||||||
// Avoid analyzing docstrings that contain implicit string concatenations.
|
|
||||||
// Python does consider these docstrings, but they're almost certainly a
|
|
||||||
// user error, and supporting them "properly" is extremely difficult.
|
|
||||||
docstring
|
|
||||||
.as_constant_expr()
|
|
||||||
.is_some_and(|constant| constant.value.is_implicit_concatenated())
|
|
||||||
}
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub(crate) fn capitalized(checker: &mut Checker, docstring: &Docstring) {
|
||||||
first_word: first_word.to_string(),
|
first_word: first_word.to_string(),
|
||||||
capitalized_word: capitalized_word.to_string(),
|
capitalized_word: capitalized_word.to_string(),
|
||||||
},
|
},
|
||||||
docstring.expr.range(),
|
docstring.range(),
|
||||||
);
|
);
|
||||||
|
|
||||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||||
|
|
|
@ -96,8 +96,8 @@ pub(crate) fn newline_after_last_paragraph(checker: &mut Checker, docstring: &Do
|
||||||
);
|
);
|
||||||
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
|
diagnostic.set_fix(Fix::safe_edit(Edit::replacement(
|
||||||
content,
|
content,
|
||||||
docstring.expr.end() - num_trailing_quotes - num_trailing_spaces,
|
docstring.end() - num_trailing_quotes - num_trailing_spaces,
|
||||||
docstring.expr.end() - num_trailing_quotes,
|
docstring.end() - num_trailing_quotes,
|
||||||
)));
|
)));
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,14 @@ pub(crate) fn repeated_keys(checker: &mut Checker, dict: &ast::ExprDict) {
|
||||||
};
|
};
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
Expr::Constant(_) | Expr::Tuple(_) | Expr::FString(_) => {
|
Expr::StringLiteral(_)
|
||||||
|
| Expr::BytesLiteral(_)
|
||||||
|
| Expr::NumberLiteral(_)
|
||||||
|
| Expr::BooleanLiteral(_)
|
||||||
|
| Expr::NoneLiteral(_)
|
||||||
|
| Expr::EllipsisLiteral(_)
|
||||||
|
| Expr::Tuple(_)
|
||||||
|
| Expr::FString(_) => {
|
||||||
if checker.enabled(Rule::MultiValueRepeatedKeyLiteral) {
|
if checker.enabled(Rule::MultiValueRepeatedKeyLiteral) {
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
MultiValueRepeatedKeyLiteral {
|
MultiValueRepeatedKeyLiteral {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Identifier, Keyword};
|
use ruff_python_ast::{self as ast, Expr, Identifier, Keyword};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -582,10 +582,7 @@ pub(crate) fn percent_format_extra_named_arguments(
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(index, key)| match key {
|
.filter_map(|(index, key)| match key {
|
||||||
Some(Expr::Constant(ast::ExprConstant {
|
Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
if summary.keywords.contains(value.as_str()) {
|
if summary.keywords.contains(value.as_str()) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -643,10 +640,7 @@ pub(crate) fn percent_format_missing_arguments(
|
||||||
let mut keywords = FxHashSet::default();
|
let mut keywords = FxHashSet::default();
|
||||||
for key in keys.iter().flatten() {
|
for key in keys.iter().flatten() {
|
||||||
match key {
|
match key {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
keywords.insert(value);
|
keywords.insert(value);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::{Arguments, CmpOp, Constant, Expr};
|
use ruff_python_ast::{Arguments, CmpOp, Expr};
|
||||||
use ruff_python_semantic::analyze::function_type;
|
use ruff_python_semantic::analyze::function_type;
|
||||||
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
use ruff_python_semantic::{ScopeKind, SemanticModel};
|
||||||
|
|
||||||
|
@ -11,11 +11,7 @@ use crate::settings::LinterSettings;
|
||||||
pub(super) fn type_param_name(arguments: &Arguments) -> Option<&str> {
|
pub(super) fn type_param_name(arguments: &Arguments) -> Option<&str> {
|
||||||
// Handle both `TypeVar("T")` and `TypeVar(name="T")`.
|
// Handle both `TypeVar("T")` and `TypeVar(name="T")`.
|
||||||
let name_param = arguments.find_argument("name", 0)?;
|
let name_param = arguments.find_argument("name", 0)?;
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value: name, .. }) = &name_param {
|
||||||
value: Constant::Str(name),
|
|
||||||
..
|
|
||||||
}) = &name_param
|
|
||||||
{
|
|
||||||
Some(name)
|
Some(name)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -45,51 +45,48 @@ impl Violation for AssertOnStringLiteral {
|
||||||
/// PLW0129
|
/// PLW0129
|
||||||
pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
|
pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
|
||||||
match test {
|
match test {
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
Constant::Str(value, ..) => {
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
AssertOnStringLiteral {
|
||||||
AssertOnStringLiteral {
|
kind: if value.is_empty() {
|
||||||
kind: if value.is_empty() {
|
Kind::Empty
|
||||||
Kind::Empty
|
} else {
|
||||||
} else {
|
Kind::NonEmpty
|
||||||
Kind::NonEmpty
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
test.range(),
|
},
|
||||||
));
|
test.range(),
|
||||||
}
|
));
|
||||||
Constant::Bytes(value) => {
|
}
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
|
||||||
AssertOnStringLiteral {
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
kind: if value.is_empty() {
|
AssertOnStringLiteral {
|
||||||
Kind::Empty
|
kind: if value.is_empty() {
|
||||||
} else {
|
Kind::Empty
|
||||||
Kind::NonEmpty
|
} else {
|
||||||
},
|
Kind::NonEmpty
|
||||||
},
|
},
|
||||||
test.range(),
|
},
|
||||||
));
|
test.range(),
|
||||||
}
|
));
|
||||||
_ => {}
|
}
|
||||||
},
|
|
||||||
Expr::FString(ast::ExprFString { values, .. }) => {
|
Expr::FString(ast::ExprFString { values, .. }) => {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
AssertOnStringLiteral {
|
AssertOnStringLiteral {
|
||||||
kind: if values.iter().all(|value| match value {
|
kind: if values.iter().all(|value| match value {
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
Constant::Str(value) => value.is_empty(),
|
value.is_empty()
|
||||||
Constant::Bytes(value) => value.is_empty(),
|
}
|
||||||
_ => false,
|
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.is_empty(),
|
||||||
},
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
Kind::Empty
|
Kind::Empty
|
||||||
} else if values.iter().any(|value| match value {
|
} else if values.iter().any(|value| match value {
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
Constant::Str(value) => !value.is_empty(),
|
!value.is_empty()
|
||||||
Constant::Bytes(value) => !value.is_empty(),
|
}
|
||||||
_ => false,
|
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
|
||||||
},
|
!value.is_empty()
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
Kind::NonEmpty
|
Kind::NonEmpty
|
||||||
|
|
|
@ -2,7 +2,7 @@ use bitflags::bitflags;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
@ -59,11 +59,7 @@ pub(crate) fn bad_open_mode(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Some(ast::ExprStringLiteral { value, .. }) = mode.as_string_literal_expr() else {
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
|
||||||
..
|
|
||||||
}) = mode
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
@ -146,18 +146,11 @@ pub(crate) fn bad_str_strip_call(checker: &mut Checker, func: &Expr, args: &[Exp
|
||||||
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func {
|
if let Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = func {
|
||||||
if matches!(
|
if matches!(
|
||||||
value.as_ref(),
|
value.as_ref(),
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(_) | Expr::BytesLiteral(_)
|
||||||
value: Constant::Str(_) | Constant::Bytes(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
) {
|
||||||
if let Some(strip) = StripKind::from_str(attr.as_str()) {
|
if let Some(strip) = StripKind::from_str(attr.as_str()) {
|
||||||
if let Some(arg) = args.get(0) {
|
if let Some(arg) = args.get(0) {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &arg {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
}) = &arg
|
|
||||||
{
|
|
||||||
if has_duplicates(value) {
|
if has_duplicates(value) {
|
||||||
let removal = if checker.settings.target_version >= PythonVersion::Py39
|
let removal = if checker.settings.target_version >= PythonVersion::Py39
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_literal::cformat::{CFormatPart, CFormatSpec, CFormatStrOrBytes, CFormatString};
|
use ruff_python_literal::cformat::{CFormatPart, CFormatSpec, CFormatStrOrBytes, CFormatString};
|
||||||
use ruff_python_parser::{lexer, AsMode};
|
use ruff_python_parser::{lexer, AsMode};
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
@ -186,12 +186,8 @@ fn is_valid_dict(
|
||||||
let Some(key) = key else {
|
let Some(key) = key else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value:
|
value: mapping_key, ..
|
||||||
Constant::Str(ast::StringConstant {
|
|
||||||
value: mapping_key, ..
|
|
||||||
}),
|
|
||||||
..
|
|
||||||
}) = key
|
}) = key
|
||||||
{
|
{
|
||||||
let Some(format) = formats_hash.get(mapping_key.as_str()) else {
|
let Some(format) = formats_hash.get(mapping_key.as_str()) else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, Expr};
|
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -83,43 +83,39 @@ pub(crate) fn compare_to_empty_string(
|
||||||
if let Ok(op) = EmptyStringCmpOp::try_from(op) {
|
if let Ok(op) = EmptyStringCmpOp::try_from(op) {
|
||||||
if std::mem::take(&mut first) {
|
if std::mem::take(&mut first) {
|
||||||
// Check the left-most expression.
|
// Check the left-most expression.
|
||||||
if let Expr::Constant(ast::ExprConstant { value, .. }) = &lhs {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &lhs {
|
||||||
if let Constant::Str(s) = value {
|
if value.is_empty() {
|
||||||
if s.is_empty() {
|
let literal = checker.generator().expr(lhs);
|
||||||
let constant = checker.generator().constant(value);
|
let expr = checker.generator().expr(rhs);
|
||||||
let expr = checker.generator().expr(rhs);
|
let existing = format!("{literal} {op} {expr}");
|
||||||
let existing = format!("{constant} {op} {expr}");
|
|
||||||
let replacement = format!("{}{expr}", op.into_unary());
|
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
|
||||||
CompareToEmptyString {
|
|
||||||
existing,
|
|
||||||
replacement,
|
|
||||||
},
|
|
||||||
lhs.range(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check all right-hand expressions.
|
|
||||||
if let Expr::Constant(ast::ExprConstant { value, .. }) = &rhs {
|
|
||||||
if let Constant::Str(s) = value {
|
|
||||||
if s.is_empty() {
|
|
||||||
let expr = checker.generator().expr(lhs);
|
|
||||||
let constant = checker.generator().constant(value);
|
|
||||||
let existing = format!("{expr} {op} {constant}");
|
|
||||||
let replacement = format!("{}{expr}", op.into_unary());
|
let replacement = format!("{}{expr}", op.into_unary());
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
CompareToEmptyString {
|
CompareToEmptyString {
|
||||||
existing,
|
existing,
|
||||||
replacement,
|
replacement,
|
||||||
},
|
},
|
||||||
rhs.range(),
|
lhs.range(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check all right-hand expressions.
|
||||||
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &rhs {
|
||||||
|
if value.is_empty() {
|
||||||
|
let expr = checker.generator().expr(lhs);
|
||||||
|
let literal = checker.generator().expr(rhs);
|
||||||
|
let existing = format!("{expr} {op} {literal}");
|
||||||
|
let replacement = format!("{}{expr}", op.into_unary());
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
CompareToEmptyString {
|
||||||
|
existing,
|
||||||
|
replacement,
|
||||||
|
},
|
||||||
|
rhs.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Expr};
|
use ruff_python_ast::{CmpOp, Expr};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -63,22 +63,12 @@ pub(crate) fn comparison_of_constant(
|
||||||
.tuple_windows()
|
.tuple_windows()
|
||||||
.zip(ops)
|
.zip(ops)
|
||||||
{
|
{
|
||||||
if let (
|
if left.is_literal_expr() && right.is_literal_expr() {
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: left_constant,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: right_constant,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) = (&left, &right)
|
|
||||||
{
|
|
||||||
let diagnostic = Diagnostic::new(
|
let diagnostic = Diagnostic::new(
|
||||||
ComparisonOfConstant {
|
ComparisonOfConstant {
|
||||||
left_constant: checker.generator().constant(left_constant),
|
left_constant: checker.generator().expr(left),
|
||||||
op: *op,
|
op: *op,
|
||||||
right_constant: checker.generator().constant(right_constant),
|
right_constant: checker.generator().expr(right),
|
||||||
},
|
},
|
||||||
left.range(),
|
left.range(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Operator};
|
use ruff_python_ast::{self as ast, Expr, Operator};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -73,10 +73,7 @@ fn is_valid_default(expr: &Expr) -> bool {
|
||||||
// Otherwise, the default must be a string or `None`.
|
// Otherwise, the default must be a string or `None`.
|
||||||
matches!(
|
matches!(
|
||||||
expr,
|
expr,
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(_) | Expr::NoneLiteral(_) | Expr::FString(_)
|
||||||
value: Constant::Str { .. } | Constant::None { .. },
|
|
||||||
..
|
|
||||||
}) | Expr::FString(_)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast};
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_semantic::analyze::type_inference::{PythonType, ResolvedPythonType};
|
use ruff_python_semantic::analyze::type_inference::{PythonType, ResolvedPythonType};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_semantic::analyze::logging;
|
use ruff_python_semantic::analyze::logging;
|
||||||
use ruff_python_stdlib::logging::LoggingLevel;
|
use ruff_python_stdlib::logging::LoggingLevel;
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::Ranged;
|
||||||
|
@ -128,10 +128,8 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(Expr::Constant(ast::ExprConstant {
|
let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) =
|
||||||
value: Constant::Str(ast::StringConstant { value, .. }),
|
call.arguments.find_positional(0)
|
||||||
..
|
|
||||||
})) = call.arguments.find_positional(0)
|
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Int, UnaryOp};
|
use ruff_python_ast::{self as ast, Expr, Int, UnaryOp};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -57,42 +57,39 @@ impl Violation for MagicValueComparison {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If an [`Expr`] is a constant (or unary operation on a constant), return the [`Constant`].
|
/// If an [`Expr`] is a literal (or unary operation on a literal), return the literal [`Expr`].
|
||||||
fn as_constant(expr: &Expr) -> Option<&Constant> {
|
fn as_literal(expr: &Expr) -> Option<&Expr> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => Some(value),
|
|
||||||
Expr::UnaryOp(ast::ExprUnaryOp {
|
Expr::UnaryOp(ast::ExprUnaryOp {
|
||||||
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
|
||||||
operand,
|
operand,
|
||||||
range: _,
|
..
|
||||||
}) => match operand.as_ref() {
|
}) if operand.is_literal_expr() => Some(operand.as_ref()),
|
||||||
Expr::Constant(ast::ExprConstant { value, .. }) => Some(value),
|
expr if expr.is_literal_expr() => Some(expr),
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if a [`Constant`] is a magic value.
|
fn is_magic_value(expr: &Expr, allowed_types: &[ConstantType]) -> bool {
|
||||||
fn is_magic_value(constant: &Constant, allowed_types: &[ConstantType]) -> bool {
|
if let Some(constant_type) = ConstantType::try_from_expr(expr) {
|
||||||
if let Ok(constant_type) = ConstantType::try_from(constant) {
|
|
||||||
if allowed_types.contains(&constant_type) {
|
if allowed_types.contains(&constant_type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match constant {
|
|
||||||
|
match expr {
|
||||||
// Ignore `None`, `Bool`, and `Ellipsis` constants.
|
// Ignore `None`, `Bool`, and `Ellipsis` constants.
|
||||||
Constant::None => false,
|
Expr::NoneLiteral(_) | Expr::BooleanLiteral(_) | Expr::EllipsisLiteral(_) => false,
|
||||||
Constant::Bool(_) => false,
|
// Special-case some common string and integer types.
|
||||||
Constant::Ellipsis => false,
|
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
|
||||||
// Otherwise, special-case some common string and integer types.
|
|
||||||
Constant::Str(ast::StringConstant { value, .. }) => {
|
|
||||||
!matches!(value.as_str(), "" | "__main__")
|
!matches!(value.as_str(), "" | "__main__")
|
||||||
}
|
}
|
||||||
Constant::Int(value) => !matches!(*value, Int::ZERO | Int::ONE),
|
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
|
||||||
Constant::Bytes(_) => true,
|
ast::Number::Int(value) => !matches!(*value, Int::ZERO | Int::ONE),
|
||||||
Constant::Float(_) => true,
|
_ => true,
|
||||||
Constant::Complex { .. } => true,
|
},
|
||||||
|
Expr::BytesLiteral(_) => true,
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,15 +99,15 @@ pub(crate) fn magic_value_comparison(checker: &mut Checker, left: &Expr, compara
|
||||||
.chain(comparators.iter())
|
.chain(comparators.iter())
|
||||||
.tuple_windows()
|
.tuple_windows()
|
||||||
{
|
{
|
||||||
// If both of the comparators are constant, skip rule for the whole expression.
|
// If both of the comparators are literals, skip rule for the whole expression.
|
||||||
// R0133: comparison-of-constants
|
// R0133: comparison-of-constants
|
||||||
if as_constant(left).is_some() && as_constant(right).is_some() {
|
if as_literal(left).is_some() && as_literal(right).is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for comparison_expr in std::iter::once(left).chain(comparators.iter()) {
|
for comparison_expr in std::iter::once(left).chain(comparators.iter()) {
|
||||||
if let Some(value) = as_constant(comparison_expr) {
|
if let Some(value) = as_literal(comparison_expr) {
|
||||||
if is_magic_value(value, &checker.settings.pylint.allow_magic_value_types) {
|
if is_magic_value(value, &checker.settings.pylint.allow_magic_value_types) {
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
MagicValueComparison {
|
MagicValueComparison {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt, StmtClassDef};
|
use ruff_python_ast::{self as ast, Expr, Stmt, StmtClassDef};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -65,13 +65,7 @@ pub(crate) fn single_string_slots(checker: &mut Checker, class: &StmtClassDef) {
|
||||||
for target in targets {
|
for target in targets {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = target {
|
if let Expr::Name(ast::ExprName { id, .. }) = target {
|
||||||
if id.as_str() == "__slots__" {
|
if id.as_str() == "__slots__" {
|
||||||
if matches!(
|
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
}) | Expr::FString(_)
|
|
||||||
) {
|
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(SingleStringSlots, stmt.identifier()));
|
.push(Diagnostic::new(SingleStringSlots, stmt.identifier()));
|
||||||
|
@ -87,13 +81,7 @@ pub(crate) fn single_string_slots(checker: &mut Checker, class: &StmtClassDef) {
|
||||||
}) => {
|
}) => {
|
||||||
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
|
if let Expr::Name(ast::ExprName { id, .. }) = target.as_ref() {
|
||||||
if id.as_str() == "__slots__" {
|
if id.as_str() == "__slots__" {
|
||||||
if matches!(
|
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
}) | Expr::FString(_)
|
|
||||||
) {
|
|
||||||
checker
|
checker
|
||||||
.diagnostics
|
.diagnostics
|
||||||
.push(Diagnostic::new(SingleStringSlots, stmt.identifier()));
|
.push(Diagnostic::new(SingleStringSlots, stmt.identifier()));
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub(crate) fn unspecified_encoding(checker: &mut Checker, call: &ast::ExprCall)
|
||||||
|
|
||||||
/// Returns `true` if the given expression is a string literal containing a `b` character.
|
/// Returns `true` if the given expression is a string literal containing a `b` character.
|
||||||
fn is_binary_mode(expr: &ast::Expr) -> Option<bool> {
|
fn is_binary_mode(expr: &ast::Expr) -> Option<bool> {
|
||||||
Some(expr.as_constant_expr()?.value.as_str()?.value.contains('b'))
|
Some(expr.as_string_literal_expr()?.value.contains('b'))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the given call lacks an explicit `encoding`.
|
/// Returns `true` if the given call lacks an explicit `encoding`.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
@ -72,13 +72,7 @@ pub(crate) fn useless_return(
|
||||||
// Skip functions that consist of a docstring and a return statement.
|
// Skip functions that consist of a docstring and a return statement.
|
||||||
if body.len() == 2 {
|
if body.len() == 2 {
|
||||||
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body[0] {
|
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body[0] {
|
||||||
if matches!(
|
if value.is_string_literal_expr() {
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(_),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
//! Settings for the `pylint` plugin.
|
//! Settings for the `pylint` plugin.
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use ruff_macros::CacheKey;
|
use ruff_macros::CacheKey;
|
||||||
use ruff_python_ast::Constant;
|
use ruff_python_ast::{Expr, ExprNumberLiteral, Number};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
|
||||||
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
|
||||||
|
@ -17,19 +16,17 @@ pub enum ConstantType {
|
||||||
Str,
|
Str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Constant> for ConstantType {
|
impl ConstantType {
|
||||||
type Error = anyhow::Error;
|
pub fn try_from_expr(expr: &Expr) -> Option<Self> {
|
||||||
|
match expr {
|
||||||
fn try_from(value: &Constant) -> Result<Self, Self::Error> {
|
Expr::StringLiteral(_) => Some(Self::Str),
|
||||||
match value {
|
Expr::BytesLiteral(_) => Some(Self::Bytes),
|
||||||
Constant::Bytes(..) => Ok(Self::Bytes),
|
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => match value {
|
||||||
Constant::Complex { .. } => Ok(Self::Complex),
|
Number::Int(_) => Some(Self::Int),
|
||||||
Constant::Float(..) => Ok(Self::Float),
|
Number::Float(_) => Some(Self::Float),
|
||||||
Constant::Int(..) => Ok(Self::Int),
|
Number::Complex { .. } => Some(Self::Complex),
|
||||||
Constant::Str(..) => Ok(Self::Str),
|
},
|
||||||
Constant::Bool(..) | Constant::Ellipsis | Constant::None => {
|
_ => None,
|
||||||
Err(anyhow!("Singleton constants are unsupported"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ use log::debug;
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::is_dunder;
|
use ruff_python_ast::helpers::is_dunder;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Keyword, Stmt};
|
||||||
self as ast, Arguments, Constant, Expr, ExprContext, Identifier, Keyword, Stmt,
|
|
||||||
};
|
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_stdlib::identifiers::is_identifier;
|
use ruff_python_stdlib::identifiers::is_identifier;
|
||||||
|
@ -183,10 +181,7 @@ fn create_fields_from_fields_arg(fields: &Expr) -> Option<Vec<Stmt>> {
|
||||||
let [field, annotation] = elts.as_slice() else {
|
let [field, annotation] = elts.as_slice() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let field = field.as_constant_expr()?;
|
let ast::ExprStringLiteral { value: field, .. } = field.as_string_literal_expr()?;
|
||||||
let Constant::Str(ast::StringConstant { value: field, .. }) = &field.value else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
if !is_identifier(field) {
|
if !is_identifier(field) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::is_dunder;
|
use ruff_python_ast::helpers::is_dunder;
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Keyword, Stmt};
|
||||||
self as ast, Arguments, Constant, Expr, ExprContext, Identifier, Keyword, Stmt,
|
|
||||||
};
|
|
||||||
use ruff_python_codegen::Generator;
|
use ruff_python_codegen::Generator;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_python_stdlib::identifiers::is_identifier;
|
use ruff_python_stdlib::identifiers::is_identifier;
|
||||||
|
@ -175,10 +173,7 @@ fn fields_from_dict_literal(keys: &[Option<Expr>], values: &[Expr]) -> Option<Ve
|
||||||
keys.iter()
|
keys.iter()
|
||||||
.zip(values.iter())
|
.zip(values.iter())
|
||||||
.map(|(key, value)| match key {
|
.map(|(key, value)| match key {
|
||||||
Some(Expr::Constant(ast::ExprConstant {
|
Some(Expr::StringLiteral(ast::ExprStringLiteral { value: field, .. })) => {
|
||||||
value: Constant::Str(ast::StringConstant { value: field, .. }),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
if !is_identifier(field) {
|
if !is_identifier(field) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, Keyword};
|
use ruff_python_ast::{self as ast, Expr, Keyword};
|
||||||
use ruff_python_literal::format::{
|
use ruff_python_literal::format::{
|
||||||
FieldName, FieldNamePart, FieldType, FormatPart, FormatString, FromTemplate,
|
FieldName, FieldNamePart, FieldType, FormatPart, FormatString, FromTemplate,
|
||||||
};
|
};
|
||||||
|
@ -159,8 +159,8 @@ fn formatted_expr<'a>(expr: &Expr, context: FormatContext, locator: &Locator<'a>
|
||||||
// E.g., `12` should be parenthesized in `f"{(12).real}"`.
|
// E.g., `12` should be parenthesized in `f"{(12).real}"`.
|
||||||
(
|
(
|
||||||
FormatContext::Accessed,
|
FormatContext::Accessed,
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(..),
|
value: ast::Number::Int(..),
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
) => text.chars().all(|c| c.is_ascii_digit()),
|
) => text.chars().all(|c| c.is_ascii_digit()),
|
||||||
|
@ -314,13 +314,7 @@ pub(crate) fn f_strings(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !matches!(
|
if !value.is_string_literal_expr() {
|
||||||
value.as_ref(),
|
|
||||||
Expr::Constant(ast::ExprConstant {
|
|
||||||
value: Constant::Str(..),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_text_size::Ranged;
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -32,35 +32,54 @@ impl FromStr for LiteralType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LiteralType> for Constant {
|
impl LiteralType {
|
||||||
fn from(value: LiteralType) -> Self {
|
fn as_zero_value_expr(self) -> Expr {
|
||||||
match value {
|
match self {
|
||||||
LiteralType::Str => Constant::Str(ast::StringConstant {
|
LiteralType::Str => ast::ExprStringLiteral {
|
||||||
value: String::new(),
|
value: String::new(),
|
||||||
unicode: false,
|
unicode: false,
|
||||||
implicit_concatenated: false,
|
implicit_concatenated: false,
|
||||||
}),
|
range: TextRange::default(),
|
||||||
LiteralType::Bytes => Constant::Bytes(ast::BytesConstant {
|
}
|
||||||
|
.into(),
|
||||||
|
LiteralType::Bytes => ast::ExprBytesLiteral {
|
||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
implicit_concatenated: false,
|
implicit_concatenated: false,
|
||||||
}),
|
range: TextRange::default(),
|
||||||
LiteralType::Int => Constant::Int(0.into()),
|
}
|
||||||
LiteralType::Float => Constant::Float(0.0),
|
.into(),
|
||||||
LiteralType::Bool => Constant::Bool(false),
|
LiteralType::Int => ast::ExprNumberLiteral {
|
||||||
|
value: ast::Number::Int(0.into()),
|
||||||
|
range: TextRange::default(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
LiteralType::Float => ast::ExprNumberLiteral {
|
||||||
|
value: ast::Number::Float(0.0),
|
||||||
|
range: TextRange::default(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
LiteralType::Bool => ast::ExprBooleanLiteral {
|
||||||
|
value: false,
|
||||||
|
range: TextRange::default(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&Constant> for LiteralType {
|
impl TryFrom<&Expr> for LiteralType {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: &Constant) -> Result<Self, Self::Error> {
|
fn try_from(expr: &Expr) -> Result<Self, Self::Error> {
|
||||||
match value {
|
match expr {
|
||||||
Constant::Str(_) => Ok(LiteralType::Str),
|
Expr::StringLiteral(_) => Ok(LiteralType::Str),
|
||||||
Constant::Bytes(_) => Ok(LiteralType::Bytes),
|
Expr::BytesLiteral(_) => Ok(LiteralType::Bytes),
|
||||||
Constant::Int(_) => Ok(LiteralType::Int),
|
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
|
||||||
Constant::Float(_) => Ok(LiteralType::Float),
|
ast::Number::Int(_) => Ok(LiteralType::Int),
|
||||||
Constant::Bool(_) => Ok(LiteralType::Bool),
|
ast::Number::Float(_) => Ok(LiteralType::Float),
|
||||||
|
ast::Number::Complex { .. } => Err(()),
|
||||||
|
},
|
||||||
|
Expr::BooleanLiteral(_) => Ok(LiteralType::Bool),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,8 +200,8 @@ pub(crate) fn native_literals(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let constant = Constant::from(literal_type);
|
let expr = literal_type.as_zero_value_expr();
|
||||||
let content = checker.generator().constant(&constant);
|
let content = checker.generator().expr(&expr);
|
||||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
|
||||||
content,
|
content,
|
||||||
call.range(),
|
call.range(),
|
||||||
|
@ -190,16 +209,12 @@ pub(crate) fn native_literals(
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
}
|
}
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = arg else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip implicit string concatenations.
|
// Skip implicit string concatenations.
|
||||||
if value.is_implicit_concatenated() {
|
if arg.is_implicit_concatenated_string() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(arg_literal_type) = LiteralType::try_from(value) else {
|
let Ok(arg_literal_type) = LiteralType::try_from(arg) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,8 +228,14 @@ pub(crate) fn native_literals(
|
||||||
// Ex) `(7).denominator` is valid but `7.denominator` is not
|
// Ex) `(7).denominator` is valid but `7.denominator` is not
|
||||||
// Note that floats do not have this problem
|
// Note that floats do not have this problem
|
||||||
// Ex) `(1.0).real` is valid and `1.0.real` is too
|
// Ex) `(1.0).real` is valid and `1.0.real` is too
|
||||||
let content = match (parent_expr, value) {
|
let content = match (parent_expr, arg) {
|
||||||
(Some(Expr::Attribute(_)), Constant::Int(_)) => format!("({arg_code})"),
|
(
|
||||||
|
Some(Expr::Attribute(_)),
|
||||||
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
|
value: ast::Number::Int(_),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
) => format!("({arg_code})"),
|
||||||
_ => arg_code.to_string(),
|
_ => arg_code.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::map_subscript;
|
use ruff_python_ast::helpers::map_subscript;
|
||||||
use ruff_python_ast::stmt_if::{if_elif_branches, BranchKind, IfElifBranch};
|
use ruff_python_ast::stmt_if::{if_elif_branches, BranchKind, IfElifBranch};
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Constant, ElifElseClause, Expr, Int, StmtIf};
|
use ruff_python_ast::{self as ast, CmpOp, ElifElseClause, Expr, Int, StmtIf};
|
||||||
use ruff_text_size::{Ranged, TextLen, TextRange};
|
use ruff_text_size::{Ranged, TextLen, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -147,8 +147,8 @@ pub(crate) fn outdated_version_block(checker: &mut Checker, stmt_if: &StmtIf) {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(int),
|
value: ast::Number::Int(int),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if op == &CmpOp::Eq {
|
if op == &CmpOp::Eq {
|
||||||
|
@ -409,8 +409,8 @@ fn fix_always_true_branch(
|
||||||
fn extract_version(elts: &[Expr]) -> Option<Vec<Int>> {
|
fn extract_version(elts: &[Expr]) -> Option<Vec<Int>> {
|
||||||
let mut version: Vec<Int> = vec![];
|
let mut version: Vec<Int> = vec![];
|
||||||
for elt in elts {
|
for elt in elts {
|
||||||
let Expr::Constant(ast::ExprConstant {
|
let Expr::NumberLiteral(ast::ExprNumberLiteral {
|
||||||
value: Constant::Int(int),
|
value: ast::Number::Int(int),
|
||||||
..
|
..
|
||||||
}) = &elt
|
}) = &elt
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
use ruff_python_ast::str::{leading_quote, trailing_quote};
|
||||||
use ruff_python_ast::whitespace::indentation;
|
use ruff_python_ast::whitespace::indentation;
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_literal::cformat::{
|
use ruff_python_literal::cformat::{
|
||||||
CConversionFlags, CFormatPart, CFormatPrecision, CFormatQuantity, CFormatString,
|
CConversionFlags, CFormatPart, CFormatPrecision, CFormatQuantity, CFormatString,
|
||||||
|
@ -223,12 +223,8 @@ fn clean_params_dictionary(right: &Expr, locator: &Locator, stylist: &Stylist) -
|
||||||
for (key, value) in keys.iter().zip(values.iter()) {
|
for (key, value) in keys.iter().zip(values.iter()) {
|
||||||
match key {
|
match key {
|
||||||
Some(key) => {
|
Some(key) => {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value:
|
value: key_string, ..
|
||||||
Constant::Str(ast::StringConstant {
|
|
||||||
value: key_string, ..
|
|
||||||
}),
|
|
||||||
..
|
|
||||||
}) = key
|
}) = key
|
||||||
{
|
{
|
||||||
// If the dictionary key is not a valid variable name, abort.
|
// If the dictionary key is not a valid variable name, abort.
|
||||||
|
@ -420,9 +416,13 @@ pub(crate) fn printf_string_formatting(checker: &mut Checker, expr: &Expr, right
|
||||||
|
|
||||||
// Parse the parameters.
|
// Parse the parameters.
|
||||||
let params_string = match right {
|
let params_string = match right {
|
||||||
Expr::Constant(_) | Expr::FString(_) => {
|
Expr::StringLiteral(_)
|
||||||
Cow::Owned(format!("({})", checker.locator().slice(right)))
|
| Expr::BytesLiteral(_)
|
||||||
}
|
| Expr::NumberLiteral(_)
|
||||||
|
| Expr::BooleanLiteral(_)
|
||||||
|
| Expr::NoneLiteral(_)
|
||||||
|
| Expr::EllipsisLiteral(_)
|
||||||
|
| Expr::FString(_) => Cow::Owned(format!("({})", checker.locator().slice(right))),
|
||||||
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_) => {
|
Expr::Name(_) | Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_) => {
|
||||||
if num_keyword_arguments > 0 {
|
if num_keyword_arguments > 0 {
|
||||||
// If we have _any_ named fields, assume the right-hand side is a mapping.
|
// If we have _any_ named fields, assume the right-hand side is a mapping.
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Constant, Expr, PySourceType};
|
use ruff_python_ast::{self as ast, Expr, PySourceType};
|
||||||
use ruff_python_parser::{lexer, AsMode};
|
use ruff_python_parser::{lexer, AsMode};
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_source_file::Locator;
|
use ruff_source_file::Locator;
|
||||||
|
@ -71,12 +71,8 @@ pub(crate) fn redundant_open_modes(checker: &mut Checker, call: &ast::ExprCall)
|
||||||
None => {
|
None => {
|
||||||
if !call.arguments.is_empty() {
|
if !call.arguments.is_empty() {
|
||||||
if let Some(keyword) = call.arguments.find_keyword(MODE_KEYWORD_ARGUMENT) {
|
if let Some(keyword) = call.arguments.find_keyword(MODE_KEYWORD_ARGUMENT) {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral {
|
||||||
value:
|
value: mode_param_value,
|
||||||
Constant::Str(ast::StringConstant {
|
|
||||||
value: mode_param_value,
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
..
|
..
|
||||||
}) = &keyword.value
|
}) = &keyword.value
|
||||||
{
|
{
|
||||||
|
@ -94,11 +90,7 @@ pub(crate) fn redundant_open_modes(checker: &mut Checker, call: &ast::ExprCall)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(mode_param) => {
|
Some(mode_param) => {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &mode_param {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
}) = &mode_param
|
|
||||||
{
|
|
||||||
if let Ok(mode) = OpenMode::from_str(value) {
|
if let Ok(mode) = OpenMode::from_str(value) {
|
||||||
checker.diagnostics.push(create_check(
|
checker.diagnostics.push(create_check(
|
||||||
call,
|
call,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::Expr;
|
||||||
|
|
||||||
use crate::fix::edits::pad;
|
use crate::fix::edits::pad;
|
||||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||||
|
@ -65,10 +65,7 @@ pub(crate) fn type_of_primitive(checker: &mut Checker, expr: &Expr, func: &Expr,
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Expr::Constant(ast::ExprConstant { value, .. }) = &arg else {
|
let Some(primitive) = Primitive::from_expr(arg) else {
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(primitive) = Primitive::from_constant(value) else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, expr.range());
|
let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, expr.range());
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::Expr;
|
use ruff_python_ast::ExprStringLiteral;
|
||||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -39,11 +39,11 @@ impl AlwaysFixableViolation for UnicodeKindPrefix {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UP025
|
/// UP025
|
||||||
pub(crate) fn unicode_kind_prefix(checker: &mut Checker, expr: &Expr, is_unicode: bool) {
|
pub(crate) fn unicode_kind_prefix(checker: &mut Checker, string: &ExprStringLiteral) {
|
||||||
if is_unicode {
|
if string.unicode {
|
||||||
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, expr.range());
|
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, string.range);
|
||||||
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
|
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
|
||||||
expr.start(),
|
string.start(),
|
||||||
TextSize::from(1),
|
TextSize::from(1),
|
||||||
))));
|
))));
|
||||||
checker.diagnostics.push(diagnostic);
|
checker.diagnostics.push(diagnostic);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::{self as ast, Arguments, Constant, Expr, Keyword, PySourceType};
|
use ruff_python_ast::{self as ast, Arguments, Expr, Keyword, PySourceType};
|
||||||
use ruff_python_parser::{lexer, AsMode, Tok};
|
use ruff_python_parser::{lexer, AsMode, Tok};
|
||||||
use ruff_source_file::Locator;
|
use ruff_source_file::Locator;
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
@ -73,11 +73,7 @@ fn match_encoded_variable(func: &Expr) -> Option<&Expr> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_utf8_encoding_arg(arg: &Expr) -> bool {
|
fn is_utf8_encoding_arg(arg: &Expr) -> bool {
|
||||||
if let Expr::Constant(ast::ExprConstant {
|
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &arg {
|
||||||
value: Constant::Str(value),
|
|
||||||
..
|
|
||||||
}) = &arg
|
|
||||||
{
|
|
||||||
UTF8_LITERALS.contains(&value.to_lowercase().as_str())
|
UTF8_LITERALS.contains(&value.to_lowercase().as_str())
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -162,10 +158,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &mut Checker, call: &ast::ExprCal
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match variable {
|
match variable {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::StringLiteral(ast::ExprStringLiteral { value: literal, .. }) => {
|
||||||
value: Constant::Str(literal),
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
// Ex) `"str".encode()`, `"str".encode("utf-8")`
|
// Ex) `"str".encode()`, `"str".encode("utf-8")`
|
||||||
if let Some(encoding_arg) = match_encoding_arg(&call.arguments) {
|
if let Some(encoding_arg) = match_encoding_arg(&call.arguments) {
|
||||||
if literal.is_ascii() {
|
if literal.is_ascii() {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue