From ff677a96e47b9be3bcffcd3867d4d2c7f1038335 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sat, 13 Sep 2025 03:37:02 +0900 Subject: [PATCH] [`ruff`] Recognize t-strings, generators, and lambdas in `invalid-index-type` (`RUF016`) (#20213) ## Summary Fixes #20204 Recognize t-strings, generators, and lambdas in RUF016 - Accept boolean literals as valid index and slice bounds. - Add TString, Generator, and Lambda to `CheckableExprType`. - Expand RUF016.py fixture and update snapshots accordingly. --- .../resources/test/fixtures/ruff/RUF016.py | 15 ++++++++++ .../rules/ruff/rules/invalid_index_type.rs | 17 +++++++++-- ..._rules__ruff__tests__RUF016_RUF016.py.snap | 28 +++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF016.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF016.py index 545ad2ec53..815456fc93 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF016.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF016.py @@ -113,3 +113,18 @@ var = bytearray(b"abc")["x"] x = "x" var = [1, 2, 3][0:x] var = [1, 2, 3][x:1] + +# https://github.com/astral-sh/ruff/issues/20204 + +# Should not emit for boolean index and slice bounds +var = [1, 2, 3][False] +var = [1, 2, 3][False:True:True] + +# Should emit for invalid access using t-string +var = [1, 2][t"x"] + +# Should emit for invalid access using lambda +var = [1, 2, 3][lambda: 0] + +# Should emit for invalid access using generator +var = [1, 2, 3][(x for x in ())] diff --git a/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs b/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs index 067a074718..5c038455a0 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/invalid_index_type.rs @@ -89,7 +89,9 @@ pub(crate) fn invalid_index_type(checker: &Checker, expr: &ExprSubscript) { if index_type.is_literal() { // If the index is a literal, require an integer - if index_type != CheckableExprType::IntLiteral { + if index_type != CheckableExprType::IntLiteral + && index_type != CheckableExprType::BooleanLiteral + { checker.report_diagnostic( InvalidIndexType { value_type: value_type.to_string(), @@ -111,7 +113,9 @@ pub(crate) fn invalid_index_type(checker: &Checker, expr: &ExprSubscript) { // If the index is a slice, require integer or null bounds if !matches!( is_slice_type, - CheckableExprType::IntLiteral | CheckableExprType::NoneLiteral + CheckableExprType::IntLiteral + | CheckableExprType::NoneLiteral + | CheckableExprType::BooleanLiteral ) { checker.report_diagnostic( InvalidIndexType { @@ -154,6 +158,7 @@ pub(crate) fn invalid_index_type(checker: &Checker, expr: &ExprSubscript) { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum CheckableExprType { FString, + TString, StringLiteral, BytesLiteral, IntLiteral, @@ -170,12 +175,15 @@ enum CheckableExprType { Dict, Tuple, Slice, + Generator, + Lambda, } impl fmt::Display for CheckableExprType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::FString => f.write_str("str"), + Self::TString => f.write_str("Template"), Self::StringLiteral => f.write_str("str"), Self::BytesLiteral => f.write_str("bytes"), Self::IntLiteral => f.write_str("int"), @@ -192,6 +200,8 @@ impl fmt::Display for CheckableExprType { Self::Slice => f.write_str("slice"), Self::Dict => f.write_str("dict"), Self::Tuple => f.write_str("tuple"), + Self::Generator => f.write_str("generator"), + Self::Lambda => f.write_str("lambda"), } } } @@ -209,6 +219,7 @@ impl CheckableExprType { Expr::BooleanLiteral(_) => Some(Self::BooleanLiteral), Expr::NoneLiteral(_) => Some(Self::NoneLiteral), Expr::EllipsisLiteral(_) => Some(Self::EllipsisLiteral), + Expr::TString(_) => Some(Self::TString), Expr::FString(_) => Some(Self::FString), Expr::List(_) => Some(Self::List), Expr::ListComp(_) => Some(Self::ListComp), @@ -218,6 +229,8 @@ impl CheckableExprType { Expr::Dict(_) => Some(Self::Dict), Expr::Tuple(_) => Some(Self::Tuple), Expr::Slice(_) => Some(Self::Slice), + Expr::Generator(_) => Some(Self::Generator), + Expr::Lambda(_) => Some(Self::Lambda), _ => None, } } diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF016_RUF016.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF016_RUF016.py.snap index e2f4ab0278..22917b601a 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF016_RUF016.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF016_RUF016.py.snap @@ -415,3 +415,31 @@ RUF016 Indexed access to type `list` uses type `str` instead of an integer or sl 95 | 96 | # Cannot emit on invalid access using variable in index | + +RUF016 Indexed access to type `list` uses type `Template` instead of an integer or slice + --> RUF016.py:124:14 + | +123 | # Should emit for invalid access using t-string +124 | var = [1, 2][t"x"] + | ^^^^ +125 | +126 | # Should emit for invalid access using lambda + | + +RUF016 Indexed access to type `list` uses type `lambda` instead of an integer or slice + --> RUF016.py:127:17 + | +126 | # Should emit for invalid access using lambda +127 | var = [1, 2, 3][lambda: 0] + | ^^^^^^^^^ +128 | +129 | # Should emit for invalid access using generator + | + +RUF016 Indexed access to type `list` uses type `generator` instead of an integer or slice + --> RUF016.py:130:17 + | +129 | # Should emit for invalid access using generator +130 | var = [1, 2, 3][(x for x in ())] + | ^^^^^^^^^^^^^^^ + |