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(),
));
if pydocstyle::helpers::should_ignore_docstring(expr) {
if expr.implicit_concatenated {
#[allow(deprecated)]
let location = checker.locator.compute_source_location(expr.start());
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_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() {
let attr = attr.as_str();
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(val),
..
}) = value.as_ref()
if let Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) =
value.as_ref()
{
if attr == "join" {
// "...".join(...) call
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" {
// "...".format(...) call
let location = expr.range();
match pyflakes::format::FormatSummary::try_from(val.as_ref()) {
match pyflakes::format::FormatSummary::try_from(string.as_ref()) {
Err(e) => {
if checker.enabled(Rule::StringDotFormatInvalidFormat) {
checker.diagnostics.push(Diagnostic::new(
@ -421,7 +419,7 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::BadStringFormatCharacter) {
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,
range: _,
}) => {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = left.as_ref()
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = left.as_ref() {
if checker.any_enabled(&[
Rule::PercentFormatInvalidFormat,
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);
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Int(_) | Constant::Float(_) | Constant::Complex { .. },
range: _,
}) => {
Expr::NumberLiteral(_) => {
if checker.source_type.is_stub() && checker.enabled(Rule::NumericLiteralTooLong) {
flake8_pyi::rules::numeric_literal_too_long(checker, expr);
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Bytes(_),
range: _,
}) => {
Expr::BytesLiteral(_) => {
if checker.source_type.is_stub() && checker.enabled(Rule::StringOrBytesTooLong) {
flake8_pyi::rules::string_or_bytes_too_long(checker, expr);
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
range: _,
}) => {
Expr::StringLiteral(string) => {
if checker.enabled(Rule::HardcodedBindAllInterfaces) {
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);
}
}
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) {
pyupgrade::rules::unicode_kind_prefix(checker, expr, value.unicode);
pyupgrade::rules::unicode_kind_prefix(checker, string);
}
if checker.source_type.is_stub() {
if checker.enabled(Rule::StringOrBytesTooLong) {

View file

@ -31,9 +31,8 @@ use std::path::Path;
use itertools::Itertools;
use log::debug;
use ruff_python_ast::{
self as ast, Arguments, Comprehension, Constant, ElifElseClause, ExceptHandler, Expr,
ExprContext, Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt,
Suite, UnaryOp,
self as ast, Arguments, Comprehension, ElifElseClause, ExceptHandler, Expr, ExprContext,
Keyword, MatchCase, Parameter, ParameterWithDefault, Parameters, Pattern, Stmt, Suite, UnaryOp,
};
use ruff_text_size::{Ranged, TextRange, TextSize};
@ -787,11 +786,7 @@ where
&& self.semantic.in_type_definition()
&& self.semantic.future_annotations()
{
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
}) = expr
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
self.deferred.string_type_definitions.push((
expr.range(),
value,
@ -1186,10 +1181,7 @@ where
}
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
range: _,
}) => {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
if self.semantic.in_type_definition()
&& !self.semantic.in_literal()
&& !self.semantic.in_f_string()

View file

@ -3,7 +3,7 @@
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::Tok;
use ruff_text_size::{Ranged, TextSize};
@ -69,15 +69,15 @@ struct StringLinesVisitor<'a> {
impl StatementVisitor<'_> for StringLinesVisitor<'_> {
fn visit_stmt(&mut self, stmt: &Stmt) {
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(..),
..
}) = value.as_ref()
{
if let Stmt::Expr(ast::StmtExpr {
value: expr,
range: _,
}) = stmt
{
if expr.is_string_literal_expr() {
for line in UniversalNewlineIterator::with_offset(
self.locator.slice(value.as_ref()),
value.start(),
self.locator.slice(expr.as_ref()),
expr.start(),
) {
self.string_lines.push(line.start());
}

View file

@ -1,30 +1,23 @@
//! 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};
/// 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()?;
// Require the docstring to be a standalone expression.
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
return None;
};
// Only match strings.
if !matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
) {
return None;
}
Some(value)
value.as_string_literal_expr()
}
/// 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 {
Definition::Module(module) => docstring_from(module.python_ast),
Definition::Member(member) => docstring_from(member.body()),

View file

@ -1,7 +1,7 @@
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use ruff_python_ast::Expr;
use ruff_python_ast::ExprStringLiteral;
use ruff_python_semantic::Definition;
use ruff_text_size::{Ranged, TextRange};
@ -14,7 +14,8 @@ pub(crate) mod styles;
#[derive(Debug)]
pub(crate) struct Docstring<'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.
pub(crate) contents: &'a str,
/// 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_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_python_ast::Constant;
use ruff_python_ast::Expr;
use ruff_text_size::Ranged;
@ -79,13 +78,7 @@ pub(crate) fn variable_name_task_id(
let keyword = arguments.find_keyword("task_id")?;
// If the keyword argument is not a string, we can't do anything.
let task_id = match &keyword.value {
Expr::Constant(constant) => match &constant.value {
Constant::Str(ast::StringConstant { value, .. }) => value,
_ => return None,
},
_ => return None,
};
let ast::ExprStringLiteral { value: task_id, .. } = keyword.value.as_string_literal_expr()?;
// If the target name is the same as the task_id, no violation.
if id == task_id {

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 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, .. })
if is_sys(value, "version_info", checker.semantic()) =>
{
if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(i),
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(i),
..
}) = slice.as_ref()
{
if *i == 0 {
if let (
[CmpOp::Eq | CmpOp::NotEq],
[Expr::Constant(ast::ExprConstant {
value: Constant::Int(n),
[Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(n),
..
})],
) = (ops, comparators)
@ -253,8 +253,8 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
} else if *i == 1 {
if let (
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
[Expr::Constant(ast::ExprConstant {
value: Constant::Int(_),
[Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(_),
..
})],
) = (ops, comparators)
@ -274,8 +274,8 @@ pub(crate) fn compare(checker: &mut Checker, left: &Expr, ops: &[CmpOp], compara
{
if let (
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
[Expr::Constant(ast::ExprConstant {
value: Constant::Int(_),
[Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(_),
..
})],
) = (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 let (
[CmpOp::Lt | CmpOp::LtE | CmpOp::Gt | CmpOp::GtE],
[Expr::Constant(ast::ExprConstant {
value: Constant::Str(s),
..
})],
[Expr::StringLiteral(ast::ExprStringLiteral { value, .. })],
) = (ops, comparators)
{
if s.len() == 1 {
if value.len() == 1 {
if checker.enabled(Rule::SysVersionCmpStr10) {
checker
.diagnostics

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 crate::checkers::ast::Checker;
@ -177,8 +177,8 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
step: None,
range: _,
}) => {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(i),
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(i),
..
}) = upper.as_ref()
{
@ -194,8 +194,8 @@ pub(crate) fn subscript(checker: &mut Checker, value: &Expr, slice: &Expr) {
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Int(i),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(i),
..
}) => {
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::identifier::Identifier;
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_semantic::analyze::visibility;
use ruff_python_semantic::Definition;
@ -431,10 +431,7 @@ fn is_none_returning(body: &[Stmt]) -> bool {
visitor.visit_body(body);
for stmt in visitor.returns {
if let Some(value) = stmt.value.as_deref() {
if !matches!(
value,
Expr::Constant(constant) if constant.value.is_none()
) {
if !value.is_none_literal_expr() {
return false;
}
}
@ -451,9 +448,10 @@ fn check_dynamically_typed<F>(
) where
F: FnOnce() -> String,
{
if let Expr::Constant(ast::ExprConstant {
if let Expr::StringLiteral(ast::ExprStringLiteral {
range,
value: Constant::Str(string),
value: string,
..
}) = annotation
{
// Quoted annotations

View file

@ -1,6 +1,6 @@
use once_cell::sync::Lazy;
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;
@ -10,10 +10,7 @@ static PASSWORD_CANDIDATE_REGEX: Lazy<Regex> = Lazy::new(|| {
pub(super) fn string_literal(expr: &Expr) -> Option<&str> {
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) => Some(string),
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => Some(value),
_ => None,
}
}

View file

@ -3,7 +3,7 @@ use anyhow::Result;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
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_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.
fn parse_mask(expr: &Expr, semantic: &SemanticModel) -> Result<Option<u16>> {
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Int(int),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(int),
..
}) => match int.as_u16() {
Some(value) => Ok(Some(value)),

View file

@ -1,7 +1,6 @@
use ruff_text_size::TextRange;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::ExprStringLiteral;
/// ## What it does
/// Checks for hardcoded bindings to all network interfaces (`0.0.0.0`).
@ -35,9 +34,9 @@ impl Violation for HardcodedBindAllInterfaces {
}
/// S104
pub(crate) fn hardcoded_bind_all_interfaces(value: &str, range: TextRange) -> Option<Diagnostic> {
if value == "0.0.0.0" {
Some(Diagnostic::new(HardcodedBindAllInterfaces, range))
pub(crate) fn hardcoded_bind_all_interfaces(string: &ExprStringLiteral) -> Option<Diagnostic> {
if string.value == "0.0.0.0" {
Some(Diagnostic::new(HardcodedBindAllInterfaces, string.range))
} else {
None
}

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 crate::checkers::ast::Checker;
@ -55,10 +55,7 @@ fn password_target(target: &Expr) -> Option<&str> {
Expr::Name(ast::ExprName { id, .. }) => id.as_str(),
// d["password"] = "s3cr3t"
Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) => string,
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value,
_ => return None,
},
// obj.password = "s3cr3t"

View file

@ -2,7 +2,6 @@ use ruff_python_ast::{self as ast, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
@ -52,13 +51,13 @@ impl Violation for HardcodedTempFile {
}
/// 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
.settings
.flake8_bandit
.hardcoded_tmp_directory
.iter()
.any(|prefix| value.starts_with(prefix))
.any(|prefix| string.value.starts_with(prefix))
{
return;
}
@ -77,8 +76,8 @@ pub(crate) fn hardcoded_tmp_directory(checker: &mut Checker, expr: &Expr, value:
checker.diagnostics.push(Diagnostic::new(
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_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 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") {
match &keyword.value {
Expr::Constant(ast::ExprConstant {
value: Constant::Bool(true),
..
}) => (),
Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: true, .. }) => (),
Expr::Call(ast::ExprCall { func, .. }) => {
if let Expr::Name(ast::ExprName { id, .. }) = func.as_ref() {
if id != "select_autoescape" {

View file

@ -3,7 +3,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
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_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
/// definition: string literals are considered okay, but dynamically-computed values are not.
fn shell_call_seems_safe(arg: &Expr) -> bool {
matches!(
arg,
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
)
arg.is_string_literal_expr()
}
/// 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_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 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 matches!(
keyword.value,
Expr::Constant(ast::ExprConstant {
value: Constant::Int(Int::ZERO | Int::ONE),
Expr::NumberLiteral(ast::ExprNumberLiteral {
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"] => {
// 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 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();
if url.starts_with("http://") || url.starts_with("https://") {
return None;

View file

@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
use ruff_diagnostics::{Diagnostic, 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 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)> {
let argument = call.arguments.find_argument(name, position)?;
let Expr::Constant(ast::ExprConstant {
value: Constant::Int(i),
let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(i),
..
}) = argument
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.
pub(super) fn is_allowed_func_call(name: &str) -> bool {
@ -62,14 +62,3 @@ pub(super) fn allow_boolean_trap(func: &Expr) -> bool {
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 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
/// Checks for the use of boolean positional arguments in function definitions,
@ -117,7 +117,10 @@ pub(crate) fn boolean_default_value_positional_argument(
range: _,
} 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(
BooleanDefaultValuePositionalArgument,
parameter.name.range(),

View file

@ -4,7 +4,7 @@ use ruff_python_ast::Expr;
use ruff_text_size::Ranged;
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
/// 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) {
return;
}
for arg in args.iter().filter(|arg| is_boolean(arg)) {
for arg in args.iter().filter(|arg| arg.is_boolean_literal_expr()) {
checker
.diagnostics
.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::Violation;
@ -126,10 +126,7 @@ pub(crate) fn boolean_type_hint_positional_argument(
// check for both bool (python class) and 'bool' (string annotation)
let hint = match annotation.as_ref() {
Expr::Name(name) => &name.id == "bool",
Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) => value == "bool",
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value == "bool",
_ => false,
};
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_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 {
body.iter().all(|stmt| match stmt {
Stmt::Pass(_) => true,
Stmt::Expr(ast::StmtExpr { value, range: _ }) => match value.as_ref() {
Expr::Constant(ast::ExprConstant { value, .. }) => {
matches!(value, Constant::Str(..) | Constant::Ellipsis)
}
_ => false,
},
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
matches!(
value.as_ref(),
Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
)
}
_ => 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 ruff_diagnostics::{Diagnostic, Violation};
@ -42,13 +42,13 @@ impl Violation for DuplicateValue {
pub(crate) fn duplicate_value(checker: &mut Checker, elts: &Vec<Expr>) {
let mut seen_values: FxHashSet<ComparableExpr> = FxHashSet::default();
for elt in elts {
if let Expr::Constant(ast::ExprConstant { value, .. }) = elt {
if elt.is_literal_expr() {
let comparable_value: ComparableExpr = elt.into();
if !seen_values.insert(comparable_value) {
checker.diagnostics.push(Diagnostic::new(
DuplicateValue {
value: checker.generator().constant(value),
value: checker.generator().expr(elt),
},
elt.range(),
));

View file

@ -1,7 +1,7 @@
use crate::fix::edits::pad;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
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_text_size::Ranged;
@ -66,11 +66,7 @@ pub(crate) fn getattr_with_constant(
if obj.is_starred_expr() {
return;
}
let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = arg
else {
let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = arg else {
return;
};
if !is_identifier(value) {

View file

@ -38,7 +38,7 @@ impl Violation for RaiseLiteral {
/// B016
pub(crate) fn raise_literal(checker: &mut Checker, expr: &Expr) {
if expr.is_constant_expr() {
if expr.is_literal_expr() {
checker
.diagnostics
.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_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
@ -80,11 +80,7 @@ pub(crate) fn setattr_with_constant(
if obj.is_starred_expr() {
return;
}
let Expr::Constant(ast::ExprConstant {
value: Constant::Str(name),
..
}) = name
else {
let Expr::StringLiteral(ast::ExprStringLiteral { value: name, .. }) = name else {
return;
};
if !is_identifier(name) {

View file

@ -1,5 +1,5 @@
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_macros::{derive_message_formats, violation};
@ -68,11 +68,7 @@ pub(crate) fn strip_with_multi_characters(
return;
}
let [Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
})] = args
else {
let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] = args else {
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_macros::{derive_message_formats, violation};
@ -67,11 +67,7 @@ pub(crate) fn unreliable_callable_check(
let [obj, attr, ..] = args else {
return;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = attr
else {
let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = attr else {
return;
};
if value != "__call__" {

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
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 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.
if matches!(
value,
Expr::FString(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::Str(..) | Constant::Ellipsis,
..
})
Expr::FString(_) | Expr::StringLiteral(_) | Expr::EllipsisLiteral(_)
) {
return;
}

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 crate::checkers::ast::Checker;
@ -79,8 +79,8 @@ pub(crate) fn unnecessary_subscript_reversal(checker: &mut Checker, call: &ast::
else {
return;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Int(val),
let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(val),
..
}) = operand.as_ref()
else {

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 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?
if let Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(format),
range: _,
})) = call.arguments.args.get(1).as_ref()
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value: format, .. })) =
call.arguments.args.get(1).as_ref()
{
if format.contains("%z") {
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_macros::{derive_message_formats, violation};
@ -80,16 +80,13 @@ pub(crate) fn all_with_model_form(
if id != "fields" {
continue;
}
let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() else {
continue;
};
match value {
Constant::Str(ast::StringConstant { value, .. }) => {
match value.as_ref() {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
if value == "__all__" {
return Some(Diagnostic::new(DjangoAllWithModelForm, element.range()));
}
}
Constant::Bytes(ast::BytesConstant { value, .. }) => {
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
if value == "__all__".as_bytes() {
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_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() {
match first {
// Check for string literals.
Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) => {
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
if checker.enabled(Rule::RawStringInException) {
if string.len() >= checker.settings.flake8_errmsg.max_string_length {
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, .. }) =
func.as_ref()
{
if attr == "format" && value.is_constant_expr() {
if attr == "format" && value.is_literal_expr() {
let mut diagnostic =
Diagnostic::new(DotFormatInException, first.range());
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 ruff_diagnostics::{Diagnostic, Violation};
@ -58,11 +58,7 @@ pub(crate) fn printf_in_gettext_func_call(checker: &mut Checker, args: &[Expr])
..
}) = &first
{
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
}) = left.as_ref()
{
if left.is_string_literal_expr() {
checker
.diagnostics
.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_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!(
left.as_ref(),
Expr::FString(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::Str(..) | Constant::Bytes(..),
..
})
Expr::FString(_) | Expr::StringLiteral(_) | Expr::BytesLiteral(_)
) && matches!(
right.as_ref(),
Expr::FString(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::Str(..) | Constant::Bytes(..),
..
})
Expr::FString(_) | Expr::StringLiteral(_) | Expr::BytesLiteral(_)
) && locator.contains_line_break(*range)
{
return Some(Diagnostic::new(ExplicitStringConcatenation, expr.range()));

View file

@ -1,5 +1,5 @@
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_stdlib::logging::LoggingLevel;
use ruff_text_size::Ranged;
@ -74,7 +74,7 @@ fn check_msg(checker: &mut Checker, msg: &Expr) {
Expr::Call(ast::ExprCall { func, .. }) => {
if checker.enabled(Rule::LoggingStringFormat) {
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
.diagnostics
.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, .. }) => {
for key in keys {
if let Some(key) = &key {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(attr),
..
}) = key
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value: attr, .. }) = key {
if is_reserved_attr(attr) {
checker.diagnostics.push(Diagnostic::new(
LoggingExtraAttrClash(attr.to_string()),

View file

@ -1,6 +1,6 @@
use itertools::Itertools;
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_text_size::Ranged;
@ -96,7 +96,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs
.iter()
.zip(values.iter())
.map(|(kwarg, value)| {
format!("{}={}", kwarg.value, checker.locator().slice(value.range()))
format!("{}={}", kwarg, checker.locator().slice(value.range()))
})
.join(", "),
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.
fn as_kwarg(key: &Expr) -> Option<&ast::StringConstant> {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
}) = key
{
fn as_kwarg(key: &Expr) -> Option<&str> {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = key {
if is_identifier(value) {
return Some(value);
}

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::{AlwaysFixableViolation, Fix};
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 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`.
let Expr::Constant(ast::ExprConstant {
value: Constant::Int(value),
let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(value),
..
}) = start
else {

View file

@ -1,4 +1,4 @@
use ruff_python_ast::Expr;
use ruff_python_ast::ExprStringLiteral;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
@ -36,8 +36,8 @@ impl Violation for DocstringInStub {
}
/// PYI021
pub(crate) fn docstring_in_stubs(checker: &mut Checker, docstring: Option<&Expr>) {
if let Some(docstr) = &docstring {
pub(crate) fn docstring_in_stubs(checker: &mut Checker, docstring: Option<&ExprStringLiteral>) {
if let Some(docstr) = docstring {
checker
.diagnostics
.push(Diagnostic::new(DocstringInStub, docstr.range()));

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, 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 crate::checkers::ast::Checker;
@ -54,13 +54,7 @@ pub(crate) fn ellipsis_in_non_empty_class_body(checker: &mut Checker, body: &[St
continue;
};
if matches!(
value.as_ref(),
Expr::Constant(ExprConstant {
value: Constant::Ellipsis,
..
})
) {
if value.is_ellipsis_literal_expr() {
let mut diagnostic = Diagnostic::new(EllipsisInNonEmptyClassBody, stmt.range());
let edit =
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_macros::{derive_message_formats, violation};
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 crate::checkers::ast::Checker;
@ -60,10 +60,8 @@ pub(crate) fn non_empty_stub_body(checker: &mut Checker, body: &[Stmt]) {
// Ignore `...` (the desired case).
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt {
if let Expr::Constant(ast::ExprConstant { value, .. }) = value.as_ref() {
if value.is_ellipsis() {
return;
}
if value.is_ellipsis_literal_expr() {
return;
}
}

View file

@ -1,7 +1,6 @@
use rustc_hash::FxHashSet;
use std::fmt;
use ast::Constant;
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
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
/// `bool`, like `True`).
fn match_constant_type(expr: &Expr) -> Option<ExprType> {
let constant = expr.as_constant_expr()?;
let result = match constant.value {
Constant::Bool(_) => ExprType::Bool,
Constant::Str(_) => ExprType::Str,
Constant::Bytes(_) => ExprType::Bytes,
Constant::Int(_) => ExprType::Int,
Constant::Float(_) => ExprType::Float,
Constant::Complex { .. } => ExprType::Complex,
let result = match expr {
Expr::BooleanLiteral(_) => ExprType::Bool,
Expr::StringLiteral(_) => ExprType::Str,
Expr::BytesLiteral(_) => ExprType::Bytes,
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
ast::Number::Int(_) => ExprType::Int,
ast::Number::Float(_) => ExprType::Float,
ast::Number::Complex { .. } => ExprType::Complex,
},
_ => return None,
};
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_python_ast::call_path::CallPath;
use ruff_python_ast::{
self as ast, Arguments, Constant, Expr, Operator, ParameterWithDefault, Parameters, Stmt,
UnaryOp,
self as ast, Arguments, Expr, Operator, ParameterWithDefault, Parameters, Stmt, UnaryOp,
};
use ruff_python_semantic::{ScopeKind, SemanticModel};
use ruff_source_file::Locator;
@ -282,7 +281,12 @@ fn is_valid_default_value_with_annotation(
semantic: &SemanticModel,
) -> bool {
match default {
Expr::Constant(_) => {
Expr::StringLiteral(_)
| Expr::BytesLiteral(_)
| Expr::NumberLiteral(_)
| Expr::BooleanLiteral(_)
| Expr::NoneLiteral(_)
| Expr::EllipsisLiteral(_) => {
return true;
}
Expr::List(ast::ExprList { elts, .. })
@ -314,10 +318,7 @@ fn is_valid_default_value_with_annotation(
}) => {
match operand.as_ref() {
// Ex) `-1`, `-3.14`, `2j`
Expr::Constant(ast::ExprConstant {
value: Constant::Int(..) | Constant::Float(..) | Constant::Complex { .. },
..
}) => return true,
Expr::NumberLiteral(_) => return true,
// Ex) `-math.inf`, `-math.pi`, etc.
Expr::Attribute(_) => {
if semantic
@ -338,14 +339,14 @@ fn is_valid_default_value_with_annotation(
range: _,
}) => {
// Ex) `1 + 2j`, `1 - 2j`, `-1 - 2j`, `-1 + 2j`
if let Expr::Constant(ast::ExprConstant {
value: Constant::Complex { .. },
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Complex { .. },
..
}) = right.as_ref()
{
// Ex) `1 + 2j`, `1 - 2j`
if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(..) | Constant::Float(..),
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(..) | ast::Number::Float(..),
..
}) = left.as_ref()
{
@ -357,8 +358,8 @@ fn is_valid_default_value_with_annotation(
}) = left.as_ref()
{
// Ex) `-1 + 2j`, `-1 - 2j`
if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(..) | Constant::Float(..),
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(..) | ast::Number::Float(..),
..
}) = operand.as_ref()
{
@ -393,13 +394,7 @@ fn is_valid_pep_604_union(annotation: &Expr) -> bool {
right,
range: _,
}) => is_valid_pep_604_union_member(left) && is_valid_pep_604_union_member(right),
Expr::Name(_)
| Expr::Subscript(_)
| Expr::Attribute(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::None,
..
}) => true,
Expr::Name(_) | Expr::Subscript(_) | Expr::Attribute(_) | Expr::NoneLiteral(_) => true,
_ => false,
}
}
@ -427,10 +422,8 @@ fn is_valid_default_value_without_annotation(default: &Expr) -> bool {
| Expr::Name(_)
| Expr::Attribute(_)
| Expr::Subscript(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::Ellipsis | Constant::None,
..
})
| Expr::EllipsisLiteral(_)
| Expr::NoneLiteral(_)
) || 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`,
/// parameterized generics, and PEP 604-style unions.
fn is_annotatable_type_alias(value: &Expr, semantic: &SemanticModel) -> bool {
matches!(
value,
Expr::Subscript(_)
| Expr::Constant(ast::ExprConstant {
value: Constant::None,
..
}),
) || is_valid_pep_604_union(value)
matches!(value, Expr::Subscript(_) | Expr::NoneLiteral(_))
|| is_valid_pep_604_union(value)
|| 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_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 {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(s),
..
}) => s.chars().count(),
Expr::Constant(ast::ExprConstant {
value: Constant::Bytes(bytes),
..
}) => bytes.len(),
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.chars().count(),
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.len(),
_ => return,
};
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_macros::{derive_message_formats, violation};
@ -123,11 +123,7 @@ pub(crate) fn unrecognized_platform(checker: &mut Checker, test: &Expr) {
return;
}
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = right
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = right {
// Other values are possible but we don't need them right now.
// This protects against typos.
if checker.enabled(Rule::UnrecognizedPlatformName) {

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
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 crate::checkers::ast::Checker;
@ -241,8 +241,8 @@ impl ExpectedComparator {
step: None,
..
}) => {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Int(upper),
if let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(upper),
..
}) = upper.as_ref()
{
@ -254,8 +254,8 @@ impl ExpectedComparator {
}
}
}
Expr::Constant(ast::ExprConstant {
value: Constant::Int(Int::ZERO),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(Int::ZERO),
..
}) => {
return Some(ExpectedComparator::MajorDigit);
@ -271,8 +271,8 @@ impl ExpectedComparator {
fn is_int_constant(expr: &Expr) -> bool {
matches!(
expr,
Expr::Constant(ast::ExprConstant {
value: ast::Constant::Int(_),
Expr::NumberLiteral(ast::ExprNumberLiteral {
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::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 {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = &keyword.value
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &keyword.value {
value == literal
} else {
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 {
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) => string.is_empty(),
Expr::Constant(constant) if constant.value.is_none() => true,
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => value.is_empty(),
Expr::NoneLiteral(_) => true,
Expr::FString(ast::ExprFString { values, .. }) => {
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::parenthesize::parenthesized_range;
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_trivia::CommentRanges;
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
@ -248,37 +248,22 @@ impl Violation for PytestDuplicateParametrizeTestCases {
}
fn elts_to_csv(elts: &[Expr], generator: Generator) -> Option<String> {
let all_literals = elts.iter().all(|expr| {
matches!(
expr,
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
)
});
if !all_literals {
if !elts.iter().all(Expr::is_string_literal_expr) {
return None;
}
let node = Expr::Constant(ast::ExprConstant {
value: elts
.iter()
.fold(String::new(), |mut acc, elt| {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = elt
{
if !acc.is_empty() {
acc.push(',');
}
acc.push_str(value.as_str());
let node = Expr::StringLiteral(ast::ExprStringLiteral {
value: elts.iter().fold(String::new(), |mut acc, elt| {
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = elt {
if !acc.is_empty() {
acc.push(',');
}
acc
})
.into(),
acc.push_str(value.as_str());
}
acc
}),
unicode: false,
implicit_concatenated: false,
range: TextRange::default(),
});
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;
match expr {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) => {
Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) => {
let names = split_names(string);
if names.len() > 1 {
match names_type {
@ -342,8 +324,10 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
elts: names
.iter()
.map(|name| {
Expr::Constant(ast::ExprConstant {
value: (*name).to_string().into(),
Expr::StringLiteral(ast::ExprStringLiteral {
value: (*name).to_string(),
unicode: false,
implicit_concatenated: false,
range: TextRange::default(),
})
})
@ -375,8 +359,10 @@ fn check_names(checker: &mut Checker, decorator: &Decorator, expr: &Expr) {
elts: names
.iter()
.map(|name| {
Expr::Constant(ast::ExprConstant {
value: (*name).to_string().into(),
Expr::StringLiteral(ast::ExprStringLiteral {
value: (*name).to_string(),
unicode: false,
implicit_concatenated: false,
range: TextRange::default(),
})
})
@ -495,15 +481,12 @@ fn check_values(checker: &mut Checker, names: &Expr, values: &Expr) {
.flake8_pytest_style
.parametrize_values_row_type;
let is_multi_named = if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(string),
..
}) = &names
{
split_names(string).len() > 1
} else {
true
};
let is_multi_named =
if let Expr::StringLiteral(ast::ExprStringLiteral { value: string, .. }) = &names {
split_names(string).len() > 1
} else {
true
};
match values {
Expr::List(ast::ExprList { elts, .. }) => {

View file

@ -2,7 +2,7 @@ use std::hash::BuildHasherDefault;
use anyhow::{anyhow, bail, Result};
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 rustc_hash::FxHashMap;
@ -368,8 +368,7 @@ impl UnittestAssert {
} else {
CmpOp::IsNot
};
let node = Expr::Constant(ast::ExprConstant {
value: Constant::None,
let node = Expr::NoneLiteral(ast::ExprNoneLiteral {
range: TextRange::default(),
});
let expr = compare(expr, cmp_op, &node);

View file

@ -1,5 +1,5 @@
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_source_file::{Locator, UniversalNewlines};
@ -8,12 +8,9 @@ use ruff_source_file::{Locator, UniversalNewlines};
/// non-`None` value.
pub(super) fn result_exists(returns: &[&ast::StmtReturn]) -> bool {
returns.iter().any(|stmt| {
stmt.value.as_deref().is_some_and(|value| {
!matches!(
value,
Expr::Constant(constant) if constant.value.is_none()
)
})
stmt.value
.as_deref()
.is_some_and(|value| !value.is_none_literal_expr())
})
}

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 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 {
return;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value: env_var, .. }),
..
}) = arg
else {
let Expr::StringLiteral(ast::ExprStringLiteral { value: env_var, .. }) = arg else {
return;
};
if !checker
@ -195,14 +191,10 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
if id != "os" || attr != "environ" {
return;
}
let Expr::Constant(ast::ExprConstant {
value:
Constant::Str(ast::StringConstant {
value: env_var,
unicode,
..
}),
range: _,
let Expr::StringLiteral(ast::ExprStringLiteral {
value: env_var,
unicode,
..
}) = slice.as_ref()
else {
return;
@ -224,12 +216,10 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
},
slice.range(),
);
let node = ast::ExprConstant {
value: ast::Constant::Str(ast::StringConstant {
value: capital_env_var,
unicode: *unicode,
implicit_concatenated: false,
}),
let node = ast::ExprStringLiteral {
value: capital_env_var,
unicode: *unicode,
implicit_concatenated: false,
range: TextRange::default(),
};
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 {
return;
};
if !matches!(key, Expr::Constant(_) | Expr::Name(_)) {
if !(key.is_literal_expr() || key.is_name_expr()) {
return;
}
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_macros::{derive_message_formats, violation};
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_semantic::analyze::typing::{is_sys_version_block, is_type_checking_block};
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.
if matches!(
test,
Expr::Constant(ast::ExprConstant {
value: Constant::Bool(..),
..
})
) {
if test.is_boolean_literal_expr() {
return None;
}
@ -259,10 +253,8 @@ fn is_main_check(expr: &Expr) -> bool {
{
if let Expr::Name(ast::ExprName { id, .. }) = left.as_ref() {
if id == "__name__" {
if let [Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
})] = comparators.as_slice()
if let [Expr::StringLiteral(ast::ExprStringLiteral { value, .. })] =
comparators.as_slice()
{
if value == "__main__" {
return true;

View file

@ -2,7 +2,7 @@ use rustc_hash::FxHashSet;
use ruff_diagnostics::{Diagnostic, 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::{self as ast, CmpOp, ElifElseClause, Expr, Stmt};
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] {
return;
}
let [Expr::Constant(ast::ExprConstant {
value: constant, ..
})] = comparators.as_slice()
else {
let [expr] = comparators.as_slice() else {
return;
};
if !expr.is_literal_expr() {
return;
}
let [Stmt::Return(ast::StmtReturn { value, range: _ })] = body.as_slice() else {
return;
};
@ -94,8 +94,9 @@ pub(crate) fn if_else_block_instead_of_dict_lookup(checker: &mut Checker, stmt_i
return;
}
let mut constants: FxHashSet<ComparableConstant> = FxHashSet::default();
constants.insert(constant.into());
// The `expr` was checked to be a literal above, so this is safe.
let mut literals: FxHashSet<ComparableExpr> = FxHashSet::default();
literals.insert(expr.into());
for clause in elif_else_clauses {
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] {
return;
}
let [Expr::Constant(ast::ExprConstant {
value: constant, ..
})] = comparators.as_slice()
else {
let [expr] = comparators.as_slice() else {
return;
};
if !expr.is_literal_expr() {
return;
}
if value.as_ref().is_some_and(|value| {
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;
};
constants.insert(constant.into());
// The `expr` was checked to be a literal above, so this is safe.
literals.insert(expr.into());
}
// 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;
}

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, 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_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 {
return None;
};
let Some(Expr::Constant(ast::ExprConstant { value, .. })) = value.as_deref() else {
return None;
};
let Constant::Bool(value) = value else {
let Some(Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. })) = value.as_deref() else {
return None;
};
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::traversal;
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_text_size::{Ranged, TextRange};
@ -280,11 +280,7 @@ fn match_loop(stmt: &Stmt) -> Option<Loop> {
let Some(value) = value else {
return None;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(value),
..
}) = value.as_ref()
else {
let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = value.as_ref() else {
return None;
};
@ -319,9 +315,8 @@ fn match_else_return(stmt: &Stmt) -> Option<Terminal> {
else {
return None;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(next_value),
..
let Expr::BooleanLiteral(ast::ExprBooleanLiteral {
value: next_value, ..
}) = next_value.as_ref()
else {
return None;
@ -362,9 +357,8 @@ fn match_sibling_return<'a>(stmt: &'a Stmt, sibling: &'a Stmt) -> Option<Termina
else {
return None;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(next_value),
..
let Expr::BooleanLiteral(ast::ExprBooleanLiteral {
value: next_value, ..
}) = next_value.as_ref()
else {
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_python_ast::call_path::compose_call_path;
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::{TextLen, TextRange};
@ -64,13 +64,7 @@ impl Violation for SuppressibleException {
fn is_empty(body: &[Stmt]) -> bool {
match body {
[Stmt::Pass(_)] => true,
[Stmt::Expr(ast::StmtExpr { value, range: _ })] => matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Ellipsis,
..
})
),
[Stmt::Expr(ast::StmtExpr { value, range: _ })] => value.is_ellipsis_literal_expr(),
_ => false,
}
}

View file

@ -82,15 +82,14 @@ impl Violation for YodaConditions {
fn is_constant_like(expr: &Expr) -> bool {
match expr {
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::Name(ast::ExprName { id, .. }) => str::is_cased_uppercase(id),
Expr::UnaryOp(ast::ExprUnaryOp {
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
operand,
range: _,
}) => operand.is_constant_expr(),
_ => false,
}) => operand.is_literal_expr(),
_ => 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;
@ -7,15 +7,7 @@ use ruff_python_ast::helpers::is_docstring_stmt;
fn is_empty_stmt(stmt: &Stmt) -> bool {
match stmt {
Stmt::Pass(_) => return true,
Stmt::Expr(ast::StmtExpr { value, range: _ }) => {
return matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Ellipsis,
..
})
)
}
Stmt::Expr(ast::StmtExpr { value, range: _ }) => return value.is_ellipsis_literal_expr(),
Stmt::Raise(ast::StmtRaise { exc, cause, .. }) => {
if cause.is_none() {
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_macros::{derive_message_formats, violation};
@ -65,11 +65,7 @@ pub(crate) fn path_constructor_current_directory(checker: &mut Checker, expr: &E
return;
}
let [Expr::Constant(ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
range,
})] = args.as_slice()
else {
let [Expr::StringLiteral(ast::ExprStringLiteral { value, range, .. })] = args.as_slice() else {
return;
};

View file

@ -1,5 +1,5 @@
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 crate::checkers::ast::Checker;
@ -118,24 +118,13 @@ pub(crate) fn replaceable_by_pathlib(checker: &mut Checker, call: &ExprCall) {
.is_some_and(|expr| {
!matches!(
expr,
Expr::Constant(ExprConstant {
value: Constant::Bool(true),
..
})
Expr::BooleanLiteral(ExprBooleanLiteral { value: true, .. })
)
})
|| call
.arguments
.find_argument("opener", 7)
.is_some_and(|expr| {
!matches!(
expr,
Expr::Constant(ExprConstant {
value: Constant::None,
..
})
)
})
.is_some_and(|expr| !expr.is_none_literal_expr())
{
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;
/// 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.
pub(super) fn to_constant_string(s: &str) -> Expr {
let node = ast::ExprConstant {
value: s.to_owned().into(),
let node = ast::ExprStringLiteral {
value: s.to_owned(),
unicode: false,
implicit_concatenated: false,
range: TextRange::default(),
};
node.into()
@ -54,20 +56,11 @@ fn is_simple_callee(func: &Expr) -> bool {
pub(super) fn to_f_string_element(expr: &Expr) -> Option<Expr> {
match expr {
// These are directly handled by `unparse_f_string_element`:
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
| Expr::FString(_)
| Expr::FormattedValue(_) => Some(expr.clone()),
Expr::StringLiteral(_) | Expr::FString(_) | Expr::FormattedValue(_) => Some(expr.clone()),
// These should be pretty safe to wrap in a formatted value.
Expr::Constant(ast::ExprConstant {
value:
Constant::Int(_) | Constant::Float(_) | Constant::Bool(_) | Constant::Complex { .. },
..
})
| Expr::Name(_)
| Expr::Attribute(_) => Some(to_formatted_value_expr(expr)),
Expr::NumberLiteral(_) | Expr::BooleanLiteral(_) | Expr::Name(_) | Expr::Attribute(_) => {
Some(to_formatted_value_expr(expr))
}
Expr::Call(_) if is_simple_call(expr) => Some(to_formatted_value_expr(expr)),
_ => None,
}

View file

@ -3,7 +3,7 @@ use itertools::Itertools;
use crate::fix::edits::pad;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
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 crate::checkers::ast::Checker;
@ -61,31 +61,20 @@ fn is_static_length(elts: &[Expr]) -> bool {
fn build_fstring(joiner: &str, joinees: &[Expr]) -> Option<Expr> {
// If all elements are string constants, join them into a single string.
if joinees.iter().all(|expr| {
matches!(
expr,
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
)
}) {
let node = ast::ExprConstant {
if joinees.iter().all(Expr::is_string_literal_expr) {
let node = ast::ExprStringLiteral {
value: joinees
.iter()
.filter_map(|expr| {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = expr
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
Some(value.as_str())
} else {
None
}
})
.join(joiner)
.into(),
.join(joiner),
unicode: false,
implicit_concatenated: false,
range: TextRange::default(),
};
return Some(node.into());

View file

@ -16,7 +16,12 @@ pub(super) enum Resolution {
/// Test an [`Expr`] for relevance to Pandas-related operations.
pub(super) fn test_expression(expr: &Expr, semantic: &SemanticModel) -> Resolution {
match expr {
Expr::Constant(_)
Expr::StringLiteral(_)
| Expr::BytesLiteral(_)
| Expr::NumberLiteral(_)
| Expr::BooleanLiteral(_)
| Expr::NoneLiteral(_)
| Expr::EllipsisLiteral(_)
| Expr::Tuple(_)
| Expr::List(_)
| Expr::Set(_)

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::Diagnostic;
use ruff_diagnostics::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 crate::checkers::ast::Checker;
@ -80,8 +80,8 @@ pub(crate) fn nunique_constant_series_check(
// Right should be the integer 1.
if !matches!(
right,
Expr::Constant(ast::ExprConstant {
value: Constant::Int(Int::ONE),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(Int::ONE),
range: _,
})
) {

View file

@ -1,7 +1,7 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast as ast;
use ruff_python_ast::{Constant, Expr};
use ruff_python_ast::Expr;
use ruff_text_size::Ranged;
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)
.is_some_and(|call_path| matches!(call_path.as_slice(), ["pandas", "read_table"]))
{
if let Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
})) = call
if let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) = call
.arguments
.find_keyword("sep")
.map(|keyword| &keyword.value)

View file

@ -1,5 +1,5 @@
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};
@ -164,10 +164,7 @@ fn extract_types(annotation: &Expr, semantic: &SemanticModel) -> Option<(Vec<Exp
// specification, or ellipsis.
let params = match param_types {
Expr::List(ast::ExprList { elts, .. }) => elts.clone(),
Expr::Constant(ast::ExprConstant {
value: Constant::Ellipsis,
..
}) => vec![],
Expr::EllipsisLiteral(_) => vec![],
_ => return None,
};

View file

@ -4,7 +4,7 @@ use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers;
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 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 let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(value),
range: _,
}) = comparator
{
if let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = comparator {
match op {
EqCmpOp::Eq => {
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 let Expr::Constant(ast::ExprConstant {
value: Constant::Bool(value),
range: _,
}) = next
{
if let Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. }) = next {
match op {
EqCmpOp::Eq => {
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::helpers::map_callable;
use ruff_python_ast::Expr;
use ruff_python_semantic::{Definition, SemanticModel};
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(),
capitalized_word: capitalized_word.to_string(),
},
docstring.expr.range(),
docstring.range(),
);
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(
content,
docstring.expr.end() - num_trailing_quotes - num_trailing_spaces,
docstring.expr.end() - num_trailing_quotes,
docstring.end() - num_trailing_quotes - num_trailing_spaces,
docstring.end() - num_trailing_quotes,
)));
checker.diagnostics.push(diagnostic);
}

View file

@ -149,7 +149,14 @@ pub(crate) fn repeated_keys(checker: &mut Checker, dict: &ast::ExprDict) {
};
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) {
let mut diagnostic = Diagnostic::new(
MultiValueRepeatedKeyLiteral {

View file

@ -4,7 +4,7 @@ use rustc_hash::FxHashSet;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Fix, FixAvailability, 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 crate::checkers::ast::Checker;
@ -582,10 +582,7 @@ pub(crate) fn percent_format_extra_named_arguments(
.iter()
.enumerate()
.filter_map(|(index, key)| match key {
Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
})) => {
Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) => {
if summary.keywords.contains(value.as_str()) {
None
} else {
@ -643,10 +640,7 @@ pub(crate) fn percent_format_missing_arguments(
let mut keywords = FxHashSet::default();
for key in keys.iter().flatten() {
match key {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) => {
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
keywords.insert(value);
}
_ => {

View file

@ -1,7 +1,7 @@
use std::fmt;
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::{ScopeKind, SemanticModel};
@ -11,11 +11,7 @@ use crate::settings::LinterSettings;
pub(super) fn type_param_name(arguments: &Arguments) -> Option<&str> {
// Handle both `TypeVar("T")` and `TypeVar(name="T")`.
let name_param = arguments.find_argument("name", 0)?;
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(name),
..
}) = &name_param
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value: name, .. }) = &name_param {
Some(name)
} else {
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_macros::{derive_message_formats, violation};
@ -45,51 +45,48 @@ impl Violation for AssertOnStringLiteral {
/// PLW0129
pub(crate) fn assert_on_string_literal(checker: &mut Checker, test: &Expr) {
match test {
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
Constant::Str(value, ..) => {
checker.diagnostics.push(Diagnostic::new(
AssertOnStringLiteral {
kind: if value.is_empty() {
Kind::Empty
} else {
Kind::NonEmpty
},
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
checker.diagnostics.push(Diagnostic::new(
AssertOnStringLiteral {
kind: if value.is_empty() {
Kind::Empty
} else {
Kind::NonEmpty
},
test.range(),
));
}
Constant::Bytes(value) => {
checker.diagnostics.push(Diagnostic::new(
AssertOnStringLiteral {
kind: if value.is_empty() {
Kind::Empty
} else {
Kind::NonEmpty
},
},
test.range(),
));
}
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
checker.diagnostics.push(Diagnostic::new(
AssertOnStringLiteral {
kind: if value.is_empty() {
Kind::Empty
} else {
Kind::NonEmpty
},
test.range(),
));
}
_ => {}
},
},
test.range(),
));
}
Expr::FString(ast::ExprFString { values, .. }) => {
checker.diagnostics.push(Diagnostic::new(
AssertOnStringLiteral {
kind: if values.iter().all(|value| match value {
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
Constant::Str(value) => value.is_empty(),
Constant::Bytes(value) => value.is_empty(),
_ => false,
},
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
value.is_empty()
}
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => value.is_empty(),
_ => false,
}) {
Kind::Empty
} else if values.iter().any(|value| match value {
Expr::Constant(ast::ExprConstant { value, .. }) => match value {
Constant::Str(value) => !value.is_empty(),
Constant::Bytes(value) => !value.is_empty(),
_ => false,
},
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
!value.is_empty()
}
Expr::BytesLiteral(ast::ExprBytesLiteral { value, .. }) => {
!value.is_empty()
}
_ => false,
}) {
Kind::NonEmpty

View file

@ -2,7 +2,7 @@ use bitflags::bitflags;
use ruff_diagnostics::{Diagnostic, 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_text_size::Ranged;
@ -59,11 +59,7 @@ pub(crate) fn bad_open_mode(checker: &mut Checker, call: &ast::ExprCall) {
return;
};
let Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
}) = mode
else {
let Some(ast::ExprStringLiteral { value, .. }) = mode.as_string_literal_expr() else {
return;
};

View file

@ -1,6 +1,6 @@
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 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 matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_) | Constant::Bytes(_),
..
})
Expr::StringLiteral(_) | Expr::BytesLiteral(_)
) {
if let Some(strip) = StripKind::from_str(attr.as_str()) {
if let Some(arg) = args.get(0) {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
}) = &arg
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &arg {
if has_duplicates(value) {
let removal = if checker.settings.target_version >= PythonVersion::Py39
{

View file

@ -1,6 +1,6 @@
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_parser::{lexer, AsMode};
use ruff_text_size::{Ranged, TextRange};
@ -186,12 +186,8 @@ fn is_valid_dict(
let Some(key) = key else {
return true;
};
if let Expr::Constant(ast::ExprConstant {
value:
Constant::Str(ast::StringConstant {
value: mapping_key, ..
}),
..
if let Expr::StringLiteral(ast::ExprStringLiteral {
value: mapping_key, ..
}) = key
{
let Some(format) = formats_hash.get(mapping_key.as_str()) else {

View file

@ -1,6 +1,6 @@
use anyhow::bail;
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_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 std::mem::take(&mut first) {
// Check the left-most expression.
if let Expr::Constant(ast::ExprConstant { value, .. }) = &lhs {
if let Constant::Str(s) = value {
if s.is_empty() {
let constant = checker.generator().constant(value);
let expr = checker.generator().expr(rhs);
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}");
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &lhs {
if value.is_empty() {
let literal = checker.generator().expr(lhs);
let expr = checker.generator().expr(rhs);
let existing = format!("{literal} {op} {expr}");
let replacement = format!("{}{expr}", op.into_unary());
checker.diagnostics.push(Diagnostic::new(
CompareToEmptyString {
existing,
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 ruff_python_ast::{self as ast, CmpOp, Expr};
use ruff_python_ast::{CmpOp, Expr};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
@ -63,22 +63,12 @@ pub(crate) fn comparison_of_constant(
.tuple_windows()
.zip(ops)
{
if let (
Expr::Constant(ast::ExprConstant {
value: left_constant,
..
}),
Expr::Constant(ast::ExprConstant {
value: right_constant,
..
}),
) = (&left, &right)
{
if left.is_literal_expr() && right.is_literal_expr() {
let diagnostic = Diagnostic::new(
ComparisonOfConstant {
left_constant: checker.generator().constant(left_constant),
left_constant: checker.generator().expr(left),
op: *op,
right_constant: checker.generator().constant(right_constant),
right_constant: checker.generator().expr(right),
},
left.range(),
);

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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 crate::checkers::ast::Checker;
@ -73,10 +73,7 @@ fn is_valid_default(expr: &Expr) -> bool {
// Otherwise, the default must be a string or `None`.
matches!(
expr,
Expr::Constant(ast::ExprConstant {
value: Constant::Str { .. } | Constant::None { .. },
..
}) | Expr::FString(_)
Expr::StringLiteral(_) | Expr::NoneLiteral(_) | Expr::FString(_)
)
}

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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_text_size::Ranged;

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{Diagnostic, 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_stdlib::logging::LoggingLevel;
use ruff_text_size::Ranged;
@ -128,10 +128,8 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) {
_ => return,
};
let Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value, .. }),
..
})) = call.arguments.find_positional(0)
let Some(Expr::StringLiteral(ast::ExprStringLiteral { value, .. })) =
call.arguments.find_positional(0)
else {
return;
};

View file

@ -1,5 +1,5 @@
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_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`].
fn as_constant(expr: &Expr) -> Option<&Constant> {
/// If an [`Expr`] is a literal (or unary operation on a literal), return the literal [`Expr`].
fn as_literal(expr: &Expr) -> Option<&Expr> {
match expr {
Expr::Constant(ast::ExprConstant { value, .. }) => Some(value),
Expr::UnaryOp(ast::ExprUnaryOp {
op: UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert,
operand,
range: _,
}) => match operand.as_ref() {
Expr::Constant(ast::ExprConstant { value, .. }) => Some(value),
_ => None,
},
..
}) if operand.is_literal_expr() => Some(operand.as_ref()),
expr if expr.is_literal_expr() => Some(expr),
_ => None,
}
}
/// Return `true` if a [`Constant`] is a magic value.
fn is_magic_value(constant: &Constant, allowed_types: &[ConstantType]) -> bool {
if let Ok(constant_type) = ConstantType::try_from(constant) {
fn is_magic_value(expr: &Expr, allowed_types: &[ConstantType]) -> bool {
if let Some(constant_type) = ConstantType::try_from_expr(expr) {
if allowed_types.contains(&constant_type) {
return false;
}
}
match constant {
match expr {
// Ignore `None`, `Bool`, and `Ellipsis` constants.
Constant::None => false,
Constant::Bool(_) => false,
Constant::Ellipsis => false,
// Otherwise, special-case some common string and integer types.
Constant::Str(ast::StringConstant { value, .. }) => {
Expr::NoneLiteral(_) | Expr::BooleanLiteral(_) | Expr::EllipsisLiteral(_) => false,
// Special-case some common string and integer types.
Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) => {
!matches!(value.as_str(), "" | "__main__")
}
Constant::Int(value) => !matches!(*value, Int::ZERO | Int::ONE),
Constant::Bytes(_) => true,
Constant::Float(_) => true,
Constant::Complex { .. } => true,
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
ast::Number::Int(value) => !matches!(*value, Int::ZERO | Int::ONE),
_ => true,
},
Expr::BytesLiteral(_) => true,
_ => false,
}
}
@ -102,15 +99,15 @@ pub(crate) fn magic_value_comparison(checker: &mut Checker, left: &Expr, compara
.chain(comparators.iter())
.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
if as_constant(left).is_some() && as_constant(right).is_some() {
if as_literal(left).is_some() && as_literal(right).is_some() {
return;
}
}
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) {
checker.diagnostics.push(Diagnostic::new(
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_macros::{derive_message_formats, violation};
@ -65,13 +65,7 @@ pub(crate) fn single_string_slots(checker: &mut Checker, class: &StmtClassDef) {
for target in targets {
if let Expr::Name(ast::ExprName { id, .. }) = target {
if id.as_str() == "__slots__" {
if matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
}) | Expr::FString(_)
) {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker
.diagnostics
.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 id.as_str() == "__slots__" {
if matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
}) | Expr::FString(_)
) {
if matches!(value.as_ref(), Expr::StringLiteral(_) | Expr::FString(_)) {
checker
.diagnostics
.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.
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`.

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_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.
if body.len() == 2 {
if let Stmt::Expr(ast::StmtExpr { value, range: _ }) = &body[0] {
if matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(_),
..
})
) {
if value.is_string_literal_expr() {
return;
}
}

View file

@ -1,10 +1,9 @@
//! Settings for the `pylint` plugin.
use anyhow::anyhow;
use serde::{Deserialize, Serialize};
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)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
@ -17,19 +16,17 @@ pub enum ConstantType {
Str,
}
impl TryFrom<&Constant> for ConstantType {
type Error = anyhow::Error;
fn try_from(value: &Constant) -> Result<Self, Self::Error> {
match value {
Constant::Bytes(..) => Ok(Self::Bytes),
Constant::Complex { .. } => Ok(Self::Complex),
Constant::Float(..) => Ok(Self::Float),
Constant::Int(..) => Ok(Self::Int),
Constant::Str(..) => Ok(Self::Str),
Constant::Bool(..) | Constant::Ellipsis | Constant::None => {
Err(anyhow!("Singleton constants are unsupported"))
}
impl ConstantType {
pub fn try_from_expr(expr: &Expr) -> Option<Self> {
match expr {
Expr::StringLiteral(_) => Some(Self::Str),
Expr::BytesLiteral(_) => Some(Self::Bytes),
Expr::NumberLiteral(ExprNumberLiteral { value, .. }) => match value {
Number::Int(_) => Some(Self::Int),
Number::Float(_) => Some(Self::Float),
Number::Complex { .. } => Some(Self::Complex),
},
_ => None,
}
}
}

View file

@ -3,9 +3,7 @@ use log::debug;
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::is_dunder;
use ruff_python_ast::{
self as ast, Arguments, Constant, Expr, ExprContext, Identifier, Keyword, Stmt,
};
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Keyword, Stmt};
use ruff_python_codegen::Generator;
use ruff_python_semantic::SemanticModel;
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 {
return None;
};
let field = field.as_constant_expr()?;
let Constant::Str(ast::StringConstant { value: field, .. }) = &field.value else {
return None;
};
let ast::ExprStringLiteral { value: field, .. } = field.as_string_literal_expr()?;
if !is_identifier(field) {
return None;
}

View file

@ -1,9 +1,7 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::is_dunder;
use ruff_python_ast::{
self as ast, Arguments, Constant, Expr, ExprContext, Identifier, Keyword, Stmt,
};
use ruff_python_ast::{self as ast, Arguments, Expr, ExprContext, Identifier, Keyword, Stmt};
use ruff_python_codegen::Generator;
use ruff_python_semantic::SemanticModel;
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()
.zip(values.iter())
.map(|(key, value)| match key {
Some(Expr::Constant(ast::ExprConstant {
value: Constant::Str(ast::StringConstant { value: field, .. }),
..
})) => {
Some(Expr::StringLiteral(ast::ExprStringLiteral { value: field, .. })) => {
if !is_identifier(field) {
return None;
}

View file

@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
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::{
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}"`.
(
FormatContext::Accessed,
Expr::Constant(ast::ExprConstant {
value: Constant::Int(..),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(..),
..
}),
) => text.chars().all(|c| c.is_ascii_digit()),
@ -314,13 +314,7 @@ pub(crate) fn f_strings(
return;
};
if !matches!(
value.as_ref(),
Expr::Constant(ast::ExprConstant {
value: Constant::Str(..),
..
}),
) {
if !value.is_string_literal_expr() {
return;
};

View file

@ -3,8 +3,8 @@ use std::str::FromStr;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Constant, Expr};
use ruff_text_size::Ranged;
use ruff_python_ast::{self as ast, Expr};
use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;
@ -32,35 +32,54 @@ impl FromStr for LiteralType {
}
}
impl From<LiteralType> for Constant {
fn from(value: LiteralType) -> Self {
match value {
LiteralType::Str => Constant::Str(ast::StringConstant {
impl LiteralType {
fn as_zero_value_expr(self) -> Expr {
match self {
LiteralType::Str => ast::ExprStringLiteral {
value: String::new(),
unicode: false,
implicit_concatenated: false,
}),
LiteralType::Bytes => Constant::Bytes(ast::BytesConstant {
range: TextRange::default(),
}
.into(),
LiteralType::Bytes => ast::ExprBytesLiteral {
value: Vec::new(),
implicit_concatenated: false,
}),
LiteralType::Int => Constant::Int(0.into()),
LiteralType::Float => Constant::Float(0.0),
LiteralType::Bool => Constant::Bool(false),
range: TextRange::default(),
}
.into(),
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 = ();
fn try_from(value: &Constant) -> Result<Self, Self::Error> {
match value {
Constant::Str(_) => Ok(LiteralType::Str),
Constant::Bytes(_) => Ok(LiteralType::Bytes),
Constant::Int(_) => Ok(LiteralType::Int),
Constant::Float(_) => Ok(LiteralType::Float),
Constant::Bool(_) => Ok(LiteralType::Bool),
fn try_from(expr: &Expr) -> Result<Self, Self::Error> {
match expr {
Expr::StringLiteral(_) => Ok(LiteralType::Str),
Expr::BytesLiteral(_) => Ok(LiteralType::Bytes),
Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. }) => match value {
ast::Number::Int(_) => Ok(LiteralType::Int),
ast::Number::Float(_) => Ok(LiteralType::Float),
ast::Number::Complex { .. } => Err(()),
},
Expr::BooleanLiteral(_) => Ok(LiteralType::Bool),
_ => Err(()),
}
}
@ -181,8 +200,8 @@ pub(crate) fn native_literals(
return;
}
let constant = Constant::from(literal_type);
let content = checker.generator().constant(&constant);
let expr = literal_type.as_zero_value_expr();
let content = checker.generator().expr(&expr);
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
content,
call.range(),
@ -190,16 +209,12 @@ pub(crate) fn native_literals(
checker.diagnostics.push(diagnostic);
}
Some(arg) => {
let Expr::Constant(ast::ExprConstant { value, .. }) = arg else {
return;
};
// Skip implicit string concatenations.
if value.is_implicit_concatenated() {
if arg.is_implicit_concatenated_string() {
return;
}
let Ok(arg_literal_type) = LiteralType::try_from(value) else {
let Ok(arg_literal_type) = LiteralType::try_from(arg) else {
return;
};
@ -213,8 +228,14 @@ pub(crate) fn native_literals(
// Ex) `(7).denominator` is valid but `7.denominator` is not
// Note that floats do not have this problem
// Ex) `(1.0).real` is valid and `1.0.real` is too
let content = match (parent_expr, value) {
(Some(Expr::Attribute(_)), Constant::Int(_)) => format!("({arg_code})"),
let content = match (parent_expr, arg) {
(
Some(Expr::Attribute(_)),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(_),
..
}),
) => format!("({arg_code})"),
_ => 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::stmt_if::{if_elif_branches, BranchKind, IfElifBranch};
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 crate::checkers::ast::Checker;
@ -147,8 +147,8 @@ pub(crate) fn outdated_version_block(checker: &mut Checker, stmt_if: &StmtIf) {
}
_ => {}
},
Expr::Constant(ast::ExprConstant {
value: Constant::Int(int),
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(int),
..
}) => {
if op == &CmpOp::Eq {
@ -409,8 +409,8 @@ fn fix_always_true_branch(
fn extract_version(elts: &[Expr]) -> Option<Vec<Int>> {
let mut version: Vec<Int> = vec![];
for elt in elts {
let Expr::Constant(ast::ExprConstant {
value: Constant::Int(int),
let Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(int),
..
}) = &elt
else {

View file

@ -5,7 +5,7 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::str::{leading_quote, trailing_quote};
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_literal::cformat::{
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()) {
match key {
Some(key) => {
if let Expr::Constant(ast::ExprConstant {
value:
Constant::Str(ast::StringConstant {
value: key_string, ..
}),
..
if let Expr::StringLiteral(ast::ExprStringLiteral {
value: key_string, ..
}) = key
{
// 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.
let params_string = match right {
Expr::Constant(_) | Expr::FString(_) => {
Cow::Owned(format!("({})", checker.locator().slice(right)))
}
Expr::StringLiteral(_)
| 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(_) => {
if num_keyword_arguments > 0 {
// 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_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_semantic::SemanticModel;
use ruff_source_file::Locator;
@ -71,12 +71,8 @@ pub(crate) fn redundant_open_modes(checker: &mut Checker, call: &ast::ExprCall)
None => {
if !call.arguments.is_empty() {
if let Some(keyword) = call.arguments.find_keyword(MODE_KEYWORD_ARGUMENT) {
if let Expr::Constant(ast::ExprConstant {
value:
Constant::Str(ast::StringConstant {
value: mode_param_value,
..
}),
if let Expr::StringLiteral(ast::ExprStringLiteral {
value: mode_param_value,
..
}) = &keyword.value
{
@ -94,11 +90,7 @@ pub(crate) fn redundant_open_modes(checker: &mut Checker, call: &ast::ExprCall)
}
}
Some(mode_param) => {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
}) = &mode_param
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &mode_param {
if let Ok(mode) = OpenMode::from_str(value) {
checker.diagnostics.push(create_check(
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 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;
}
let Expr::Constant(ast::ExprConstant { value, .. }) = &arg else {
return;
};
let Some(primitive) = Primitive::from_constant(value) else {
let Some(primitive) = Primitive::from_expr(arg) else {
return;
};
let mut diagnostic = Diagnostic::new(TypeOfPrimitive { primitive }, expr.range());

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
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 crate::checkers::ast::Checker;
@ -39,11 +39,11 @@ impl AlwaysFixableViolation for UnicodeKindPrefix {
}
/// UP025
pub(crate) fn unicode_kind_prefix(checker: &mut Checker, expr: &Expr, is_unicode: bool) {
if is_unicode {
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, expr.range());
pub(crate) fn unicode_kind_prefix(checker: &mut Checker, string: &ExprStringLiteral) {
if string.unicode {
let mut diagnostic = Diagnostic::new(UnicodeKindPrefix, string.range);
diagnostic.set_fix(Fix::safe_edit(Edit::range_deletion(TextRange::at(
expr.start(),
string.start(),
TextSize::from(1),
))));
checker.diagnostics.push(diagnostic);

View file

@ -1,6 +1,6 @@
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
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_source_file::Locator;
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 {
if let Expr::Constant(ast::ExprConstant {
value: Constant::Str(value),
..
}) = &arg
{
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = &arg {
UTF8_LITERALS.contains(&value.to_lowercase().as_str())
} else {
false
@ -162,10 +158,7 @@ pub(crate) fn unnecessary_encode_utf8(checker: &mut Checker, call: &ast::ExprCal
return;
};
match variable {
Expr::Constant(ast::ExprConstant {
value: Constant::Str(literal),
..
}) => {
Expr::StringLiteral(ast::ExprStringLiteral { value: literal, .. }) => {
// Ex) `"str".encode()`, `"str".encode("utf-8")`
if let Some(encoding_arg) = match_encoding_arg(&call.arguments) {
if literal.is_ascii() {

Some files were not shown because too many files have changed in this diff Show more