mirror of https://github.com/astral-sh/ruff
Add RUF016: Detection of invalid index types (#5602)
Detects invalid types for tuple, list, bytes, string indices.
For example, the following will raise a `TypeError` at runtime and when
imported Python will display a `SyntaxWarning`
```python
var = [1, 2, 3]["x"]
```
```
example.py:1: SyntaxWarning: list indices must be integers or slices, not str; perhaps you missed a comma?
var = [1, 2, 3]["x"]
Traceback (most recent call last):
File "example.py", line 1, in <module>
var = [1, 2, 3]["x"]
~~~~~~~~~^^^^^
TypeError: list indices must be integers or slices, not str
```
Previously, Ruff would not report the invalid syntax but now a violation
will be reported. This does not apply to cases where a variable, call,
or complex expression is used in the index — detection is roughly
limited to static definitions, which matches Python's warnings.
```
❯ ./target/debug/ruff example.py --select RUF015 --show-source --no-cache
example.py:1:17: RUF015 Indexed access to type `list` uses type `str` instead of an integer or slice.
|
1 | var = [1, 2, 3]["x"]
| ^^^ RUF015
|
```
Closes https://github.com/astral-sh/ruff/issues/5082
xref
ffff1440d1
This commit is contained in:
parent
7566ca8ff7
commit
0666added9
|
|
@ -0,0 +1,115 @@
|
||||||
|
# Should not emit for valid access with index
|
||||||
|
var = "abc"[0]
|
||||||
|
var = f"abc"[0]
|
||||||
|
var = [1, 2, 3][0]
|
||||||
|
var = (1, 2, 3)[0]
|
||||||
|
var = b"abc"[0]
|
||||||
|
|
||||||
|
# Should not emit for valid access with slice
|
||||||
|
var = "abc"[0:2]
|
||||||
|
var = f"abc"[0:2]
|
||||||
|
var = b"abc"[0:2]
|
||||||
|
var = [1, 2, 3][0:2]
|
||||||
|
var = (1, 2, 3)[0:2]
|
||||||
|
var = [1, 2, 3][None:2]
|
||||||
|
var = [1, 2, 3][0:None]
|
||||||
|
var = [1, 2, 3][:2]
|
||||||
|
var = [1, 2, 3][0:]
|
||||||
|
|
||||||
|
# Should emit for invalid access on strings
|
||||||
|
var = "abc"["x"]
|
||||||
|
var = f"abc"["x"]
|
||||||
|
|
||||||
|
# Should emit for invalid access on bytes
|
||||||
|
var = b"abc"["x"]
|
||||||
|
|
||||||
|
# Should emit for invalid access on lists and tuples
|
||||||
|
var = [1, 2, 3]["x"]
|
||||||
|
var = (1, 2, 3)["x"]
|
||||||
|
|
||||||
|
# Should emit for invalid access on list comprehensions
|
||||||
|
var = [x for x in range(10)]["x"]
|
||||||
|
|
||||||
|
# Should emit for invalid access using tuple
|
||||||
|
var = "abc"[1, 2]
|
||||||
|
|
||||||
|
# Should emit for invalid access using string
|
||||||
|
var = [1, 2]["x"]
|
||||||
|
|
||||||
|
# Should emit for invalid access using float
|
||||||
|
var = [1, 2][0.25]
|
||||||
|
|
||||||
|
# Should emit for invalid access using dict
|
||||||
|
var = [1, 2][{"x": "y"}]
|
||||||
|
|
||||||
|
# Should emit for invalid access using dict comp
|
||||||
|
var = [1, 2][{x: "y" for x in range(2)}]
|
||||||
|
|
||||||
|
# Should emit for invalid access using list
|
||||||
|
var = [1, 2][2, 3]
|
||||||
|
|
||||||
|
# Should emit for invalid access using list comp
|
||||||
|
var = [1, 2][[x for x in range(2)]]
|
||||||
|
|
||||||
|
# Should emit on invalid access using set
|
||||||
|
var = [1, 2][{"x", "y"}]
|
||||||
|
|
||||||
|
# Should emit on invalid access using set comp
|
||||||
|
var = [1, 2][{x for x in range(2)}]
|
||||||
|
|
||||||
|
# Should emit on invalid access using bytes
|
||||||
|
var = [1, 2][b"x"]
|
||||||
|
|
||||||
|
# Should emit for non-integer slice start
|
||||||
|
var = [1, 2, 3]["x":2]
|
||||||
|
var = [1, 2, 3][f"x":2]
|
||||||
|
var = [1, 2, 3][1.2:2]
|
||||||
|
var = [1, 2, 3][{"x"}:2]
|
||||||
|
var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||||
|
var = [1, 2, 3][[x for x in range(2)]:2]
|
||||||
|
|
||||||
|
# Should emit for non-integer slice end
|
||||||
|
var = [1, 2, 3][0:"x"]
|
||||||
|
var = [1, 2, 3][0:f"x"]
|
||||||
|
var = [1, 2, 3][0:1.2]
|
||||||
|
var = [1, 2, 3][0:{"x"}]
|
||||||
|
var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||||
|
var = [1, 2, 3][0:[x for x in range(2)]]
|
||||||
|
|
||||||
|
# Should emit for non-integer slice step
|
||||||
|
var = [1, 2, 3][0:1:"x"]
|
||||||
|
var = [1, 2, 3][0:1:f"x"]
|
||||||
|
var = [1, 2, 3][0:1:1.2]
|
||||||
|
var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||||
|
var = [1, 2, 3][0:1:[x for x in range(2)]]
|
||||||
|
|
||||||
|
# Should emit for non-integer slice start and end; should emit twice with specific ranges
|
||||||
|
var = [1, 2, 3]["x":"y"]
|
||||||
|
|
||||||
|
# Should emit once for repeated invalid access
|
||||||
|
var = [1, 2, 3]["x"]["y"]["z"]
|
||||||
|
|
||||||
|
# Cannot emit on invalid access using variable in index
|
||||||
|
x = "x"
|
||||||
|
var = "abc"[x]
|
||||||
|
|
||||||
|
# Cannot emit on invalid access using call
|
||||||
|
def func():
|
||||||
|
return 1
|
||||||
|
var = "abc"[func()]
|
||||||
|
|
||||||
|
# Cannot emit on invalid access using a variable in parent
|
||||||
|
x = [1, 2, 3]
|
||||||
|
var = x["y"]
|
||||||
|
|
||||||
|
# Cannot emit for invalid access on byte array
|
||||||
|
var = bytearray(b"abc")["x"]
|
||||||
|
|
||||||
|
# Cannot emit for slice bound using variable
|
||||||
|
x = "x"
|
||||||
|
var = [1, 2, 3][0:x]
|
||||||
|
var = [1, 2, 3][x:1]
|
||||||
|
|
@ -2142,7 +2142,7 @@ where
|
||||||
|
|
||||||
// Pre-visit.
|
// Pre-visit.
|
||||||
match expr {
|
match expr {
|
||||||
subscript @ Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => {
|
Expr::Subscript(subscript @ ast::ExprSubscript { value, slice, .. }) => {
|
||||||
// Ex) Optional[...], Union[...]
|
// Ex) Optional[...], Union[...]
|
||||||
if self.any_enabled(&[
|
if self.any_enabled(&[
|
||||||
Rule::FutureRewritableTypeAnnotation,
|
Rule::FutureRewritableTypeAnnotation,
|
||||||
|
|
@ -2235,6 +2235,10 @@ where
|
||||||
ruff::rules::unnecessary_iterable_allocation_for_first_element(self, subscript);
|
ruff::rules::unnecessary_iterable_allocation_for_first_element(self, subscript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.enabled(Rule::InvalidIndexType) {
|
||||||
|
ruff::rules::invalid_index_type(self, subscript);
|
||||||
|
}
|
||||||
|
|
||||||
pandas_vet::rules::subscript(self, value, expr);
|
pandas_vet::rules::subscript(self, value, expr);
|
||||||
}
|
}
|
||||||
Expr::Tuple(ast::ExprTuple {
|
Expr::Tuple(ast::ExprTuple {
|
||||||
|
|
|
||||||
|
|
@ -781,6 +781,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
#[cfg(feature = "unreachable-code")]
|
#[cfg(feature = "unreachable-code")]
|
||||||
(Ruff, "014") => (RuleGroup::Nursery, rules::ruff::rules::UnreachableCode),
|
(Ruff, "014") => (RuleGroup::Nursery, rules::ruff::rules::UnreachableCode),
|
||||||
(Ruff, "015") => (RuleGroup::Unspecified, rules::ruff::rules::UnnecessaryIterableAllocationForFirstElement),
|
(Ruff, "015") => (RuleGroup::Unspecified, rules::ruff::rules::UnnecessaryIterableAllocationForFirstElement),
|
||||||
|
(Ruff, "016") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidIndexType),
|
||||||
(Ruff, "100") => (RuleGroup::Unspecified, rules::ruff::rules::UnusedNOQA),
|
(Ruff, "100") => (RuleGroup::Unspecified, rules::ruff::rules::UnusedNOQA),
|
||||||
(Ruff, "200") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidPyprojectToml),
|
(Ruff, "200") => (RuleGroup::Unspecified, rules::ruff::rules::InvalidPyprojectToml),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ mod tests {
|
||||||
Rule::UnnecessaryIterableAllocationForFirstElement,
|
Rule::UnnecessaryIterableAllocationForFirstElement,
|
||||||
Path::new("RUF015.py")
|
Path::new("RUF015.py")
|
||||||
)]
|
)]
|
||||||
|
#[test_case(Rule::InvalidIndexType, Path::new("RUF016.py"))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "unreachable-code",
|
feature = "unreachable-code",
|
||||||
test_case(Rule::UnreachableCode, Path::new("RUF014.py"))
|
test_case(Rule::UnreachableCode, Path::new("RUF014.py"))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,214 @@
|
||||||
|
use rustpython_parser::ast::{Constant, Expr, ExprConstant, ExprSlice, ExprSubscript, Ranged};
|
||||||
|
|
||||||
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for indexed access to lists, strings, tuples, bytes, and comprehensions
|
||||||
|
/// using a type other than an integer or slice.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// Only integers or slices can be used as indices to these types. Using
|
||||||
|
/// other types will result in a `TypeError` at runtime and a `SyntaxWarning` at
|
||||||
|
/// import time.
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
/// ```python
|
||||||
|
/// var = [1, 2, 3]["x"]
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// var = [1, 2, 3][0]
|
||||||
|
/// ```
|
||||||
|
#[violation]
|
||||||
|
pub struct InvalidIndexType {
|
||||||
|
value_type: String,
|
||||||
|
index_type: String,
|
||||||
|
is_slice: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Violation for InvalidIndexType {
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let InvalidIndexType {
|
||||||
|
value_type,
|
||||||
|
index_type,
|
||||||
|
is_slice,
|
||||||
|
} = self;
|
||||||
|
if *is_slice {
|
||||||
|
format!("Slice in indexed access to type `{value_type}` uses type `{index_type}` instead of an integer.")
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"Indexed access to type `{value_type}` uses type `{index_type}` instead of an integer or slice."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RUF015
|
||||||
|
pub(crate) fn invalid_index_type(checker: &mut Checker, expr: &ExprSubscript) {
|
||||||
|
let ExprSubscript {
|
||||||
|
value,
|
||||||
|
slice: index,
|
||||||
|
..
|
||||||
|
} = expr;
|
||||||
|
|
||||||
|
// Check the value being indexed is a list, tuple, string, f-string, bytes, or comprehension
|
||||||
|
if !matches!(
|
||||||
|
value.as_ref(),
|
||||||
|
Expr::List(_)
|
||||||
|
| Expr::ListComp(_)
|
||||||
|
| Expr::Tuple(_)
|
||||||
|
| Expr::JoinedStr(_)
|
||||||
|
| Expr::Constant(ExprConstant {
|
||||||
|
value: Constant::Str(_) | Constant::Bytes(_),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value types supported by this rule should always be checkable
|
||||||
|
let Some(value_type) = CheckableExprType::try_from(value) else {
|
||||||
|
debug_assert!(false, "Index value must be a checkable type to generate a violation message.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the index is not a checkable type then we can't easily determine if there is a violation
|
||||||
|
let Some(index_type) = CheckableExprType::try_from(index) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Then check the contents of the index
|
||||||
|
match index.as_ref() {
|
||||||
|
Expr::Constant(ExprConstant {
|
||||||
|
value: index_value, ..
|
||||||
|
}) => {
|
||||||
|
// If the index is a constant, require an integer
|
||||||
|
if !index_value.is_int() {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
InvalidIndexType {
|
||||||
|
value_type: value_type.to_string(),
|
||||||
|
index_type: constant_type_name(index_value).to_string(),
|
||||||
|
is_slice: false,
|
||||||
|
},
|
||||||
|
index.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Slice(ExprSlice {
|
||||||
|
lower, upper, step, ..
|
||||||
|
}) => {
|
||||||
|
// If the index is a slice, require integer or null bounds
|
||||||
|
for is_slice in [lower, upper, step].into_iter().flatten() {
|
||||||
|
if let Expr::Constant(ExprConstant {
|
||||||
|
value: index_value, ..
|
||||||
|
}) = is_slice.as_ref()
|
||||||
|
{
|
||||||
|
if !(index_value.is_int() || index_value.is_none()) {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
InvalidIndexType {
|
||||||
|
value_type: value_type.to_string(),
|
||||||
|
index_type: constant_type_name(index_value).to_string(),
|
||||||
|
is_slice: true,
|
||||||
|
},
|
||||||
|
is_slice.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else if let Some(is_slice_type) = CheckableExprType::try_from(is_slice.as_ref()) {
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
InvalidIndexType {
|
||||||
|
value_type: value_type.to_string(),
|
||||||
|
index_type: is_slice_type.to_string(),
|
||||||
|
is_slice: true,
|
||||||
|
},
|
||||||
|
is_slice.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// If it's some other checkable data type, it's a violation
|
||||||
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
|
InvalidIndexType {
|
||||||
|
value_type: value_type.to_string(),
|
||||||
|
index_type: index_type.to_string(),
|
||||||
|
is_slice: false,
|
||||||
|
},
|
||||||
|
index.range(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An expression that can be checked for type compatibility.
|
||||||
|
///
|
||||||
|
/// These are generally "literal" type expressions in that we know their concrete type
|
||||||
|
/// without additional analysis; opposed to expressions like a function call where we
|
||||||
|
/// cannot determine what type it may return.
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum CheckableExprType<'a> {
|
||||||
|
Constant(&'a Constant),
|
||||||
|
JoinedStr,
|
||||||
|
List,
|
||||||
|
ListComp,
|
||||||
|
SetComp,
|
||||||
|
DictComp,
|
||||||
|
Set,
|
||||||
|
Dict,
|
||||||
|
Tuple,
|
||||||
|
Slice,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CheckableExprType<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Constant(constant) => f.write_str(constant_type_name(constant)),
|
||||||
|
Self::JoinedStr => f.write_str("str"),
|
||||||
|
Self::List => f.write_str("list"),
|
||||||
|
Self::SetComp => f.write_str("set comprehension"),
|
||||||
|
Self::ListComp => f.write_str("list comprehension"),
|
||||||
|
Self::DictComp => f.write_str("dict comprehension"),
|
||||||
|
Self::Set => f.write_str("set"),
|
||||||
|
Self::Slice => f.write_str("slice"),
|
||||||
|
Self::Dict => f.write_str("dict"),
|
||||||
|
Self::Tuple => f.write_str("tuple"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CheckableExprType<'a> {
|
||||||
|
fn try_from(expr: &'a Expr) -> Option<Self> {
|
||||||
|
match expr {
|
||||||
|
Expr::Constant(ExprConstant { value, .. }) => Some(Self::Constant(value)),
|
||||||
|
Expr::JoinedStr(_) => Some(Self::JoinedStr),
|
||||||
|
Expr::List(_) => Some(Self::List),
|
||||||
|
Expr::ListComp(_) => Some(Self::ListComp),
|
||||||
|
Expr::SetComp(_) => Some(Self::SetComp),
|
||||||
|
Expr::DictComp(_) => Some(Self::DictComp),
|
||||||
|
Expr::Set(_) => Some(Self::Set),
|
||||||
|
Expr::Dict(_) => Some(Self::Dict),
|
||||||
|
Expr::Tuple(_) => Some(Self::Tuple),
|
||||||
|
Expr::Slice(_) => Some(Self::Slice),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constant_type_name(constant: &Constant) -> &'static str {
|
||||||
|
match constant {
|
||||||
|
Constant::None => "None",
|
||||||
|
Constant::Bool(_) => "bool",
|
||||||
|
Constant::Str(_) => "str",
|
||||||
|
Constant::Bytes(_) => "bytes",
|
||||||
|
Constant::Int(_) => "int",
|
||||||
|
Constant::Tuple(_) => "tuple",
|
||||||
|
Constant::Float(_) => "float",
|
||||||
|
Constant::Complex { .. } => "complex",
|
||||||
|
Constant::Ellipsis => "ellipsis",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ pub(crate) use collection_literal_concatenation::*;
|
||||||
pub(crate) use explicit_f_string_type_conversion::*;
|
pub(crate) use explicit_f_string_type_conversion::*;
|
||||||
pub(crate) use function_call_in_dataclass_default::*;
|
pub(crate) use function_call_in_dataclass_default::*;
|
||||||
pub(crate) use implicit_optional::*;
|
pub(crate) use implicit_optional::*;
|
||||||
|
pub(crate) use invalid_index_type::*;
|
||||||
pub(crate) use invalid_pyproject_toml::*;
|
pub(crate) use invalid_pyproject_toml::*;
|
||||||
pub(crate) use mutable_class_default::*;
|
pub(crate) use mutable_class_default::*;
|
||||||
pub(crate) use mutable_dataclass_default::*;
|
pub(crate) use mutable_dataclass_default::*;
|
||||||
|
|
@ -22,6 +23,7 @@ mod explicit_f_string_type_conversion;
|
||||||
mod function_call_in_dataclass_default;
|
mod function_call_in_dataclass_default;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
mod implicit_optional;
|
mod implicit_optional;
|
||||||
|
mod invalid_index_type;
|
||||||
mod invalid_pyproject_toml;
|
mod invalid_pyproject_toml;
|
||||||
mod mutable_class_default;
|
mod mutable_class_default;
|
||||||
mod mutable_dataclass_default;
|
mod mutable_dataclass_default;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::{One, Zero};
|
use num_traits::{One, Zero};
|
||||||
use rustpython_parser::ast::{self, Comprehension, Constant, Expr};
|
use rustpython_parser::ast::{self, Comprehension, Constant, Expr, ExprSubscript};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
|
@ -78,17 +78,14 @@ impl AlwaysAutofixableViolation for UnnecessaryIterableAllocationForFirstElement
|
||||||
/// RUF015
|
/// RUF015
|
||||||
pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
subscript: &Expr,
|
subscript: &ExprSubscript,
|
||||||
) {
|
) {
|
||||||
let Expr::Subscript(ast::ExprSubscript {
|
let ast::ExprSubscript {
|
||||||
value,
|
value,
|
||||||
slice,
|
slice,
|
||||||
range,
|
range,
|
||||||
..
|
..
|
||||||
}) = subscript
|
} = subscript;
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(subscript_kind) = classify_subscript(slice) else {
|
let Some(subscript_kind) = classify_subscript(slice) else {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,379 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff/src/rules/ruff/mod.rs
|
||||||
|
---
|
||||||
|
RUF016.py:20:13: RUF016 Indexed access to type `str` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
19 | # Should emit for invalid access on strings
|
||||||
|
20 | var = "abc"["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
21 | var = f"abc"["x"]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:21:14: RUF016 Indexed access to type `str` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
19 | # Should emit for invalid access on strings
|
||||||
|
20 | var = "abc"["x"]
|
||||||
|
21 | var = f"abc"["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
22 |
|
||||||
|
23 | # Should emit for invalid access on bytes
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:24:14: RUF016 Indexed access to type `bytes` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
23 | # Should emit for invalid access on bytes
|
||||||
|
24 | var = b"abc"["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
25 |
|
||||||
|
26 | # Should emit for invalid access on lists and tuples
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:27:17: RUF016 Indexed access to type `list` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
26 | # Should emit for invalid access on lists and tuples
|
||||||
|
27 | var = [1, 2, 3]["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
28 | var = (1, 2, 3)["x"]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:28:17: RUF016 Indexed access to type `tuple` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
26 | # Should emit for invalid access on lists and tuples
|
||||||
|
27 | var = [1, 2, 3]["x"]
|
||||||
|
28 | var = (1, 2, 3)["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
29 |
|
||||||
|
30 | # Should emit for invalid access on list comprehensions
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:31:30: RUF016 Indexed access to type `list comprehension` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
30 | # Should emit for invalid access on list comprehensions
|
||||||
|
31 | var = [x for x in range(10)]["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
32 |
|
||||||
|
33 | # Should emit for invalid access using tuple
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:34:13: RUF016 Indexed access to type `str` uses type `tuple` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
33 | # Should emit for invalid access using tuple
|
||||||
|
34 | var = "abc"[1, 2]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
35 |
|
||||||
|
36 | # Should emit for invalid access using string
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:37:14: RUF016 Indexed access to type `list` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
36 | # Should emit for invalid access using string
|
||||||
|
37 | var = [1, 2]["x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
38 |
|
||||||
|
39 | # Should emit for invalid access using float
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:40:14: RUF016 Indexed access to type `list` uses type `float` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
39 | # Should emit for invalid access using float
|
||||||
|
40 | var = [1, 2][0.25]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
41 |
|
||||||
|
42 | # Should emit for invalid access using dict
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:43:14: RUF016 Indexed access to type `list` uses type `dict` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
42 | # Should emit for invalid access using dict
|
||||||
|
43 | var = [1, 2][{"x": "y"}]
|
||||||
|
| ^^^^^^^^^^ RUF016
|
||||||
|
44 |
|
||||||
|
45 | # Should emit for invalid access using dict comp
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:46:14: RUF016 Indexed access to type `list` uses type `dict comprehension` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
45 | # Should emit for invalid access using dict comp
|
||||||
|
46 | var = [1, 2][{x: "y" for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
47 |
|
||||||
|
48 | # Should emit for invalid access using list
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:49:14: RUF016 Indexed access to type `list` uses type `tuple` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
48 | # Should emit for invalid access using list
|
||||||
|
49 | var = [1, 2][2, 3]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
50 |
|
||||||
|
51 | # Should emit for invalid access using list comp
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:52:14: RUF016 Indexed access to type `list` uses type `list comprehension` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
51 | # Should emit for invalid access using list comp
|
||||||
|
52 | var = [1, 2][[x for x in range(2)]]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
53 |
|
||||||
|
54 | # Should emit on invalid access using set
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:55:14: RUF016 Indexed access to type `list` uses type `set` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
54 | # Should emit on invalid access using set
|
||||||
|
55 | var = [1, 2][{"x", "y"}]
|
||||||
|
| ^^^^^^^^^^ RUF016
|
||||||
|
56 |
|
||||||
|
57 | # Should emit on invalid access using set comp
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:58:14: RUF016 Indexed access to type `list` uses type `set comprehension` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
57 | # Should emit on invalid access using set comp
|
||||||
|
58 | var = [1, 2][{x for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
59 |
|
||||||
|
60 | # Should emit on invalid access using bytes
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:61:14: RUF016 Indexed access to type `list` uses type `bytes` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
60 | # Should emit on invalid access using bytes
|
||||||
|
61 | var = [1, 2][b"x"]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
62 |
|
||||||
|
63 | # Should emit for non-integer slice start
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:64:17: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
63 | # Should emit for non-integer slice start
|
||||||
|
64 | var = [1, 2, 3]["x":2]
|
||||||
|
| ^^^ RUF016
|
||||||
|
65 | var = [1, 2, 3][f"x":2]
|
||||||
|
66 | var = [1, 2, 3][1.2:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:65:17: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
63 | # Should emit for non-integer slice start
|
||||||
|
64 | var = [1, 2, 3]["x":2]
|
||||||
|
65 | var = [1, 2, 3][f"x":2]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
66 | var = [1, 2, 3][1.2:2]
|
||||||
|
67 | var = [1, 2, 3][{"x"}:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:66:17: RUF016 Slice in indexed access to type `list` uses type `float` instead of an integer.
|
||||||
|
|
|
||||||
|
64 | var = [1, 2, 3]["x":2]
|
||||||
|
65 | var = [1, 2, 3][f"x":2]
|
||||||
|
66 | var = [1, 2, 3][1.2:2]
|
||||||
|
| ^^^ RUF016
|
||||||
|
67 | var = [1, 2, 3][{"x"}:2]
|
||||||
|
68 | var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:67:17: RUF016 Slice in indexed access to type `list` uses type `set` instead of an integer.
|
||||||
|
|
|
||||||
|
65 | var = [1, 2, 3][f"x":2]
|
||||||
|
66 | var = [1, 2, 3][1.2:2]
|
||||||
|
67 | var = [1, 2, 3][{"x"}:2]
|
||||||
|
| ^^^^^ RUF016
|
||||||
|
68 | var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
69 | var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:68:17: RUF016 Slice in indexed access to type `list` uses type `set comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
66 | var = [1, 2, 3][1.2:2]
|
||||||
|
67 | var = [1, 2, 3][{"x"}:2]
|
||||||
|
68 | var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
69 | var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||||
|
70 | var = [1, 2, 3][[x for x in range(2)]:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:69:17: RUF016 Slice in indexed access to type `list` uses type `dict comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
67 | var = [1, 2, 3][{"x"}:2]
|
||||||
|
68 | var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
69 | var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
70 | var = [1, 2, 3][[x for x in range(2)]:2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:70:17: RUF016 Slice in indexed access to type `list` uses type `list comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
68 | var = [1, 2, 3][{x for x in range(2)}:2]
|
||||||
|
69 | var = [1, 2, 3][{"x": x for x in range(2)}:2]
|
||||||
|
70 | var = [1, 2, 3][[x for x in range(2)]:2]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
71 |
|
||||||
|
72 | # Should emit for non-integer slice end
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:73:19: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
72 | # Should emit for non-integer slice end
|
||||||
|
73 | var = [1, 2, 3][0:"x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
74 | var = [1, 2, 3][0:f"x"]
|
||||||
|
75 | var = [1, 2, 3][0:1.2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:74:19: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
72 | # Should emit for non-integer slice end
|
||||||
|
73 | var = [1, 2, 3][0:"x"]
|
||||||
|
74 | var = [1, 2, 3][0:f"x"]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
75 | var = [1, 2, 3][0:1.2]
|
||||||
|
76 | var = [1, 2, 3][0:{"x"}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:75:19: RUF016 Slice in indexed access to type `list` uses type `float` instead of an integer.
|
||||||
|
|
|
||||||
|
73 | var = [1, 2, 3][0:"x"]
|
||||||
|
74 | var = [1, 2, 3][0:f"x"]
|
||||||
|
75 | var = [1, 2, 3][0:1.2]
|
||||||
|
| ^^^ RUF016
|
||||||
|
76 | var = [1, 2, 3][0:{"x"}]
|
||||||
|
77 | var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:76:19: RUF016 Slice in indexed access to type `list` uses type `set` instead of an integer.
|
||||||
|
|
|
||||||
|
74 | var = [1, 2, 3][0:f"x"]
|
||||||
|
75 | var = [1, 2, 3][0:1.2]
|
||||||
|
76 | var = [1, 2, 3][0:{"x"}]
|
||||||
|
| ^^^^^ RUF016
|
||||||
|
77 | var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
78 | var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:77:19: RUF016 Slice in indexed access to type `list` uses type `set comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
75 | var = [1, 2, 3][0:1.2]
|
||||||
|
76 | var = [1, 2, 3][0:{"x"}]
|
||||||
|
77 | var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
78 | var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||||
|
79 | var = [1, 2, 3][0:[x for x in range(2)]]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:78:19: RUF016 Slice in indexed access to type `list` uses type `dict comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
76 | var = [1, 2, 3][0:{"x"}]
|
||||||
|
77 | var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
78 | var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
79 | var = [1, 2, 3][0:[x for x in range(2)]]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:79:19: RUF016 Slice in indexed access to type `list` uses type `list comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
77 | var = [1, 2, 3][0:{x for x in range(2)}]
|
||||||
|
78 | var = [1, 2, 3][0:{"x": x for x in range(2)}]
|
||||||
|
79 | var = [1, 2, 3][0:[x for x in range(2)]]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
80 |
|
||||||
|
81 | # Should emit for non-integer slice step
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:82:21: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
81 | # Should emit for non-integer slice step
|
||||||
|
82 | var = [1, 2, 3][0:1:"x"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
83 | var = [1, 2, 3][0:1:f"x"]
|
||||||
|
84 | var = [1, 2, 3][0:1:1.2]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:83:21: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
81 | # Should emit for non-integer slice step
|
||||||
|
82 | var = [1, 2, 3][0:1:"x"]
|
||||||
|
83 | var = [1, 2, 3][0:1:f"x"]
|
||||||
|
| ^^^^ RUF016
|
||||||
|
84 | var = [1, 2, 3][0:1:1.2]
|
||||||
|
85 | var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:84:21: RUF016 Slice in indexed access to type `list` uses type `float` instead of an integer.
|
||||||
|
|
|
||||||
|
82 | var = [1, 2, 3][0:1:"x"]
|
||||||
|
83 | var = [1, 2, 3][0:1:f"x"]
|
||||||
|
84 | var = [1, 2, 3][0:1:1.2]
|
||||||
|
| ^^^ RUF016
|
||||||
|
85 | var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
86 | var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:85:21: RUF016 Slice in indexed access to type `list` uses type `set` instead of an integer.
|
||||||
|
|
|
||||||
|
83 | var = [1, 2, 3][0:1:f"x"]
|
||||||
|
84 | var = [1, 2, 3][0:1:1.2]
|
||||||
|
85 | var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
| ^^^^^ RUF016
|
||||||
|
86 | var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
87 | var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:86:21: RUF016 Slice in indexed access to type `list` uses type `set comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
84 | var = [1, 2, 3][0:1:1.2]
|
||||||
|
85 | var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
86 | var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
87 | var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||||
|
88 | var = [1, 2, 3][0:1:[x for x in range(2)]]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:87:21: RUF016 Slice in indexed access to type `list` uses type `dict comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
85 | var = [1, 2, 3][0:1:{"x"}]
|
||||||
|
86 | var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
87 | var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
88 | var = [1, 2, 3][0:1:[x for x in range(2)]]
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:88:21: RUF016 Slice in indexed access to type `list` uses type `list comprehension` instead of an integer.
|
||||||
|
|
|
||||||
|
86 | var = [1, 2, 3][0:1:{x for x in range(2)}]
|
||||||
|
87 | var = [1, 2, 3][0:1:{"x": x for x in range(2)}]
|
||||||
|
88 | var = [1, 2, 3][0:1:[x for x in range(2)]]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF016
|
||||||
|
89 |
|
||||||
|
90 | # Should emit for non-integer slice start and end; should emit twice with specific ranges
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:91:17: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
90 | # Should emit for non-integer slice start and end; should emit twice with specific ranges
|
||||||
|
91 | var = [1, 2, 3]["x":"y"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
92 |
|
||||||
|
93 | # Should emit once for repeated invalid access
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:91:21: RUF016 Slice in indexed access to type `list` uses type `str` instead of an integer.
|
||||||
|
|
|
||||||
|
90 | # Should emit for non-integer slice start and end; should emit twice with specific ranges
|
||||||
|
91 | var = [1, 2, 3]["x":"y"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
92 |
|
||||||
|
93 | # Should emit once for repeated invalid access
|
||||||
|
|
|
||||||
|
|
||||||
|
RUF016.py:94:17: RUF016 Indexed access to type `list` uses type `str` instead of an integer or slice.
|
||||||
|
|
|
||||||
|
93 | # Should emit once for repeated invalid access
|
||||||
|
94 | var = [1, 2, 3]["x"]["y"]["z"]
|
||||||
|
| ^^^ RUF016
|
||||||
|
95 |
|
||||||
|
96 | # Cannot emit on invalid access using variable in index
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2405,6 +2405,7 @@
|
||||||
"RUF013",
|
"RUF013",
|
||||||
"RUF014",
|
"RUF014",
|
||||||
"RUF015",
|
"RUF015",
|
||||||
|
"RUF016",
|
||||||
"RUF1",
|
"RUF1",
|
||||||
"RUF10",
|
"RUF10",
|
||||||
"RUF100",
|
"RUF100",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue