Skip to content

Commit

Permalink
[pylint] - add potential-index-error rule (PLE0643)
Browse files Browse the repository at this point in the history
  • Loading branch information
diceroll123 committed Jan 16, 2024
1 parent 21f2d0c commit 652603a
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
print([1, 2, 3][3]) # PLE0643
print([1, 2, 3][-4]) # PLE0643
print([1, 2, 3][2147483647]) # PLE0643
print([1, 2, 3][-2147483647]) # PLE0643

print([1, 2, 3][2]) # OK
print([1, 2, 3][0]) # OK
print([1, 2, 3][-3]) # OK
print([1, 2, 3][3:]) # OK
print([1, 2, 3][2147483648]) # OK (i32 overflow, ignored)
print([1, 2, 3][-2147483648]) # OK (i32 overflow, ignored)
3 changes: 3 additions & 0 deletions crates/ruff_linter/src/checkers/ast/analyze/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) {
if checker.enabled(Rule::SliceCopy) {
refurb::rules::slice_copy(checker, subscript);
}
if checker.enabled(Rule::PotentialIndexError) {
pylint::rules::potential_index_error(checker, value, slice);
}

pandas_vet::rules::subscript(checker, value, expr);
}
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "E0307") => (RuleGroup::Stable, rules::pylint::rules::InvalidStrReturnType),
(Pylint, "E0604") => (RuleGroup::Stable, rules::pylint::rules::InvalidAllObject),
(Pylint, "E0605") => (RuleGroup::Stable, rules::pylint::rules::InvalidAllFormat),
(Pylint, "E0643") => (RuleGroup::Preview, rules::pylint::rules::PotentialIndexError),
(Pylint, "E0704") => (RuleGroup::Preview, rules::pylint::rules::MisplacedBareRaise),
(Pylint, "E1132") => (RuleGroup::Preview, rules::pylint::rules::RepeatedKeywordArgument),
(Pylint, "E1142") => (RuleGroup::Stable, rules::pylint::rules::AwaitOutsideAsync),
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pylint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ mod tests {
#[test_case(Rule::NoClassmethodDecorator, Path::new("no_method_decorator.py"))]
#[test_case(Rule::UnnecessaryDunderCall, Path::new("unnecessary_dunder_call.py"))]
#[test_case(Rule::NoStaticmethodDecorator, Path::new("no_method_decorator.py"))]
#[test_case(Rule::PotentialIndexError, Path::new("potential_index_error.py"))]
#[test_case(Rule::SuperWithoutBrackets, Path::new("super_without_brackets.py"))]
#[test_case(
Rule::UnnecessaryDictIndexLookup,
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/pylint/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) use no_self_use::*;
pub(crate) use non_ascii_module_import::*;
pub(crate) use non_ascii_name::*;
pub(crate) use nonlocal_without_binding::*;
pub(crate) use potential_index_error::*;
pub(crate) use property_with_parameters::*;
pub(crate) use redefined_argument_from_local::*;
pub(crate) use redefined_loop_name::*;
Expand Down Expand Up @@ -124,6 +125,7 @@ mod no_self_use;
mod non_ascii_module_import;
mod non_ascii_name;
mod nonlocal_without_binding;
mod potential_index_error;
mod property_with_parameters;
mod redefined_argument_from_local;
mod redefined_loop_name;
Expand Down
77 changes: 77 additions & 0 deletions crates/ruff_linter/src/rules/pylint/rules/potential_index_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use ruff_python_ast::{self as ast, Expr};

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_text_size::TextRange;

use crate::checkers::ast::Checker;

/// ## What it does
/// Checks for potential hard-coded IndexErrors, which occurs when accessing
/// a list or tuple with an index that is known to be out of bounds.
///
/// ## Why is this bad?
/// This will cause a runtime error.
///
/// ## Example
/// ```python
/// print([1, 2, 3][123])
/// ```
///
#[violation]
pub struct PotentialIndexError;

impl Violation for PotentialIndexError {
#[derive_message_formats]
fn message(&self) -> String {
format!("Potential IndexError")
}
}

/// PLE0643
pub(crate) fn potential_index_error(checker: &mut Checker, value: &Expr, slice: &Expr) {
let length = match value {
Expr::Tuple(ast::ExprTuple { elts, .. }) | Expr::List(ast::ExprList { elts, .. }) => {
i32::try_from(elts.len())
}
_ => {
return;
}
};

let Ok(length) = length else {
return;
};

let (number_value, range) = match slice {
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(number_value),
range,
}) => (number_value.as_i32(), *range),
Expr::UnaryOp(ast::ExprUnaryOp {
op: ast::UnaryOp::USub,
operand,
range,
}) => match operand.as_ref() {
Expr::NumberLiteral(ast::ExprNumberLiteral {
value: ast::Number::Int(number_value),
..
}) => match number_value.as_i32() {
Some(value) => (Some(-value), *range),
None => (None, TextRange::default()),
},
_ => (None, TextRange::default()),
},
_ => (None, TextRange::default()),
};

let Some(number_value) = number_value else {
return;
};

if number_value >= length || number_value < -length {
checker
.diagnostics
.push(Diagnostic::new(PotentialIndexError, range));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
potential_index_error.py:1:17: PLE0643 Potential IndexError
|
1 | print([1, 2, 3][3]) # PLE0643
| ^ PLE0643
2 | print([1, 2, 3][-4]) # PLE0643
3 | print([1, 2, 3][2147483647]) # PLE0643
|

potential_index_error.py:2:17: PLE0643 Potential IndexError
|
1 | print([1, 2, 3][3]) # PLE0643
2 | print([1, 2, 3][-4]) # PLE0643
| ^^ PLE0643
3 | print([1, 2, 3][2147483647]) # PLE0643
4 | print([1, 2, 3][-2147483647]) # PLE0643
|

potential_index_error.py:3:17: PLE0643 Potential IndexError
|
1 | print([1, 2, 3][3]) # PLE0643
2 | print([1, 2, 3][-4]) # PLE0643
3 | print([1, 2, 3][2147483647]) # PLE0643
| ^^^^^^^^^^ PLE0643
4 | print([1, 2, 3][-2147483647]) # PLE0643
|

potential_index_error.py:4:17: PLE0643 Potential IndexError
|
2 | print([1, 2, 3][-4]) # PLE0643
3 | print([1, 2, 3][2147483647]) # PLE0643
4 | print([1, 2, 3][-2147483647]) # PLE0643
| ^^^^^^^^^^^ PLE0643
5 |
6 | print([1, 2, 3][2]) # OK
|


2 changes: 2 additions & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 652603a

Please sign in to comment.