about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.register_all.rs1
-rw-r--r--clippy_lints/src/lib.register_lints.rs1
-rw-r--r--clippy_lints/src/lib.register_style.rs1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/needless_parens_on_range_literals.rs87
-rw-r--r--tests/ui/almost_complete_letter_range.fixed1
-rw-r--r--tests/ui/almost_complete_letter_range.rs1
-rw-r--r--tests/ui/almost_complete_letter_range.stderr24
-rw-r--r--tests/ui/needless_parens_on_range_literals.fixed14
-rw-r--r--tests/ui/needless_parens_on_range_literals.rs14
-rw-r--r--tests/ui/needless_parens_on_range_literals.stderr40
12 files changed, 175 insertions, 12 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ef338b819d..462e56892fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3605,6 +3605,7 @@ Released 2018-09-13
 [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
+[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
 [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index d4ec046d0bb..4691f957cb4 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -242,6 +242,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
+    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index b927ba3b17c..efb073af6b6 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -408,6 +408,7 @@ store.register_lints(&[
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
     needless_late_init::NEEDLESS_LATE_INIT,
+    needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
     needless_update::NEEDLESS_UPDATE,
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index 35575351784..b6992ae0ad2 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -91,6 +91,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
     LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
+    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
     LintId::of(neg_multiply::NEG_MULTIPLY),
     LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ee0416fc0ff..a0a6bab407c 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -315,6 +315,7 @@ mod needless_borrowed_ref;
 mod needless_continue;
 mod needless_for_each;
 mod needless_late_init;
+mod needless_parens_on_range_literals;
 mod needless_pass_by_value;
 mod needless_question_mark;
 mod needless_update;
@@ -746,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
     store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
     store.register_early_pass(|| Box::new(precedence::Precedence));
+    store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
     store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
     store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
     store.register_late_pass(|| Box::new(create_dir::CreateDir));
diff --git a/clippy_lints/src/needless_parens_on_range_literals.rs b/clippy_lints/src/needless_parens_on_range_literals.rs
new file mode 100644
index 00000000000..6e54b243c03
--- /dev/null
+++ b/clippy_lints/src/needless_parens_on_range_literals.rs
@@ -0,0 +1,87 @@
+use clippy_utils::{
+    diagnostics::span_lint_and_then,
+    higher,
+    source::{snippet, snippet_with_applicability},
+};
+
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+  /// ### What it does
+  /// The lint checks for parenthesis on literals in range statements that are
+  /// superfluous.
+  ///
+  /// ### Why is this bad?
+  /// Having superfluous parenthesis makes the code less readable
+  /// overhead when reading.
+  ///
+  /// ### Example
+  ///
+  /// ```rust
+  /// for i in (0)..10 {
+  ///   println!("{i}");
+  /// }
+  /// ```
+  ///
+  /// Use instead:
+  ///
+  /// ```rust
+  /// for i in 0..10 {
+  ///   println!("{i}");
+  /// }
+  /// ```
+  #[clippy::version = "1.63.0"]
+  pub NEEDLESS_PARENS_ON_RANGE_LITERALS,
+  style,
+  "needless parenthesis on range literals can be removed"
+}
+
+declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]);
+
+fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
+    snippet.starts_with('(') && snippet.ends_with(')')
+}
+
+fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
+    if is_start &&
+    let ExprKind::Lit(ref literal) = e.kind &&
+    let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
+    {
+        // don't check floating point literals on the start expression of a range
+        return;
+    }
+    if_chain! {
+        if let ExprKind::Lit(ref literal) = e.kind;
+        // the indicator that parenthesis surround the literal is that the span of the expression and the literal differ
+        if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo);
+        // inspect the source code of the expression for parenthesis
+        if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, ""));
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span,
+                "needless parenthesis on range literals can be removed",
+                |diag| {
+                    let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability);
+                    diag.span_suggestion(e.span, "try", suggestion, applicability);
+                });
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) {
+            if let Some(start) = start {
+                check_for_parens(cx, start, true);
+            }
+            if let Some(end) = end {
+                check_for_parens(cx, end, false);
+            }
+        }
+    }
+}
diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed
index 39f8f0c2949..e69b40f35f4 100644
--- a/tests/ui/almost_complete_letter_range.fixed
+++ b/tests/ui/almost_complete_letter_range.fixed
@@ -6,6 +6,7 @@
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
 #![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
 
 macro_rules! a {
     () => {
diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs
index 3dc02199257..f2240981d45 100644
--- a/tests/ui/almost_complete_letter_range.rs
+++ b/tests/ui/almost_complete_letter_range.rs
@@ -6,6 +6,7 @@
 #![feature(stmt_expr_attributes)]
 #![warn(clippy::almost_complete_letter_range)]
 #![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
 
 macro_rules! a {
     () => {
diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr
index 74980ec1a92..5b5dc40ee54 100644
--- a/tests/ui/almost_complete_letter_range.stderr
+++ b/tests/ui/almost_complete_letter_range.stderr
@@ -1,5 +1,5 @@
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:19:17
+  --> $DIR/almost_complete_letter_range.rs:20:17
    |
 LL |         let _ = ('a') ..'z';
    |                 ^^^^^^--^^^
@@ -9,7 +9,7 @@ LL |         let _ = ('a') ..'z';
    = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:20:17
+  --> $DIR/almost_complete_letter_range.rs:21:17
    |
 LL |         let _ = 'A' .. ('Z');
    |                 ^^^^--^^^^^^
@@ -17,7 +17,7 @@ LL |         let _ = 'A' .. ('Z');
    |                     help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:26:13
+  --> $DIR/almost_complete_letter_range.rs:27:13
    |
 LL |     let _ = (b'a')..(b'z');
    |             ^^^^^^--^^^^^^
@@ -25,7 +25,7 @@ LL |     let _ = (b'a')..(b'z');
    |                   help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:27:13
+  --> $DIR/almost_complete_letter_range.rs:28:13
    |
 LL |     let _ = b'A'..b'Z';
    |             ^^^^--^^^^
@@ -33,7 +33,7 @@ LL |     let _ = b'A'..b'Z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:32:13
+  --> $DIR/almost_complete_letter_range.rs:33:13
    |
 LL |     let _ = a!()..'z';
    |             ^^^^--^^^
@@ -41,7 +41,7 @@ LL |     let _ = a!()..'z';
    |                 help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:35:9
+  --> $DIR/almost_complete_letter_range.rs:36:9
    |
 LL |         b'a'..b'z' if true => 1,
    |         ^^^^--^^^^
@@ -49,7 +49,7 @@ LL |         b'a'..b'z' if true => 1,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:36:9
+  --> $DIR/almost_complete_letter_range.rs:37:9
    |
 LL |         b'A'..b'Z' if true => 2,
    |         ^^^^--^^^^
@@ -57,7 +57,7 @@ LL |         b'A'..b'Z' if true => 2,
    |             help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:43:9
+  --> $DIR/almost_complete_letter_range.rs:44:9
    |
 LL |         'a'..'z' if true => 1,
    |         ^^^--^^^
@@ -65,7 +65,7 @@ LL |         'a'..'z' if true => 1,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:44:9
+  --> $DIR/almost_complete_letter_range.rs:45:9
    |
 LL |         'A'..'Z' if true => 2,
    |         ^^^--^^^
@@ -73,7 +73,7 @@ LL |         'A'..'Z' if true => 2,
    |            help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:54:9
+  --> $DIR/almost_complete_letter_range.rs:55:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
@@ -81,7 +81,7 @@ LL |         'a'..'z' => 1,
    |            help: use an inclusive range: `...`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:61:13
+  --> $DIR/almost_complete_letter_range.rs:62:13
    |
 LL |     let _ = 'a'..'z';
    |             ^^^--^^^
@@ -89,7 +89,7 @@ LL |     let _ = 'a'..'z';
    |                help: use an inclusive range: `..=`
 
 error: almost complete ascii letter range
-  --> $DIR/almost_complete_letter_range.rs:63:9
+  --> $DIR/almost_complete_letter_range.rs:64:9
    |
 LL |         'a'..'z' => 1,
    |         ^^^--^^^
diff --git a/tests/ui/needless_parens_on_range_literals.fixed b/tests/ui/needless_parens_on_range_literals.fixed
new file mode 100644
index 00000000000..1bd75c806bc
--- /dev/null
+++ b/tests/ui/needless_parens_on_range_literals.fixed
@@ -0,0 +1,14 @@
+// run-rustfix
+// edition:2018
+
+#![warn(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::almost_complete_letter_range)]
+
+fn main() {
+    let _ = 'a'..='z';
+    let _ = 'a'..'z';
+    let _ = (1.)..2.;
+    let _ = (1.)..2.;
+    let _ = 'a'..;
+    let _ = ..'z';
+}
diff --git a/tests/ui/needless_parens_on_range_literals.rs b/tests/ui/needless_parens_on_range_literals.rs
new file mode 100644
index 00000000000..7abb8a1adc1
--- /dev/null
+++ b/tests/ui/needless_parens_on_range_literals.rs
@@ -0,0 +1,14 @@
+// run-rustfix
+// edition:2018
+
+#![warn(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::almost_complete_letter_range)]
+
+fn main() {
+    let _ = ('a')..=('z');
+    let _ = 'a'..('z');
+    let _ = (1.)..2.;
+    let _ = (1.)..(2.);
+    let _ = ('a')..;
+    let _ = ..('z');
+}
diff --git a/tests/ui/needless_parens_on_range_literals.stderr b/tests/ui/needless_parens_on_range_literals.stderr
new file mode 100644
index 00000000000..505f7ac916d
--- /dev/null
+++ b/tests/ui/needless_parens_on_range_literals.stderr
@@ -0,0 +1,40 @@
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:8:13
+   |
+LL |     let _ = ('a')..=('z');
+   |             ^^^^^ help: try: `'a'`
+   |
+   = note: `-D clippy::needless-parens-on-range-literals` implied by `-D warnings`
+
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:8:21
+   |
+LL |     let _ = ('a')..=('z');
+   |                     ^^^^^ help: try: `'z'`
+
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:9:18
+   |
+LL |     let _ = 'a'..('z');
+   |                  ^^^^^ help: try: `'z'`
+
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:11:19
+   |
+LL |     let _ = (1.)..(2.);
+   |                   ^^^^ help: try: `2.`
+
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:12:13
+   |
+LL |     let _ = ('a')..;
+   |             ^^^^^ help: try: `'a'`
+
+error: needless parenthesis on range literals can be removed
+  --> $DIR/needless_parens_on_range_literals.rs:13:15
+   |
+LL |     let _ = ..('z');
+   |               ^^^^^ help: try: `'z'`
+
+error: aborting due to 6 previous errors
+