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:
Zanie 2023-07-12 00:23:06 -05:00 committed by GitHub
parent 7566ca8ff7
commit 0666added9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 722 additions and 8 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1
ruff.schema.json generated
View File

@ -2405,6 +2405,7 @@
"RUF013", "RUF013",
"RUF014", "RUF014",
"RUF015", "RUF015",
"RUF016",
"RUF1", "RUF1",
"RUF10", "RUF10",
"RUF100", "RUF100",