Auto merge of #8933 - DennisOSRM:needless_braces_range_literal, r=dswij

Add new lint [`needless_parens_on_range_literals`]

changelog: Adds a new lint [`needless_parens_on_range_literals`] to warn on needless braces on literals in a range statement

For example, the lint would catch

```log
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`
```
This commit is contained in:
bors 2022-06-06 10:58:33 +00:00
commit 0f6e50fe1b
12 changed files with 175 additions and 12 deletions

View File

@ -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

View File

@ -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),

View File

@ -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,

View File

@ -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),

View File

@ -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));

View File

@ -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);
}
}
}
}

View File

@ -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 {
() => {

View File

@ -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 {
() => {

View File

@ -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,
| ^^^--^^^

View File

@ -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';
}

View File

@ -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');
}

View File

@ -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