diff --git a/crates/ruff_linter/src/rules/flake8_2020/rules/compare.rs b/crates/ruff_linter/src/rules/flake8_2020/rules/compare.rs index 00e310e9d8..359be0dbcb 100644 --- a/crates/ruff_linter/src/rules/flake8_2020/rules/compare.rs +++ b/crates/ruff_linter/src/rules/flake8_2020/rules/compare.rs @@ -52,17 +52,20 @@ impl Violation for SysVersionCmpStr3 { /// ## What it does /// Checks for equality comparisons against the major version returned by -/// `sys.version_info` (e.g., `sys.version_info[0] == 3`). +/// `sys.version_info` (e.g., `sys.version_info[0] == 3` or `sys.version_info[0] != 3`). /// /// ## Why is this bad? /// Using `sys.version_info[0] == 3` to verify that the major version is /// Python 3 or greater will fail if the major version number is ever /// incremented (e.g., to Python 4). This is likely unintended, as code /// that uses this comparison is likely intended to be run on Python 2, -/// but would now run on Python 4 too. +/// but would now run on Python 4 too. Similarly, using `sys.version_info[0] != 3` +/// to check for Python 2 will also fail if the major version number is +/// incremented. /// /// Instead, use `>=` to check if the major version number is 3 or greater, -/// to future-proof the code. +/// or `<` to check if the major version number is less than 3, to future-proof +/// the code. /// /// ## Example /// ```python @@ -88,12 +91,18 @@ impl Violation for SysVersionCmpStr3 { /// - [Python documentation: `sys.version`](https://docs.python.org/3/library/sys.html#sys.version) /// - [Python documentation: `sys.version_info`](https://docs.python.org/3/library/sys.html#sys.version_info) #[derive(ViolationMetadata)] -pub(crate) struct SysVersionInfo0Eq3; +pub(crate) struct SysVersionInfo0Eq3 { + eq: bool, +} impl Violation for SysVersionInfo0Eq3 { #[derive_message_formats] fn message(&self) -> String { - "`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string() + if self.eq { + "`sys.version_info[0] == 3` referenced (python4), use `>=`".to_string() + } else { + "`sys.version_info[0] != 3` referenced (python4), use `<`".to_string() + } } } @@ -235,7 +244,7 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators { if *i == 0 { if let ( - [CmpOp::Eq | CmpOp::NotEq], + [operator @ (CmpOp::Eq | CmpOp::NotEq)], [ Expr::NumberLiteral(ast::ExprNumberLiteral { value: ast::Number::Int(n), @@ -246,7 +255,9 @@ pub(crate) fn compare(checker: &Checker, left: &Expr, ops: &[CmpOp], comparators { if *n == 3 && checker.enabled(Rule::SysVersionInfo0Eq3) { checker.report_diagnostic(Diagnostic::new( - SysVersionInfo0Eq3, + SysVersionInfo0Eq3 { + eq: matches!(*operator, CmpOp::Eq), + }, left.range(), )); } diff --git a/crates/ruff_linter/src/rules/flake8_2020/snapshots/ruff_linter__rules__flake8_2020__tests__YTT201_YTT201.py.snap b/crates/ruff_linter/src/rules/flake8_2020/snapshots/ruff_linter__rules__flake8_2020__tests__YTT201_YTT201.py.snap index c2ae9a2698..f5422cad3d 100644 --- a/crates/ruff_linter/src/rules/flake8_2020/snapshots/ruff_linter__rules__flake8_2020__tests__YTT201_YTT201.py.snap +++ b/crates/ruff_linter/src/rules/flake8_2020/snapshots/ruff_linter__rules__flake8_2020__tests__YTT201_YTT201.py.snap @@ -20,7 +20,7 @@ YTT201.py:8:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` 10 | PY2 = version_info[0] != 3 | -YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` +YTT201.py:9:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<` | 7 | PY3 = sys.version_info[0] == 3 8 | PY3 = version_info[0] == 3 @@ -29,7 +29,7 @@ YTT201.py:9:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` 10 | PY2 = version_info[0] != 3 | -YTT201.py:10:7: YTT201 `sys.version_info[0] == 3` referenced (python4), use `>=` +YTT201.py:10:7: YTT201 `sys.version_info[0] != 3` referenced (python4), use `<` | 8 | PY3 = version_info[0] == 3 9 | PY2 = sys.version_info[0] != 3