From 7c1a6bce7beedef7119495b8354a7524de8d1dab Mon Sep 17 00:00:00 2001 From: Maksudul Haque <saad.mk112@gmail.com> Date: Wed, 1 Feb 2023 05:09:40 +0600 Subject: [PATCH] [`flake8-raise`] Add Plugin and `RSE102` Rule (#2354) --- LICENSE | 25 +++++++++++++++ README.md | 11 +++++++ .../test/fixtures/flake8_raise/RSE102.py | 15 +++++++++ ruff.schema.json | 4 +++ src/checkers/ast.rs | 11 ++++++- src/registry.rs | 5 +++ src/rules/flake8_raise/mod.rs | 28 +++++++++++++++++ src/rules/flake8_raise/rules/mod.rs | 5 +++ .../unnecessary_paren_on_raise_exception.rs | 31 +++++++++++++++++++ ...ry-paren-on-raise-exception_RSE102.py.snap | 25 +++++++++++++++ src/rules/mod.rs | 1 + 11 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 resources/test/fixtures/flake8_raise/RSE102.py create mode 100644 src/rules/flake8_raise/mod.rs create mode 100644 src/rules/flake8_raise/rules/mod.rs create mode 100644 src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs create mode 100644 src/rules/flake8_raise/snapshots/ruff__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap diff --git a/LICENSE b/LICENSE index 7811d42465347d..d7dbadbd38591c 100644 --- a/LICENSE +++ b/LICENSE @@ -1005,3 +1005,28 @@ are: See the License for the specific language governing permissions and limitations under the License. """ + +- flake8-raise, licensed as follows: + """ + MIT License + + Copyright (c) 2020 Jon Dufresne + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + """ diff --git a/README.md b/README.md index d83845dd90ffc2..a4fd8ceb80d366 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ developer of [Zulip](https://github.com/zulip/zulip): 1. [pygrep-hooks (PGH)](#pygrep-hooks-pgh) 1. [Pylint (PL)](#pylint-pl) 1. [tryceratops (TRY)](#tryceratops-try) + 1. [flake8-raise (RSE)](#flake8-raise-rse) 1. [Ruff-specific rules (RUF)](#ruff-specific-rules-ruf)<!-- End auto-generated table of contents. --> 1. [Editor Integrations](#editor-integrations) 1. [FAQ](#faq) @@ -1357,6 +1358,14 @@ For more, see [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) on PyPI | TRY301 | raise-within-try | Abstract `raise` to an inner function | | | TRY400 | error-instead-of-exception | Use `logging.exception` instead of `logging.error` | | +### flake8-raise (RSE) + +For more, see [flake8-raise](https://pypi.org/project/flake8-raise/) on PyPI. + +| Code | Name | Message | Fix | +| ---- | ---- | ------- | --- | +| RSE102 | unnecessary-paren-on-raise-exception | Unnecessary parentheses on raised exception | | + ### Ruff-specific rules (RUF) | Code | Name | Message | Fix | @@ -1648,6 +1657,7 @@ natively, including: - [`flake8-print`](https://pypi.org/project/flake8-print/) - [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/) - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/) +- [`flake8-raise`](https://pypi.org/project/flake8-raise/) - [`flake8-return`](https://pypi.org/project/flake8-return/) - [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) - [`flake8-super`](https://pypi.org/project/flake8-super/) @@ -1736,6 +1746,7 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl - [`flake8-print`](https://pypi.org/project/flake8-print/) - [`flake8-pytest-style`](https://pypi.org/project/flake8-pytest-style/) - [`flake8-quotes`](https://pypi.org/project/flake8-quotes/) +- [`flake8-raise`](https://pypi.org/project/flake8-raise/) - [`flake8-return`](https://pypi.org/project/flake8-return/) - [`flake8-simplify`](https://pypi.org/project/flake8-simplify/) ([#998](https://github.com/charliermarsh/ruff/issues/998)) - [`flake8-super`](https://pypi.org/project/flake8-super/) diff --git a/resources/test/fixtures/flake8_raise/RSE102.py b/resources/test/fixtures/flake8_raise/RSE102.py new file mode 100644 index 00000000000000..ba377a856b2e17 --- /dev/null +++ b/resources/test/fixtures/flake8_raise/RSE102.py @@ -0,0 +1,15 @@ +try: + y = 6 + "7" +except TypeError: + raise ValueError() # RSE102 + +try: + x = 1 / 0 +except ZeroDivisionError: + raise + +raise TypeError() # RSE102 + +raise AssertionError + +raise AttributeError("test message") diff --git a/ruff.schema.json b/ruff.schema.json index 16f6986bd08d5e..f8e532a1b99426 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -1739,6 +1739,10 @@ "RET506", "RET507", "RET508", + "RSE", + "RSE1", + "RSE10", + "RSE102", "RUF", "RUF0", "RUF00", diff --git a/src/checkers/ast.rs b/src/checkers/ast.rs index 8f2bd34e794885..b885deba7322d6 100644 --- a/src/checkers/ast.rs +++ b/src/checkers/ast.rs @@ -36,7 +36,7 @@ use crate::rules::{ flake8_2020, flake8_annotations, flake8_bandit, flake8_blind_except, flake8_boolean_trap, flake8_bugbear, flake8_builtins, flake8_comprehensions, flake8_datetimez, flake8_debugger, flake8_errmsg, flake8_implicit_str_concat, flake8_import_conventions, flake8_logging_format, - flake8_pie, flake8_print, flake8_pytest_style, flake8_return, flake8_simplify, + flake8_pie, flake8_print, flake8_pytest_style, flake8_raise, flake8_return, flake8_simplify, flake8_tidy_imports, flake8_type_checking, flake8_unused_arguments, flake8_use_pathlib, mccabe, pandas_vet, pep8_naming, pycodestyle, pydocstyle, pyflakes, pygrep_hooks, pylint, pyupgrade, ruff, tryceratops, @@ -1434,6 +1434,15 @@ where tryceratops::rules::raise_vanilla_args(self, expr); } } + if self + .settings + .rules + .enabled(&Rule::UnnecessaryParenOnRaiseException) + { + if let Some(expr) = exc { + flake8_raise::rules::unnecessary_paren_on_raise_exception(self, expr); + } + } } StmtKind::AugAssign { target, .. } => { self.handle_node_load(target); diff --git a/src/registry.rs b/src/registry.rs index fd7f9484ee2352..3f656eec1283c9 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -481,6 +481,8 @@ ruff_macros::define_rule_mapping!( G101 => rules::flake8_logging_format::violations::LoggingExtraAttrClash, G201 => rules::flake8_logging_format::violations::LoggingExcInfo, G202 => rules::flake8_logging_format::violations::LoggingRedundantExcInfo, + // flake8-raise + RSE102 => rules::flake8_raise::rules::UnnecessaryParenOnRaiseException, // ruff RUF001 => violations::AmbiguousUnicodeCharacterString, RUF002 => violations::AmbiguousUnicodeCharacterDocstring, @@ -610,6 +612,9 @@ pub enum Linter { /// [tryceratops](https://pypi.org/project/tryceratops/1.1.0/) #[prefix = "TRY"] Tryceratops, + /// [flake8-raise](https://pypi.org/project/flake8-raise/) + #[prefix = "RSE"] + Flake8Raise, /// Ruff-specific rules #[prefix = "RUF"] Ruff, diff --git a/src/rules/flake8_raise/mod.rs b/src/rules/flake8_raise/mod.rs new file mode 100644 index 00000000000000..21345e25c77140 --- /dev/null +++ b/src/rules/flake8_raise/mod.rs @@ -0,0 +1,28 @@ +//! Rules from [flake8-raise](https://pypi.org/project/flake8-raise/). +pub(crate) mod rules; + +#[cfg(test)] +mod tests { + use std::convert::AsRef; + use std::path::Path; + + use anyhow::Result; + use test_case::test_case; + + use crate::linter::test_path; + use crate::registry::Rule; + use crate::{assert_yaml_snapshot, settings}; + + #[test_case(Rule::UnnecessaryParenOnRaiseException, Path::new("RSE102.py"); "RSE102")] + fn rules(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!("{}_{}", rule_code.as_ref(), path.to_string_lossy()); + let diagnostics = test_path( + Path::new("./resources/test/fixtures/flake8_raise") + .join(path) + .as_path(), + &settings::Settings::for_rule(rule_code), + )?; + assert_yaml_snapshot!(snapshot, diagnostics); + Ok(()) + } +} diff --git a/src/rules/flake8_raise/rules/mod.rs b/src/rules/flake8_raise/rules/mod.rs new file mode 100644 index 00000000000000..7177469472841a --- /dev/null +++ b/src/rules/flake8_raise/rules/mod.rs @@ -0,0 +1,5 @@ +pub use unnecessary_paren_on_raise_exception::{ + unnecessary_paren_on_raise_exception, UnnecessaryParenOnRaiseException, +}; + +mod unnecessary_paren_on_raise_exception; diff --git a/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs b/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs new file mode 100644 index 00000000000000..40f26e98475c8c --- /dev/null +++ b/src/rules/flake8_raise/rules/unnecessary_paren_on_raise_exception.rs @@ -0,0 +1,31 @@ +use ruff_macros::derive_message_formats; + +use crate::ast::types::Range; +use crate::checkers::ast::Checker; +use crate::define_violation; +use crate::registry::Diagnostic; +use crate::violation::Violation; +use rustpython_ast::{Expr, ExprKind}; + +define_violation!( + pub struct UnnecessaryParenOnRaiseException; +); +impl Violation for UnnecessaryParenOnRaiseException { + #[derive_message_formats] + fn message(&self) -> String { + format!("Unnecessary parentheses on raised exception") + } +} + +/// RSE102 +pub fn unnecessary_paren_on_raise_exception(checker: &mut Checker, expr: &Expr) { + match &expr.node { + ExprKind::Call { args, keywords, .. } if args.is_empty() && keywords.is_empty() => { + checker.diagnostics.push(Diagnostic::new( + UnnecessaryParenOnRaiseException, + Range::from_located(expr), + )); + } + _ => (), + } +} diff --git a/src/rules/flake8_raise/snapshots/ruff__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap b/src/rules/flake8_raise/snapshots/ruff__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap new file mode 100644 index 00000000000000..c023452f3bf5e6 --- /dev/null +++ b/src/rules/flake8_raise/snapshots/ruff__rules__flake8_raise__tests__unnecessary-paren-on-raise-exception_RSE102.py.snap @@ -0,0 +1,25 @@ +--- +source: src/rules/flake8_raise/mod.rs +expression: diagnostics +--- +- kind: + UnnecessaryParenOnRaiseException: ~ + location: + row: 4 + column: 10 + end_location: + row: 4 + column: 22 + fix: ~ + parent: ~ +- kind: + UnnecessaryParenOnRaiseException: ~ + location: + row: 11 + column: 6 + end_location: + row: 11 + column: 17 + fix: ~ + parent: ~ + diff --git a/src/rules/mod.rs b/src/rules/mod.rs index 30f9c28d255a65..38ae33a825c3ce 100644 --- a/src/rules/mod.rs +++ b/src/rules/mod.rs @@ -21,6 +21,7 @@ pub mod flake8_pie; pub mod flake8_print; pub mod flake8_pytest_style; pub mod flake8_quotes; +pub mod flake8_raise; pub mod flake8_return; pub mod flake8_simplify; pub mod flake8_tidy_imports;