diff --git a/CHANGELOG.md b/CHANGELOG.md index 559b560dde4b..0fb96b266c7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4967,6 +4967,7 @@ Released 2018-09-13 [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err [`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity [`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds +[`unary_parenthesis_followed_by_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unary_parenthesis_followed_by_cast [`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction [`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks [`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index f24dab627809..61e3c80d2cf2 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -610,6 +610,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::types::REDUNDANT_ALLOCATION_INFO, crate::types::TYPE_COMPLEXITY_INFO, crate::types::VEC_BOX_INFO, + crate::unary_parenthesis_followed_by_cast::UNARY_PARENTHESIS_FOLLOWED_BY_CAST_INFO, crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO, crate::unicode::INVISIBLE_CHARACTERS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index bac82eca8174..0180c37e7b7b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -298,6 +298,7 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod types; +mod unary_parenthesis_followed_by_cast; mod undocumented_unsafe_blocks; mod unicode; mod uninit_vec; @@ -960,6 +961,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments)); + store.register_early_pass(|| Box::new(unary_parenthesis_followed_by_cast::UnaryParenthesisFollowedByCast)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unary_parenthesis_followed_by_cast.rs b/clippy_lints/src/unary_parenthesis_followed_by_cast.rs new file mode 100644 index 000000000000..52ca6a965107 --- /dev/null +++ b/clippy_lints/src/unary_parenthesis_followed_by_cast.rs @@ -0,0 +1,67 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, Path}; +use rustc_ast::ast_traits::AstDeref; +use rustc_ast::ptr::P; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for cast which argument is parenthesized variable. + /// + /// ### Why is this bad? + /// It's same effect as `variable as Type`, thus you don't need parentheses. + /// + /// ### Example + /// ```rust + /// fn no_op(arg_1: f64) {} + /// + /// let x = (1.0f32) as f64; + /// let y = (2.0f32) as f64; + /// no_op(y); + /// ``` + /// Use instead: + /// ```rust + /// fn no_op(arg_1: f64) {} + /// + /// let x = 1.0f32 as f64; + /// let y = 2.0f32 as f64; + /// no_op(y); + /// ``` + #[clippy::version = "1.70.0"] + pub UNARY_PARENTHESIS_FOLLOWED_BY_CAST, + complexity, + "`as` cast with parenthesized simple argument" +} +declare_lint_pass!(UnaryParenthesisFollowedByCast => [UNARY_PARENTHESIS_FOLLOWED_BY_CAST]); + +impl EarlyLintPass for UnaryParenthesisFollowedByCast { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let ExprKind::Cast(ref expr, _) = expr.kind + && let ExprKind::Paren(ref parenthesized) = expr.kind + && is_item_path_is_local_and_not_qualified(parenthesized) + { + span_lint_and_help( + cx, + UNARY_PARENTHESIS_FOLLOWED_BY_CAST, + expr.span, + "unnecessary parenthesis", + None, + "consider remove parenthesis" + ); + } + } +} + +fn is_item_path_is_local_and_not_qualified(parenthesized: &P<Expr>) -> bool { + if let ExprKind::Path(ref impl_qualifier, ref item_path) = parenthesized.ast_deref().kind + && impl_qualifier.is_none() + // is item_path local variable? + && !item_path.is_global() + && let Path { segments, .. } = item_path + && segments.len() == 1 { + true + } else { + false + } +} diff --git a/tests/ui/unary_parenthesis_followed_by_cast.rs b/tests/ui/unary_parenthesis_followed_by_cast.rs new file mode 100644 index 000000000000..1e7dc367ca52 --- /dev/null +++ b/tests/ui/unary_parenthesis_followed_by_cast.rs @@ -0,0 +1,12 @@ +#![allow(unused)] +#![allow(clippy::unnecessary_cast)] +#![warn(clippy::unary_parenthesis_followed_by_cast)] + +fn hello(arg_1: f64) {} + +fn main() { + // fire + let x = 3.0f32; + + hello((x) as f64); +} diff --git a/tests/ui/unary_parenthesis_followed_by_cast.stderr b/tests/ui/unary_parenthesis_followed_by_cast.stderr new file mode 100644 index 000000000000..3096e614b85e --- /dev/null +++ b/tests/ui/unary_parenthesis_followed_by_cast.stderr @@ -0,0 +1,11 @@ +error: unnecessary parenthesis + --> $DIR/unary_parenthesis_followed_by_cast.rs:11:11 + | +LL | hello((x) as f64); + | ^^^ + | + = help: consider remove parenthesis + = note: `-D clippy::unary-parenthesis-followed-by-cast` implied by `-D warnings` + +error: aborting due to previous error +