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:
Dhruv Manilawala 2023-10-30 12:13:23 +05:30 committed by GitHub
parent 78bbf6d403
commit 230c9ce236
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
268 changed files with 6663 additions and 6741 deletions

View file

@ -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!(

View file

@ -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) {

View file

@ -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()

View file

@ -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());
} }

View file

@ -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()),

View file

@ -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`].

View file

@ -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 {

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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,
} }
} }

View file

@ -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)),

View file

@ -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
} }

View file

@ -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"

View file

@ -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,
)); ));
} }

View file

@ -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" {

View file

@ -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.

View file

@ -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),
.. ..
}) })
) { ) {

View file

@ -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;

View file

@ -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 {

View file

@ -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(_),
..
})
)
}

View file

@ -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(&parameters.args) } in parameters.posonlyargs.iter().chain(&parameters.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(),

View file

@ -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()));

View file

@ -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") {

View file

@ -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,
}) })
} }

View file

@ -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(),
)); ));

View file

@ -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) {

View file

@ -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()));

View file

@ -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) {

View file

@ -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;
}; };

View file

@ -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__" {

View file

@ -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;
} }

View file

@ -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 {

View file

@ -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;

View file

@ -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()));
} }

View file

@ -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) =

View file

@ -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()));

View file

@ -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()));

View file

@ -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()),

View file

@ -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);
} }

View file

@ -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 {

View file

@ -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()));

View file

@ -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());

View file

@ -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;
}
} }
} }

View file

@ -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)

View file

@ -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")
} }

View file

@ -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 {

View file

@ -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) {

View file

@ -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(_),
.. ..
}) })
) )

View file

@ -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)
} }

View file

@ -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, .. }) => {

View file

@ -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);

View file

@ -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()
)
})
}) })
} }

View file

@ -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 {

View file

@ -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;

View file

@ -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;
} }

View file

@ -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())

View file

@ -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;

View file

@ -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,
} }
} }

View file

@ -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(),
} }
} }

View file

@ -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 {

View file

@ -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;
}; };

View file

@ -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;
} }

View file

@ -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,
} }

View file

@ -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());

View file

@ -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(_)

View file

@ -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: _,
}) })
) { ) {

View file

@ -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)

View file

@ -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,
}; };

View file

@ -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 =

View file

@ -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())
}

View file

@ -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(

View file

@ -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);
} }

View file

@ -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 {

View file

@ -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);
} }
_ => { _ => {

View file

@ -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

View file

@ -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

View file

@ -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;
}; };

View file

@ -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
{ {

View file

@ -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 {

View file

@ -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(),
));
}
}
} }
} }
} }

View file

@ -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(),
); );

View file

@ -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(_)
) )
} }

View file

@ -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;

View file

@ -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;
}; };

View file

@ -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 {

View file

@ -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()));

View file

@ -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`.

View file

@ -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;
} }
} }

View file

@ -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"))
}
} }
} }
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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;
}; };

View file

@ -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(),
}; };

View file

@ -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 {

View file

@ -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.

View file

@ -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,

View file

@ -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());

View file

@ -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);

View file

@ -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