From dc89bb1135afc31fc9ee2272e627192c04354d22 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:21:58 +1200 Subject: [PATCH 01/97] Use if_chain in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 43 +++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7f998c63f497b..9f3be26e67236 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2162,15 +2162,16 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { if lhs.hir_id == expr.hir_id { - if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) { - *state = match *state { - VarState::Initial if self.depth == 0 => VarState::IncrOnce, - _ => VarState::DontWarn, - }; + *state = if op.node == BinOpKind::Add + && is_integer_const(self.cx, rhs, 1) + && *state == VarState::Initial + && self.depth == 0 + { + VarState::IncrOnce } else { - // Assigned some other value - *state = VarState::DontWarn; - } + // Assigned some other value or assigned multiple times + VarState::DontWarn + }; } }, ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, @@ -2212,18 +2213,20 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { // Look for declarations of the variable - if let StmtKind::Local(ref local) = stmt.kind { - if local.pat.hir_id == self.var_id { - if let PatKind::Binding(.., ident, _) = local.pat.kind { - self.name = Some(ident.name); - - self.state = local.init.as_ref().map_or(VarState::Declared, |init| { - if is_integer_const(&self.cx, init, 0) { - VarState::Warn - } else { - VarState::Declared - } - }) + if_chain! { + if let StmtKind::Local(ref local) = stmt.kind; + if local.pat.hir_id == self.var_id; + if let PatKind::Binding(.., ident, _) = local.pat.kind; + then { + self.name = Some(ident.name); + self.state = if_chain! { + if let Some(ref init) = local.init; + if is_integer_const(&self.cx, init, 0); + then { + VarState::Warn + } else { + VarState::Declared + } } } } From 116f30dc33d9e3744f257f2f7f5467acfbff178b Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:25:06 +1200 Subject: [PATCH 02/97] Use else blocks instead of return statements in Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9f3be26e67236..a59d3ef8708b6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2181,16 +2181,17 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { _ => (), } } + + walk_expr(self, expr); } else if is_loop(expr) || is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; } else if let ExprKind::Continue(_) = expr.kind { self.done = true; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { NestedVisitorMap::None @@ -2272,16 +2273,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { self.state = VarState::DontWarn; return; } + walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; - return; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); self.depth -= 1; - return; + } else { + walk_expr(self, expr); } - walk_expr(self, expr); } fn nested_visit_map(&mut self) -> NestedVisitorMap { From b2d5b89a1de15df9052fdf44d01b174add82837f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:41:09 +1200 Subject: [PATCH 03/97] Check if it's after the loop earlier --- clippy_lints/src/loops.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index a59d3ef8708b6..20e75546d7104 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2250,6 +2250,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { + if self.past_loop { + self.state = VarState::DontWarn; + return; + } + if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { @@ -2269,10 +2274,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } } - if self.past_loop { - self.state = VarState::DontWarn; - return; - } walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { self.state = VarState::DontWarn; From 31cb1109648bf4242cab47571343578244e7fb9d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 14:48:26 +1200 Subject: [PATCH 04/97] add concinient methods to Increment/InitializeVisitor --- clippy_lints/src/loops.rs | 63 +++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 20e75546d7104..61f0059cca47e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1536,31 +1536,17 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor { - cx, - states: FxHashMap::default(), - depth: 0, - done: false, - }; + let mut visitor = IncrementVisitor::new(cx); walk_expr(&mut visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) { - let mut visitor2 = InitializeVisitor { - cx, - end_expr: expr, - var_id: *id, - state: VarState::IncrOnce, - name: None, - depth: 0, - past_loop: false, - }; + for id in visitor.into_results() { + let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if visitor2.state == VarState::Warn { - if let Some(name) = visitor2.name { + if let Some(name) = visitor2.get_result() { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1585,7 +1571,6 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); - } } } } @@ -2142,6 +2127,24 @@ struct IncrementVisitor<'a, 'tcx> { done: bool, } +impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + Self { + cx, + states: FxHashMap::default(), + depth: 0, + done: false, + } + } + + fn into_results(self) -> impl Iterator { + self.states + .into_iter() + .filter(|(_, state)| *state == VarState::IncrOnce) + .map(|(id, _)| id) + } +} + impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { type Map = Map<'tcx>; @@ -2209,6 +2212,28 @@ struct InitializeVisitor<'a, 'tcx> { past_loop: bool, } +impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + Self { + cx, + end_expr, + var_id, + state: VarState::IncrOnce, + name: None, + depth: 0, + past_loop: false, + } + } + + fn get_result(&self) -> Option { + if self.state == VarState::Warn { + self.name + } else { + None + } + } +} + impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type Map = Map<'tcx>; From c599e2fcfaaedb12b560f4136bab3d0b450acf8f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:05:51 +1200 Subject: [PATCH 05/97] Split VarState --- clippy_lints/src/loops.rs | 87 +++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 61f0059cca47e..2b7830e7cadb2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -2107,23 +2107,18 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool { } } -// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be -// incremented exactly once in the loop body, and initialized to zero -// at the start of the loop. #[derive(Debug, PartialEq)] -enum VarState { +enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter - Declared, // Declared but not (yet) initialized to zero - Warn, DontWarn, } /// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, // context reference - states: FxHashMap, // incremented variables - depth: u32, // depth of conditional expressions + cx: &'a LateContext<'tcx>, // context reference + states: FxHashMap, // incremented variables + depth: u32, // depth of conditional expressions done: bool, } @@ -2140,7 +2135,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { fn into_results(self) -> impl Iterator { self.states .into_iter() - .filter(|(_, state)| *state == VarState::IncrOnce) + .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) .map(|(id, _)| id) } } @@ -2156,9 +2151,9 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { // If node is a variable if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { - let state = self.states.entry(def_id).or_insert(VarState::Initial); - if *state == VarState::IncrOnce { - *state = VarState::DontWarn; + let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial); + if *state == IncrementVisitorVarState::IncrOnce { + *state = IncrementVisitorVarState::DontWarn; return; } @@ -2167,19 +2162,21 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if lhs.hir_id == expr.hir_id { *state = if op.node == BinOpKind::Add && is_integer_const(self.cx, rhs, 1) - && *state == VarState::Initial + && *state == IncrementVisitorVarState::Initial && self.depth == 0 { - VarState::IncrOnce + IncrementVisitorVarState::IncrOnce } else { // Assigned some other value or assigned multiple times - VarState::DontWarn + IncrementVisitorVarState::DontWarn }; } }, - ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => *state = VarState::DontWarn, + ExprKind::Assign(ref lhs, _, _) if lhs.hir_id == expr.hir_id => { + *state = IncrementVisitorVarState::DontWarn + }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - *state = VarState::DontWarn + *state = IncrementVisitorVarState::DontWarn }, _ => (), } @@ -2201,13 +2198,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -/// Checks whether a variable is initialized to zero at the start of a loop. +enum InitializeVisitorState { + Initial, // Not examined yet + Declared(Symbol), // Declared but not (yet) initialized + Initialized { name: Symbol }, + DontWarn, +} + +/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: VarState, - name: Option, + state: InitializeVisitorState, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2218,16 +2222,15 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { cx, end_expr, var_id, - state: VarState::IncrOnce, - name: None, + state: InitializeVisitorState::Initial, depth: 0, past_loop: false, } } fn get_result(&self) -> Option { - if self.state == VarState::Warn { - self.name + if let InitializeVisitorState::Initialized { name } = self.state { + Some(name) } else { None } @@ -2244,23 +2247,24 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.name = Some(ident.name); self.state = if_chain! { if let Some(ref init) = local.init; if is_integer_const(&self.cx, init, 0); then { - VarState::Warn - } else { - VarState::Declared + InitializeVisitorState::Initialized { + name: ident.name } + } else { + InitializeVisitorState::Declared(ident.name) } } } + } walk_stmt(self, stmt); } fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.state == VarState::DontWarn { + if matches!(self.state, InitializeVisitorState::DontWarn) { return; } if expr.hir_id == self.end_expr.hir_id { @@ -2269,31 +2273,36 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { } // No need to visit expressions before the variable is // declared - if self.state == VarState::IncrOnce { + if matches!(self.state, InitializeVisitorState::Initial) { return; } // If node is the desired variable, see how it's used if var_def_id(self.cx, expr) == Some(self.var_id) { if self.past_loop { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; return; } if let Some(parent) = get_parent_expr(self.cx, expr) { match parent.kind { ExprKind::AssignOp(_, ref lhs, _) if lhs.hir_id == expr.hir_id => { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { - self.state = if is_integer_const(&self.cx, rhs, 0) && self.depth == 0 { - VarState::Warn - } else { - VarState::DontWarn + self.state = if_chain! { + if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if let InitializeVisitorState::Declared(name) + | InitializeVisitorState::Initialized { name, ..} = self.state; + then { + InitializeVisitorState::Initialized { name } + } else { + InitializeVisitorState::DontWarn + } } }, ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => { - self.state = VarState::DontWarn + self.state = InitializeVisitorState::DontWarn }, _ => (), } @@ -2301,7 +2310,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { walk_expr(self, expr); } else if !self.past_loop && is_loop(expr) { - self.state = VarState::DontWarn; + self.state = InitializeVisitorState::DontWarn; } else if is_conditional(expr) { self.depth += 1; walk_expr(self, expr); From 13c207d3756754c54a6b20d852087616d5abfbf4 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:14:57 +1200 Subject: [PATCH 06/97] Generalise `InitializeVisitor` --- clippy_lints/src/loops.rs | 36 +++++++++++++++++++---------------- clippy_lints/src/utils/mod.rs | 2 +- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2b7830e7cadb2..bf067c70a7ec6 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1528,6 +1528,9 @@ fn check_arg_type(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { } } +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be +// incremented exactly once in the loop body, and initialized to zero +// at the start of the loop. fn check_for_loop_explicit_counter<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -1546,7 +1549,10 @@ fn check_for_loop_explicit_counter<'tcx>( let mut visitor2 = InitializeVisitor::new(cx, expr, id); walk_block(&mut visitor2, block); - if let Some(name) = visitor2.get_result() { + if_chain! { + if let Some((name, initializer)) = visitor2.get_result(); + if is_integer_const(cx, initializer, 0); + then { let mut applicability = Applicability::MachineApplicable; // for some reason this is the only way to get the `Span` @@ -1571,6 +1577,7 @@ fn check_for_loop_explicit_counter<'tcx>( ), applicability, ); + } } } } @@ -2198,20 +2205,20 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { } } -enum InitializeVisitorState { +enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol }, + Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, DontWarn, } -/// Checks whether a variable is initialized to zero at the start of a loop and not modified +/// Checks whether a variable is initialized at the start of a loop and not modified /// and used after the loop. struct InitializeVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference end_expr: &'tcx Expr<'tcx>, // the for loop. Stop scanning here. var_id: HirId, - state: InitializeVisitorState, + state: InitializeVisitorState<'tcx>, depth: u32, // depth of conditional expressions past_loop: bool, } @@ -2228,9 +2235,9 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option { - if let InitializeVisitorState::Initialized { name } = self.state { - Some(name) + fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + if let InitializeVisitorState::Initialized { name, initializer } = self.state { + Some((name, initializer)) } else { None } @@ -2247,19 +2254,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if_chain! { - if let Some(ref init) = local.init; - if is_integer_const(&self.cx, init, 0); - then { + self.state = if let Some(ref init) = local.init { InitializeVisitorState::Initialized { - name: ident.name + initializer: init, + name: ident.name, } } else { InitializeVisitorState::Declared(ident.name) } } } - } walk_stmt(self, stmt); } @@ -2291,11 +2295,11 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { }, ExprKind::Assign(ref lhs, ref rhs, _) if lhs.hir_id == expr.hir_id => { self.state = if_chain! { - if is_integer_const(&self.cx, rhs, 0) && self.depth == 0; + if self.depth == 0; if let InitializeVisitorState::Declared(name) | InitializeVisitorState::Initialized { name, ..} = self.state; then { - InitializeVisitorState::Initialized { name } + InitializeVisitorState::Initialized { initializer: rhs, name } } else { InitializeVisitorState::DontWarn } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 92cb31fcf8544..a0ddcce111e1c 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -707,7 +707,7 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, } /// Gets the parent expression, if any –- this is useful to constrain a lint. -pub fn get_parent_expr<'c>(cx: &'c LateContext<'_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> { +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { let map = &cx.tcx.hir(); let hir_id = e.hir_id; let parent_id = map.get_parent_node(hir_id); From 9573a0d378033a81e55ca834a5d305d3cf2be24d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:15:20 +1200 Subject: [PATCH 07/97] Rename variables --- clippy_lints/src/loops.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bf067c70a7ec6..1c316c00f4337 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1539,18 +1539,18 @@ fn check_for_loop_explicit_counter<'tcx>( expr: &'tcx Expr<'_>, ) { // Look for variables that are incremented once per loop iteration. - let mut visitor = IncrementVisitor::new(cx); - walk_expr(&mut visitor, body); + let mut increment_visitor = IncrementVisitor::new(cx); + walk_expr(&mut increment_visitor, body); // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { - for id in visitor.into_results() { - let mut visitor2 = InitializeVisitor::new(cx, expr, id); - walk_block(&mut visitor2, block); + for id in increment_visitor.into_results() { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); + walk_block(&mut initialize_visitor, block); if_chain! { - if let Some((name, initializer)) = visitor2.get_result(); + if let Some((name, initializer)) = initialize_visitor.get_result(); if is_integer_const(cx, initializer, 0); then { let mut applicability = Applicability::MachineApplicable; From 1026b42f0694eb9239b5cebe80be743d5ded0da5 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:32:24 +1200 Subject: [PATCH 08/97] Rename a struct and variables --- clippy_lints/src/loops.rs | 65 ++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 1c316c00f4337..d9896f7fd6b14 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -820,9 +820,9 @@ impl Offset { } } -struct FixedOffsetVar<'hir> { - var: &'hir Expr<'hir>, - offset: Offset, +struct IndexExpr<'hir> { + base: &'hir Expr<'hir>, + idx_offset: Offset, } fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { @@ -845,14 +845,14 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, var: HirId) -> Option { +fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { + fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), _ => None, } } @@ -860,20 +860,20 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, var: HirId) -> Optio match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if same_var(cx, lhs, var) { - extract_offset(cx, rhs, var) - } else if same_var(cx, rhs, var) { - extract_offset(cx, lhs, var) + let offset_opt = if same_var(cx, lhs, start) { + extract_offset(cx, rhs, start) + } else if same_var(cx, rhs, start) { + extract_offset(cx, lhs, start) } else { None }; offset_opt.map(Offset::positive) }, - BinOpKind::Sub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative), + BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, var) => Some(Offset::positive("0".into())), + ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), _ => None, } } @@ -916,8 +916,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst_var: FixedOffsetVar<'_>, - src_var: FixedOffsetVar<'_>, + dst: IndexExpr<'_>, + src: IndexExpr<'_>, ) -> String { fn print_sum(arg1: &str, arg2: &Offset) -> String { match (arg1, &arg2.value[..], arg2.sign) { @@ -944,13 +944,13 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, offset: Offset, var: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| { if_chain! { if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; if method.ident.name == sym!(len); if len_args.len() == 1; if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, var); + if var_def_id(cx, arg) == var_def_id(cx, base); then { match offset.sign { OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), @@ -971,25 +971,26 @@ fn build_manual_memcpy_suggestion<'tcx>( }; let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst_var.offset); - let dst_limit = print_limit(end, dst_var.offset, dst_var.var); - let src_offset = print_offset(&start_str, &src_var.offset); - let src_limit = print_limit(end, src_var.offset, src_var.var); + let dst_offset = print_offset(&start_str, &dst.idx_offset); + let dst_limit = print_limit(end, dst.idx_offset, dst.base); + let src_offset = print_offset(&start_str, &src.idx_offset); + let src_limit = print_limit(end, src.idx_offset, src.base); - let dst_var_name = snippet_opt(cx, dst_var.var.span).unwrap_or_else(|| "???".into()); - let src_var_name = snippet_opt(cx, src_var.var.span).unwrap_or_else(|| "???".into()); + let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); + let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); let dst = if dst_offset == "" && dst_limit == "" { - dst_var_name + dst_base_str } else { - format!("{}[{}..{}]", dst_var_name, dst_offset, dst_limit) + format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_var_name, src_offset, src_limit + dst, src_base_str, src_offset, src_limit ) } + /// Checks for for loops that sequentially copy items from one slice-like /// object to another. fn detect_manual_memcpy<'tcx>( @@ -1014,18 +1015,18 @@ fn detect_manual_memcpy<'tcx>( o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); if_chain! { - if let ExprKind::Index(seqexpr_left, idx_left) = lhs.kind; - if let ExprKind::Index(seqexpr_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_left)) - && is_slice_like(cx, cx.typeck_results().expr_ty(seqexpr_right)); + if let ExprKind::Index(base_left, idx_left) = lhs.kind; + if let ExprKind::Index(base_right, idx_right) = rhs.kind; + if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) + && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); // Source and destination must be different - if var_def_id(cx, seqexpr_left) != var_def_id(cx, seqexpr_right); + if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((FixedOffsetVar { var: seqexpr_left, offset: offset_left }, - FixedOffsetVar { var: seqexpr_right, offset: offset_right })) + Some((IndexExpr { base: base_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx_offset: offset_right })) } else { None } From b4b4da162f19e9a4c63854a2b5a6167b83f9d8b9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:51:23 +1200 Subject: [PATCH 09/97] Introduce Start and StartKind --- clippy_lints/src/loops.rs | 67 +++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d9896f7fd6b14..157dff59d4491 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -818,13 +818,29 @@ impl Offset { sign: OffsetSign::Positive, } } + + fn empty() -> Self { + Self::positive("0".into()) + } +} + +#[derive(Debug, Clone, Copy)] +enum StartKind<'hir> { + Range, + Counter { initializer: &'hir Expr<'hir> }, } struct IndexExpr<'hir> { base: &'hir Expr<'hir>, + idx: StartKind<'hir>, idx_offset: Offset, } +struct Start<'hir> { + id: HirId, + kind: StartKind<'hir>, +} + fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { let is_slice = match ty.kind() { ty::Ref(_, subty, _) => is_slice_like(cx, subty), @@ -845,14 +861,32 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Option { - fn extract_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, start: HirId) -> Option { +fn get_offset<'tcx>( + cx: &LateContext<'tcx>, + idx: &Expr<'_>, + starts: &[Start<'tcx>], +) -> Option<(StartKind<'tcx>, Offset)> { + fn extract_start<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option> { + starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + } + + fn extract_offset<'tcx>( + cx: &LateContext<'tcx>, + e: &Expr<'_>, + starts: &[Start<'tcx>], + ) -> Option { match &e.kind { ExprKind::Lit(l) => match l.node { ast::LitKind::Int(x, _ty) => Some(x.to_string()), _ => None, }, - ExprKind::Path(..) if !same_var(cx, e, start) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())), + ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + }, _ => None, } } @@ -860,20 +894,21 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, idx: &Expr<'_>, start: HirId) -> Opt match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if same_var(cx, lhs, start) { - extract_offset(cx, rhs, start) - } else if same_var(cx, rhs, start) { - extract_offset(cx, lhs, start) + let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { + extract_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = extract_start(cx, rhs, starts) { + extract_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; - offset_opt.map(Offset::positive) + offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub if same_var(cx, lhs, start) => extract_offset(cx, rhs, start).map(Offset::negative), + BinOpKind::Sub => extract_start(cx, lhs, starts) + .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), _ => None, }, - ExprKind::Path(..) if same_var(cx, idx, start) => Some(Offset::positive("0".into())), + ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1008,6 +1043,10 @@ fn detect_manual_memcpy<'tcx>( { // the var must be a single name if let PatKind::Binding(_, canonical_id, _, _) = pat.kind { + let mut starts = vec![Start { + id: canonical_id, + kind: StartKind::Range, + }]; // The only statements in the for loops can be indexed assignments from // indexed retrievals. let big_sugg = get_assignments(body) @@ -1019,14 +1058,14 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some(offset_left) = get_offset(cx, &idx_left, canonical_id); - if let Some(offset_right) = get_offset(cx, &idx_right, canonical_id); + if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); then { - Some((IndexExpr { base: base_left, idx_offset: offset_left }, - IndexExpr { base: base_right, idx_offset: offset_right })) + Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None } From 720f19f2ec4282f636889b35beabf31272e3b1b2 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 10 Jun 2020 15:58:12 +1200 Subject: [PATCH 10/97] Implement detecting `manual_memcpy` with loop counters --- clippy_lints/src/loops.rs | 90 ++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 157dff59d4491..c036d63c33514 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -913,37 +913,62 @@ fn get_offset<'tcx>( } } -fn get_assignments<'tcx>(body: &'tcx Expr<'tcx>) -> impl Iterator, &'tcx Expr<'tcx>)>> { - fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if let ExprKind::Assign(lhs, rhs, _) = e.kind { - Some((lhs, rhs)) - } else { - None - } +fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::Assign(lhs, rhs, _) = e.kind { + Some((lhs, rhs)) + } else { + None } +} - // This is one of few ways to return different iterators - // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 - let mut iter_a = None; - let mut iter_b = None; +fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( + cx: &'a LateContext<'a, 'tcx>, + stmts: &'tcx [Stmt<'tcx>], + expr: Option<&'tcx Expr<'tcx>>, + loop_counters: &'c [Start<'tcx>], +) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + stmts + .iter() + .filter_map(move |stmt| match stmt.kind { + StmtKind::Local(..) | StmtKind::Item(..) => None, + StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { + if let ExprKind::AssignOp(_, var, _) = e.kind; + // skip StartKind::Range + if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); + then { None } else { Some(e) } + }, + }) + .chain(expr.into_iter()) + .map(get_assignment) +} - if let ExprKind::Block(b, _) = body.kind { - let Block { stmts, expr, .. } = *b; +fn get_loop_counters<'a, 'tcx>( + cx: &'a LateContext<'a, 'tcx>, + body: &'tcx Block<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option> + 'a> { + // Look for variables that are incremented once per loop iteration. + let mut increment_visitor = IncrementVisitor::new(cx); + walk_block(&mut increment_visitor, body); - iter_a = stmts - .iter() - .filter_map(|stmt| match stmt.kind { - StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), + // For each candidate, check the parent block to see if + // it's initialized to zero at the start of the loop. + if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + increment_visitor + .into_results() + .filter_map(move |var_id| { + let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id); + walk_block(&mut initialize_visitor, block); + + initialize_visitor.get_result().map(|(_, initializer)| Start { + id: var_id, + kind: StartKind::Counter { initializer }, + }) }) - .chain(expr.into_iter()) - .map(get_assignment) .into() } else { - iter_b = Some(get_assignment(body)) + None } - - iter_a.into_iter().flatten().chain(iter_b.into_iter()) } fn build_manual_memcpy_suggestion<'tcx>( @@ -1047,9 +1072,26 @@ fn detect_manual_memcpy<'tcx>( id: canonical_id, kind: StartKind::Range, }]; + + // This is one of few ways to return different iterators + // derived from: https://stackoverflow.com/questions/29760668/conditionally-iterate-over-one-of-several-possible-iterators/52064434#52064434 + let mut iter_a = None; + let mut iter_b = None; + + if let ExprKind::Block(block, _) = body.kind { + if let Some(loop_counters) = get_loop_counters(cx, block, expr) { + starts.extend(loop_counters); + } + iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + } else { + iter_b = Some(get_assignment(body)); + } + // The only statements in the for loops can be indexed assignments from // indexed retrievals. - let big_sugg = get_assignments(body) + let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); + + let big_sugg = assignments .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); From de56279cd9047832216e1d1c06dc45375bf01b31 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:06:13 +1200 Subject: [PATCH 11/97] Implement building the `manual_memcpy` sugggestion with loop counters --- clippy_lints/src/loops.rs | 185 +++++++++++++++++++++++++-------- clippy_lints/src/utils/sugg.rs | 78 +++++++++++++- 2 files changed, 213 insertions(+), 50 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index c036d63c33514..bd2ae47b72347 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -800,19 +800,19 @@ enum OffsetSign { } struct Offset { - value: String, + value: MinifyingSugg<'static>, sign: OffsetSign, } impl Offset { - fn negative(value: String) -> Self { + fn negative(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Negative, } } - fn positive(value: String) -> Self { + fn positive(value: MinifyingSugg<'static>) -> Self { Self { value, sign: OffsetSign::Positive, @@ -820,7 +820,88 @@ impl Offset { } fn empty() -> Self { - Self::positive("0".into()) + Self::positive(MinifyingSugg::non_paren("0")) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + +#[derive(Clone)] +struct MinifyingSugg<'a>(sugg::Sugg<'a>); + +impl std::fmt::Display for MinifyingSugg<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + std::fmt::Display::fmt(&self.0, f) + } +} + +impl<'a> MinifyingSugg<'a> { + fn as_str(&self) -> &str { + let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + s.as_ref() + } + + fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + Self(sugg::Sugg::hir(cx, expr, default)) + } + + fn maybe_par(self) -> Self { + Self(self.0.maybe_par()) + } + + fn non_paren(str: impl Into>) -> Self { + Self(sugg::Sugg::NonParen(str.into())) + } +} + +impl std::ops::Add for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self.clone(), + (_, _) => MinifyingSugg(&self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub for &MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self.clone(), + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(&self.0 - &rhs.0), + } + } +} + +impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn add(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + ("0", _) => rhs.clone(), + (_, "0") => self, + (_, _) => MinifyingSugg(self.0 + &rhs.0), + } + } +} + +impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { + type Output = MinifyingSugg<'static>; + fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { + match (self.as_str(), rhs.as_str()) { + (_, "0") => self, + ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + (x, y) if x == y => MinifyingSugg::non_paren("0"), + (_, _) => MinifyingSugg(self.0 - &rhs.0), + } } } @@ -878,14 +959,15 @@ fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], - ) -> Option { + ) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(x.to_string()), + ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { - Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())) + // `e` is always non paren as it's a `Path` + Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, _ => None, } @@ -979,32 +1061,15 @@ fn build_manual_memcpy_suggestion<'tcx>( dst: IndexExpr<'_>, src: IndexExpr<'_>, ) -> String { - fn print_sum(arg1: &str, arg2: &Offset) -> String { - match (arg1, &arg2.value[..], arg2.sign) { - ("0", "0", _) => "0".into(), - ("0", x, OffsetSign::Positive) | (x, "0", _) => x.into(), - ("0", x, OffsetSign::Negative) => format!("-{}", x), - (x, y, OffsetSign::Positive) => format!("({} + {})", x, y), - (x, y, OffsetSign::Negative) => { - if x == y { - "0".into() - } else { - format!("({} - {})", x, y) - } - }, - } - } - - fn print_offset(start_str: &str, inline_offset: &Offset) -> String { - let offset = print_sum(start_str, inline_offset); + fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - "".into() + MinifyingSugg::non_paren("") } else { offset } } - let print_limit = |end: &Expr<'_>, offset: Offset, base: &Expr<'_>| { + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { if_chain! { if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; if method.ident.name == sym!(len); @@ -1012,42 +1077,72 @@ fn build_manual_memcpy_suggestion<'tcx>( if let Some(arg) = len_args.get(0); if var_def_id(cx, arg) == var_def_id(cx, base); then { - match offset.sign { - OffsetSign::Negative => format!("({} - {})", snippet(cx, end.span, ".len()"), offset.value), - OffsetSign::Positive => "".into(), + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg } } else { - let end_str = match limits { + match limits { ast::RangeLimits::Closed => { - let end = sugg::Sugg::hir(cx, end, ""); - format!("{}", end + sugg::ONE) + sugg + &MinifyingSugg::non_paren("1") }, - ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")), - }; - - print_sum(&end_str, &offset) + ast::RangeLimits::HalfOpen => sugg, + } } } }; - let start_str = snippet(cx, start.span, "").to_string(); - let dst_offset = print_offset(&start_str, &dst.idx_offset); - let dst_limit = print_limit(end, dst.idx_offset, dst.base); - let src_offset = print_offset(&start_str, &src.idx_offset); - let src_limit = print_limit(end, src.idx_offset, src.base); + let start_str = MinifyingSugg::hir(cx, start, ""); + let end_str = MinifyingSugg::hir(cx, end, ""); + + let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { + StartKind::Range => ( + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset), + ), + ), + StartKind::Counter { initializer } => { + let counter_start = MinifyingSugg::hir(cx, initializer, ""); + ( + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_limit( + end, + end_str.as_str(), + idx_expr.base, + apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, + ), + ) + }, + }; + + let (dst_offset, dst_limit) = print_offset_and_limit(&dst); + let (src_offset, src_limit) = print_offset_and_limit(&src); let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); - let dst = if dst_offset == "" && dst_limit == "" { + let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str } else { - format!("{}[{}..{}]", dst_base_str, dst_offset, dst_limit) + format!( + "{}[{}..{}]", + dst_base_str, + dst_offset.maybe_par(), + dst_limit.maybe_par() + ) }; format!( "{}.clone_from_slice(&{}[{}..{}])", - dst, src_base_str, src_offset, src_limit + dst, + src_base_str, + src_offset.maybe_par(), + src_limit.maybe_par() ) } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index ec8b7e59b5976..50d48650a0938 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -36,6 +36,46 @@ impl Display for Sugg<'_> { } } +// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp +impl Clone for Sugg<'_> { + fn clone(&self) -> Self { + /// manually cloning AssocOp + fn clone_assoc_op(this: &AssocOp) -> AssocOp { + match this { + AssocOp::Add => AssocOp::Add, + AssocOp::Subtract => AssocOp::Subtract, + AssocOp::Multiply => AssocOp::Multiply, + AssocOp::Divide => AssocOp::Divide, + AssocOp::Modulus => AssocOp::Modulus, + AssocOp::LAnd => AssocOp::LAnd, + AssocOp::LOr => AssocOp::LOr, + AssocOp::BitXor => AssocOp::BitXor, + AssocOp::BitAnd => AssocOp::BitAnd, + AssocOp::BitOr => AssocOp::BitOr, + AssocOp::ShiftLeft => AssocOp::ShiftLeft, + AssocOp::ShiftRight => AssocOp::ShiftRight, + AssocOp::Equal => AssocOp::Equal, + AssocOp::Less => AssocOp::Less, + AssocOp::LessEqual => AssocOp::LessEqual, + AssocOp::NotEqual => AssocOp::NotEqual, + AssocOp::Greater => AssocOp::Greater, + AssocOp::GreaterEqual => AssocOp::GreaterEqual, + AssocOp::Assign => AssocOp::Assign, + AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), + AssocOp::As => AssocOp::As, + AssocOp::DotDot => AssocOp::DotDot, + AssocOp::DotDotEq => AssocOp::DotDotEq, + AssocOp::Colon => AssocOp::Colon, + } + } + match self { + Sugg::NonParen(x) => Sugg::NonParen(x.clone()), + Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), + Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), + } + } +} + #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. @@ -267,21 +307,49 @@ impl<'a> Sugg<'a> { } } -impl<'a, 'b> std::ops::Add> for Sugg<'a> { +impl std::ops::Add for Sugg<'_> { type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Add, &self, &rhs) } } -impl<'a, 'b> std::ops::Sub> for Sugg<'a> { +impl std::ops::Sub for Sugg<'_> { type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> { + fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Sub, &self, &rhs) } } -impl<'a> std::ops::Not for Sugg<'a> { +impl std::ops::Add<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, &self, rhs) + } +} + +impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, &self, rhs) + } +} + +impl std::ops::Add for &Sugg<'_> { + type Output = Sugg<'static>; + fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Add, self, rhs) + } +} + +impl std::ops::Sub for &Sugg<'_> { + type Output = Sugg<'static>; + fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { + make_binop(ast::BinOpKind::Sub, self, rhs) + } +} + +impl std::ops::Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { make_unop("!", self) From 8da6cfd17b7707d678d01a6688572169015ea83e Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 12:11:06 +1200 Subject: [PATCH 12/97] fmt --- clippy_lints/src/loops.rs | 41 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index bd2ae47b72347..2aee3b93e8251 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1069,29 +1069,30 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, base); - then { - if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + let print_limit = + |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, base); + then { + if sugg.as_str() == end_str { + MinifyingSugg::non_paren("") + } else { + sugg + } } else { - sugg - } - } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") - }, - ast::RangeLimits::HalfOpen => sugg, + match limits { + ast::RangeLimits::Closed => { + sugg + &MinifyingSugg::non_paren("1") + }, + ast::RangeLimits::HalfOpen => sugg, + } } } - } - }; + }; let start_str = MinifyingSugg::hir(cx, start, ""); let end_str = MinifyingSugg::hir(cx, end, ""); From d9a88be0b05c2cfec0679caf428d129c9d46256d Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:24:12 +1200 Subject: [PATCH 13/97] Rename `get_offset` and its private items --- clippy_lints/src/loops.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2aee3b93e8251..98c411f5ae60e 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -942,20 +942,20 @@ fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { } } -fn get_offset<'tcx>( +fn get_details_from_idx<'tcx>( cx: &LateContext<'tcx>, idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn extract_start<'tcx>( + fn get_start<'tcx>( cx: &LateContext<'tcx>, - expr: &Expr<'_>, + e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, expr, var.id)).map(|v| v.kind) + starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) } - fn extract_offset<'tcx>( + fn get_offset<'tcx>( cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>], @@ -965,7 +965,7 @@ fn get_offset<'tcx>( ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), _ => None, }, - ExprKind::Path(..) if extract_start(cx, e, starts).is_none() => { + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) }, @@ -976,21 +976,22 @@ fn get_offset<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = extract_start(cx, lhs, starts) { - extract_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = extract_start(cx, rhs, starts) { - extract_offset(cx, lhs, starts).map(|o| (s, o)) + let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { + get_offset(cx, rhs, starts).map(|o| (s, o)) + } else if let Some(s) = get_start(cx, rhs, starts) { + get_offset(cx, lhs, starts).map(|o| (s, o)) } else { None }; offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, - BinOpKind::Sub => extract_start(cx, lhs, starts) - .and_then(|s| extract_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))), + BinOpKind::Sub => { + get_start(cx, lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o)))) + }, _ => None, }, - ExprKind::Path(..) => extract_start(cx, idx, starts).map(|s| (s, Offset::empty())), + ExprKind::Path(..) => get_start(cx, idx, starts).map(|s| (s, Offset::empty())), _ => None, } } @@ -1196,8 +1197,8 @@ fn detect_manual_memcpy<'tcx>( if let ExprKind::Index(base_right, idx_right) = rhs.kind; if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)) && is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); - if let Some((start_left, offset_left)) = get_offset(cx, &idx_left, &starts); - if let Some((start_right, offset_right)) = get_offset(cx, &idx_right, &starts); + if let Some((start_left, offset_left)) = get_details_from_idx(cx, &idx_left, &starts); + if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts); // Source and destination must be different if var_def_id(cx, base_left) != var_def_id(cx, base_right); From 774e82a566c6c3349700a8bece44d6a8c6755039 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 11 Jun 2020 11:19:44 +1200 Subject: [PATCH 14/97] Add the tests for `manual_memcpy` with loop counters --- tests/ui/manual_memcpy.rs | 54 +++++++++++++++++++++++++++++++ tests/ui/manual_memcpy.stderr | 60 +++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 0083f94798fe4..87bfb4fdd163b 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,6 +115,60 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } +#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count] = src[i]; + count += 1; + count += 1; + } +} + #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] pub fn manual_clone(src: &[String], dst: &mut [String]) { for i in 0..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index bad84a5890081..a5698b08103b6 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -16,7 +16,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:17:14 | LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..])` + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:22:14 @@ -79,10 +79,64 @@ LL | for i in 0..0 { | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:14 + --> $DIR/manual_memcpy.rs:121:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:127:14 + | +LL | for i in 3..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:133:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:139:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:145:14 + | +LL | for i in 3..(3 + src.len()) { + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:151:14 + | +LL | for i in 5..src.len() { + | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:157:14 + | +LL | for i in 3..10 { + | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:164:14 + | +LL | for i in 0..src.len() { + | ^^^^^^^^^^^^ + | +help: try replacing the loop by + | +LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { + | + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:174:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -error: aborting due to 13 previous errors +error: aborting due to 21 previous errors From 9aad38bf614c3fb6d306f5dec4a0af606bb3c9c8 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 10:48:44 +1200 Subject: [PATCH 15/97] Update `manual_memcpy.stderr` to reflect additional parentheses --- tests/ui/manual_memcpy.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index a5698b08103b6..e6bef9981a3dc 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -58,13 +58,13 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:94:14 | LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + src.len()].clone_from_slice(&src[..(from + src.len() - from)])` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:98:14 | LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..from + 3].clone_from_slice(&src[..(from + 3 - from)])` + | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:103:14 @@ -106,7 +106,7 @@ error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:145:14 | LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(3 + src.len())].clone_from_slice(&src[..((3 + src.len()) - 3)])` + | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:151:14 From 4ea4a972500a8ddecfc737d51eec960324dcb02f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Thu, 18 Jun 2020 12:31:13 +1200 Subject: [PATCH 16/97] Add tests for bitwise operations --- tests/ui/manual_memcpy.rs | 8 ++++++++ tests/ui/manual_memcpy.stderr | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 87bfb4fdd163b..4846ab5eaaa38 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -167,6 +167,14 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; count += 1; } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } } #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index e6bef9981a3dc..d189bde0508e1 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -135,8 +135,14 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { error: it looks like you're manually copying between slices --> $DIR/manual_memcpy.rs:174:14 | +LL | for i in 0..1 << 1 { + | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` + +error: it looks like you're manually copying between slices + --> $DIR/manual_memcpy.rs:182:14 + | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` -error: aborting due to 21 previous errors +error: aborting due to 22 previous errors From eb3ffe6ed2999e9d3385be0ff981e30082ea0d2c Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 23 Jun 2020 18:51:05 +1200 Subject: [PATCH 17/97] make use of macros in operator overloading --- clippy_lints/src/utils/sugg.rs | 55 ++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 50d48650a0938..aada4122e78a4 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,6 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; +use std::ops::{Add, Sub, Not}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -307,49 +308,53 @@ impl<'a> Sugg<'a> { } } -impl std::ops::Add for Sugg<'_> { - type Output = Sugg<'static>; - fn add(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Add, &self, &rhs) - } -} +// Copied from the rust standart library, and then edited +macro_rules! forward_binop_impls_to_ref { + (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { + impl $imp<$t> for &$t { + type Output = $o; -impl std::ops::Sub for Sugg<'_> { - type Output = Sugg<'static>; - fn sub(self, rhs: Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Sub, &self, &rhs) - } -} + fn $method(self, other: $t) -> $o { + $imp::$method(self, &other) + } + } -impl std::ops::Add<&Sugg<'_>> for Sugg<'_> { - type Output = Sugg<'static>; - fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Add, &self, rhs) - } -} + impl $imp<&$t> for $t { + type Output = $o; -impl std::ops::Sub<&Sugg<'_>> for Sugg<'_> { - type Output = Sugg<'static>; - fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { - make_binop(ast::BinOpKind::Sub, &self, rhs) + fn $method(self, other: &$t) -> $o { + $imp::$method(&self, other) + } + } + + impl $imp for $t { + type Output = $o; + + fn $method(self, other: $t) -> $o { + $imp::$method(&self, &other) + } + } } } -impl std::ops::Add for &Sugg<'_> { +impl Add for &Sugg<'_> { type Output = Sugg<'static>; fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Add, self, rhs) } } -impl std::ops::Sub for &Sugg<'_> { +impl Sub for &Sugg<'_> { type Output = Sugg<'static>; fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> { make_binop(ast::BinOpKind::Sub, self, rhs) } } -impl std::ops::Not for Sugg<'_> { +forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); +forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); + +impl Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { make_unop("!", self) From 10d7a18f72155f03dbd27b872a52b5dd45def8db Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Tue, 23 Jun 2020 22:37:36 +1200 Subject: [PATCH 18/97] fmt --- clippy_lints/src/utils/sugg.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index aada4122e78a4..8f2aa724d20ba 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Sub, Not}; +use std::ops::{Add, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -334,7 +334,7 @@ macro_rules! forward_binop_impls_to_ref { $imp::$method(&self, &other) } } - } + }; } impl Add for &Sugg<'_> { From 174065fc98ef9335ea45a234aa18286cdf6c3934 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 24 Jun 2020 11:33:49 +1200 Subject: [PATCH 19/97] fix the multiple counters test --- tests/ui/manual_memcpy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 4846ab5eaaa38..8318fd898112b 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -160,12 +160,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) } let mut count = 3; - let mut count = 30; + let mut count2 = 30; for i in 0..src.len() { dst[count] = src[i]; - dst2[count] = src[i]; - count += 1; + dst2[count2] = src[i]; count += 1; + count2 += 1; } // make sure parentheses are added properly to bitwise operators, which have lower precedence than From 44187383f4724bd7e4b2b220235e93438043947a Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 24 Jun 2020 12:41:46 +1200 Subject: [PATCH 20/97] Use operator overloading instead of direct calls of `make_binop` --- clippy_lints/src/loops.rs | 4 ++-- clippy_lints/src/utils/sugg.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 98c411f5ae60e..5928d12f30bec 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -875,7 +875,7 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(&self.0 - &rhs.0), } @@ -898,7 +898,7 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(sugg::make_unop("-", rhs.0.clone())), + ("0", _) => MinifyingSugg(-(rhs.0.clone())), (x, y) if x == y => MinifyingSugg::non_paren("0"), (_, _) => MinifyingSugg(self.0 - &rhs.0), } diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 8f2aa724d20ba..1f448f86d72e3 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -13,7 +13,7 @@ use rustc_span::{BytePos, Pos}; use std::borrow::Cow; use std::convert::TryInto; use std::fmt::Display; -use std::ops::{Add, Not, Sub}; +use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. pub enum Sugg<'a> { @@ -354,6 +354,13 @@ impl Sub for &Sugg<'_> { forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>); forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>); +impl Neg for Sugg<'_> { + type Output = Sugg<'static>; + fn neg(self) -> Sugg<'static> { + make_unop("-", self) + } +} + impl Not for Sugg<'_> { type Output = Sugg<'static>; fn not(self) -> Sugg<'static> { From f410df3c4810e80b6703dcfdbc4d48f812eb4889 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sat, 27 Jun 2020 18:56:38 +1200 Subject: [PATCH 21/97] make clippy happy (`needless_pass_by_value`, `filter_map` and `find_map`) --- clippy_lints/src/loops.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 5928d12f30bec..f684e7de8f83f 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -952,7 +952,13 @@ fn get_details_from_idx<'tcx>( e: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option> { - starts.iter().find(|var| same_var(cx, e, var.id)).map(|v| v.kind) + starts.iter().find_map(|start| { + if same_var(cx, e, start.id) { + Some(start.kind) + } else { + None + } + }) } fn get_offset<'tcx>( @@ -1059,8 +1065,8 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, - dst: IndexExpr<'_>, - src: IndexExpr<'_>, + dst: &IndexExpr<'_>, + src: &IndexExpr<'_>, ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { @@ -1211,7 +1217,7 @@ fn detect_manual_memcpy<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, dst, src))) + .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) .collect::>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -2319,10 +2325,13 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { } fn into_results(self) -> impl Iterator { - self.states - .into_iter() - .filter(|(_, state)| *state == IncrementVisitorVarState::IncrOnce) - .map(|(id, _)| id) + self.states.into_iter().filter_map(|(id, state)| { + if state == IncrementVisitorVarState::IncrOnce { + Some(id) + } else { + None + } + }) } } From ce653d65a8f08d785c5061e26570b4e59372e325 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Wed, 1 Jul 2020 12:22:16 +1200 Subject: [PATCH 22/97] use `#[derive]` instead of the manual implementation --- clippy_lints/src/utils/sugg.rs | 41 +--------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 1f448f86d72e3..062b273c0f40f 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,6 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. +#[derive(Clone)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -37,46 +38,6 @@ impl Display for Sugg<'_> { } } -// It's impossible to derive Clone due to the lack of the impl Clone for AssocOp -impl Clone for Sugg<'_> { - fn clone(&self) -> Self { - /// manually cloning AssocOp - fn clone_assoc_op(this: &AssocOp) -> AssocOp { - match this { - AssocOp::Add => AssocOp::Add, - AssocOp::Subtract => AssocOp::Subtract, - AssocOp::Multiply => AssocOp::Multiply, - AssocOp::Divide => AssocOp::Divide, - AssocOp::Modulus => AssocOp::Modulus, - AssocOp::LAnd => AssocOp::LAnd, - AssocOp::LOr => AssocOp::LOr, - AssocOp::BitXor => AssocOp::BitXor, - AssocOp::BitAnd => AssocOp::BitAnd, - AssocOp::BitOr => AssocOp::BitOr, - AssocOp::ShiftLeft => AssocOp::ShiftLeft, - AssocOp::ShiftRight => AssocOp::ShiftRight, - AssocOp::Equal => AssocOp::Equal, - AssocOp::Less => AssocOp::Less, - AssocOp::LessEqual => AssocOp::LessEqual, - AssocOp::NotEqual => AssocOp::NotEqual, - AssocOp::Greater => AssocOp::Greater, - AssocOp::GreaterEqual => AssocOp::GreaterEqual, - AssocOp::Assign => AssocOp::Assign, - AssocOp::AssignOp(t) => AssocOp::AssignOp(*t), - AssocOp::As => AssocOp::As, - AssocOp::DotDot => AssocOp::DotDot, - AssocOp::DotDotEq => AssocOp::DotDotEq, - AssocOp::Colon => AssocOp::Colon, - } - } - match self { - Sugg::NonParen(x) => Sugg::NonParen(x.clone()), - Sugg::MaybeParen(x) => Sugg::MaybeParen(x.clone()), - Sugg::BinOp(op, x) => Sugg::BinOp(clone_assoc_op(op), x.clone()), - } - } -} - #[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. From e855fe3620c0a6981a4238df548fa5c2f36a24c9 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 16 Aug 2020 14:01:56 +1200 Subject: [PATCH 23/97] Reflect the changes that has been made and fmt --- clippy_lints/src/loops.rs | 45 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f684e7de8f83f..4ff567ffb0ec5 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -846,7 +846,7 @@ impl<'a> MinifyingSugg<'a> { s.as_ref() } - fn hir(cx: &LateContext<'_, '_>, expr: &Expr<'_>, default: &'a str) -> Self { + fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { Self(sugg::Sugg::hir(cx, expr, default)) } @@ -947,11 +947,7 @@ fn get_details_from_idx<'tcx>( idx: &Expr<'_>, starts: &[Start<'tcx>], ) -> Option<(StartKind<'tcx>, Offset)> { - fn get_start<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_start<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { starts.iter().find_map(|start| { if same_var(cx, e, start.id) { Some(start.kind) @@ -982,13 +978,9 @@ fn get_details_from_idx<'tcx>( match idx.kind { ExprKind::Binary(op, lhs, rhs) => match op.node { BinOpKind::Add => { - let offset_opt = if let Some(s) = get_start(cx, lhs, starts) { - get_offset(cx, rhs, starts).map(|o| (s, o)) - } else if let Some(s) = get_start(cx, rhs, starts) { - get_offset(cx, lhs, starts).map(|o| (s, o)) - } else { - None - }; + let offset_opt = get_start(cx, lhs, starts) + .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o))) + .or_else(|| get_start(cx, rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o)))); offset_opt.map(|(s, o)| (s, Offset::positive(o))) }, @@ -1011,7 +1003,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, stmts: &'tcx [Stmt<'tcx>], expr: Option<&'tcx Expr<'tcx>>, loop_counters: &'c [Start<'tcx>], @@ -1032,7 +1024,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( } fn get_loop_counters<'a, 'tcx>( - cx: &'a LateContext<'a, 'tcx>, + cx: &'a LateContext<'tcx>, body: &'tcx Block<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> + 'a> { @@ -1042,7 +1034,7 @@ fn get_loop_counters<'a, 'tcx>( // For each candidate, check the parent block to see if // it's initialized to zero at the start of the loop. - if let Some(block) = get_enclosing_block(&cx, expr.hir_id) { + get_enclosing_block(&cx, expr.hir_id).and_then(|block| { increment_visitor .into_results() .filter_map(move |var_id| { @@ -1055,9 +1047,7 @@ fn get_loop_counters<'a, 'tcx>( }) }) .into() - } else { - None - } + }) } fn build_manual_memcpy_suggestion<'tcx>( @@ -2315,7 +2305,7 @@ struct IncrementVisitor<'a, 'tcx> { } impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, states: FxHashMap::default(), @@ -2396,7 +2386,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { enum InitializeVisitorState<'hir> { Initial, // Not examined yet Declared(Symbol), // Declared but not (yet) initialized - Initialized { name: Symbol, initializer: &'hir Expr<'hir> }, + Initialized { + name: Symbol, + initializer: &'hir Expr<'hir>, + }, DontWarn, } @@ -2412,7 +2405,7 @@ struct InitializeVisitor<'a, 'tcx> { } impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'a, 'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { + fn new(cx: &'a LateContext<'tcx>, end_expr: &'tcx Expr<'tcx>, var_id: HirId) -> Self { Self { cx, end_expr, @@ -2423,7 +2416,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { } } - fn get_result(&self) -> Option<(Name, &'tcx Expr<'tcx>)> { + fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> { if let InitializeVisitorState::Initialized { name, initializer } = self.state { Some((name, initializer)) } else { @@ -2442,14 +2435,12 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { if local.pat.hir_id == self.var_id; if let PatKind::Binding(.., ident, _) = local.pat.kind; then { - self.state = if let Some(ref init) = local.init { + self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| { InitializeVisitorState::Initialized { initializer: init, name: ident.name, } - } else { - InitializeVisitorState::Declared(ident.name) - } + }) } } walk_stmt(self, stmt); From 4918e7ad62259e4c35a0b9d6ac0f0e98e51ff7b1 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:19:43 +1300 Subject: [PATCH 24/97] Replace `snippet_opt` + `unwrap_or_else` with `snippet` --- clippy_lints/src/loops.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4ff567ffb0ec5..647537933f722 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -5,9 +5,8 @@ use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, - match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, - snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, - SpanlessEq, + match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite, + span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq, }; use if_chain::if_chain; use rustc_ast::ast; @@ -1121,8 +1120,8 @@ fn build_manual_memcpy_suggestion<'tcx>( let (dst_offset, dst_limit) = print_offset_and_limit(&dst); let (src_offset, src_limit) = print_offset_and_limit(&src); - let dst_base_str = snippet_opt(cx, dst.base.span).unwrap_or_else(|| "???".into()); - let src_base_str = snippet_opt(cx, src.base.span).unwrap_or_else(|| "???".into()); + let dst_base_str = snippet(cx, dst.base.span, "???"); + let src_base_str = snippet(cx, src.base.span, "???"); let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { dst_base_str @@ -1133,6 +1132,7 @@ fn build_manual_memcpy_suggestion<'tcx>( dst_offset.maybe_par(), dst_limit.maybe_par() ) + .into() }; format!( From 5c71352b18ee7a48e825aefd2862b8e0d16ea45b Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 14:52:06 +1300 Subject: [PATCH 25/97] Prevent unnecessary lints from triggering --- clippy_lints/src/loops.rs | 12 ++++++++---- tests/ui/manual_memcpy.rs | 1 - tests/ui/manual_memcpy.stderr | 20 ++++++++++---------- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 647537933f722..f2df53aee4f68 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -768,12 +768,14 @@ fn check_for_loop<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - check_for_loop_range(cx, pat, arg, body, expr); + let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr); + if !is_manual_memcpy_triggered { + check_for_loop_range(cx, pat, arg, body, expr); + check_for_loop_explicit_counter(cx, pat, arg, body, expr); + } check_for_loop_arg(cx, pat, arg, expr); - check_for_loop_explicit_counter(cx, pat, arg, body, expr); check_for_loop_over_map_kv(cx, pat, arg, body, expr); check_for_mut_range_bound(cx, arg, body); - detect_manual_memcpy(cx, pat, arg, body, expr); detect_same_item_push(cx, pat, arg, body, expr); } @@ -1152,7 +1154,7 @@ fn detect_manual_memcpy<'tcx>( arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, -) { +) -> bool { if let Some(higher::Range { start: Some(start), end: Some(end), @@ -1222,9 +1224,11 @@ fn detect_manual_memcpy<'tcx>( big_sugg, Applicability::Unspecified, ); + return true; } } } + false } // Scans the body of the for loop and determines whether lint should be given diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy.rs index 8318fd898112b..84758275dd747 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy.rs @@ -115,7 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -#[allow(clippy::needless_range_loop, clippy::explicit_counter_loop)] pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { let mut count = 0; for i in 3..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index d189bde0508e1..464b18984fbc5 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -79,49 +79,49 @@ LL | for i in 0..0 { | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:121:14 + --> $DIR/manual_memcpy.rs:120:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:127:14 + --> $DIR/manual_memcpy.rs:126:14 | LL | for i in 3..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:133:14 + --> $DIR/manual_memcpy.rs:132:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:139:14 + --> $DIR/manual_memcpy.rs:138:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:145:14 + --> $DIR/manual_memcpy.rs:144:14 | LL | for i in 3..(3 + src.len()) { | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:151:14 + --> $DIR/manual_memcpy.rs:150:14 | LL | for i in 5..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:157:14 + --> $DIR/manual_memcpy.rs:156:14 | LL | for i in 3..10 { | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:164:14 + --> $DIR/manual_memcpy.rs:163:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ @@ -133,13 +133,13 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:174:14 + --> $DIR/manual_memcpy.rs:173:14 | LL | for i in 0..1 << 1 { | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:182:14 + --> $DIR/manual_memcpy.rs:181:14 | LL | for i in 0..src.len() { | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` From 99aceebf1c7cb382e18d66914bd9f576e529aa99 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Sun, 27 Sep 2020 16:38:41 +1300 Subject: [PATCH 26/97] Use the spans of the entire `for` loops for suggestions --- clippy_lints/src/loops.rs | 23 ++-- tests/ui/manual_memcpy.stderr | 196 ++++++++++++++++++++++------------ 2 files changed, 140 insertions(+), 79 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index f2df53aee4f68..7c8b6f483bcbf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -779,6 +779,17 @@ fn check_for_loop<'tcx>( detect_same_item_push(cx, pat, arg, body, expr); } +// this function assumes the given expression is a `for` loop. +fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span { + // for some reason this is the only way to get the `Span` + // of the entire `for` loop + if let ExprKind::Match(_, arms, _) = &expr.kind { + arms[0].body.span + } else { + unreachable!() + } +} + fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { if_chain! { if let ExprKind::Path(qpath) = &expr.kind; @@ -1138,7 +1149,7 @@ fn build_manual_memcpy_suggestion<'tcx>( }; format!( - "{}.clone_from_slice(&{}[{}..{}])", + "{}.clone_from_slice(&{}[{}..{}]);", dst, src_base_str, src_offset.maybe_par(), @@ -1218,7 +1229,7 @@ fn detect_manual_memcpy<'tcx>( span_lint_and_sugg( cx, MANUAL_MEMCPY, - expr.span, + get_span_of_entire_for_loop(expr), "it looks like you're manually copying between slices", "try replacing the loop by", big_sugg, @@ -1734,13 +1745,7 @@ fn check_for_loop_explicit_counter<'tcx>( then { let mut applicability = Applicability::MachineApplicable; - // for some reason this is the only way to get the `Span` - // of the entire `for` loop - let for_span = if let ExprKind::Match(_, arms, _) = &expr.kind { - arms[0].body.span - } else { - unreachable!() - }; + let for_span = get_span_of_entire_for_loop(expr); span_lint_and_sugg( cx, diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy.stderr index 464b18984fbc5..db62ed90d97d6 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy.stderr @@ -1,148 +1,204 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:14 + --> $DIR/manual_memcpy.rs:7:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` | = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:14 + --> $DIR/manual_memcpy.rs:12:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i + 10] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:14 + --> $DIR/manual_memcpy.rs:17:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i + 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:14 + --> $DIR/manual_memcpy.rs:22:5 | -LL | for i in 11..src.len() { - | ^^^^^^^^^^^^^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)])` +LL | / for i in 11..src.len() { +LL | | dst[i] = src[i - 10]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:14 + --> $DIR/manual_memcpy.rs:27:5 | -LL | for i in 0..dst.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()])` +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:14 + --> $DIR/manual_memcpy.rs:40:5 | -LL | for i in 10..256 { - | ^^^^^^^ +LL | / for i in 10..256 { +LL | | dst[i] = src[i - 5]; +LL | | dst2[i + 500] = src[i] +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]) -LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]) { +LL | dst[10..256].clone_from_slice(&src[(10 - 5)..(256 - 5)]); +LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:14 + --> $DIR/manual_memcpy.rs:52:5 | -LL | for i in 10..LOOP_OFFSET { - | ^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)])` +LL | / for i in 10..LOOP_OFFSET { +LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:14 + --> $DIR/manual_memcpy.rs:65:5 | -LL | for i in 0..src_vec.len() { - | ^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..])` +LL | / for i in 0..src_vec.len() { +LL | | dst_vec[i] = src_vec[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:14 + --> $DIR/manual_memcpy.rs:94:5 | -LL | for i in from..from + src.len() { - | ^^^^^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)])` +LL | / for i in from..from + src.len() { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:98:14 + --> $DIR/manual_memcpy.rs:98:5 | -LL | for i in from..from + 3 { - | ^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)])` +LL | / for i in from..from + 3 { +LL | | dst[i] = src[i - from]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:103:14 + --> $DIR/manual_memcpy.rs:103:5 | -LL | for i in 0..5 { - | ^^^^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5])` +LL | / for i in 0..5 { +LL | | dst[i - 0] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:108:14 + --> $DIR/manual_memcpy.rs:108:5 | -LL | for i in 0..0 { - | ^^^^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0])` +LL | / for i in 0..0 { +LL | | dst[i] = src[i]; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:14 + --> $DIR/manual_memcpy.rs:120:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)])` +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:14 + --> $DIR/manual_memcpy.rs:126:5 | -LL | for i in 3..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..])` +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:14 + --> $DIR/manual_memcpy.rs:132:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:14 + --> $DIR/manual_memcpy.rs:138:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:14 + --> $DIR/manual_memcpy.rs:144:5 | -LL | for i in 3..(3 + src.len()) { - | ^^^^^^^^^^^^^^^^^^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)])` +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:14 + --> $DIR/manual_memcpy.rs:150:5 | -LL | for i in 5..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)])` +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:14 + --> $DIR/manual_memcpy.rs:156:5 | -LL | for i in 3..10 { - | ^^^^^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)])` +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:14 + --> $DIR/manual_memcpy.rs:163:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ | help: try replacing the loop by | -LL | for i in dst[3..(src.len() + 3)].clone_from_slice(&src[..]) -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]) { +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:14 + --> $DIR/manual_memcpy.rs:173:5 | -LL | for i in 0..1 << 1 { - | ^^^^^^^^^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)])` +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:14 + --> $DIR/manual_memcpy.rs:181:5 | -LL | for i in 0..src.len() { - | ^^^^^^^^^^^^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..])` +LL | / for i in 0..src.len() { +LL | | dst[i] = src[i].clone(); +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` error: aborting due to 22 previous errors From 9725f00f4dd089b875a5d5306ff906494e041f38 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:27:55 +1300 Subject: [PATCH 27/97] Use the `From` trait to make `MinifyingSugg` --- clippy_lints/src/loops.rs | 84 +++++++++++++++------------------- clippy_lints/src/utils/sugg.rs | 6 ++- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 7c8b6f483bcbf..215700fed8cd2 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -817,22 +817,22 @@ struct Offset { } impl Offset { - fn negative(value: MinifyingSugg<'static>) -> Self { + fn negative(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Negative, } } - fn positive(value: MinifyingSugg<'static>) -> Self { + fn positive(value: Sugg<'static>) -> Self { Self { - value, + value: value.into(), sign: OffsetSign::Positive, } } fn empty() -> Self { - Self::positive(MinifyingSugg::non_paren("0")) + Self::positive(sugg::ZERO) } } @@ -844,30 +844,22 @@ fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'st } #[derive(Clone)] -struct MinifyingSugg<'a>(sugg::Sugg<'a>); - -impl std::fmt::Display for MinifyingSugg<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - std::fmt::Display::fmt(&self.0, f) - } -} +struct MinifyingSugg<'a>(Sugg<'a>); impl<'a> MinifyingSugg<'a> { fn as_str(&self) -> &str { - let sugg::Sugg::NonParen(s) | sugg::Sugg::MaybeParen(s) | sugg::Sugg::BinOp(_, s) = &self.0; + let Sugg::NonParen(s) | Sugg::MaybeParen(s) | Sugg::BinOp(_, s) = &self.0; s.as_ref() } - fn hir(cx: &LateContext<'_>, expr: &Expr<'_>, default: &'a str) -> Self { - Self(sugg::Sugg::hir(cx, expr, default)) - } - - fn maybe_par(self) -> Self { - Self(self.0.maybe_par()) + fn into_sugg(self) -> Sugg<'a> { + self.0 } +} - fn non_paren(str: impl Into>) -> Self { - Self(sugg::Sugg::NonParen(str.into())) +impl<'a> From> for MinifyingSugg<'a> { + fn from(sugg: Sugg<'a>) -> Self { + Self(sugg) } } @@ -877,7 +869,7 @@ impl std::ops::Add for &MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self.clone(), - (_, _) => MinifyingSugg(&self.0 + &rhs.0), + (_, _) => (&self.0 + &rhs.0).into(), } } } @@ -887,9 +879,9 @@ impl std::ops::Sub for &MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self.clone(), - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(&self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (&self.0 - &rhs.0).into(), } } } @@ -900,7 +892,7 @@ impl std::ops::Add<&MinifyingSugg<'static>> for MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { ("0", _) => rhs.clone(), (_, "0") => self, - (_, _) => MinifyingSugg(self.0 + &rhs.0), + (_, _) => (self.0 + &rhs.0).into(), } } } @@ -910,9 +902,9 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { fn sub(self, rhs: &MinifyingSugg<'static>) -> MinifyingSugg<'static> { match (self.as_str(), rhs.as_str()) { (_, "0") => self, - ("0", _) => MinifyingSugg(-(rhs.0.clone())), - (x, y) if x == y => MinifyingSugg::non_paren("0"), - (_, _) => MinifyingSugg(self.0 - &rhs.0), + ("0", _) => (-rhs.0.clone()).into(), + (x, y) if x == y => sugg::ZERO.into(), + (_, _) => (self.0 - &rhs.0).into(), } } } @@ -969,19 +961,15 @@ fn get_details_from_idx<'tcx>( }) } - fn get_offset<'tcx>( - cx: &LateContext<'tcx>, - e: &Expr<'_>, - starts: &[Start<'tcx>], - ) -> Option> { + fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option> { match &e.kind { ExprKind::Lit(l) => match l.node { - ast::LitKind::Int(x, _ty) => Some(MinifyingSugg::non_paren(x.to_string())), + ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { // `e` is always non paren as it's a `Path` - Some(MinifyingSugg::non_paren(snippet(cx, e.span, "???"))) + Some(Sugg::NonParen(snippet(cx, e.span, "???"))) }, _ => None, } @@ -1072,7 +1060,7 @@ fn build_manual_memcpy_suggestion<'tcx>( ) -> String { fn print_offset(offset: MinifyingSugg<'static>) -> MinifyingSugg<'static> { if offset.as_str() == "0" { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { offset } @@ -1088,14 +1076,14 @@ fn build_manual_memcpy_suggestion<'tcx>( if var_def_id(cx, arg) == var_def_id(cx, base); then { if sugg.as_str() == end_str { - MinifyingSugg::non_paren("") + sugg::EMPTY.into() } else { sugg } } else { match limits { ast::RangeLimits::Closed => { - sugg + &MinifyingSugg::non_paren("1") + sugg + &sugg::ONE.into() }, ast::RangeLimits::HalfOpen => sugg, } @@ -1103,29 +1091,31 @@ fn build_manual_memcpy_suggestion<'tcx>( } }; - let start_str = MinifyingSugg::hir(cx, start, ""); - let end_str = MinifyingSugg::hir(cx, end, ""); + let start_str = Sugg::hir(cx, start, "").into(); + let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); let print_offset_and_limit = |idx_expr: &IndexExpr<'_>| match idx_expr.idx { StartKind::Range => ( - print_offset(apply_offset(&start_str, &idx_expr.idx_offset)), + print_offset(apply_offset(&start_str, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset), - ), + ) + .into_sugg(), ), StartKind::Counter { initializer } => { - let counter_start = MinifyingSugg::hir(cx, initializer, ""); + let counter_start = Sugg::hir(cx, initializer, "").into(); ( - print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)), + print_offset(apply_offset(&counter_start, &idx_expr.idx_offset)).into_sugg(), print_limit( end, end_str.as_str(), idx_expr.base, apply_offset(&end_str, &idx_expr.idx_offset) + &counter_start - &start_str, - ), + ) + .into_sugg(), ) }, }; @@ -1136,7 +1126,7 @@ fn build_manual_memcpy_suggestion<'tcx>( let dst_base_str = snippet(cx, dst.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???"); - let dst = if dst_offset.as_str() == "" && dst_limit.as_str() == "" { + let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { dst_base_str } else { format!( diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index 062b273c0f40f..0b2cb667bf413 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -16,7 +16,7 @@ use std::fmt::Display; use std::ops::{Add, Neg, Not, Sub}; /// A helper type to build suggestion correctly handling parenthesis. -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum Sugg<'a> { /// An expression that never needs parenthesis such as `1337` or `[0; 42]`. NonParen(Cow<'a, str>), @@ -27,8 +27,12 @@ pub enum Sugg<'a> { BinOp(AssocOp, Cow<'a, str>), } +/// Literal constant `0`, for convenience. +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0")); /// Literal constant `1`, for convenience. pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1")); +/// a constant represents an empty string, for convenience. +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("")); impl Display for Sugg<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { From ec94bd6cb45e337fd10ca19fadb9a71ecb67db5f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Mon, 28 Sep 2020 02:43:45 +1300 Subject: [PATCH 28/97] split up the `manual_memcpy` test --- tests/ui/manual_memcpy/with_loop_counters.rs | 64 ++++++++++ .../manual_memcpy/with_loop_counters.stderr | 93 ++++++++++++++ .../without_loop_counters.rs} | 61 --------- .../without_loop_counters.stderr} | 117 +++--------------- 4 files changed, 171 insertions(+), 164 deletions(-) create mode 100644 tests/ui/manual_memcpy/with_loop_counters.rs create mode 100644 tests/ui/manual_memcpy/with_loop_counters.stderr rename tests/ui/{manual_memcpy.rs => manual_memcpy/without_loop_counters.rs} (68%) rename tests/ui/{manual_memcpy.stderr => manual_memcpy/without_loop_counters.stderr} (50%) diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs new file mode 100644 index 0000000000000..a49ba9eb10afa --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -0,0 +1,64 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] + +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[count] = src[i]; + count += 1; + } + + let mut count = 3; + for i in 0..src.len() { + dst[i] = src[count]; + count += 1; + } + + let mut count = 0; + for i in 3..(3 + src.len()) { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + for i in 5..src.len() { + dst[i] = src[count - 2]; + count += 1; + } + + let mut count = 5; + for i in 3..10 { + dst[i] = src[count]; + count += 1; + } + + let mut count = 3; + let mut count2 = 30; + for i in 0..src.len() { + dst[count] = src[i]; + dst2[count2] = src[i]; + count += 1; + count2 += 1; + } + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetric ones + let mut count = 0 << 1; + for i in 0..1 << 1 { + dst[count] = src[i + 2]; + count += 1; + } +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr new file mode 100644 index 0000000000000..24393ad9b4d42 --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -0,0 +1,93 @@ +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:5:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + | + = note: `-D clippy::manual-memcpy` implied by `-D warnings` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:11:5 + | +LL | / for i in 3..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:17:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:23:5 + | +LL | / for i in 0..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:29:5 + | +LL | / for i in 3..(3 + src.len()) { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:35:5 + | +LL | / for i in 5..src.len() { +LL | | dst[i] = src[count - 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:41:5 + | +LL | / for i in 3..10 { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:48:5 + | +LL | / for i in 0..src.len() { +LL | | dst[count] = src[i]; +LL | | dst2[count2] = src[i]; +LL | | count += 1; +LL | | count2 += 1; +LL | | } + | |_____^ + | +help: try replacing the loop by + | +LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); +LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); + | + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:58:5 + | +LL | / for i in 0..1 << 1 { +LL | | dst[count] = src[i + 2]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` + +error: aborting due to 9 previous errors + diff --git a/tests/ui/manual_memcpy.rs b/tests/ui/manual_memcpy/without_loop_counters.rs similarity index 68% rename from tests/ui/manual_memcpy.rs rename to tests/ui/manual_memcpy/without_loop_counters.rs index 84758275dd747..0083f94798fe4 100644 --- a/tests/ui/manual_memcpy.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -115,67 +115,6 @@ pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { } } -pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { - let mut count = 0; - for i in 3..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[count] = src[i]; - count += 1; - } - - let mut count = 3; - for i in 0..src.len() { - dst[i] = src[count]; - count += 1; - } - - let mut count = 0; - for i in 3..(3 + src.len()) { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - for i in 5..src.len() { - dst[i] = src[count - 2]; - count += 1; - } - - let mut count = 5; - for i in 3..10 { - dst[i] = src[count]; - count += 1; - } - - let mut count = 3; - let mut count2 = 30; - for i in 0..src.len() { - dst[count] = src[i]; - dst2[count2] = src[i]; - count += 1; - count2 += 1; - } - - // make sure parentheses are added properly to bitwise operators, which have lower precedence than - // arithmetric ones - let mut count = 0 << 1; - for i in 0..1 << 1 { - dst[count] = src[i + 2]; - count += 1; - } -} - #[warn(clippy::needless_range_loop, clippy::manual_memcpy)] pub fn manual_clone(src: &[String], dst: &mut [String]) { for i in 0..src.len() { diff --git a/tests/ui/manual_memcpy.stderr b/tests/ui/manual_memcpy/without_loop_counters.stderr similarity index 50% rename from tests/ui/manual_memcpy.stderr rename to tests/ui/manual_memcpy/without_loop_counters.stderr index db62ed90d97d6..54b966f6e5419 100644 --- a/tests/ui/manual_memcpy.stderr +++ b/tests/ui/manual_memcpy/without_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:7:5 + --> $DIR/without_loop_counters.rs:7:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i]; @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::manual-memcpy` implied by `-D warnings` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:12:5 + --> $DIR/without_loop_counters.rs:12:5 | LL | / for i in 0..src.len() { LL | | dst[i + 10] = src[i]; @@ -17,7 +17,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[10..(src.len() + 10)].clone_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:17:5 + --> $DIR/without_loop_counters.rs:17:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i + 10]; @@ -25,7 +25,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[10..(src.len() + 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:22:5 + --> $DIR/without_loop_counters.rs:22:5 | LL | / for i in 11..src.len() { LL | | dst[i] = src[i - 10]; @@ -33,7 +33,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[11..src.len()].clone_from_slice(&src[(11 - 10)..(src.len() - 10)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:27:5 + --> $DIR/without_loop_counters.rs:27:5 | LL | / for i in 0..dst.len() { LL | | dst[i] = src[i]; @@ -41,7 +41,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[..dst.len()]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:40:5 + --> $DIR/without_loop_counters.rs:40:5 | LL | / for i in 10..256 { LL | | dst[i] = src[i - 5]; @@ -56,7 +56,7 @@ LL | dst2[(10 + 500)..(256 + 500)].clone_from_slice(&src[10..256]); | error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:52:5 + --> $DIR/without_loop_counters.rs:52:5 | LL | / for i in 10..LOOP_OFFSET { LL | | dst[i + LOOP_OFFSET] = src[i - some_var]; @@ -64,7 +64,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].clone_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:65:5 + --> $DIR/without_loop_counters.rs:65:5 | LL | / for i in 0..src_vec.len() { LL | | dst_vec[i] = src_vec[i]; @@ -72,7 +72,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst_vec[..src_vec.len()].clone_from_slice(&src_vec[..]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:94:5 + --> $DIR/without_loop_counters.rs:94:5 | LL | / for i in from..from + src.len() { LL | | dst[i] = src[i - from]; @@ -80,7 +80,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + src.len())].clone_from_slice(&src[..(from + src.len() - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:98:5 + --> $DIR/without_loop_counters.rs:98:5 | LL | / for i in from..from + 3 { LL | | dst[i] = src[i - from]; @@ -88,7 +88,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[from..(from + 3)].clone_from_slice(&src[..(from + 3 - from)]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:103:5 + --> $DIR/without_loop_counters.rs:103:5 | LL | / for i in 0..5 { LL | | dst[i - 0] = src[i]; @@ -96,7 +96,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..5].clone_from_slice(&src[..5]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:108:5 + --> $DIR/without_loop_counters.rs:108:5 | LL | / for i in 0..0 { LL | | dst[i] = src[i]; @@ -104,101 +104,12 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..0].clone_from_slice(&src[..0]);` error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:120:5 - | -LL | / for i in 3..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:126:5 - | -LL | / for i in 3..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].clone_from_slice(&src[3..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:132:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].clone_from_slice(&src[..]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:138:5 - | -LL | / for i in 0..src.len() { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[3..(src.len() + 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:144:5 - | -LL | / for i in 3..(3 + src.len()) { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..((3 + src.len()))].clone_from_slice(&src[..((3 + src.len()) - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:150:5 - | -LL | / for i in 5..src.len() { -LL | | dst[i] = src[count - 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[5..src.len()].clone_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:156:5 - | -LL | / for i in 3..10 { -LL | | dst[i] = src[count]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:163:5 - | -LL | / for i in 0..src.len() { -LL | | dst[count] = src[i]; -LL | | dst2[count2] = src[i]; -LL | | count += 1; -LL | | count2 += 1; -LL | | } - | |_____^ - | -help: try replacing the loop by - | -LL | dst[3..(src.len() + 3)].clone_from_slice(&src[..]); -LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); - | - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:173:5 - | -LL | / for i in 0..1 << 1 { -LL | | dst[count] = src[i + 2]; -LL | | count += 1; -LL | | } - | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` - -error: it looks like you're manually copying between slices - --> $DIR/manual_memcpy.rs:181:5 + --> $DIR/without_loop_counters.rs:120:5 | LL | / for i in 0..src.len() { LL | | dst[i] = src[i].clone(); LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].clone_from_slice(&src[..]);` -error: aborting due to 22 previous errors +error: aborting due to 13 previous errors From 388384177e89db60280dc275e4239e97e61f6a59 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 13:30:50 +1300 Subject: [PATCH 29/97] document `MinifyingSugg` and `Offset` ...and also swap their position --- clippy_lints/src/loops.rs | 82 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 215700fed8cd2..9a1ba4907cda0 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -805,44 +805,10 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool { } } -#[derive(Clone, Copy)] -enum OffsetSign { - Positive, - Negative, -} - -struct Offset { - value: MinifyingSugg<'static>, - sign: OffsetSign, -} - -impl Offset { - fn negative(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Negative, - } - } - - fn positive(value: Sugg<'static>) -> Self { - Self { - value: value.into(), - sign: OffsetSign::Positive, - } - } - - fn empty() -> Self { - Self::positive(sugg::ZERO) - } -} - -fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { - match rhs.sign { - OffsetSign::Positive => lhs + &rhs.value, - OffsetSign::Negative => lhs - &rhs.value, - } -} - +/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; +/// and also, it avoids subtracting a variable from the same one by replacing it with `0`. +/// it exists for the convenience of the overloaded operators while normal functions can do the +/// same. #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); @@ -909,6 +875,46 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> { } } +/// a wrapper around `MinifyingSugg`, which carries a operator like currying +/// so that the suggested code become more efficient (e.g. `foo + -bar` `foo - bar`). +struct Offset { + value: MinifyingSugg<'static>, + sign: OffsetSign, +} + +#[derive(Clone, Copy)] +enum OffsetSign { + Positive, + Negative, +} + +impl Offset { + fn negative(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Negative, + } + } + + fn positive(value: Sugg<'static>) -> Self { + Self { + value: value.into(), + sign: OffsetSign::Positive, + } + } + + fn empty() -> Self { + Self::positive(sugg::ZERO) + } +} + +fn apply_offset(lhs: &MinifyingSugg<'static>, rhs: &Offset) -> MinifyingSugg<'static> { + match rhs.sign { + OffsetSign::Positive => lhs + &rhs.value, + OffsetSign::Negative => lhs - &rhs.value, + } +} + #[derive(Debug, Clone, Copy)] enum StartKind<'hir> { Range, From 94d7b82340792af6e5c9b7c105dca1f2fef1b495 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:04:46 +1300 Subject: [PATCH 30/97] simplify the code --- clippy_lints/src/loops.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 9a1ba4907cda0..2ae5a9acb75cf 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -973,10 +973,7 @@ fn get_details_from_idx<'tcx>( ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())), _ => None, }, - ExprKind::Path(..) if get_start(cx, e, starts).is_none() => { - // `e` is always non paren as it's a `Path` - Some(Sugg::NonParen(snippet(cx, e.span, "???"))) - }, + ExprKind::Path(..) if get_start(cx, e, starts).is_none() => Some(Sugg::hir(cx, e, "???")), _ => None, } } @@ -1010,8 +1007,7 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, - stmts: &'tcx [Stmt<'tcx>], - expr: Option<&'tcx Expr<'tcx>>, + Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { stmts @@ -1025,7 +1021,7 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( then { None } else { Some(e) } }, }) - .chain(expr.into_iter()) + .chain((*expr).into_iter()) .map(get_assignment) } @@ -1184,7 +1180,7 @@ fn detect_manual_memcpy<'tcx>( if let Some(loop_counters) = get_loop_counters(cx, block, expr) { starts.extend(loop_counters); } - iter_a = Some(get_assignments(cx, block.stmts, block.expr, &starts)); + iter_a = Some(get_assignments(cx, block, &starts)); } else { iter_b = Some(get_assignment(body)); } From 1402d8ae4f0c07ac48bd8297ec07901346c32d32 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:18:37 +1300 Subject: [PATCH 31/97] fix a FN where incr exprs with no semicolon at ends --- clippy_lints/src/loops.rs | 18 ++++++++++++------ tests/ui/manual_memcpy/with_loop_counters.rs | 7 +++++++ .../ui/manual_memcpy/with_loop_counters.stderr | 11 ++++++++++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 2ae5a9acb75cf..ea40c2af4f717 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,14 +1014,20 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( .iter() .filter_map(move |stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, - StmtKind::Expr(e) | StmtKind::Semi(e) => if_chain! { - if let ExprKind::AssignOp(_, var, _) = e.kind; - // skip StartKind::Range - if loop_counters.iter().skip(1).any(|counter| Some(counter.id) == var_def_id(cx, var)); - then { None } else { Some(e) } - }, + StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) .chain((*expr).into_iter()) + .filter(move |e| { + if let ExprKind::AssignOp(_, place, _) = e.kind { + !loop_counters + .iter() + // skip StartKind::Range + .skip(1) + .any(|counter| same_var(cx, place, counter.id)) + } else { + true + } + }) .map(get_assignment) } diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index a49ba9eb10afa..70873c9e99447 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -59,6 +59,13 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[count] = src[i + 2]; count += 1; } + + // make sure incrementing expressions without semicolons at the end of loops are handled correctly. + let mut count = 0; + for i in 3..src.len() { + dst[i] = src[count]; + count += 1 + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 24393ad9b4d42..598c881b4d6cd 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -89,5 +89,14 @@ LL | | count += 1; LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` -error: aborting due to 9 previous errors +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:65:5 + | +LL | / for i in 3..src.len() { +LL | | dst[i] = src[count]; +LL | | count += 1 +LL | | } + | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` + +error: aborting due to 10 previous errors From 41a0ccbc57902e488e62ed8ca9a1ebb565129c09 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 21:19:14 +1300 Subject: [PATCH 32/97] add comments around `loop_counters` --- clippy_lints/src/loops.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index ea40c2af4f717..4f279cc5ef7bc 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1005,6 +1005,10 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx } } +/// Get assignments from the given block. +/// The returned iterator yields `None` if no assignment expressions are there, +/// filtering out the increments of the given whitelisted loop counters; +/// because its job is to make sure there's nothing other than assignments and the increments. fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( cx: &'a LateContext<'tcx>, Block { stmts, expr, .. }: &'tcx Block<'tcx>, @@ -1021,7 +1025,8 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( if let ExprKind::AssignOp(_, place, _) = e.kind { !loop_counters .iter() - // skip StartKind::Range + // skip the first item which should be `StartKind::Range` + // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop. .skip(1) .any(|counter| same_var(cx, place, counter.id)) } else { @@ -1191,11 +1196,11 @@ fn detect_manual_memcpy<'tcx>( iter_b = Some(get_assignment(body)); } - // The only statements in the for loops can be indexed assignments from - // indexed retrievals. let assignments = iter_a.into_iter().flatten().chain(iter_b.into_iter()); let big_sugg = assignments + // The only statements in the for loops can be indexed assignments from + // indexed retrievals (except increments of loop counters). .map(|o| { o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); From 2a0e45b8dbfa9c8494371983b128b933082a9c13 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 21:30:21 +1300 Subject: [PATCH 33/97] supress `clippy::filter_map` --- clippy_lints/src/loops.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 4f279cc5ef7bc..d3a1683cc0cbb 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1014,6 +1014,9 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>( Block { stmts, expr, .. }: &'tcx Block<'tcx>, loop_counters: &'c [Start<'tcx>], ) -> impl Iterator, &'tcx Expr<'tcx>)>> + 'c { + // As the `filter` and `map` below do different things, I think putting together + // just increases complexity. (cc #3188 and #4193) + #[allow(clippy::filter_map)] stmts .iter() .filter_map(move |stmt| match stmt.kind { From 7820cb14421f751c05d6d2d5925236c3429cd93f Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 23:21:24 +1300 Subject: [PATCH 34/97] Add tests for * `dst.len()` as the end of the range with loop counters * the increment of the loop counter at the top of the loop --- tests/ui/manual_memcpy/with_loop_counters.rs | 17 +++++++++++++++++ .../ui/manual_memcpy/with_loop_counters.stderr | 17 +++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index 70873c9e99447..ba388a05a2859 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -37,6 +37,12 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) count += 1; } + let mut count = 2; + for i in 0..dst.len() { + dst[i] = src[count]; + count += 1; + } + let mut count = 5; for i in 3..10 { dst[i] = src[count]; @@ -66,6 +72,17 @@ pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) dst[i] = src[count]; count += 1 } + + // make sure ones where the increment is not at the end of the loop. + // As a possible enhancement, one could adjust the offset in the suggestion according to + // the position. For example, if the increment is at the top of the loop; + // treating the loop counter as if it were initialized 1 greater than the original value. + let mut count = 0; + #[allow(clippy::needless_range_loop)] + for i in 0..src.len() { + count += 1; + dst[i] = src[count]; + } } fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 598c881b4d6cd..2547b19f5d1d4 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -57,6 +57,15 @@ LL | | } error: it looks like you're manually copying between slices --> $DIR/with_loop_counters.rs:41:5 | +LL | / for i in 0..dst.len() { +LL | | dst[i] = src[count]; +LL | | count += 1; +LL | | } + | |_____^ help: try replacing the loop by: `dst.clone_from_slice(&src[2..(dst.len() + 2)]);` + +error: it looks like you're manually copying between slices + --> $DIR/with_loop_counters.rs:47:5 + | LL | / for i in 3..10 { LL | | dst[i] = src[count]; LL | | count += 1; @@ -64,7 +73,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..10].clone_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:48:5 + --> $DIR/with_loop_counters.rs:54:5 | LL | / for i in 0..src.len() { LL | | dst[count] = src[i]; @@ -81,7 +90,7 @@ LL | dst2[30..(src.len() + 30)].clone_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:58:5 + --> $DIR/with_loop_counters.rs:64:5 | LL | / for i in 0..1 << 1 { LL | | dst[count] = src[i + 2]; @@ -90,7 +99,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].clone_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> $DIR/with_loop_counters.rs:65:5 + --> $DIR/with_loop_counters.rs:71:5 | LL | / for i in 3..src.len() { LL | | dst[i] = src[count]; @@ -98,5 +107,5 @@ LL | | count += 1 LL | | } | |_____^ help: try replacing the loop by: `dst[3..src.len()].clone_from_slice(&src[..(src.len() - 3)]);` -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors From b54188429409a6da5f931fa4be5df1f40b377015 Mon Sep 17 00:00:00 2001 From: rail <12975677+rail-rain@users.noreply.github.com> Date: Fri, 2 Oct 2020 23:38:10 +1300 Subject: [PATCH 35/97] remove the explicit return value of `print_limit` --- clippy_lints/src/loops.rs | 41 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index d3a1683cc0cbb..3e9265c121530 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -1082,30 +1082,29 @@ fn build_manual_memcpy_suggestion<'tcx>( } } - let print_limit = - |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| -> MinifyingSugg<'static> { - if_chain! { - if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; - if method.ident.name == sym!(len); - if len_args.len() == 1; - if let Some(arg) = len_args.get(0); - if var_def_id(cx, arg) == var_def_id(cx, base); - then { - if sugg.as_str() == end_str { - sugg::EMPTY.into() - } else { - sugg - } + let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { + if_chain! { + if let ExprKind::MethodCall(method, _, len_args, _) = end.kind; + if method.ident.name == sym!(len); + if len_args.len() == 1; + if let Some(arg) = len_args.get(0); + if var_def_id(cx, arg) == var_def_id(cx, base); + then { + if sugg.as_str() == end_str { + sugg::EMPTY.into() } else { - match limits { - ast::RangeLimits::Closed => { - sugg + &sugg::ONE.into() - }, - ast::RangeLimits::HalfOpen => sugg, - } + sugg + } + } else { + match limits { + ast::RangeLimits::Closed => { + sugg + &sugg::ONE.into() + }, + ast::RangeLimits::HalfOpen => sugg, } } - }; + } + }; let start_str = Sugg::hir(cx, start, "").into(); let end_str: MinifyingSugg<'_> = Sugg::hir(cx, end, "").into(); From f302af33bc1901ddd0226c8d3a6433682a85f69b Mon Sep 17 00:00:00 2001 From: nahuakang Date: Fri, 2 Oct 2020 20:13:01 +0200 Subject: [PATCH 36/97] Add doc comment issue of #5834 to known problems of lint doc_markdown --- clippy_lints/src/doc.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 62bb70af06e93..07f604cf71472 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -32,6 +32,11 @@ declare_clippy_lint! { /// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks /// for is limited, and there are still false positives. /// + /// In addition, when writing documentation comments, including `[]` brackets + /// inside a link text would trip the parser. Therfore, documenting link with + /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec + /// would fail. + /// /// **Examples:** /// ```rust /// /// Do something with the foo_bar parameter. See also @@ -39,6 +44,14 @@ declare_clippy_lint! { /// // ^ `foo_bar` and `that::other::module::foo` should be ticked. /// fn doit(foo_bar: usize) {} /// ``` + /// + /// ```rust + /// // Link text with `[]` brackets should be written as following: + /// /// Consume the array and return the inner + /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec]. + /// /// [SmallVec]: SmallVec + /// fn main() {} + /// ``` pub DOC_MARKDOWN, pedantic, "presence of `_`, `::` or camel-case outside backticks in documentation" From 5bad9175fb363917ffee2c2da7223e96daa2f5ac Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:37:32 +0200 Subject: [PATCH 37/97] New lint: Recommend using `ptr::eq` when possible This is based almost entirely on the code available in the previous PR #4596. --- clippy_lints/src/lib.rs | 5 ++ clippy_lints/src/ptr_eq.rs | 96 ++++++++++++++++++++++++++++++++++++++ tests/ui/ptr_eq.fixed | 38 +++++++++++++++ tests/ui/ptr_eq.rs | 38 +++++++++++++++ tests/ui/ptr_eq.stderr | 16 +++++++ 5 files changed, 193 insertions(+) create mode 100644 clippy_lints/src/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.fixed create mode 100644 tests/ui/ptr_eq.rs create mode 100644 tests/ui/ptr_eq.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 826a059f92abe..d11eeff80323d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -281,6 +281,7 @@ mod path_buf_push_overwrite; mod pattern_type_mismatch; mod precedence; mod ptr; +mod ptr_eq; mod ptr_offset_with_cast; mod question_mark; mod ranges; @@ -778,6 +779,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &ptr::CMP_NULL, &ptr::MUT_FROM_REF, &ptr::PTR_ARG, + &ptr_eq::PTR_EQ, &ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, &question_mark::QUESTION_MARK, &ranges::RANGE_MINUS_ONE, @@ -916,6 +918,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move || box bit_mask::BitMask::new(verbose_bit_mask_threshold)); store.register_late_pass(|| box ptr::Ptr); + store.register_late_pass(|| box ptr_eq::PtrEq); store.register_late_pass(|| box needless_bool::NeedlessBool); store.register_late_pass(|| box needless_bool::BoolComparison); store.register_late_pass(|| box approx_const::ApproxConstant); @@ -1456,6 +1459,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::MUT_FROM_REF), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&ranges::RANGE_ZIP_WITH_LEN), @@ -1612,6 +1616,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&panic_unimplemented::PANIC_PARAMS), LintId::of(&ptr::CMP_NULL), LintId::of(&ptr::PTR_ARG), + LintId::of(&ptr_eq::PTR_EQ), LintId::of(&question_mark::QUESTION_MARK), LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES), LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs new file mode 100644 index 0000000000000..a05cb6270b762 --- /dev/null +++ b/clippy_lints/src/ptr_eq.rs @@ -0,0 +1,96 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Use `std::ptr::eq` when applicable + /// + /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```rust + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(PtrEq => [PTR_EQ]); + +static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; + +impl LateLintPass<'_> for PtrEq { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if utils::in_macro(expr.span) { + return; + } + + if let ExprKind::Binary(ref op, ref left, ref right) = expr.kind { + if BinOpKind::Eq == op.node { + let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs), + _ => (&**left, &**right), + }; + + if_chain! { + if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left); + if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right); + if let Some(left_snip) = utils::snippet_opt(cx, left_var.span); + if let Some(right_snip) = utils::snippet_opt(cx, right_var.span); + then { + utils::span_lint_and_sugg( + cx, + PTR_EQ, + expr.span, + LINT_MSG, + "try", + format!("std::ptr::eq({}, {})", left_snip, right_snip), + Applicability::MachineApplicable, + ); + } + } + } + } + } +} + +// If the given expression is a cast to an usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} + +// If the given expression is a cast to a `*const` pointer, return the lhs of the cast +// E.g., `foo as *const _` returns `foo`. +fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() { + if let ExprKind::Cast(ref expr, _) = cast_expr.kind { + return Some(expr); + } + } + None +} diff --git a/tests/ui/ptr_eq.fixed b/tests/ui/ptr_eq.fixed new file mode 100644 index 0000000000000..209081e6e8011 --- /dev/null +++ b/tests/ui/ptr_eq.fixed @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = std::ptr::eq(a, b); + let _ = std::ptr::eq(a, b); + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.rs b/tests/ui/ptr_eq.rs new file mode 100644 index 0000000000000..69162870807a2 --- /dev/null +++ b/tests/ui/ptr_eq.rs @@ -0,0 +1,38 @@ +// run-rustfix +#![warn(clippy::ptr_eq)] + +macro_rules! mac { + ($a:expr, $b:expr) => { + $a as *const _ as usize == $b as *const _ as usize + }; +} + +macro_rules! another_mac { + ($a:expr, $b:expr) => { + $a as *const _ == $b as *const _ + }; +} + +fn main() { + let a = &[1, 2, 3]; + let b = &[1, 2, 3]; + + let _ = a as *const _ as usize == b as *const _ as usize; + let _ = a as *const _ == b as *const _; + let _ = a.as_ptr() == b as *const _; + let _ = a.as_ptr() == b.as_ptr(); + + // Do not lint + + let _ = mac!(a, b); + let _ = another_mac!(a, b); + + let a = &mut [1, 2, 3]; + let b = &mut [1, 2, 3]; + + let _ = a.as_mut_ptr() == b as *mut [i32] as *mut _; + let _ = a.as_mut_ptr() == b.as_mut_ptr(); + + let _ = a == b; + let _ = core::ptr::eq(a, b); +} diff --git a/tests/ui/ptr_eq.stderr b/tests/ui/ptr_eq.stderr new file mode 100644 index 0000000000000..45d8c60382b59 --- /dev/null +++ b/tests/ui/ptr_eq.stderr @@ -0,0 +1,16 @@ +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:20:13 + | +LL | let _ = a as *const _ as usize == b as *const _ as usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + | + = note: `-D clippy::ptr-eq` implied by `-D warnings` + +error: use `std::ptr::eq` when comparing raw pointers + --> $DIR/ptr_eq.rs:21:13 + | +LL | let _ = a as *const _ == b as *const _; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` + +error: aborting due to 2 previous errors + From aa7c42f75668bc514baf0a64f31771353c3af6cb Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:54:00 +0200 Subject: [PATCH 38/97] fixup! New lint: Recommend using `ptr::eq` when possible Add missing modified files. --- CHANGELOG.md | 1 + src/lintlist/mod.rs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 617bf32f4639e..552a0e96ceb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1774,6 +1774,7 @@ Released 2018-09-13 [`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline [`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string [`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 0dba5a71c502d..3cba9867600d6 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1844,6 +1844,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "ptr", }, + Lint { + name: "ptr_eq", + group: "style", + desc: "use `std::ptr::eq` when comparing raw pointers", + deprecation: None, + module: "ptr_eq", + }, Lint { name: "ptr_offset_with_cast", group: "complexity", From 9ef311b47706cc0babce08ee86bc9ca7727fc2f5 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 11:59:20 +0200 Subject: [PATCH 39/97] Rename tables to typecheck_result() --- doc/common_tools_writing_lints.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 53c3d084dbc98..27d4f1b03d77c 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -45,11 +45,13 @@ Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`] to retrieve a type from a pattern. Two noticeable items here: -- `cx` is the lint context [`LateContext`][LateContext]. - The two most useful data structures in this context are `tcx` and `tables`, - allowing us to jump to type definitions and other compilation stages such as HIR. -- `tables` is [`TypeckResults`][TypeckResults] and is created by type checking step, - it includes useful information such as types of expressions, ways to resolve methods and so on. +- `cx` is the lint context [`LateContext`][LateContext]. The two most useful + data structures in this context are `tcx` and the `TypeckResults` returned by + 'LateContext::typeck_results', allowing us to jump to type definitions and + other compilation stages such as HIR. +- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is + created by type checking step, it includes useful information such as types + of expressions, ways to resolve methods and so on. # Checking if an expr is calling a specific method From 326090dbb8285e9486d77fd9aa2ea206f78bf978 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 17:10:31 +0200 Subject: [PATCH 40/97] fixup! Rename tables to typecheck_result() Co-authored-by: Takayuki Nakata --- doc/common_tools_writing_lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 27d4f1b03d77c..d56079a4ab735 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -47,7 +47,7 @@ to retrieve a type from a pattern. Two noticeable items here: - `cx` is the lint context [`LateContext`][LateContext]. The two most useful data structures in this context are `tcx` and the `TypeckResults` returned by - 'LateContext::typeck_results', allowing us to jump to type definitions and + `LateContext::typeck_results`, allowing us to jump to type definitions and other compilation stages such as HIR. - `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is created by type checking step, it includes useful information such as types From 6edde811b5437f926c26b1b844acd60e3dd0a0c9 Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Wed, 7 Oct 2020 17:11:11 +0200 Subject: [PATCH 41/97] fixup! New lint: Recommend using `ptr::eq` when possible Co-authored-by: Takayuki Nakata --- clippy_lints/src/ptr_eq.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index a05cb6270b762..3be792ce5e4fa 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** Use `std::ptr::eq` when applicable /// - /// **Why is this bad?**`ptr::eq` can be used to compare `&T` references + /// **Why is this bad?** `ptr::eq` can be used to compare `&T` references /// (which coerce to `*const T` implicitly) by their address rather than /// comparing the values they point to. /// From 1385eb9b5562ec78f772341e1ad5c3d2f5443141 Mon Sep 17 00:00:00 2001 From: Darshan Kathiriya Date: Wed, 7 Oct 2020 09:09:59 -0300 Subject: [PATCH 42/97] Replace run_compiler with RunCompiler builder pattern. RunCompiler::new takes non-optional params, and optional params can be set using set_*field_name* method. finally `run` will forward all fields to `run_compiler`. --- src/driver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index f4f2259cefd51..377f6d2244635 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -357,7 +357,7 @@ pub fn main() { args.extend(vec!["--sysroot".into(), sys_root]); }; - return rustc_driver::run_compiler(&args, &mut DefaultCallbacks, None, None, None); + return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run(); } if orig_args.iter().any(|a| a == "--version" || a == "-V") { @@ -420,6 +420,6 @@ pub fn main() { let mut default = DefaultCallbacks; let callbacks: &mut (dyn rustc_driver::Callbacks + Send) = if clippy_enabled { &mut clippy } else { &mut default }; - rustc_driver::run_compiler(&args, callbacks, None, None, None) + rustc_driver::RunCompiler::new(&args, callbacks).run() })) } From 2c8cd7d93cd137d9f606d15c20b92634c68c98f4 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 09:17:48 +0200 Subject: [PATCH 43/97] Update actions due to deprecation of set-env and add-path --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/clippy_bors.yml | 14 +++++++------- .github/workflows/clippy_dev.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- .github/workflows/remark.yml | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 99e371631b149..0cbe73affda32 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -36,14 +36,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index fd0cd7a1890bd..a5c00b3418e5e 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -20,7 +20,7 @@ jobs: with: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 with: ref: ${{ github.ref }} @@ -81,14 +81,14 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: ${{ matrix.host }} profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update @@ -177,14 +177,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update @@ -258,14 +258,14 @@ jobs: github_token: "${{ secrets.github_token }}" - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu profile: minimal - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Run cargo update run: cargo update diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index ec3b43c2f43bc..5ee157cf23b86 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: rust-toolchain - uses: actions-rs/toolchain@v1.0.3 + uses: actions-rs/toolchain@v1.0.6 with: toolchain: nightly target: x86_64-unknown-linux-gnu @@ -31,7 +31,7 @@ jobs: components: rustfmt - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f542f9b02c17b..5b7252532c21c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index cc175e8bf247f..4f25a86b2e4df 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,10 +16,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.0.0 + uses: actions/checkout@v2.3.3 - name: Setup Node.js - uses: actions/setup-node@v1.1.0 + uses: actions/setup-node@v1.4.4 - name: Install remark run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended From c85cb76064ae24ee16efa426280ccbd7b7b54f4b Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 09:40:57 +0200 Subject: [PATCH 44/97] Update release documentation --- doc/release.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release.md b/doc/release.md index 391952ea6b14f..eaa6a9af277d2 100644 --- a/doc/release.md +++ b/doc/release.md @@ -68,7 +68,7 @@ be updated. ```bash # Assuming the current directory corresponds to the Clippy repository $ git checkout beta -$ git rebase $BETA_SHA +$ git reset --hard $BETA_SHA $ git push upstream beta ``` From 732d370404198ca68a96901b25a5f2ab3ba9d562 Mon Sep 17 00:00:00 2001 From: Jean SIMARD Date: Fri, 9 Oct 2020 09:47:53 +0200 Subject: [PATCH 45/97] clippy_lint: Fix doc on 'option_if_let_else' - a typo in `expresion` - a useless double space - Some back-tick quotes for 'if let' --- clippy_lints/src/option_if_let_else.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4a3eb9c983a11..383a62da821e9 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -13,14 +13,14 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more + /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more /// idiomatically done with `Option::map_or` (if the else bit is a pure /// expression) or `Option::map_or_else` (if the else bit is an impure - /// expresion). + /// expression). /// /// **Why is this bad?** /// Using the dedicated functions of the Option type is clearer and - /// more concise than an if let expression. + /// more concise than an `if let` expression. /// /// **Known problems:** /// This lint uses a deliberately conservative metric for checking From 64a7d019f10bafa386817de3bb15ef2eead4cda5 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 10:40:04 +0200 Subject: [PATCH 46/97] Remove all usage of set-env --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 8 ++++---- .github/workflows/deploy.yml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 0cbe73affda32..cf4aa39e49b90 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -63,7 +63,7 @@ jobs: - name: Set LD_LIBRARY_PATH (Linux) run: | SYSROOT=$(rustc --print sysroot) - echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Build run: cargo build --features deny-warnings diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index a5c00b3418e5e..f83861950f8c7 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -112,7 +112,7 @@ jobs: if: runner.os == 'Linux' run: | SYSROOT=$(rustc --print sysroot) - echo "::set-env name=LD_LIBRARY_PATH::${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" + echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV - name: Link rustc dylib (MacOS) if: runner.os == 'macOS' run: | @@ -122,9 +122,9 @@ jobs: - name: Set PATH (Windows) if: runner.os == 'Windows' run: | - $sysroot = rustc --print sysroot - $env:PATH += ';' + $sysroot + '\bin' - echo "::set-env name=PATH::$env:PATH" + SYSROOT=$(rustc --print sysroot) + echo "$SYSROOT/bin" >> $GITHUB_PATH + shell: bash - name: Build run: cargo build --features deny-warnings diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5b7252532c21c..15aeaf907dc6b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -34,10 +34,10 @@ jobs: if: startswith(github.ref, 'refs/tags/') run: | TAG=$(basename ${{ github.ref }}) - echo "::set-env name=TAG_NAME::$TAG" + echo "TAG_NAME=$TAG" >> $GITHUB_ENV - name: Set beta to true if: github.ref == 'refs/heads/beta' - run: echo "::set-env name=BETA::true" + run: echo "BETA=true" >> $GITHUB_ENV - name: Use scripts and templates from master branch run: | From c4c9453ccfc69d00ced80db4cdd5be805f1ee1c2 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 11:53:10 +0200 Subject: [PATCH 47/97] Use defaults.run.shell instead of setting shell every time --- .github/workflows/clippy_bors.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f83861950f8c7..7509d90c6c2fc 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -11,6 +11,10 @@ env: CARGO_TARGET_DIR: '${{ github.workspace }}/target' NO_FMT_TEST: 1 +defaults: + run: + shell: bash + jobs: changelog: runs-on: ubuntu-latest @@ -105,7 +109,6 @@ jobs: run: bash setup-toolchain.sh env: HOST_TOOLCHAIN: ${{ matrix.host }} - shell: bash # Run - name: Set LD_LIBRARY_PATH (Linux) @@ -124,39 +127,31 @@ jobs: run: | SYSROOT=$(rustc --print sysroot) echo "$SYSROOT/bin" >> $GITHUB_PATH - shell: bash - name: Build run: cargo build --features deny-warnings - shell: bash - name: Test run: cargo test --features deny-warnings - shell: bash - name: Test clippy_lints run: cargo test --features deny-warnings - shell: bash working-directory: clippy_lints - name: Test rustc_tools_util run: cargo test --features deny-warnings - shell: bash working-directory: rustc_tools_util - name: Test clippy_dev run: cargo test --features deny-warnings - shell: bash working-directory: clippy_dev - name: Test cargo-clippy run: ../target/debug/cargo-clippy - shell: bash working-directory: clippy_workspace_tests - name: Test clippy-driver run: bash .github/driver.sh - shell: bash env: OS: ${{ runner.os }} @@ -165,7 +160,7 @@ jobs: run: | cargo +nightly install cargo-cache --no-default-features --features ci-autoclean cargo-cache cargo cache - shell: bash + integration_build: needs: changelog runs-on: ubuntu-latest From fbf2430f0279adb8132efe60b16f8dd7b6a2acb3 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 12:45:29 +0200 Subject: [PATCH 48/97] Merge commit '2f6439ae6a6803d030cceb3ee14c9150e91b328b' into clippyup --- CHANGELOG.md | 129 +++++++++++- README.md | 2 +- clippy_dev/Cargo.toml | 2 +- clippy_dev/src/lib.rs | 43 ++-- clippy_dev/src/main.rs | 20 +- clippy_dev/src/serve.rs | 64 ++++++ clippy_lints/Cargo.toml | 1 - clippy_lints/src/asm_syntax.rs | 125 ++++++++++++ clippy_lints/src/attrs.rs | 6 +- clippy_lints/src/disallowed_method.rs | 73 +++++++ clippy_lints/src/escape.rs | 9 +- clippy_lints/src/lib.rs | 31 ++- clippy_lints/src/lifetimes.rs | 169 +++++++-------- clippy_lints/src/literal_representation.rs | 9 +- clippy_lints/src/loops.rs | 12 +- clippy_lints/src/macro_use.rs | 4 +- clippy_lints/src/methods/mod.rs | 24 +-- clippy_lints/src/missing_const_for_fn.rs | 2 +- .../src/needless_arbitrary_self_type.rs | 30 ++- clippy_lints/src/non_copy_const.rs | 28 ++- clippy_lints/src/regex.rs | 2 +- clippy_lints/src/strings.rs | 24 ++- clippy_lints/src/types.rs | 17 +- clippy_lints/src/unicode.rs | 23 ++- clippy_lints/src/unnecessary_sort_by.rs | 58 ++---- clippy_lints/src/utils/conf.rs | 10 +- clippy_lints/src/utils/mod.rs | 14 +- clippy_lints/src/utils/numeric_literal.rs | 19 +- clippy_lints/src/utils/paths.rs | 3 + .../src/utils/qualify_min_const_fn.rs | 193 +++++++----------- clippy_lints/src/write.rs | 17 +- clippy_workspace_tests/build.rs | 7 + doc/adding_lints.md | 3 +- doc/basics.md | 6 + src/lintlist/mod.rs | 43 +++- tests/cargo/mod.rs | 37 ++-- tests/compile-test.rs | 3 +- tests/dogfood.rs | 7 +- .../toml_disallowed_method/clippy.toml | 1 + .../conf_disallowed_method.rs | 13 ++ .../conf_disallowed_method.stderr | 16 ++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/asm_syntax.rs | 31 +++ tests/ui/asm_syntax.stderr | 44 ++++ tests/ui/attrs.rs | 3 - tests/ui/attrs.stderr | 25 +-- tests/ui/auxiliary/proc_macro_attr.rs | 62 +++++- tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/blanket_clippy_restriction_lints.rs | 8 + .../blanket_clippy_restriction_lints.stderr | 27 +++ tests/ui/crashes/associated-constant-ice.rs | 2 - .../ui/crashes/auxiliary/proc_macro_crash.rs | 1 + tests/ui/crashes/cc_seme.rs | 2 - tests/ui/crashes/enum-glob-import-crate.rs | 2 - tests/ui/crashes/ice-1588.rs | 2 - tests/ui/crashes/ice-1782.rs | 2 - tests/ui/crashes/ice-1969.rs | 2 - tests/ui/crashes/ice-2499.rs | 2 - tests/ui/crashes/ice-2594.rs | 2 - tests/ui/crashes/ice-2727.rs | 2 - tests/ui/crashes/ice-2760.rs | 2 - tests/ui/crashes/ice-2774.rs | 2 - tests/ui/crashes/ice-2774.stderr | 10 + tests/ui/crashes/ice-2862.rs | 2 - tests/ui/crashes/ice-2865.rs | 2 - tests/ui/crashes/ice-3151.rs | 2 - tests/ui/crashes/ice-3462.rs | 2 - tests/ui/crashes/ice-3741.rs | 1 - tests/ui/crashes/ice-3747.rs | 2 - tests/ui/crashes/ice-4727.rs | 2 - tests/ui/crashes/ice-4760.rs | 1 - tests/ui/crashes/ice-700.rs | 2 - tests/ui/crashes/ice_exacte_size.rs | 2 - tests/ui/crashes/if_same_then_else.rs | 2 - tests/ui/crashes/issue-825.rs | 2 - tests/ui/crashes/issues_loop_mut_cond.rs | 2 - tests/ui/crashes/match_same_arms_const.rs | 2 - tests/ui/crashes/mut_mut_macro.rs | 2 - tests/ui/crashes/needless_borrow_fp.rs | 2 - .../crashes/needless_lifetimes_impl_trait.rs | 2 - .../needless_lifetimes_impl_trait.stderr | 14 ++ tests/ui/crashes/procedural_macro.rs | 2 - tests/ui/crashes/regressions.rs | 2 - tests/ui/crashes/returns.rs | 2 - tests/ui/crashes/single-match-else.rs | 2 - tests/ui/crashes/trivial_bounds.rs | 2 - .../crashes/used_underscore_binding_macro.rs | 2 - tests/ui/escape_analysis.rs | 8 + tests/ui/explicit_counter_loop.rs | 29 ++- tests/ui/inconsistent_digit_grouping.fixed | 4 + tests/ui/inconsistent_digit_grouping.rs | 4 + tests/ui/mistyped_literal_suffix.fixed | 11 +- tests/ui/mistyped_literal_suffix.rs | 11 +- tests/ui/mistyped_literal_suffix.stderr | 36 ++-- .../needless_arbitrary_self_type_unfixable.rs | 45 ++++ ...dless_arbitrary_self_type_unfixable.stderr | 10 + tests/ui/needless_lifetimes.rs | 98 +++++++++ tests/ui/needless_lifetimes.stderr | 50 ++++- tests/ui/needless_range_loop2.rs | 14 ++ tests/ui/or_fun_call.fixed | 25 +-- tests/ui/or_fun_call.rs | 25 +-- tests/ui/or_fun_call.stderr | 20 +- tests/ui/print_stdout_build_script.rs | 12 ++ tests/ui/regex.rs | 3 + tests/ui/unicode.rs | 6 +- tests/ui/unicode.stderr | 22 +- tests/ui/unnecessary_sort_by.fixed | 30 +-- tests/ui/unnecessary_sort_by.rs | 10 +- tests/ui/unnecessary_sort_by.stderr | 52 ++++- 109 files changed, 1502 insertions(+), 616 deletions(-) create mode 100644 clippy_dev/src/serve.rs create mode 100644 clippy_lints/src/asm_syntax.rs create mode 100644 clippy_lints/src/disallowed_method.rs create mode 100644 clippy_workspace_tests/build.rs create mode 100644 tests/ui-toml/toml_disallowed_method/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs create mode 100644 tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr create mode 100644 tests/ui/asm_syntax.rs create mode 100644 tests/ui/asm_syntax.stderr create mode 100644 tests/ui/blanket_clippy_restriction_lints.rs create mode 100644 tests/ui/blanket_clippy_restriction_lints.stderr create mode 100644 tests/ui/crashes/ice-2774.stderr create mode 100644 tests/ui/crashes/needless_lifetimes_impl_trait.stderr create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.rs create mode 100644 tests/ui/needless_arbitrary_self_type_unfixable.stderr create mode 100644 tests/ui/print_stdout_build_script.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d1dfe36ffd825..0bd13320dc952 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,129 @@ document. ## Unreleased / In Rust Nightly -[09bd400...master](https://github.com/rust-lang/rust-clippy/compare/09bd400...master) +[e636b88...master](https://github.com/rust-lang/rust-clippy/compare/e636b88...master) + +## Rust 1.48 + +Current beta, release 2020-11-19 + +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88) + +### New lints + +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894) +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720) +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038) +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998) +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044) +* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831) +* [`single_char_push_str`] [#5881](https://github.com/rust-lang/rust-clippy/pull/5881) + +### Moves and Deprecations + +* Downgrade [`verbose_bit_mask`] to pedantic + [#6036](https://github.com/rust-lang/rust-clippy/pull/6036) + +### Enhancements + +* Extend [`precedence`] to handle chains of methods combined with unary negation + [#5928](https://github.com/rust-lang/rust-clippy/pull/5928) +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack + [#5907](https://github.com/rust-lang/rust-clippy/pull/5907) +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr` + [#5884](https://github.com/rust-lang/rust-clippy/pull/5884) +* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` + [#6025](https://github.com/rust-lang/rust-clippy/pull/6025) +* Avoid [`redundant_pattern_matching`] triggering in macros + [#6069](https://github.com/rust-lang/rust-clippy/pull/6069) +* [`option_if_let_else`]: distinguish pure from impure `else` expressions + [#5937](https://github.com/rust-lang/rust-clippy/pull/5937) +* [`needless_doctest_main`]: parse doctests instead of using textual search + [#5912](https://github.com/rust-lang/rust-clippy/pull/5912) +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import + [#5929](https://github.com/rust-lang/rust-clippy/pull/5929) +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable + [#5961](https://github.com/rust-lang/rust-clippy/pull/5961) +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut` + [#5933](https://github.com/rust-lang/rust-clippy/pull/5933) + +### False Positive Fixes + +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`] + [#5994](https://github.com/rust-lang/rust-clippy/pull/5994) +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts + [#5999](https://github.com/rust-lang/rust-clippy/pull/5999) +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures + [#5920](https://github.com/rust-lang/rust-clippy/pull/5920) +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer + [#5949](https://github.com/rust-lang/rust-clippy/pull/5949) +* [`doc_markdown`]: allow using "GraphQL" without backticks + [#5996](https://github.com/rust-lang/rust-clippy/pull/5996) +* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self` + [#5971](https://github.com/rust-lang/rust-clippy/pull/5971) +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays + [#6034](https://github.com/rust-lang/rust-clippy/pull/6034) +* [`should_implement_trait`]: ignore methods with lifetime parameters + [#5725](https://github.com/rust-lang/rust-clippy/pull/5725) +* [`needless_return`]: avoid linting if a temporary borrows a local variable + [#5903](https://github.com/rust-lang/rust-clippy/pull/5903) +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types + [#6006](https://github.com/rust-lang/rust-clippy/pull/6006) +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`] + [#5919](https://github.com/rust-lang/rust-clippy/pull/5919) +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types + [#6046](https://github.com/rust-lang/rust-clippy/pull/6046) + +### Suggestion Fixes/Improvements + +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments + [#5946](https://github.com/rust-lang/rust-clippy/pull/5946) +* [`useless_conversion`]: show the type in the error message + [#6035](https://github.com/rust-lang/rust-clippy/pull/6035) +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message + [#5892](https://github.com/rust-lang/rust-clippy/pull/5892) +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous + [#6043](https://github.com/rust-lang/rust-clippy/pull/6043) +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion + [#5993](https://github.com/rust-lang/rust-clippy/pull/5993) +* [`collapsible_if`]: don't use expanded code in the suggestion + [#5992](https://github.com/rust-lang/rust-clippy/pull/5992) +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`] + [#6042](https://github.com/rust-lang/rust-clippy/pull/6042) +* [`unit_arg`]: improve the readability of the suggestion + [#5931](https://github.com/rust-lang/rust-clippy/pull/5931) +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message + [#5935](https://github.com/rust-lang/rust-clippy/pull/5935) +* Show line count and max lines in [`too_many_lines`] lint message + [#6009](https://github.com/rust-lang/rust-clippy/pull/6009) +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable + [#5900](https://github.com/rust-lang/rust-clippy/pull/5900) +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly + [#6024](https://github.com/rust-lang/rust-clippy/pull/6024) +* [`redundant_allocation`]: suggest replacing `Rc>` with `Rc` + [#5899](https://github.com/rust-lang/rust-clippy/pull/5899) +* Make lint messages adhere to rustc dev guide conventions + [#5893](https://github.com/rust-lang/rust-clippy/pull/5893) + +### ICE Fixes + +* Fix ICE in [`repeat_once`] + [#5948](https://github.com/rust-lang/rust-clippy/pull/5948) + +### Documentation Improvements + +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation + [#6019](https://github.com/rust-lang/rust-clippy/pull/6019) +* [`unnecessary_mut_passed`]: fix typo + [#5913](https://github.com/rust-lang/rust-clippy/pull/5913) +* Add example of false positive to [`ptr_arg`] docs. + [#5885](https://github.com/rust-lang/rust-clippy/pull/5885) +* [`box_vec`], [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box` + [#6023](https://github.com/rust-lang/rust-clippy/pull/6023) ## Rust 1.47 -Current beta, release 2020-10-08 +Current stable, released 2020-10-08 [c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400) @@ -112,7 +230,7 @@ Current beta, release 2020-10-08 ## Rust 1.46 -Current stable, released 2020-08-27 +Released 2020-08-27 [7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa) @@ -1559,6 +1677,7 @@ Released 2018-09-13 [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons @@ -1634,6 +1753,8 @@ Released 2018-09-13 [`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string [`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display [`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax [`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body [`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic @@ -1644,6 +1765,7 @@ Released 2018-09-13 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop @@ -1919,6 +2041,5 @@ Released 2018-09-13 [`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero [`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal [`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr -[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/README.md b/README.md index a2984d7364169..62a8be0abf22c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 350 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index c861efc8afb50..b8a4a20114bb8 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" bytecount = "0.6" clap = "2.33" itertools = "0.9" +opener = "0.4" regex = "1" -lazy_static = "1.0" shell-escape = "0.1" walkdir = "2" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 5baa31d5cde0c..43cb2954b74b3 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,42 +1,47 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![feature(once_cell)] use itertools::Itertools; -use lazy_static::lazy_static; use regex::Regex; use std::collections::HashMap; use std::ffi::OsStr; use std::fs; +use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use walkdir::WalkDir; pub mod fmt; pub mod new_lint; pub mod ra_setup; +pub mod serve; pub mod stderr_length_check; pub mod update_lints; -lazy_static! { - static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new( +static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { + Regex::new( r#"(?x) - declare_clippy_lint!\s*[\{(] - (?:\s+///.*)* - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - (?P[a-z_]+)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] - "# + declare_clippy_lint!\s*[\{(] + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + (?P[a-z_]+)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] +"#, ) - .unwrap(); - static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new( + .unwrap() +}); + +static DEC_DEPRECATED_LINT_RE: SyncLazy = SyncLazy::new(|| { + Regex::new( r#"(?x) - declare_deprecated_lint!\s*[{(]\s* - (?:\s+///.*)* - \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* - "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] - "# + declare_deprecated_lint!\s*[{(]\s* + (?:\s+///.*)* + \s+pub\s+(?P[A-Z_][A-Z_0-9]*)\s*,\s* + "(?P(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})] +"#, ) - .unwrap(); - static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap(); -} + .unwrap() +}); +static NL_ESCAPE_RE: SyncLazy = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap()); pub static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index 281037ae37c97..7a8cbd5251da9 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "deny-warnings", deny(warnings))] use clap::{App, Arg, SubCommand}; -use clippy_dev::{fmt, new_lint, ra_setup, stderr_length_check, update_lints}; +use clippy_dev::{fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints}; fn main() { let matches = App::new("Clippy developer tooling") @@ -100,6 +100,19 @@ fn main() { .required(true), ), ) + .subcommand( + SubCommand::with_name("serve") + .about("Launch a local 'ALL the Clippy Lints' website in a browser") + .arg( + Arg::with_name("port") + .long("port") + .short("p") + .help("Local port for the http server") + .default_value("8000") + .validator_os(serve::validate_port), + ) + .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")), + ) .get_matches(); match matches.subcommand() { @@ -129,6 +142,11 @@ fn main() { stderr_length_check::check(); }, ("ra-setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")), + ("serve", Some(matches)) => { + let port = matches.value_of("port").unwrap().parse().unwrap(); + let lint = matches.value_of("lint"); + serve::run(port, lint); + }, _ => {}, } } diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs new file mode 100644 index 0000000000000..a46c0e4d3f0a1 --- /dev/null +++ b/clippy_dev/src/serve.rs @@ -0,0 +1,64 @@ +use std::ffi::{OsStr, OsString}; +use std::path::Path; +use std::process::Command; +use std::thread; +use std::time::{Duration, SystemTime}; + +pub fn run(port: u16, lint: Option<&str>) -> ! { + let mut url = Some(match lint { + None => format!("http://localhost:{}", port), + Some(lint) => format!("http://localhost:{}/#{}", port, lint), + }); + + loop { + if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") { + Command::new("python3") + .arg("util/export.py") + .spawn() + .unwrap() + .wait() + .unwrap(); + } + if let Some(url) = url.take() { + thread::spawn(move || { + Command::new("python3") + .arg("-m") + .arg("http.server") + .arg(port.to_string()) + .current_dir("util/gh-pages") + .spawn() + .unwrap(); + // Give some time for python to start + thread::sleep(Duration::from_millis(500)); + // Launch browser after first export.py has completed and http.server is up + let _ = opener::open(url); + }); + } + thread::sleep(Duration::from_millis(1000)); + } +} + +fn mtime(path: impl AsRef) -> SystemTime { + let path = path.as_ref(); + if path.is_dir() { + path.read_dir() + .into_iter() + .flatten() + .flatten() + .map(|entry| mtime(&entry.path())) + .max() + .unwrap_or(SystemTime::UNIX_EPOCH) + } else { + path.metadata() + .and_then(|metadata| metadata.modified()) + .unwrap_or(SystemTime::UNIX_EPOCH) + } +} + +#[allow(clippy::missing_errors_doc)] +pub fn validate_port(arg: &OsStr) -> Result<(), OsString> { + match arg.to_string_lossy().parse::() { + Ok(_port) => Ok(()), + Err(err) => Err(OsString::from(err.to_string())), + } +} diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 341d9e601ee6f..fcf817b82c89e 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -20,7 +20,6 @@ edition = "2018" cargo_metadata = "0.11.1" if_chain = "1.0.0" itertools = "0.9" -lazy_static = "1.0.2" pulldown-cmark = { version = "0.8", default-features = false } quine-mc_cluskey = "0.2.2" regex-syntax = "0.6" diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs new file mode 100644 index 0000000000000..ef1f1a14afcac --- /dev/null +++ b/clippy_lints/src/asm_syntax.rs @@ -0,0 +1,125 @@ +use std::fmt; + +use crate::utils::span_lint_and_help; +use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum AsmStyle { + Intel, + Att, +} + +impl fmt::Display for AsmStyle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + AsmStyle::Intel => f.write_str("Intel"), + AsmStyle::Att => f.write_str("AT&T"), + } + } +} + +impl std::ops::Not for AsmStyle { + type Output = AsmStyle; + + fn not(self) -> AsmStyle { + match self { + AsmStyle::Intel => AsmStyle::Att, + AsmStyle::Att => AsmStyle::Intel, + } + } +} + +fn check_expr_asm_syntax(lint: &'static Lint, cx: &EarlyContext<'_>, expr: &Expr, check_for: AsmStyle) { + if let ExprKind::InlineAsm(ref inline_asm) = expr.kind { + let style = if inline_asm.options.contains(InlineAsmOptions::ATT_SYNTAX) { + AsmStyle::Att + } else { + AsmStyle::Intel + }; + + if style == check_for { + span_lint_and_help( + cx, + lint, + expr.span, + &format!("{} x86 assembly syntax used", style), + None, + &format!("use {} x86 assembly syntax", !style), + ); + } + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of Intel x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for AT&T x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + pub INLINE_ASM_X86_INTEL_SYNTAX, + restriction, + "prefer AT&T x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86IntelSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Intel); + } +} + +declare_clippy_lint! { + /// **What it does:** Checks for usage of AT&T x86 assembly syntax. + /// + /// **Why is this bad?** The lint has been enabled to indicate a preference + /// for Intel x86 assembly syntax. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax)); + /// # } + /// ``` + /// Use instead: + /// ```rust,no_run + /// # #![feature(asm)] + /// # unsafe { let ptr = "".as_ptr(); + /// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr); + /// # } + /// ``` + pub INLINE_ASM_X86_ATT_SYNTAX, + restriction, + "prefer Intel x86 assembly syntax" +} + +declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]); + +impl EarlyLintPass for InlineAsmX86AttSyntax { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + check_expr_asm_syntax(Self::get_lints()[0], cx, expr, AsmStyle::Att); + } +} diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index c8f153e7201cb..f6eadbdef0bb0 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -137,17 +137,17 @@ declare_clippy_lint! { /// **Example:** /// ```rust /// // Good (as inner attribute) - /// #![inline(always)] + /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// /// // Bad - /// #[inline(always)] + /// #[allow(dead_code)] /// /// fn not_quite_good_code() { } /// /// // Good (as outer attribute) - /// #[inline(always)] + /// #[allow(dead_code)] /// fn this_is_fine_too() { } /// ``` pub EMPTY_LINE_AFTER_OUTER_ATTR, diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs new file mode 100644 index 0000000000000..581c3242e3745 --- /dev/null +++ b/clippy_lints/src/disallowed_method.rs @@ -0,0 +1,73 @@ +use crate::utils::span_lint; + +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Symbol; + +declare_clippy_lint! { + /// **What it does:** Lints for specific trait methods defined in clippy.toml + /// + /// **Why is this bad?** Some methods are undesirable in certain contexts, + /// and it would be beneficial to lint for them as needed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // example code where clippy issues a warning + /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration + /// ``` + /// Use instead: + /// ```rust,ignore + /// // example code which does not raise clippy warning + /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed + /// ``` + pub DISALLOWED_METHOD, + nursery, + "use of a disallowed method call" +} + +#[derive(Clone, Debug)] +pub struct DisallowedMethod { + disallowed: FxHashSet>, +} + +impl DisallowedMethod { + pub fn new(disallowed: &FxHashSet) -> Self { + Self { + disallowed: disallowed + .iter() + .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) + .collect(), + } + } +} + +impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); + +impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind { + let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); + + let method_call = cx.get_def_path(def_id); + if self.disallowed.contains(&method_call) { + let method = method_call + .iter() + .map(|s| s.to_ident_string()) + .collect::>() + .join("::"); + + span_lint( + cx, + DISALLOWED_METHOD, + expr.span, + &format!("use of a disallowed method `{}`", method), + ); + } + } + } +} diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 82549c12d0a20..8b0229125738a 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -6,6 +6,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::abi::LayoutOf; +use rustc_target::spec::abi::Abi; use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use crate::utils::span_lint; @@ -60,12 +61,18 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal { fn check_fn( &mut self, cx: &LateContext<'tcx>, - _: intravisit::FnKind<'tcx>, + fn_kind: intravisit::FnKind<'tcx>, _: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, _: Span, hir_id: HirId, ) { + if let Some(header) = fn_kind.header() { + if header.abi != Abi::Rust { + return; + } + } + // If the method is an impl for a trait, don't warn. let parent_id = cx.tcx.hir().get_parent_item(hir_id); let parent_node = cx.tcx.hir().find(parent_id); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 70efdaeb9c669..93b5d9e178c23 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -7,6 +7,7 @@ #![feature(crate_visibility_modifier)] #![feature(drain_filter)] #![feature(in_band_lifetimes)] +#![feature(once_cell)] #![feature(or_patterns)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] @@ -153,6 +154,7 @@ mod utils; mod approx_const; mod arithmetic; mod as_conversions; +mod asm_syntax; mod assertions_on_constants; mod assign_ops; mod async_yields_async; @@ -176,6 +178,7 @@ mod dbg_macro; mod default_trait_access; mod dereference; mod derive; +mod disallowed_method; mod doc; mod double_comparison; mod double_parens; @@ -489,6 +492,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &arithmetic::FLOAT_ARITHMETIC, &arithmetic::INTEGER_ARITHMETIC, &as_conversions::AS_CONVERSIONS, + &asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, + &asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, &assertions_on_constants::ASSERTIONS_ON_CONSTANTS, &assign_ops::ASSIGN_OP_PATTERN, &assign_ops::MISREFACTORED_ASSIGN_OP, @@ -529,6 +534,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &derive::DERIVE_ORD_XOR_PARTIAL_ORD, &derive::EXPL_IMPL_CLONE_ON_COPY, &derive::UNSAFE_DERIVE_DESERIALIZE, + &disallowed_method::DISALLOWED_METHOD, &doc::DOC_MARKDOWN, &doc::MISSING_ERRORS_DOC, &doc::MISSING_SAFETY_DOC, @@ -851,9 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &types::UNIT_CMP, &types::UNNECESSARY_CAST, &types::VEC_BOX, + &unicode::INVISIBLE_CHARACTERS, &unicode::NON_ASCII_LITERAL, &unicode::UNICODE_NOT_NFC, - &unicode::ZERO_WIDTH_SPACE, &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, &unnamed_address::FN_ADDRESS_COMPARISONS, &unnamed_address::VTABLE_ADDRESS_COMPARISONS, @@ -1120,11 +1126,18 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem); + let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); + store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods)); + store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax); + store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), LintId::of(&arithmetic::INTEGER_ARITHMETIC), LintId::of(&as_conversions::AS_CONVERSIONS), + LintId::of(&asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), + LintId::of(&asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(&create_dir::CREATE_DIR), LintId::of(&dbg_macro::DBG_MACRO), LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE), @@ -1159,6 +1172,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&shadow::SHADOW_REUSE), LintId::of(&shadow::SHADOW_SAME), LintId::of(&strings::STRING_ADD), + LintId::of(&types::RC_BUFFER), LintId::of(&unwrap_in_result::UNWRAP_IN_RESULT), LintId::of(&verbose_file_reads::VERBOSE_FILE_READS), LintId::of(&write::PRINT_STDOUT), @@ -1463,7 +1477,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), - LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(&suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(&swap::ALMOST_SWAPPED), @@ -1492,14 +1505,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::CHAR_LIT_AS_U8), LintId::of(&types::FN_TO_NUMERIC_CAST), LintId::of(&types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&types::TYPE_COMPLEXITY), LintId::of(&types::UNIT_ARG), LintId::of(&types::UNIT_CMP), LintId::of(&types::UNNECESSARY_CAST), LintId::of(&types::VEC_BOX), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1592,6 +1604,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(&neg_multiply::NEG_MULTIPLY), LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT), + LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), + LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(&non_expressive_names::MANY_SINGLE_CHAR_NAMES), LintId::of(&panic_unimplemented::PANIC_PARAMS), @@ -1604,7 +1618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&returns::LET_AND_RETURN), LintId::of(&returns::NEEDLESS_RETURN), LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(&to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(&try_err::TRY_ERR), @@ -1747,8 +1760,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&misc::FLOAT_CMP), LintId::of(&misc::MODULO_ONE), LintId::of(&mut_key::MUTABLE_KEY_TYPE), - LintId::of(&non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(&non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), LintId::of(&open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(&option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(&ptr::MUT_FROM_REF), @@ -1766,7 +1777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&types::ABSURD_EXTREME_COMPARISONS), LintId::of(&types::CAST_REF_TO_MUT), LintId::of(&types::UNIT_CMP), - LintId::of(&unicode::ZERO_WIDTH_SPACE), + LintId::of(&unicode::INVISIBLE_CHARACTERS), LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS), @@ -1793,7 +1804,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(&stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(&types::BOX_VEC), - LintId::of(&types::RC_BUFFER), LintId::of(&types::REDUNDANT_ALLOCATION), LintId::of(&vec::USELESS_VEC), ]); @@ -1807,6 +1817,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(&attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(&cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(&disallowed_method::DISALLOWED_METHOD), LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS), LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS), @@ -1818,6 +1829,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_borrow::NEEDLESS_BORROW), LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE), + LintId::of(&strings::STRING_LIT_AS_BYTES), LintId::of(&transmute::USELESS_TRANSMUTE), LintId::of(&use_self::USE_SELF), ]); @@ -1896,6 +1908,7 @@ pub fn register_renamed(ls: &mut rustc_lint::LintStore) { ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); + ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4df6827d77f94..d7043e7bd8f71 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,21 +1,22 @@ +use crate::utils::paths; +use crate::utils::{get_trait_def_id, in_macro, span_lint, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_param_bound, walk_ty, NestedVisitorMap, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, + NestedVisitorMap, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, - ItemKind, Lifetime, LifetimeName, ParamName, QPath, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, - TyKind, WhereClause, WherePredicate, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, + TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Symbol}; - -use crate::utils::{in_macro, last_path_segment, span_lint, trait_ref_of_method}; +use std::iter::FromIterator; declare_clippy_lint! { /// **What it does:** Checks for lifetime annotations which can be removed by @@ -25,8 +26,11 @@ declare_clippy_lint! { /// complicated, while there is nothing out of the ordinary going on. Removing /// them leads to more readable code. /// - /// **Known problems:** Potential false negatives: we bail out if the function - /// has a `where` clause where lifetimes are mentioned. + /// **Known problems:** + /// - We bail out if the function has a `where` clause where lifetimes + /// are mentioned due to potenial false positives. + /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the + /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`. /// /// **Example:** /// ```rust @@ -108,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } /// The lifetime of a &-reference. -#[derive(PartialEq, Eq, Hash, Debug)] +#[derive(PartialEq, Eq, Hash, Debug, Clone)] enum RefLt { Unnamed, Static, @@ -127,7 +131,6 @@ fn check_fn_inner<'tcx>( return; } - let mut bounds_lts = Vec::new(); let types = generics .params .iter() @@ -156,13 +159,12 @@ fn check_fn_inner<'tcx>( if bound.name != LifetimeName::Static && !bound.is_elided() { return; } - bounds_lts.push(bound); } } } } } - if could_use_elision(cx, decl, body, &generics.params, bounds_lts) { + if could_use_elision(cx, decl, body, &generics.params) { span_lint( cx, NEEDLESS_LIFETIMES, @@ -181,7 +183,6 @@ fn could_use_elision<'tcx>( func: &'tcx FnDecl<'_>, body: Option, named_generics: &'tcx [GenericParam<'_>], - bounds_lts: Vec<&'tcx Lifetime>, ) -> bool { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -204,15 +205,31 @@ fn could_use_elision<'tcx>( if let Return(ref ty) = func.output { output_visitor.visit_ty(ty); } + for lt in named_generics { + input_visitor.visit_generic_param(lt) + } + + if input_visitor.abort() || output_visitor.abort() { + return false; + } - let input_lts = match input_visitor.into_vec() { - Some(lts) => lts_from_bounds(lts, bounds_lts.into_iter()), - None => return false, - }; - let output_lts = match output_visitor.into_vec() { - Some(val) => val, - None => return false, - }; + if allowed_lts + .intersection(&FxHashSet::from_iter( + input_visitor + .nested_elision_site_lts + .iter() + .chain(output_visitor.nested_elision_site_lts.iter()) + .cloned() + .filter(|v| matches!(v, RefLt::Named(_))), + )) + .next() + .is_some() + { + return false; + } + + let input_lts = input_visitor.lts; + let output_lts = output_visitor.lts; if let Some(body_id) = body { let mut checker = BodyLifetimeChecker { @@ -277,27 +294,20 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { allowed_lts } -fn lts_from_bounds<'a, T: Iterator>(mut vec: Vec, bounds_lts: T) -> Vec { - for lt in bounds_lts { - if lt.name != LifetimeName::Static { - vec.push(RefLt::Named(lt.name.ident().name)); - } - } - - vec -} - /// Number of unique lifetimes in the given vector. #[must_use] fn unique_lifetimes(lts: &[RefLt]) -> usize { lts.iter().collect::>().len() } +const CLOSURE_TRAIT_BOUNDS: [&[&str]; 3] = [&paths::FN, &paths::FN_MUT, &paths::FN_ONCE]; + /// A visitor usable for `rustc_front::visit::walk_ty()`. struct RefVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, lts: Vec, - abort: bool, + nested_elision_site_lts: Vec, + unelided_trait_object_lifetime: bool, } impl<'a, 'tcx> RefVisitor<'a, 'tcx> { @@ -305,7 +315,8 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), - abort: false, + nested_elision_site_lts: Vec::new(), + unelided_trait_object_lifetime: false, } } @@ -325,40 +336,16 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { } } - fn into_vec(self) -> Option> { - if self.abort { - None - } else { - Some(self.lts) - } + fn all_lts(&self) -> Vec { + self.lts + .iter() + .chain(self.nested_elision_site_lts.iter()) + .cloned() + .collect::>() } - fn collect_anonymous_lifetimes(&mut self, qpath: &QPath<'_>, ty: &Ty<'_>) { - if let Some(ref last_path_segment) = last_path_segment(qpath).args { - if !last_path_segment.parenthesized - && !last_path_segment - .args - .iter() - .any(|arg| matches!(arg, GenericArg::Lifetime(_))) - { - let hir_id = ty.hir_id; - match self.cx.qpath_res(qpath, hir_id) { - Res::Def(DefKind::TyAlias | DefKind::Struct, def_id) => { - let generics = self.cx.tcx.generics_of(def_id); - for _ in generics.params.as_slice() { - self.record(&None); - } - }, - Res::Def(DefKind::Trait, def_id) => { - let trait_def = self.cx.tcx.trait_def(def_id); - for _ in &self.cx.tcx.generics_of(trait_def.def_id).params { - self.record(&None); - } - }, - _ => (), - } - } - } + fn abort(&self) -> bool { + self.unelided_trait_object_lifetime } } @@ -370,30 +357,37 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { self.record(&Some(*lifetime)); } + fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>, tbm: TraitBoundModifier) { + let trait_ref = &poly_tref.trait_ref; + if CLOSURE_TRAIT_BOUNDS + .iter() + .any(|trait_path| trait_ref.trait_def_id() == get_trait_def_id(self.cx, trait_path)) + { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_trait_ref(trait_ref); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + } else { + walk_poly_trait_ref(self, poly_tref, tbm); + } + } + fn visit_ty(&mut self, ty: &'tcx Ty<'_>) { match ty.kind { - TyKind::Rptr(ref lt, _) if lt.is_elided() => { - self.record(&None); - }, - TyKind::Path(ref path) => { - self.collect_anonymous_lifetimes(path, ty); - }, TyKind::OpaqueDef(item, _) => { let map = self.cx.tcx.hir(); - if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).kind { - for bound in exist_ty.bounds { - if let GenericBound::Outlives(_) = *bound { - self.record(&None); - } - } - } else { - unreachable!() - } + let item = map.expect_item(item.id); + walk_item(self, item); walk_ty(self, ty); }, + TyKind::BareFn(&BareFnTy { decl, .. }) => { + let mut sub_visitor = RefVisitor::new(self.cx); + sub_visitor.visit_fn_decl(decl); + self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); + return; + }, TyKind::TraitObject(bounds, ref lt) => { if !lt.is_elided() { - self.abort = true; + self.unelided_trait_object_lifetime = true; } for bound in bounds { self.visit_poly_trait_ref(bound, TraitBoundModifier::None); @@ -430,16 +424,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereCl walk_param_bound(&mut visitor, bound); } // and check that all lifetimes are allowed - match visitor.into_vec() { - None => return false, - Some(lts) => { - for lt in lts { - if !allowed_lts.contains(<) { - return true; - } - } - }, - } + return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)); }, WherePredicate::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index a36fdca5d5de6..c54103b25c20e 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -264,13 +264,10 @@ impl LiteralDigitGrouping { let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { (exponent, &["32", "64"][..], 'f') + } else if num_lit.fraction.is_some() { + (&mut num_lit.integer, &["32", "64"][..], 'f') } else { - num_lit - .fraction - .as_mut() - .map_or((&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i'), |fraction| { - (fraction, &["32", "64"][..], 'f') - }) + (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') }; let mut split = part.rsplit('_'); diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 3410341a1e3c5..61b63597b1633 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -3,7 +3,7 @@ use crate::utils::paths; use crate::utils::sugg::Sugg; use crate::utils::usage::{is_unused, mutated_variables}; use crate::utils::{ - get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, + contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, @@ -1276,6 +1276,8 @@ fn check_for_loop_range<'tcx>( let skip = if starts_at_zero { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) { + return; } else { format!(".skip({})", snippet(cx, start.span, "..")) }; @@ -1302,6 +1304,8 @@ fn check_for_loop_range<'tcx>( if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) { String::new() + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { + return; } else { match limits { ast::RangeLimits::Closed => { @@ -2134,7 +2138,7 @@ enum VarState { DontWarn, } -/// Scan a for loop for variables that are incremented exactly once. +/// Scan a for loop for variables that are incremented exactly once and not used after that. struct IncrementVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, // context reference states: FxHashMap, // incremented variables @@ -2154,6 +2158,10 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> { if let Some(def_id) = var_def_id(self.cx, expr) { if let Some(parent) = get_parent_expr(self.cx, expr) { let state = self.states.entry(def_id).or_insert(VarState::Initial); + if *state == VarState::IncrOnce { + *state = VarState::DontWarn; + return; + } match parent.kind { ExprKind::AssignOp(op, ref lhs, ref rhs) => { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 065c7c042d367..b4b4b3dc18d51 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -18,9 +18,9 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// #[macro_use] - /// use lazy_static; + /// use some_macro; /// ``` pub MACRO_USE_IMPORTS, pedantic, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e0651f9ab5d6c..c0824bacbc735 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -400,8 +400,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.map(_).flatten(_)`, /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call using `_.flat_map(_)` + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.flat_map(_)` /// /// **Known problems:** /// @@ -424,8 +424,8 @@ declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter(_).map(_)`, /// `_.filter(_).flat_map(_)`, `_.filter_map(_).flat_map(_)` and similar. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.filter_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -452,8 +452,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.filter_map(_).next()`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** None /// @@ -496,8 +496,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.find(_).map(_)`. /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.find_map(_)`. /// /// **Known problems:** Often requires a condition + Option/Iterator creation /// inside the closure. @@ -1276,8 +1276,8 @@ declare_clippy_lint! { declare_clippy_lint! { /// **What it does:** Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). /// - /// **Why is this bad?** Readability, this can be written more concisely as a - /// single method call. + /// **Why is this bad?** Readability, this can be written more concisely as + /// `_.as_deref()`. /// /// **Known problems:** None. /// @@ -1668,9 +1668,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let ty::Opaque(def_id, _) = *ret_ty.kind() { // one of the associated types must be Self for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateAtom::Projection(projection_predicate) = - predicate.skip_binders() - { + if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() { // walk the associated type and check for Self if contains_ty(projection_predicate.ty, self_ty) { return; diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 80da04fb7de9d..25245b3dbf08e 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,10 +1,10 @@ +use crate::utils::qualify_min_const_fn::is_min_const_fn; use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use crate::utils::qualify_min_const_fn::is_min_const_fn; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; use rustc_typeck::hir_ty_to_ty; diff --git a/clippy_lints/src/needless_arbitrary_self_type.rs b/clippy_lints/src/needless_arbitrary_self_type.rs index 38bdd0f7ed23b..7687962bdd9bf 100644 --- a/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,4 +1,4 @@ -use crate::utils::span_lint_and_sugg; +use crate::utils::{in_macro, span_lint_and_sugg}; use if_chain::if_chain; use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind}; use rustc_errors::Applicability; @@ -69,11 +69,30 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod if let [segment] = &path.segments[..]; if segment.ident.name == kw::SelfUpper; then { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let mut applicability = Applicability::MachineApplicable; let self_param = match (binding_mode, mutbl) { (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => format!("&{} mut self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Mut) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ mut self".to_string() + } else { + format!("&{} mut self", &lifetime.ident.name) + } + }, (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => format!("&{} self", &lifetime.ident.name), + (Mode::Ref(Some(lifetime)), Mutability::Not) => { + if in_macro(lifetime.ident.span) { + applicability = Applicability::HasPlaceholders; + "&'_ self".to_string() + } else { + format!("&{} self", &lifetime.ident.name) + } + }, (Mode::Value, Mutability::Mut) => "mut self".to_string(), (Mode::Value, Mutability::Not) => "self".to_string(), }; @@ -85,7 +104,7 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod "the type of the `self` parameter does not need to be arbitrary", "consider to change this parameter to", self_param, - Applicability::MachineApplicable, + applicability, ) } } @@ -93,7 +112,8 @@ fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mod impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { - if !p.is_self() { + // Bail out if the parameter it's not a receiver or was not written by the user + if !p.is_self() || in_macro(p.span) { return; } diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index bb44eeb6adc51..7b662eae7753e 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,6 +1,6 @@ //! Checks for uses of const which the type is not `Freeze` (`Cell`-free). //! -//! This lint is **deny** by default. +//! This lint is **warn** by default. use std::ptr; @@ -17,6 +17,8 @@ use rustc_typeck::hir_ty_to_ty; use crate::utils::{in_constant, qpath_res, span_lint_and_then}; use if_chain::if_chain; +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks for declaration of `const` items which is interior /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.). @@ -34,6 +36,15 @@ declare_clippy_lint! { /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit, /// and this lint should be suppressed. /// + /// When an enum has variants with interior mutability, use of its non interior mutable + /// variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) + /// /// **Example:** /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; @@ -49,10 +60,12 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub DECLARE_INTERIOR_MUTABLE_CONST, - correctness, + style, "declaring `const` with interior mutability" } +// FIXME: this is a correctness problem but there's no suitable +// warn-by-default category. declare_clippy_lint! { /// **What it does:** Checks if `const` items which is interior mutable (e.g., /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly. @@ -64,7 +77,14 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// **Known problems:** None + /// **Known problems:** When an enum has variants with interior mutability, use of its non + /// interior mutable variants can generate false positives. See issue + /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) + /// + /// Types that have underlying or potential interior mutability trigger the lint whether + /// the interior mutable field is used or not. See issues + /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and + /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) /// /// **Example:** /// ```rust @@ -81,7 +101,7 @@ declare_clippy_lint! { /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance /// ``` pub BORROW_INTERIOR_MUTABLE_CONST, - correctness, + style, "referencing `const` with interior mutability" } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index dfc158661cbf6..95594e38c9ec1 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -143,7 +143,7 @@ fn check_set<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) { let mut parser = regex_syntax::ParserBuilder::new() - .unicode(utf8) + .unicode(true) .allow_invalid_utf8(!utf8) .build(); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 15b66684eab70..3783bd78de25e 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -69,7 +69,27 @@ declare_clippy_lint! { /// **Why is this bad?** Byte string literals (e.g., `b"foo"`) can be used /// instead. They are shorter but less discoverable than `as_bytes()`. /// - /// **Known Problems:** None. + /// **Known Problems:** + /// `"str".as_bytes()` and the suggested replacement of `b"str"` are not + /// equivalent because they have different types. The former is `&[u8]` + /// while the latter is `&[u8; 3]`. That means in general they will have a + /// different set of methods and different trait implementations. + /// + /// ```compile_fail + /// fn f(v: Vec) {} + /// + /// f("...".as_bytes().to_owned()); // works + /// f(b"...".to_owned()); // does not work, because arg is [u8; 3] not Vec + /// + /// fn g(r: impl std::io::Read) {} + /// + /// g("...".as_bytes()); // works + /// g(b"..."); // does not work + /// ``` + /// + /// The actual equivalent of `"str".as_bytes()` with the same type is not + /// `b"str"` but `&b"str"[..]`, which is a great deal of punctuation and not + /// more readable than a function call. /// /// **Example:** /// ```rust @@ -80,7 +100,7 @@ declare_clippy_lint! { /// let bs = b"a byte string"; /// ``` pub STRING_LIT_AS_BYTES, - style, + nursery, "calling `as_bytes` on a string literal instead of using a byte string literal" } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a29a199b8c3aa..5e83b6c81ec8f 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -216,18 +216,19 @@ declare_clippy_lint! { } declare_clippy_lint! { - /// **What it does:** Checks for Rc and Arc when T is a mutable buffer type such as String or Vec + /// **What it does:** Checks for `Rc` and `Arc` when `T` is a mutable buffer type such as `String` or `Vec`. /// - /// **Why is this bad?** Expressions such as Rc have no advantage over Rc, since - /// it is larger and involves an extra level of indirection, and doesn't implement Borrow. + /// **Why is this bad?** Expressions such as `Rc` usually have no advantage over `Rc`, since + /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow`. /// - /// While mutating a buffer type would still be possible with Rc::get_mut(), it only - /// works if there are no additional references yet, which defeats the purpose of + /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only + /// works if there are no additional references yet, which usually defeats the purpose of /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner - /// type with an interior mutable container (such as RefCell or Mutex) would normally + /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally /// be used. /// - /// **Known problems:** None. + /// **Known problems:** This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for + /// cases where mutation only happens before there are any additional references. /// /// **Example:** /// ```rust,ignore @@ -241,7 +242,7 @@ declare_clippy_lint! { /// fn foo(interned: Rc) { ... } /// ``` pub RC_BUFFER, - perf, + restriction, "shared ownership of a buffer type" } diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index d8c57f0e7ae7f..93d59cc7fcd17 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -8,18 +8,18 @@ use rustc_span::source_map::Span; use unicode_normalization::UnicodeNormalization; declare_clippy_lint! { - /// **What it does:** Checks for the Unicode zero-width space in the code. + /// **What it does:** Checks for invisible Unicode characters in the code. /// /// **Why is this bad?** Having an invisible character in the code makes for all /// sorts of April fools, but otherwise is very much frowned upon. /// /// **Known problems:** None. /// - /// **Example:** You don't see it, but there may be a zero-width space - /// somewhere in this text. - pub ZERO_WIDTH_SPACE, + /// **Example:** You don't see it, but there may be a zero-width space or soft hyphen + /// some­where in this text. + pub INVISIBLE_CHARACTERS, correctness, - "using a zero-width space in a string literal, which is confusing" + "using an invisible character in a string literal, which is confusing" } declare_clippy_lint! { @@ -63,7 +63,7 @@ declare_clippy_lint! { "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)" } -declare_lint_pass!(Unicode => [ZERO_WIDTH_SPACE, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]); impl LateLintPass<'_> for Unicode { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { @@ -91,14 +91,17 @@ fn escape>(s: T) -> String { fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) { let string = snippet(cx, span, ""); - if string.contains('\u{200B}') { + if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) { span_lint_and_sugg( cx, - ZERO_WIDTH_SPACE, + INVISIBLE_CHARACTERS, span, - "zero-width space detected", + "invisible character detected", "consider replacing the string with", - string.replace("\u{200B}", "\\u{200B}"), + string + .replace("\u{200B}", "\\u{200B}") + .replace("\u{ad}", "\\u{AD}") + .replace("\u{2060}", "\\u{2060}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 9b6a9075a2954..1307237dbc70a 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -170,22 +170,12 @@ fn mirrored_exprs( } fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { - // NOTE: Vectors of references are not supported. In order to avoid hitting https://github.com/rust-lang/rust/issues/34162, - // (different unnamed lifetimes for closure arg and return type) we need to make sure the suggested - // closure parameter is not a reference in case we suggest `Reverse`. Trying to destructure more - // than one level of references would add some extra complexity as we would have to compensate - // in the closure body. - if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; if let name = name_ident.ident.name.to_ident_string(); if name == "sort_by" || name == "sort_unstable_by"; if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args; - let vec_ty = cx.typeck_results().expr_ty(vec); - if utils::is_type_diagnostic_item(cx, vec_ty, sym!(vec_type)); - let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); // T in Vec - if !matches!(&ty.kind(), ty::Ref(..)); - if utils::is_copy(cx, ty); + if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym!(vec_type)); if let closure_body = cx.tcx.hir().body(*closure_body_id); if let &[ Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..}, @@ -210,40 +200,32 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { let vec_name = Sugg::hir(cx, &args[0], "..").to_string(); let unstable = name == "sort_unstable_by"; - if_chain! { - if let ExprKind::Path(QPath::Resolved(_, Path { - segments: [PathSegment { ident: left_name, .. }], .. - })) = &left_expr.kind; - if left_name == left_ident; - then { - return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })) - } else { - if !key_returns_borrow(cx, left_expr) { - return Some(LintTrigger::SortByKey(SortByKeyDetection { - vec_name, - unstable, - closure_arg, - closure_body, - reverse - })) - } + if let ExprKind::Path(QPath::Resolved(_, Path { + segments: [PathSegment { ident: left_name, .. }], .. + })) = &left_expr.kind { + if left_name == left_ident { + return Some(LintTrigger::Sort(SortDetection { vec_name, unstable })); } } + + if !expr_borrows(cx, left_expr) { + return Some(LintTrigger::SortByKey(SortByKeyDetection { + vec_name, + unstable, + closure_arg, + closure_body, + reverse + })); + } } } None } -fn key_returns_borrow(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(def_id) = utils::fn_def_id(cx, expr) { - let output = cx.tcx.fn_sig(def_id).output(); - let ty = output.skip_binder(); - return matches!(ty.kind(), ty::Ref(..)) - || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))); - } - - false +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { @@ -256,7 +238,7 @@ impl LateLintPass<'_> for UnnecessarySortBy { "use Vec::sort_by_key here instead", "try", format!( - "{}.sort{}_by_key(|&{}| {})", + "{}.sort{}_by_key(|{}| {})", trigger.vec_name, if trigger.unstable { "_unstable" } else { "" }, trigger.closure_arg, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c5a12ea9c8e1..dd2fd0bb445fe 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -2,10 +2,10 @@ #![deny(clippy::missing_docs_in_private_items)] -use lazy_static::lazy_static; use rustc_ast::ast::{LitKind, MetaItemKind, NestedMetaItem}; use rustc_span::source_map; use source_map::Span; +use std::lazy::SyncLazy; use std::path::{Path, PathBuf}; use std::sync::Mutex; use std::{env, fmt, fs, io}; @@ -54,9 +54,8 @@ impl From for Error { } } -lazy_static! { - static ref ERRORS: Mutex> = Mutex::new(Vec::new()); -} +/// Vec of errors that might be collected during config toml parsing +static ERRORS: SyncLazy>> = SyncLazy::new(|| Mutex::new(Vec::new())); macro_rules! define_Conf { ($(#[$doc:meta] ($config:ident, $config_str:literal: $Ty:ty, $default:expr),)+) => { @@ -82,6 +81,7 @@ macro_rules! define_Conf { use serde::Deserialize; pub fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<$Ty, D::Error> { use super::super::{ERRORS, Error}; + Ok( <$Ty>::deserialize(deserializer).unwrap_or_else(|e| { ERRORS @@ -164,6 +164,8 @@ define_Conf! { (max_fn_params_bools, "max_fn_params_bools": u64, 3), /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests). (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false), + /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses + (disallowed_methods, "disallowed_methods": Vec, Vec::::new()), } impl Default for Conf { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 247effde19b9c..790ac4f7dd8aa 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -18,9 +18,9 @@ pub mod internal_lints; pub mod numeric_literal; pub mod paths; pub mod ptr; +pub mod qualify_min_const_fn; pub mod sugg; pub mod usage; -pub mod qualify_min_const_fn; pub use self::attrs::*; pub use self::diagnostics::*; @@ -47,7 +47,6 @@ use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable}; -use rustc_mir::const_eval; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::symbol::{self, kw, Symbol}; @@ -884,19 +883,11 @@ pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { /// Checks if an expression is constructing a tuple-like enum variant or struct pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn has_no_arguments(cx: &LateContext<'_>, def_id: DefId) -> bool { - cx.tcx.fn_sig(def_id).skip_binder().inputs().is_empty() - } - if let ExprKind::Call(ref fun, _) = expr.kind { if let ExprKind::Path(ref qp) = fun.kind { let res = cx.qpath_res(qp, fun.hir_id); return match res { def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true, - // FIXME: check the constness of the arguments, see https://github.com/rust-lang/rust-clippy/pull/5682#issuecomment-638681210 - def::Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) if has_no_arguments(cx, def_id) => { - const_eval::is_const_fn(cx.tcx, def_id) - }, def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id), _ => false, }; @@ -1287,8 +1278,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Opaque(ref def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateAtom::Trait(trait_predicate, _) = predicate.skip_binders() { - if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() - { + if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { return true; } } diff --git a/clippy_lints/src/utils/numeric_literal.rs b/clippy_lints/src/utils/numeric_literal.rs index 5e8800d38eb52..52d3c2c1daf09 100644 --- a/clippy_lints/src/utils/numeric_literal.rs +++ b/clippy_lints/src/utils/numeric_literal.rs @@ -36,8 +36,9 @@ pub struct NumericLiteral<'a> { pub integer: &'a str, /// The fraction part of the number. pub fraction: Option<&'a str>, - /// The character used as exponent separator (b'e' or b'E') and the exponent part. - pub exponent: Option<(char, &'a str)>, + /// The exponent separator (b'e' or b'E') including preceding underscore if present + /// and the exponent part. + pub exponent: Option<(&'a str, &'a str)>, /// The type suffix, including preceding underscore if present. pub suffix: Option<&'a str>, @@ -100,7 +101,7 @@ impl<'a> NumericLiteral<'a> { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { + pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -113,12 +114,14 @@ impl<'a> NumericLiteral<'a> { fraction = Some(&digits[i + 1..]); }, 'e' | 'E' => { - if integer.len() > i { - integer = &digits[..i]; + let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i }; + + if integer.len() > exp_start { + integer = &digits[..exp_start]; } else { - fraction = Some(&digits[integer.len() + 1..i]); + fraction = Some(&digits[integer.len() + 1..exp_start]); }; - exponent = Some((c, &digits[i + 1..])); + exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, _ => {}, @@ -153,7 +156,7 @@ impl<'a> NumericLiteral<'a> { } if let Some((separator, exponent)) = self.exponent { - output.push(separator); + output.push_str(separator); Self::group_digits(&mut output, exponent, group_size, true, false); } diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index be837a61dc07e..277da9d3f3a27 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -41,6 +41,9 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"]; pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"]; pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"]; pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"]; +pub const FN: [&str; 3] = ["core", "ops", "Fn"]; +pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"]; +pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"]; pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index 19d890b4554af..1b4f20342729a 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -1,9 +1,12 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::mir::*; +use rustc_middle::mir::{ + Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt}; -use rustc_span::symbol::{sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi::RustIntrinsic; use std::borrow::Cow; @@ -24,15 +27,9 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) | ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue, - ty::PredicateAtom::ObjectSafe(_) => { - panic!("object safe predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::ClosureKind(..) => { - panic!("closure kind predicate on function: {:#?}", predicate) - } - ty::PredicateAtom::Subtype(_) => { - panic!("subtype predicate on function: {:#?}", predicate) - } + ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), + ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), + ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), ty::PredicateAtom::Trait(pred, _) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; @@ -48,12 +45,12 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>) -> McfResult { on const fn parameters are unstable" .into(), )); - } + }, // other kinds of bounds are either tautologies // or cause errors in other passes _ => continue, } - } + }, } } match predicates.parent { @@ -93,24 +90,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { match ty.kind() { ty::Ref(_, _, hir::Mutability::Mut) => { - return Err((span, "mutable references in const fn are unstable".into())); - } + return Err((span, "mutable references in const fn are unstable".into())); + }, ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())), ty::FnPtr(..) => { - return Err((span, "function pointers in const fn are unstable".into())); - } + return Err((span, "function pointers in const fn are unstable".into())); + }, ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { - ty::ExistentialPredicate::AutoTrait(_) - | ty::ExistentialPredicate::Projection(_) => { + ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { return Err(( span, "trait bounds other than `Sized` \ on const fn parameters are unstable" .into(), )); - } + }, ty::ExistentialPredicate::Trait(trait_ref) => { if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() { return Err(( @@ -120,34 +116,23 @@ fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { .into(), )); } - } + }, } } - } - _ => {} + }, + _ => {}, } } Ok(()) } -fn check_rvalue( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - rvalue: &Rvalue<'tcx>, - span: Span, -) -> McfResult { +fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult { match rvalue { - Rvalue::ThreadLocalRef(_) => { - Err((span, "cannot access thread local storage in const fn".into())) - } - Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => { - check_operand(tcx, operand, span, body) - } - Rvalue::Len(place) - | Rvalue::Discriminant(place) - | Rvalue::Ref(_, _, place) - | Rvalue::AddressOf(_, place) => check_place(tcx, *place, span, body), + Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), + Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body), + Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + check_place(tcx, *place, span, body) + }, Rvalue::Cast(CastKind::Misc, operand, cast_ty) => { use rustc_middle::ty::cast::CastTy; let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast"); @@ -155,20 +140,16 @@ fn check_rvalue( match (cast_in, cast_out) { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => { Err((span, "casting pointers to ints is unstable in const fn".into())) - } + }, _ => check_operand(tcx, operand, span, body), } - } - Rvalue::Cast( - CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), - operand, - _, - ) => check_operand(tcx, operand, span, body), + }, + Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => { + check_operand(tcx, operand, span, body) + }, Rvalue::Cast( CastKind::Pointer( - PointerCast::UnsafeFnPointer - | PointerCast::ClosureFnPointer(_) - | PointerCast::ReifyFnPointer, + PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, ), _, _, @@ -178,10 +159,7 @@ fn check_rvalue( deref_ty.ty } else { // We cannot allow this for now. - return Err(( - span, - "unsizing casts are only allowed for references right now".into(), - )); + return Err((span, "unsizing casts are only allowed for references right now".into())); }; let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id)); if let ty::Slice(_) | ty::Str = unsized_ty.kind() { @@ -192,7 +170,7 @@ fn check_rvalue( // We just can't allow trait objects until we have figured out trait method calls. Err((span, "unsizing casts are not allowed in const fn".into())) } - } + }, // binops are fine on integers Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => { check_operand(tcx, lhs, span, body)?; @@ -201,13 +179,14 @@ fn check_rvalue( if ty.is_integral() || ty.is_bool() || ty.is_char() { Ok(()) } else { - Err((span, "only int, `bool` and `char` operations are stable in const fn".into())) + Err(( + span, + "only int, `bool` and `char` operations are stable in const fn".into(), + )) } - } + }, Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()), - Rvalue::NullaryOp(NullOp::Box, _) => { - Err((span, "heap allocations are not allowed in const fn".into())) - } + Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); if ty.is_integral() || ty.is_bool() { @@ -215,39 +194,29 @@ fn check_rvalue( } else { Err((span, "only int and `bool` operations are stable in const fn".into())) } - } + }, Rvalue::Aggregate(_, operands) => { for operand in operands { check_operand(tcx, operand, span, body)?; } Ok(()) - } + }, } } -fn check_statement( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, - statement: &Statement<'tcx>, -) -> McfResult { +fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult { let span = statement.source_info.span; match &statement.kind { StatementKind::Assign(box (place, rval)) => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_rvalue(tcx, body, def_id, rval, span) - } - - StatementKind::FakeRead(_, place) => check_place(tcx, **place, span, body), + }, + StatementKind::FakeRead(_, place) | // just an assignment - StatementKind::SetDiscriminant { place, .. } => { - check_place(tcx, **place, span, body) - } + StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body), - StatementKind::LlvmInlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), // These are all NOPs StatementKind::StorageLive(_) @@ -259,12 +228,7 @@ fn check_statement( } } -fn check_operand( - tcx: TyCtxt<'tcx>, - operand: &Operand<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { match operand { Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body), Operand::Constant(c) => match c.check_static_ptr(tcx) { @@ -274,14 +238,9 @@ fn check_operand( } } -fn check_place( - tcx: TyCtxt<'tcx>, - place: Place<'tcx>, - span: Span, - body: &Body<'tcx>, -) -> McfResult { +fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult { let mut cursor = place.projection.as_ref(); - while let &[ref proj_base @ .., elem] = cursor { + while let [ref proj_base @ .., elem] = *cursor { cursor = proj_base; match elem { ProjectionElem::Field(..) => { @@ -289,26 +248,22 @@ fn check_place( if let Some(def) = base_ty.ty_adt_def() { // No union field accesses in `const fn` if def.is_union() { - return Err((span, "accessing union fields is unstable".into())); + return Err((span, "accessing union fields is unstable".into())); } } - } + }, ProjectionElem::ConstantIndex { .. } | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Deref - | ProjectionElem::Index(_) => {} + | ProjectionElem::Index(_) => {}, } } Ok(()) } -fn check_terminator( - tcx: TyCtxt<'tcx>, - body: &'a Body<'tcx>, - terminator: &Terminator<'tcx>, -) -> McfResult { +fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { TerminatorKind::FalseEdge { .. } @@ -318,20 +273,23 @@ fn check_terminator( | TerminatorKind::Resume | TerminatorKind::Unreachable => Ok(()), - TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), + TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body), TerminatorKind::DropAndReplace { place, value, .. } => { - check_place(tcx, *place, span, body)?; + check_place(tcx, *place, span, body)?; check_operand(tcx, value, span, body) - } + }, - TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => { - check_operand(tcx, discr, span, body) - } + TerminatorKind::SwitchInt { + discr, + switch_ty: _, + values: _, + targets: _, + } => check_operand(tcx, discr, span, body), TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { Err((span, "const fn generators are unstable".into())) - } + }, TerminatorKind::Call { func, @@ -343,8 +301,7 @@ fn check_terminator( } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { - if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) - { + if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) { return Err(( span, format!( @@ -360,9 +317,7 @@ fn check_terminator( // within const fns. `transmute` is allowed in all other const contexts. // This won't really scale to more intrinsics or functions. Let's allow const // transmutes in const fn before we add more hacks to this. - if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic - && tcx.item_name(fn_def_id) == sym::transmute - { + if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute { return Err(( span, "can only call `transmute` from const items, not `const fn`".into(), @@ -378,14 +333,16 @@ fn check_terminator( } else { Err((span, "can only call other const fns within const fn".into())) } - } + }, - TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => { - check_operand(tcx, cond, span, body) - } + TerminatorKind::Assert { + cond, + expected: _, + msg: _, + target: _, + cleanup: _, + } => check_operand(tcx, cond, span, body), - TerminatorKind::InlineAsm { .. } => { - Err((span, "cannot use inline assembly in const fn".into())) - } + TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())), } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index fac63bcb99378..d9d60fffcd7ae 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -235,8 +235,19 @@ impl EarlyLintPass for Write { } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) { + fn is_build_script(cx: &EarlyContext<'_>) -> bool { + // Cargo sets the crate name for build scripts to `build_script_build` + cx.sess + .opts + .crate_name + .as_ref() + .map_or(false, |crate_name| crate_name == "build_script_build") + } + if mac.path == sym!(println) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`"); + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if fmt_str.symbol == Symbol::intern("") { span_lint_and_sugg( @@ -251,7 +262,9 @@ impl EarlyLintPass for Write { } } } else if mac.path == sym!(print) { - span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + if !is_build_script(cx) { + span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`"); + } if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) { if check_newlines(&fmt_str) { span_lint_and_then( diff --git a/clippy_workspace_tests/build.rs b/clippy_workspace_tests/build.rs new file mode 100644 index 0000000000000..3507168a3a964 --- /dev/null +++ b/clippy_workspace_tests/build.rs @@ -0,0 +1,7 @@ +#![deny(clippy::print_stdout)] + +fn main() { + // Test for #6041 + println!("Hello"); + print!("Hello"); +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 21e0f6f4fc766..2869c3bf7d44b 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -189,7 +189,8 @@ declare_clippy_lint! { * The section of lines prefixed with `///` constitutes the lint documentation section. This is the default documentation style and will be displayed - [like this][example_lint_page]. + [like this][example_lint_page]. To render and open this documentation locally + in a browser, run `cargo dev serve`. * `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming guidelines][lint_naming] here when naming your lint. In short, the name should state the thing that is being checked for and diff --git a/doc/basics.md b/doc/basics.md index c81e7f6e0692b..38959e2331b40 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -13,6 +13,7 @@ Lints] or [Common Tools]. - [Setup](#setup) - [Building and Testing](#building-and-testing) - [`cargo dev`](#cargo-dev) + - [PR](#pr) ## Get the Code @@ -110,3 +111,8 @@ cargo dev new_lint # (experimental) Setup Clippy to work with rust-analyzer cargo dev ra-setup ``` + +## PR + +We follow a rustc no merge-commit policy. +See . diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index f6d529de9a3a2..ce3d0efab3aa7 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -110,7 +110,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "borrow_interior_mutable_const", - group: "correctness", + group: "style", desc: "referencing `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -334,7 +334,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "declare_interior_mutable_const", - group: "correctness", + group: "style", desc: "declaring `const` with interior mutability", deprecation: None, module: "non_copy_const", @@ -381,6 +381,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "derive", }, + Lint { + name: "disallowed_method", + group: "nursery", + desc: "use of a disallowed method call", + deprecation: None, + module: "disallowed_method", + }, Lint { name: "diverging_sub_expression", group: "complexity", @@ -885,6 +892,20 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "attrs", }, + Lint { + name: "inline_asm_x86_att_syntax", + group: "restriction", + desc: "prefer Intel x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, + Lint { + name: "inline_asm_x86_intel_syntax", + group: "restriction", + desc: "prefer AT&T x86 assembly syntax", + deprecation: None, + module: "asm_syntax", + }, Lint { name: "inline_fn_without_body", group: "correctness", @@ -941,6 +962,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "types", }, + Lint { + name: "invisible_characters", + group: "correctness", + desc: "using an invisible character in a string literal, which is confusing", + deprecation: None, + module: "unicode", + }, Lint { name: "items_after_statements", group: "pedantic", @@ -1860,7 +1888,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "rc_buffer", - group: "perf", + group: "restriction", desc: "shared ownership of a buffer type", deprecation: None, module: "types", @@ -2133,7 +2161,7 @@ pub static ref ALL_LINTS: Vec = vec![ }, Lint { name: "string_lit_as_bytes", - group: "style", + group: "nursery", desc: "calling `as_bytes` on a string literal instead of using a byte string literal", deprecation: None, module: "strings", @@ -2782,13 +2810,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "misc", }, - Lint { - name: "zero_width_space", - group: "correctness", - desc: "using a zero-width space in a string literal, which is confusing", - deprecation: None, - module: "unicode", - }, Lint { name: "zst_offset", group: "correctness", diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index 3c385343f7010..a8f3e3145f64d 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -1,27 +1,24 @@ -use lazy_static::lazy_static; use std::env; +use std::lazy::SyncLazy; use std::path::PathBuf; -lazy_static! { - pub static ref CARGO_TARGET_DIR: PathBuf = { - match env::var_os("CARGO_TARGET_DIR") { - Some(v) => v.into(), - None => env::current_dir().unwrap().join("target"), - } - }; - pub static ref TARGET_LIB: PathBuf = { - if let Some(path) = option_env!("TARGET_LIBS") { - path.into() - } else { - let mut dir = CARGO_TARGET_DIR.clone(); - if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { - dir.push(target); - } - dir.push(env!("PROFILE")); - dir +pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { + Some(v) => v.into(), + None => env::current_dir().unwrap().join("target"), +}); + +pub static TARGET_LIB: SyncLazy = SyncLazy::new(|| { + if let Some(path) = option_env!("TARGET_LIBS") { + path.into() + } else { + let mut dir = CARGO_TARGET_DIR.clone(); + if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { + dir.push(target); } - }; -} + dir.push(env!("PROFILE")); + dir + } +}); #[must_use] pub fn is_rustc_test_suite() -> bool { diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 697823712bf05..0e8f7683103d6 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,4 +1,5 @@ #![feature(test)] // compiletest_rs requires this attribute +#![feature(once_cell)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; @@ -71,7 +72,7 @@ fn default_config() -> compiletest::Config { } config.target_rustcflags = Some(format!( - "-L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), third_party_crates(), diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 81af3d3033b23..48e0478f16992 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -1,15 +1,14 @@ // Dogfood cannot run on Windows #![cfg(not(windows))] +#![feature(once_cell)] -use lazy_static::lazy_static; +use std::lazy::SyncLazy; use std::path::PathBuf; use std::process::Command; mod cargo; -lazy_static! { - static ref CLIPPY_PATH: PathBuf = cargo::TARGET_LIB.join("cargo-clippy"); -} +static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy")); #[test] fn dogfood_clippy() { diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml new file mode 100644 index 0000000000000..a1f515e443dc5 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -0,0 +1 @@ +disallowed-methods = ["core::iter::traits::iterator::Iterator::sum", "regex::re_unicode::Regex::is_match"] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs new file mode 100644 index 0000000000000..3d3f0729abd85 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.rs @@ -0,0 +1,13 @@ +#![warn(clippy::disallowed_method)] + +extern crate regex; +use regex::Regex; + +fn main() { + let a = vec![1, 2, 3, 4]; + let re = Regex::new(r"ab.*c").unwrap(); + + re.is_match("abc"); + + a.iter().sum::(); +} diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr new file mode 100644 index 0000000000000..ed91b5a6796d8 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -0,0 +1,16 @@ +error: use of a disallowed method `regex::re_unicode::Regex::is_match` + --> $DIR/conf_disallowed_method.rs:10:5 + | +LL | re.is_match("abc"); + | ^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::disallowed-method` implied by `-D warnings` + +error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` + --> $DIR/conf_disallowed_method.rs:12:5 + | +LL | a.iter().sum::(); + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6fbba01416a8d..103ec27e7d755 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/asm_syntax.rs b/tests/ui/asm_syntax.rs new file mode 100644 index 0000000000000..658cae397e149 --- /dev/null +++ b/tests/ui/asm_syntax.rs @@ -0,0 +1,31 @@ +#![feature(asm)] +// only-x86_64 + +#[warn(clippy::inline_asm_x86_intel_syntax)] +mod warn_intel { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +#[warn(clippy::inline_asm_x86_att_syntax)] +mod warn_att { + pub(super) unsafe fn use_asm() { + asm!(""); + asm!("", options()); + asm!("", options(nostack)); + asm!("", options(att_syntax)); + asm!("", options(nostack, att_syntax)); + } +} + +fn main() { + unsafe { + warn_att::use_asm(); + warn_intel::use_asm(); + } +} diff --git a/tests/ui/asm_syntax.stderr b/tests/ui/asm_syntax.stderr new file mode 100644 index 0000000000000..27b51166eacb8 --- /dev/null +++ b/tests/ui/asm_syntax.stderr @@ -0,0 +1,44 @@ +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:7:9 + | +LL | asm!(""); + | ^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:8:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> $DIR/asm_syntax.rs:9:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:21:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> $DIR/asm_syntax.rs:22:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 5 previous errors + diff --git a/tests/ui/attrs.rs b/tests/ui/attrs.rs index 32685038067d6..8df6e19421ec5 100644 --- a/tests/ui/attrs.rs +++ b/tests/ui/attrs.rs @@ -1,8 +1,5 @@ #![warn(clippy::inline_always, clippy::deprecated_semver)] #![allow(clippy::assertions_on_constants)] -// Test that the whole restriction group is not enabled -#![warn(clippy::restriction)] -#![deny(clippy::restriction)] #![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)] #[inline(always)] diff --git a/tests/ui/attrs.stderr b/tests/ui/attrs.stderr index 4324984dd60eb..df4e9e20b649c 100644 --- a/tests/ui/attrs.stderr +++ b/tests/ui/attrs.stderr @@ -1,5 +1,5 @@ error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea - --> $DIR/attrs.rs:8:1 + --> $DIR/attrs.rs:5:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[inline(always)] = note: `-D clippy::inline-always` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:28:14 + --> $DIR/attrs.rs:25:14 | LL | #[deprecated(since = "forever")] | ^^^^^^^^^^^^^^^^^ @@ -15,27 +15,10 @@ LL | #[deprecated(since = "forever")] = note: `-D clippy::deprecated-semver` implied by `-D warnings` error: the since field must contain a semver-compliant version - --> $DIR/attrs.rs:31:14 + --> $DIR/attrs.rs:28:14 | LL | #[deprecated(since = "1")] | ^^^^^^^^^^^ -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:4:9 - | -LL | #![warn(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` - = help: try enabling only the lints you really need - -error: restriction lints are not meant to be all enabled - --> $DIR/attrs.rs:5:9 - | -LL | #![deny(clippy::restriction)] - | ^^^^^^^^^^^^^^^^^^^ - | - = help: try enabling only the lints you really need - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e6626d57a7722..de670cdfc31f1 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -1,7 +1,8 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] -#![feature(repr128, proc_macro_hygiene, proc_macro_quote)] +#![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] #![allow(clippy::useless_conversion)] extern crate proc_macro; @@ -11,7 +12,11 @@ extern crate syn; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; use syn::parse_macro_input; -use syn::{parse_quote, ItemTrait, TraitItem}; +use syn::spanned::Spanned; +use syn::token::Star; +use syn::{ + parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, +}; #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { @@ -35,3 +40,56 @@ pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { } TokenStream::from(quote!(#item)) } + +#[proc_macro_attribute] +pub fn rename_my_lifetimes(_args: TokenStream, input: TokenStream) -> TokenStream { + fn make_name(count: usize) -> String { + format!("'life{}", count) + } + + fn mut_receiver_of(sig: &mut Signature) -> Option<&mut FnArg> { + let arg = sig.inputs.first_mut()?; + if let FnArg::Typed(PatType { pat, .. }) = arg { + if let Pat::Ident(PatIdent { ident, .. }) = &**pat { + if ident == "self" { + return Some(arg); + } + } + } + None + } + + let mut elided = 0; + let mut item = parse_macro_input!(input as ItemImpl); + + // Look for methods having arbitrary self type taken by &mut ref + for inner in &mut item.items { + if let ImplItem::Method(method) = inner { + if let Some(FnArg::Typed(pat_type)) = mut_receiver_of(&mut method.sig) { + if let box Type::Reference(reference) = &mut pat_type.ty { + // Target only unnamed lifetimes + let name = match &reference.lifetime { + Some(lt) if lt.ident == "_" => make_name(elided), + None => make_name(elided), + _ => continue, + }; + elided += 1; + + // HACK: Syn uses `Span` from the proc_macro2 crate, and does not seem to reexport it. + // In order to avoid adding the dependency, get a default span from a non-existent token. + // A default span is needed to mark the code as coming from expansion. + let span = Star::default().span(); + + // Replace old lifetime with the named one + let lifetime = Lifetime::new(&name, span); + reference.lifetime = Some(parse_quote!(#lifetime)); + + // Add lifetime to the generics of the method + method.sig.generics.params.push(parse_quote!(#lifetime)); + } + } + } + } + + TokenStream::from(quote!(#item)) +} diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 05ffb55f6207e..3df8be6c23230 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic #![crate_type = "proc-macro"] diff --git a/tests/ui/blanket_clippy_restriction_lints.rs b/tests/ui/blanket_clippy_restriction_lints.rs new file mode 100644 index 0000000000000..d055f17526b9b --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.rs @@ -0,0 +1,8 @@ +#![warn(clippy::blanket_clippy_restriction_lints)] + +//! Test that the whole restriction group is not enabled +#![warn(clippy::restriction)] +#![deny(clippy::restriction)] +#![forbid(clippy::restriction)] + +fn main() {} diff --git a/tests/ui/blanket_clippy_restriction_lints.stderr b/tests/ui/blanket_clippy_restriction_lints.stderr new file mode 100644 index 0000000000000..537557f8b0af0 --- /dev/null +++ b/tests/ui/blanket_clippy_restriction_lints.stderr @@ -0,0 +1,27 @@ +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:4:9 + | +LL | #![warn(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings` + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:5:9 + | +LL | #![deny(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: restriction lints are not meant to be all enabled + --> $DIR/blanket_clippy_restriction_lints.rs:6:11 + | +LL | #![forbid(clippy::restriction)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: try enabling only the lints you really need + +error: aborting due to 3 previous errors + diff --git a/tests/ui/crashes/associated-constant-ice.rs b/tests/ui/crashes/associated-constant-ice.rs index 4bb833795bb16..948deba3ea6e3 100644 --- a/tests/ui/crashes/associated-constant-ice.rs +++ b/tests/ui/crashes/associated-constant-ice.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 086548e58ed67..619d11cefc46d 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -1,3 +1,4 @@ +// compile-flags: --emit=link // no-prefer-dynamic // ^ compiletest by default builds all aux files as dylibs, but we don't want that for proc-macro // crates. If we don't set this, compiletest will override the `crate_type` attribute below and diff --git a/tests/ui/crashes/cc_seme.rs b/tests/ui/crashes/cc_seme.rs index c48c7e9e6c6ba..98588be9cf829 100644 --- a/tests/ui/crashes/cc_seme.rs +++ b/tests/ui/crashes/cc_seme.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/478 diff --git a/tests/ui/crashes/enum-glob-import-crate.rs b/tests/ui/crashes/enum-glob-import-crate.rs index db1fa871afe00..dca32aa3b5615 100644 --- a/tests/ui/crashes/enum-glob-import-crate.rs +++ b/tests/ui/crashes/enum-glob-import-crate.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] #![allow(unused_imports)] diff --git a/tests/ui/crashes/ice-1588.rs b/tests/ui/crashes/ice-1588.rs index 15d0f705b367f..b0a3d11bce463 100644 --- a/tests/ui/crashes/ice-1588.rs +++ b/tests/ui/crashes/ice-1588.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/ice-1782.rs b/tests/ui/crashes/ice-1782.rs index 1ca6b6976b38f..81af88962a64d 100644 --- a/tests/ui/crashes/ice-1782.rs +++ b/tests/ui/crashes/ice-1782.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessEq` / `consts::constant` diff --git a/tests/ui/crashes/ice-1969.rs b/tests/ui/crashes/ice-1969.rs index 837ec9df31ab1..96a8fe6c24d56 100644 --- a/tests/ui/crashes/ice-1969.rs +++ b/tests/ui/crashes/ice-1969.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1969 diff --git a/tests/ui/crashes/ice-2499.rs b/tests/ui/crashes/ice-2499.rs index ffef1631775ee..45b3b1869dde6 100644 --- a/tests/ui/crashes/ice-2499.rs +++ b/tests/ui/crashes/ice-2499.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2594.rs b/tests/ui/crashes/ice-2594.rs index ac19f1976e912..3f3986b6fc692 100644 --- a/tests/ui/crashes/ice-2594.rs +++ b/tests/ui/crashes/ice-2594.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code, unused_variables)] /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` diff --git a/tests/ui/crashes/ice-2727.rs b/tests/ui/crashes/ice-2727.rs index d832c2860332d..56024abc8f58d 100644 --- a/tests/ui/crashes/ice-2727.rs +++ b/tests/ui/crashes/ice-2727.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { diff --git a/tests/ui/crashes/ice-2760.rs b/tests/ui/crashes/ice-2760.rs index 9e5e299c336a5..f1a229f3f4faf 100644 --- a/tests/ui/crashes/ice-2760.rs +++ b/tests/ui/crashes/ice-2760.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow( unused_variables, clippy::blacklisted_name, diff --git a/tests/ui/crashes/ice-2774.rs b/tests/ui/crashes/ice-2774.rs index 47f8e3b18eeaa..d44b0fae82001 100644 --- a/tests/ui/crashes/ice-2774.rs +++ b/tests/ui/crashes/ice-2774.rs @@ -1,5 +1,3 @@ -// run-pass - use std::collections::HashSet; // See rust-lang/rust-clippy#2774. diff --git a/tests/ui/crashes/ice-2774.stderr b/tests/ui/crashes/ice-2774.stderr new file mode 100644 index 0000000000000..0c2d48f938fcb --- /dev/null +++ b/tests/ui/crashes/ice-2774.stderr @@ -0,0 +1,10 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/ice-2774.rs:15:1 + | +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::needless-lifetimes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/crashes/ice-2862.rs b/tests/ui/crashes/ice-2862.rs index 47324ce183169..8326e3663b054 100644 --- a/tests/ui/crashes/ice-2862.rs +++ b/tests/ui/crashes/ice-2862.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index c4f6c0fed6820..6b1ceb5056933 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,5 +1,3 @@ -// run-pass - #[allow(dead_code)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index ffad2d06b56e2..fef4d7db84ddf 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 #[derive(Clone)] diff --git a/tests/ui/crashes/ice-3462.rs b/tests/ui/crashes/ice-3462.rs index 95c7dff9be36b..7d62e315da2fc 100644 --- a/tests/ui/crashes/ice-3462.rs +++ b/tests/ui/crashes/ice-3462.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::all)] #![allow(clippy::blacklisted_name)] #![allow(unused)] diff --git a/tests/ui/crashes/ice-3741.rs b/tests/ui/crashes/ice-3741.rs index a548415da62bd..1253ddcfaeb3b 100644 --- a/tests/ui/crashes/ice-3741.rs +++ b/tests/ui/crashes/ice-3741.rs @@ -1,5 +1,4 @@ // aux-build:proc_macro_crash.rs -// run-pass #![warn(clippy::suspicious_else_formatting)] diff --git a/tests/ui/crashes/ice-3747.rs b/tests/ui/crashes/ice-3747.rs index d0b44ebafeeb2..cdf018cbc88d8 100644 --- a/tests/ui/crashes/ice-3747.rs +++ b/tests/ui/crashes/ice-3747.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { diff --git a/tests/ui/crashes/ice-4727.rs b/tests/ui/crashes/ice-4727.rs index cdb59caec67ed..2a4bc83f58a55 100644 --- a/tests/ui/crashes/ice-4727.rs +++ b/tests/ui/crashes/ice-4727.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::use_self)] #[path = "auxiliary/ice-4727-aux.rs"] diff --git a/tests/ui/crashes/ice-4760.rs b/tests/ui/crashes/ice-4760.rs index ead67d5ed1b1e..08b06961760ff 100644 --- a/tests/ui/crashes/ice-4760.rs +++ b/tests/ui/crashes/ice-4760.rs @@ -1,4 +1,3 @@ -// run-pass const COUNT: usize = 2; struct Thing; trait Dummy {} diff --git a/tests/ui/crashes/ice-700.rs b/tests/ui/crashes/ice-700.rs index b06df83d51a5b..0cbceedbd6bdb 100644 --- a/tests/ui/crashes/ice-700.rs +++ b/tests/ui/crashes/ice-700.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/700 diff --git a/tests/ui/crashes/ice_exacte_size.rs b/tests/ui/crashes/ice_exacte_size.rs index e02eb28ab8650..30e4b11ec0bd4 100644 --- a/tests/ui/crashes/ice_exacte_size.rs +++ b/tests/ui/crashes/ice_exacte_size.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::all)] /// Test for https://github.com/rust-lang/rust-clippy/issues/1336 diff --git a/tests/ui/crashes/if_same_then_else.rs b/tests/ui/crashes/if_same_then_else.rs index 4ef992b05e761..2f913292995ea 100644 --- a/tests/ui/crashes/if_same_then_else.rs +++ b/tests/ui/crashes/if_same_then_else.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] diff --git a/tests/ui/crashes/issue-825.rs b/tests/ui/crashes/issue-825.rs index 3d4a88ab3c4e5..05696e3d7d56f 100644 --- a/tests/ui/crashes/issue-825.rs +++ b/tests/ui/crashes/issue-825.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(warnings)] /// Test for https://github.com/rust-lang/rust-clippy/issues/825 diff --git a/tests/ui/crashes/issues_loop_mut_cond.rs b/tests/ui/crashes/issues_loop_mut_cond.rs index c4acd5cda1b0a..bb238c81ebc05 100644 --- a/tests/ui/crashes/issues_loop_mut_cond.rs +++ b/tests/ui/crashes/issues_loop_mut_cond.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(dead_code)] /// Issue: https://github.com/rust-lang/rust-clippy/issues/2596 diff --git a/tests/ui/crashes/match_same_arms_const.rs b/tests/ui/crashes/match_same_arms_const.rs index 848f0ea52ca5e..94c939665e616 100644 --- a/tests/ui/crashes/match_same_arms_const.rs +++ b/tests/ui/crashes/match_same_arms_const.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::match_same_arms)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2427 diff --git a/tests/ui/crashes/mut_mut_macro.rs b/tests/ui/crashes/mut_mut_macro.rs index d8fbaa5414664..a238e7896fc6b 100644 --- a/tests/ui/crashes/mut_mut_macro.rs +++ b/tests/ui/crashes/mut_mut_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_borrow_fp.rs b/tests/ui/crashes/needless_borrow_fp.rs index 48507efe1e98b..4f61c76828db8 100644 --- a/tests/ui/crashes/needless_borrow_fp.rs +++ b/tests/ui/crashes/needless_borrow_fp.rs @@ -1,5 +1,3 @@ -// run-pass - #[deny(clippy::all)] #[derive(Debug)] pub enum Error { diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.rs b/tests/ui/crashes/needless_lifetimes_impl_trait.rs index bd1fa4a0b1ef2..676564b2445d5 100644 --- a/tests/ui/crashes/needless_lifetimes_impl_trait.rs +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.rs @@ -1,5 +1,3 @@ -// run-pass - #![deny(clippy::needless_lifetimes)] #![allow(dead_code)] diff --git a/tests/ui/crashes/needless_lifetimes_impl_trait.stderr b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr new file mode 100644 index 0000000000000..d68bbe7880213 --- /dev/null +++ b/tests/ui/crashes/needless_lifetimes_impl_trait.stderr @@ -0,0 +1,14 @@ +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes_impl_trait.rs:15:5 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/needless_lifetimes_impl_trait.rs:1:9 + | +LL | #![deny(clippy::needless_lifetimes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/tests/ui/crashes/procedural_macro.rs b/tests/ui/crashes/procedural_macro.rs index f79d9ab6460b8..c7468493380c8 100644 --- a/tests/ui/crashes/procedural_macro.rs +++ b/tests/ui/crashes/procedural_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #[macro_use] extern crate clippy_mini_macro_test; diff --git a/tests/ui/crashes/regressions.rs b/tests/ui/crashes/regressions.rs index 3d5063d1a3a7d..a41bcb33b4460 100644 --- a/tests/ui/crashes/regressions.rs +++ b/tests/ui/crashes/regressions.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::blacklisted_name)] pub fn foo(bar: *const u8) { diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index f2153efc38800..8021ed4607dde 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -1,5 +1,3 @@ -// run-pass - /// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] diff --git a/tests/ui/crashes/single-match-else.rs b/tests/ui/crashes/single-match-else.rs index 3a4bbe310ccaa..1ba7ac082132f 100644 --- a/tests/ui/crashes/single-match-else.rs +++ b/tests/ui/crashes/single-match-else.rs @@ -1,5 +1,3 @@ -// run-pass - #![warn(clippy::single_match_else)] //! Test for https://github.com/rust-lang/rust-clippy/issues/1588 diff --git a/tests/ui/crashes/trivial_bounds.rs b/tests/ui/crashes/trivial_bounds.rs index 2bb95c18a3912..60105a8213feb 100644 --- a/tests/ui/crashes/trivial_bounds.rs +++ b/tests/ui/crashes/trivial_bounds.rs @@ -1,5 +1,3 @@ -// run-pass - #![feature(trivial_bounds)] #![allow(unused, trivial_bounds)] diff --git a/tests/ui/crashes/used_underscore_binding_macro.rs b/tests/ui/crashes/used_underscore_binding_macro.rs index 265017c51d924..6d2124c12fe98 100644 --- a/tests/ui/crashes/used_underscore_binding_macro.rs +++ b/tests/ui/crashes/used_underscore_binding_macro.rs @@ -1,5 +1,3 @@ -// run-pass - #![allow(clippy::useless_attribute)] //issue #2910 #[macro_use] diff --git a/tests/ui/escape_analysis.rs b/tests/ui/escape_analysis.rs index c0a52d832c00a..07004489610d0 100644 --- a/tests/ui/escape_analysis.rs +++ b/tests/ui/escape_analysis.rs @@ -174,3 +174,11 @@ mod issue_3739 { }; } } + +/// Issue #5542 +/// +/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code. +pub extern "C" fn do_not_warn_me(_c_pointer: Box) -> () {} + +#[rustfmt::skip] // Forces rustfmt to not add ABI +pub extern fn do_not_warn_me_no_abi(_c_pointer: Box) -> () {} diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index aa6ef162fe401..81d8221bd13e0 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -38,54 +38,54 @@ mod issue_1219 { let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { continue; } count += 1; - println!("{}", count); } // should not trigger the lint because the count is conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); if ch == 'a' { count += 1; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; if ch == 'a' { continue; } - println!("{}", count); } // should trigger the lint because the count is not conditional let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { let _ = 123; } - println!("{}", count); } // should not trigger the lint because the count is incremented multiple times let text = "banana"; let mut count = 0; for ch in text.chars() { + println!("{}", count); count += 1; for i in 0..2 { count += 1; } - println!("{}", count); } } } @@ -96,30 +96,30 @@ mod issue_3308 { let mut skips = 0; let erasures = vec![]; for i in 0..10 { + println!("{}", skips); while erasures.contains(&(i + skips)) { skips += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); let mut j = 0; while j < 5 { skips += 1; j += 1; } - println!("{}", skips); } // should not trigger the lint because the count is incremented multiple times let mut skips = 0; for i in 0..10 { + println!("{}", skips); for j in 0..5 { skips += 1; } - println!("{}", skips); } } } @@ -145,3 +145,16 @@ mod issue_4732 { let _closure = || println!("index: {}", index); } } + +mod issue_4677 { + pub fn test() { + let slice = &[1, 2, 3]; + + // should not trigger the lint because the count is used after incremented + let mut count = 0; + for _i in slice { + count += 1; + println!("{}", count); + } + } +} diff --git a/tests/ui/inconsistent_digit_grouping.fixed b/tests/ui/inconsistent_digit_grouping.fixed index b75f10917df18..dd683e7f746a4 100644 --- a/tests/ui/inconsistent_digit_grouping.fixed +++ b/tests/ui/inconsistent_digit_grouping.fixed @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/inconsistent_digit_grouping.rs b/tests/ui/inconsistent_digit_grouping.rs index 79ce38be19bd3..d5d27c853c28d 100644 --- a/tests/ui/inconsistent_digit_grouping.rs +++ b/tests/ui/inconsistent_digit_grouping.rs @@ -40,4 +40,8 @@ fn main() { // Ignore literals in macros let _ = mac1!(); let _ = mac2!(); + + // Issue #6096 + // Allow separating exponent with '_' + let _ = 1.025_011_10_E0; } diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index baee773573038..70cdb067d9138 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_i32; @@ -12,13 +17,13 @@ fn main() { let fail20 = 2_i8; // let fail21 = 4_i16; // - let fail24 = 12.34_f64; + let ok24 = 12.34_64; let fail25 = 1E2_f32; let fail26 = 43E7_f64; let fail27 = 243E17_f32; #[allow(overflowing_literals)] let fail28 = 241_251_235E723_f64; - let fail29 = 42_279.911_f32; + let ok29 = 42279.911_32; let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 6de447f40214b..729990af3998d 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(dead_code, unused_variables, clippy::excessive_precision)] +#![allow( + dead_code, + unused_variables, + clippy::excessive_precision, + clippy::inconsistent_digit_grouping +)] fn main() { let fail14 = 2_32; @@ -12,13 +17,13 @@ fn main() { let fail20 = 2__8; // let fail21 = 4___16; // - let fail24 = 12.34_64; + let ok24 = 12.34_64; let fail25 = 1E2_32; let fail26 = 43E7_64; let fail27 = 243E17_32; #[allow(overflowing_literals)] let fail28 = 241251235E723_64; - let fail29 = 42279.911_32; + let ok29 = 42279.911_32; let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index 48a7ae904948c..b338b8aa6228d 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:6:18 + --> $DIR/mistyped_literal_suffix.rs:11:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,76 +7,64 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:7:18 + --> $DIR/mistyped_literal_suffix.rs:12:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:8:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:9:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:17:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:18:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 - | -LL | let fail24 = 12.34_64; - | ^^^^^^^^ help: did you mean to write: `12.34_f64` - -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:16:18 + --> $DIR/mistyped_literal_suffix.rs:21:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:17:18 + --> $DIR/mistyped_literal_suffix.rs:22:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:20:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:21:18 - | -LL | let fail29 = 42279.911_32; - | ^^^^^^^^^^^^ help: did you mean to write: `42_279.911_f32` - -error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:13 + --> $DIR/mistyped_literal_suffix.rs:28:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 13 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.rs b/tests/ui/needless_arbitrary_self_type_unfixable.rs new file mode 100644 index 0000000000000..a39d96109f17d --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.rs @@ -0,0 +1,45 @@ +// aux-build:proc_macro_attr.rs + +#![warn(clippy::needless_arbitrary_self_type)] + +#[macro_use] +extern crate proc_macro_attr; + +mod issue_6089 { + // Check that we don't lint if the `self` parameter comes from expansion + + macro_rules! test_from_expansion { + () => { + trait T1 { + fn test(self: &Self); + } + + struct S1 {} + + impl T1 for S1 { + fn test(self: &Self) {} + } + }; + } + + test_from_expansion!(); + + // If only the lifetime name comes from expansion we will lint, but the suggestion will have + // placeholders and will not be applied automatically, as we can't reliably know the original name. + // This specific case happened with async_trait. + + trait T2 { + fn call_with_mut_self(&mut self); + } + + struct S2 {} + + // The method's signature will be expanded to: + // fn call_with_mut_self<'life0>(self: &'life0 mut Self) {} + #[rename_my_lifetimes] + impl T2 for S2 { + fn call_with_mut_self(self: &mut Self) {} + } +} + +fn main() {} diff --git a/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/tests/ui/needless_arbitrary_self_type_unfixable.stderr new file mode 100644 index 0000000000000..44a0e6ddeace6 --- /dev/null +++ b/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -0,0 +1,10 @@ +error: the type of the `self` parameter does not need to be arbitrary + --> $DIR/needless_arbitrary_self_type_unfixable.rs:41:31 + | +LL | fn call_with_mut_self(self: &mut Self) {} + | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'_ mut self` + | + = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 913cd004f19f4..d482d466e4499 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -259,4 +259,102 @@ mod issue4291 { } } +mod issue2944 { + trait Foo {} + struct Bar {} + struct Baz<'a> { + bar: &'a Bar, + } + + impl<'a> Foo for Baz<'a> {} + impl Bar { + fn baz<'a>(&'a self) -> impl Foo + 'a { + Baz { bar: self } + } + } +} + +mod nested_elision_sites { + // issue #issue2944 + + // closure trait bounds subject to nested elision + // don't lint because they refer to outer lifetimes + fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 { + move || i + } + fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 { + move || i + } + + // don't lint + fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 { + f() + } + fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 { + move || i + } + // lint + fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + f(i) + } + fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + f(i) + } + + // don't lint + fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 { + f() + } + // lint + fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + f(i) + } + + // don't lint + fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32 + where + T: Fn() -> &'a i32, + { + f() + } + // lint + fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + where + T: Fn(&i32) -> &i32, + { + f(i) + } + + // don't lint + fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 { + f(i) + } + fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 { + |i| i + } + // lint + fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + f(i) + } + + // don't lint + fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) { + |f| () + } + + // lint + fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + |f| 42 + } + fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + |f| () + } +} + fn main() {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index d3a360ed8b576..c8a2e8b81c019 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -102,5 +102,53 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 17 previous errors +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:271:9 + | +LL | fn baz<'a>(&'a self) -> impl Foo + 'a { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:300:5 + | +LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:303:5 + | +LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:312:5 + | +LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:324:5 + | +LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:339:5 + | +LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:352:5 + | +LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) + --> $DIR/needless_lifetimes.rs:355:5 + | +LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/needless_range_loop2.rs b/tests/ui/needless_range_loop2.rs index a82b115916190..7633316e0f877 100644 --- a/tests/ui/needless_range_loop2.rs +++ b/tests/ui/needless_range_loop2.rs @@ -82,6 +82,20 @@ fn main() { for i in 1..3 { println!("{}", arr[i]); } + + // Fix #5945 + let mut vec = vec![1, 2, 3, 4]; + for i in 0..vec.len() - 1 { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() { + vec[i] += 1; + } + let mut vec = vec![1, 2, 3, 4]; + for i in vec.len() - 3..vec.len() - 1 { + vec[i] += 1; + } } mod issue2277 { diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 5fb568672d356..2045ffdb5f09d 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or_else(Foo::new); + let mut map = HashMap::::new(); + map.entry(42).or_insert_with(String::new); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert_with(String::new); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or_else(|| "".to_owned()); @@ -110,23 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 737b0f7e55bc7..522f31b72d01f 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -58,6 +58,12 @@ fn or_fun_call() { let without_default = Some(Foo); without_default.unwrap_or(Foo::new()); + let mut map = HashMap::::new(); + map.entry(42).or_insert(String::new()); + + let mut btree = BTreeMap::::new(); + btree.entry(42).or_insert(String::new()); + let stringy = Some(String::from("")); let _ = stringy.unwrap_or("".to_owned()); @@ -110,23 +116,4 @@ fn f() -> Option<()> { Some(()) } -// Issue 5886 - const fn (with no arguments) -pub fn skip_const_fn_with_no_args() { - const fn foo() -> Option { - Some(42) - } - let _ = None.or(foo()); - - // See issue #5693. - let mut map = std::collections::HashMap::new(); - map.insert(1, vec![1]); - map.entry(1).or_insert(vec![]); - - let mut map = HashMap::::new(); - map.entry(42).or_insert(String::new()); - - let mut btree = BTreeMap::::new(); - btree.entry(42).or_insert(String::new()); -} - fn main() {} diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index b8a436993f329..bc5978b538f16 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -60,23 +60,35 @@ error: use of `unwrap_or` followed by a function call LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)` +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:62:19 + | +LL | map.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + +error: use of `or_insert` followed by a function call + --> $DIR/or_fun_call.rs:65:21 + | +LL | btree.entry(42).or_insert(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)` + error: use of `unwrap_or` followed by a function call - --> $DIR/or_fun_call.rs:62:21 + --> $DIR/or_fun_call.rs:68:21 | LL | let _ = stringy.unwrap_or("".to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:87:35 + --> $DIR/or_fun_call.rs:93:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))` error: use of `or` followed by a function call - --> $DIR/or_fun_call.rs:91:10 + --> $DIR/or_fun_call.rs:97:10 | LL | .or(Some(Bar(b, Duration::from_secs(2)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))` -error: aborting due to 13 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/print_stdout_build_script.rs b/tests/ui/print_stdout_build_script.rs new file mode 100644 index 0000000000000..997ebef8a6992 --- /dev/null +++ b/tests/ui/print_stdout_build_script.rs @@ -0,0 +1,12 @@ +// compile-flags: --crate-name=build_script_build + +#![warn(clippy::print_stdout)] + +fn main() { + // Fix #6041 + // + // The `print_stdout` lint shouldn't emit in `build.rs` + // as these methods are used for the build script. + println!("Hello"); + print!("Hello"); +} diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index 9767e5bf76a85..f7f3b195ccc18 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -71,6 +71,9 @@ fn trivial_regex() { let non_trivial_ends_with = Regex::new("foo|bar"); let non_trivial_binary = BRegex::new("foo|bar"); let non_trivial_binary_builder = BRegexBuilder::new("foo|bar"); + + // #6005: unicode classes in bytes::Regex + let a_byte_of_unicode = BRegex::new(r"\p{C}"); } fn main() { diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index 27db9594f3b33..1f596c312fe39 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,7 +1,11 @@ -#[warn(clippy::zero_width_space)] +#[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); print!("This\u{200B}is\u{200B}fine"); + print!("Here >­< is a SHY, and ­another"); + print!("This\u{ad}is\u{ad}fine"); + print!("Here >⁠< is a WJ, and ⁠another"); + print!("This\u{2060}is\u{2060}fine"); } #[warn(clippy::unicode_not_nfc)] diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 4575a132e5b2c..3fca463c620b5 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,13 +1,25 @@ -error: zero-width space detected +error: invisible character detected --> $DIR/unicode.rs:3:12 | LL | print!("Here >​< is a ZWS, and ​another"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"` | - = note: `-D clippy::zero-width-space` implied by `-D warnings` + = note: `-D clippy::invisible-characters` implied by `-D warnings` + +error: invisible character detected + --> $DIR/unicode.rs:5:12 + | +LL | print!("Here >­< is a SHY, and ­another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"` + +error: invisible character detected + --> $DIR/unicode.rs:7:12 + | +LL | print!("Here >⁠< is a WJ, and ⁠another"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"` error: non-NFC Unicode sequence detected - --> $DIR/unicode.rs:9:12 + --> $DIR/unicode.rs:13:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -15,12 +27,12 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:15:12 + --> $DIR/unicode.rs:19:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` | = note: `-D clippy::non-ascii-literal` implied by `-D warnings` -error: aborting due to 3 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index ad0d0387db03c..b45b27d8f23b1 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -13,12 +13,12 @@ fn unnecessary_sort_by() { // Forward examples vec.sort(); vec.sort_unstable(); - vec.sort_by_key(|&a| (a + 5).abs()); - vec.sort_unstable_by_key(|&a| id(-a)); + vec.sort_by_key(|a| (a + 5).abs()); + vec.sort_unstable_by_key(|a| id(-a)); // Reverse examples - vec.sort_by_key(|&b| Reverse(b)); - vec.sort_by_key(|&b| Reverse((b + 5).abs())); - vec.sort_unstable_by_key(|&b| Reverse(id(-b))); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow + vec.sort_by_key(|b| Reverse((b + 5).abs())); + vec.sort_unstable_by_key(|b| Reverse(id(-b))); // Negative examples (shouldn't be changed) let c = &7; vec.sort_by(|a, b| (b - a).cmp(&(a - b))); @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; - vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); - vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + vec.sort_by_key(|a| (***a).abs()); + vec.sort_unstable_by_key(|a| (***a).abs()); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { @@ -85,11 +85,11 @@ mod issue_6001 { let mut args: Vec = vec![]; // Forward - args.sort_by(|a, b| a.name().cmp(&b.name())); - args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + args.sort_by_key(|a| a.name()); + args.sort_unstable_by_key(|a| a.name()); // Reverse - args.sort_by(|a, b| b.name().cmp(&a.name())); - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + args.sort_by_key(|b| Reverse(b.name())); + args.sort_unstable_by_key(|b| Reverse(b.name())); } } diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index 9746f6e6849dd..be2abe7f7014d 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -16,7 +16,7 @@ fn unnecessary_sort_by() { vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); // Reverse examples - vec.sort_by(|a, b| b.cmp(a)); + vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); // Negative examples (shouldn't be changed) @@ -26,10 +26,11 @@ fn unnecessary_sort_by() { vec.sort_by(|_, b| b.cmp(c)); vec.sort_unstable_by(|a, _| a.cmp(c)); - // Ignore vectors of references + // Vectors of references are fine as long as the resulting key does not borrow let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5]; vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + // `Reverse(b)` would borrow in the following cases, don't lint vec.sort_by(|a, b| b.cmp(a)); vec.sort_unstable_by(|a, b| b.cmp(a)); } @@ -68,10 +69,9 @@ mod issue_5754 { } } -// `Vec::sort_by_key` closure parameter is `F: FnMut(&T) -> K` -// The suggestion is destructuring T and we know T is not a reference, so test that non-Copy T are -// not linted. +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted mod issue_6001 { + use super::*; struct Test(String); impl Test { diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index 70c6cf0a3b631..50607933e18f7 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -16,31 +16,61 @@ error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:16:5 | LL | vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&a| (a + 5).abs())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:17:5 | LL | vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&a| id(-a))` - -error: use Vec::sort_by_key here instead - --> $DIR/unnecessary_sort_by.rs:19:5 - | -LL | vec.sort_by(|a, b| b.cmp(a)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse(b))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:20:5 | LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|&b| Reverse((b + 5).abs()))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))` error: use Vec::sort_by_key here instead --> $DIR/unnecessary_sort_by.rs:21:5 | LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|&b| Reverse(id(-b)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:31:5 + | +LL | vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:32:5 + | +LL | vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:88:9 + | +LL | args.sort_by(|a, b| a.name().cmp(&b.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:89:9 + | +LL | args.sort_unstable_by(|a, b| a.name().cmp(&b.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:91:9 + | +LL | args.sort_by(|a, b| b.name().cmp(&a.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))` + +error: use Vec::sort_by_key here instead + --> $DIR/unnecessary_sort_by.rs:92:9 + | +LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))` -error: aborting due to 7 previous errors +error: aborting due to 12 previous errors From 7f846c930b9d35061e6eb6b8dbfbc890d38d539c Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 14:14:49 +0200 Subject: [PATCH 49/97] Update backport documentation to the subtree workflow --- doc/backport.md | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/doc/backport.md b/doc/backport.md index 259696658eaba..15f3d1f080604 100644 --- a/doc/backport.md +++ b/doc/backport.md @@ -5,7 +5,7 @@ Backports in Clippy are rare and should be approved by the Clippy team. For example, a backport is done, if a crucial ICE was fixed or a lint is broken to a point, that it has to be disabled, before landing on stable. -Backports are done to the `beta` release of Clippy. Backports to stable Clippy +Backports are done to the `beta` branch of Clippy. Backports to stable Clippy releases basically don't exist, since this would require a Rust point release, which is almost never justifiable for a Clippy fix. @@ -18,7 +18,31 @@ Backports are done on the beta branch of the Clippy repository. # Assuming the current directory corresponds to the Clippy repository $ git checkout beta $ git checkout -b backport -$ git cherry-pick # `` is the commit hash of the commit, that should be backported +$ git cherry-pick # `` is the commit hash of the commit(s), that should be backported +$ git push origin backport +``` + +Now you should test that the backport passes all the tests in the Rust +repository. You can do this with: + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ git subtree pull -p src/tools/clippy https://github.com//rust-clippy backport +$ ./x.py test src/tools/clippy +``` + +Should the test fail, you can fix Clippy directly in the Rust repository. This +has to be first applied to the Clippy beta branch and then again synced to the +Rust repository, though. The easiest way to do this is: + +```bash +# In the Rust repository +$ git diff --patch --relative=src/tools/clippy > clippy.patch +# In the Clippy repository +$ git apply /path/to/clippy.patch +$ git add -u +$ git commit -m "Fix rustup fallout" $ git push origin backport ``` @@ -29,22 +53,19 @@ After this, you can open a PR to the `beta` branch of the Clippy repository. This step must be done, **after** the PR of the previous step was merged. -After the backport landed in the Clippy repository, also the Clippy version on -the Rust `beta` branch has to be updated. +After the backport landed in the Clippy repository, the branch has to be synced +back to the beta branch of the Rust repository. ```bash # Assuming the current directory corresponds to the Rust repository $ git checkout beta $ git checkout -b clippy_backport -$ pushd src/tools/clippy -$ git fetch -$ git checkout beta -$ popd -$ git add src/tools/clippy -§ git commit -m "Update Clippy" +$ git subtree pull -p src/tools/clippy https://github.com/rust-lang/rust-clippy beta $ git push origin clippy_backport ``` -After this you can open a PR to the `beta` branch of the Rust repository. In -this PR you should tag the Clippy team member, that agreed to the backport or -the `@rust-lang/clippy` team. Make sure to add `[beta]` to the title of the PR. +Make sure to test the backport in the Rust repository before opening a PR. This +is done with `./x.py test src/tools/clippy`. If that passes all tests, open a PR +to the `beta` branch of the Rust repository. In this PR you should tag the +Clippy team member, that agreed to the backport or the `@rust-lang/clippy` team. +Make sure to add `[beta]` to the title of the PR. From ecd308ec394f9d01b4392ee0315ea64d52ab7caa Mon Sep 17 00:00:00 2001 From: hosseind75 Date: Sat, 19 Sep 2020 17:51:24 +0430 Subject: [PATCH 50/97] ICEs should print the top of the query stack --- src/driver.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/driver.rs b/src/driver.rs index f4f2259cefd51..c88dffc88f422 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -274,12 +274,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { handler.note_without_error(¬e); } - // If backtraces are enabled, also print the query stack - let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); - - if backtrace { - TyCtxt::try_print_query_stack(&handler); - } + TyCtxt::try_print_query_stack(&handler, Some(2)); } fn toolchain_path(home: Option, toolchain: Option) -> Option { From a9053e4baf251f53d8d6b95cd3f5b8a829ad0ba6 Mon Sep 17 00:00:00 2001 From: hosseind75 Date: Sat, 19 Sep 2020 18:37:58 +0430 Subject: [PATCH 51/97] run full query stack print just when RUST_BACKTRACE is set --- src/driver.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index c88dffc88f422..0b324775b0d1a 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -274,7 +274,10 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { handler.note_without_error(¬e); } - TyCtxt::try_print_query_stack(&handler, Some(2)); + // If backtraces are enabled, also print the query stack + let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); + + TyCtxt::try_print_query_stack(&handler, Some(2), Some(backtrace)); } fn toolchain_path(home: Option, toolchain: Option) -> Option { From 49bc85e947ab7ca793c14b6f3af4a8e9d8db0337 Mon Sep 17 00:00:00 2001 From: hosseind75 Date: Fri, 25 Sep 2020 19:55:32 +0330 Subject: [PATCH 52/97] fix clippy custom_ice_message test --- tests/ui/custom_ice_message.stderr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr index a9a65a38c109d..784ab9e5c7078 100644 --- a/tests/ui/custom_ice_message.stderr +++ b/tests/ui/custom_ice_message.stderr @@ -9,3 +9,5 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy note: Clippy version: foo +query stack during panic: +we're just showing a limited slice of the query stack \ No newline at end of file From 7f07577e6f873f4a1b3428d29bf520189f4ef79e Mon Sep 17 00:00:00 2001 From: hosseind75 Date: Mon, 28 Sep 2020 22:07:31 +0330 Subject: [PATCH 53/97] add new line --- tests/ui/custom_ice_message.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr index 784ab9e5c7078..87cdb7a8b99dd 100644 --- a/tests/ui/custom_ice_message.stderr +++ b/tests/ui/custom_ice_message.stderr @@ -10,4 +10,4 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy note: Clippy version: foo query stack during panic: -we're just showing a limited slice of the query stack \ No newline at end of file +we're just showing a limited slice of the query stack From 3c94914f0c87ba00987e515063dcc9e079a8918d Mon Sep 17 00:00:00 2001 From: hosseind75 Date: Tue, 29 Sep 2020 18:14:07 +0330 Subject: [PATCH 54/97] rebase with master --- src/driver.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index 0b324775b0d1a..bf9110b43492f 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -277,7 +277,9 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // If backtraces are enabled, also print the query stack let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0"); - TyCtxt::try_print_query_stack(&handler, Some(2), Some(backtrace)); + let num_frames = if backtrace { None } else { Some(2) }; + + TyCtxt::try_print_query_stack(&handler, num_frames); } fn toolchain_path(home: Option, toolchain: Option) -> Option { From 7b7ddfa55da889b41e90243ac59a04eed832a71e Mon Sep 17 00:00:00 2001 From: Sebastian Andersson Date: Fri, 9 Oct 2020 20:23:03 +0200 Subject: [PATCH 55/97] Preserve raw strs for: format!(s) to s.to_string() lint Ie: | let s = format!(r#""hello""#); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.to_string()`: `r#""hello""#.to_string()` --- clippy_lints/src/format.rs | 8 ++++++-- tests/ui/format.fixed | 3 ++- tests/ui/format.stderr | 8 +++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index d6541010bca23..26da058598e4f 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use crate::utils::paths; use crate::utils::{ - is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, + is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt, span_lint_and_then, }; use if_chain::if_chain; @@ -132,7 +132,11 @@ fn on_new_v1<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option [], }` if tup.is_empty() { - return Some(format!("{:?}.to_string()", s.as_str())); + if let Some(s_src) = snippet_opt(cx, lit.span) { + // Simulate macro expansion, converting {{ and }} to { and }. + let s_expand = s_src.replace("{{", "{").replace("}}", "}"); + return Some(format!("{}.to_string()", s_expand)) + } } else if s.as_str().is_empty() { return on_argumentv1_new(cx, &tup[0], arms); } diff --git a/tests/ui/format.fixed b/tests/ui/format.fixed index 306514769990d..740a22a07d747 100644 --- a/tests/ui/format.fixed +++ b/tests/ui/format.fixed @@ -13,7 +13,8 @@ fn main() { "foo".to_string(); "{}".to_string(); "{} abc {}".to_string(); - "foo {}\n\" bar".to_string(); + r##"foo {} +" bar"##.to_string(); "foo".to_string(); format!("{:?}", "foo"); // Don't warn about `Debug`. diff --git a/tests/ui/format.stderr b/tests/ui/format.stderr index 9734492154e80..96df7f37f7792 100644 --- a/tests/ui/format.stderr +++ b/tests/ui/format.stderr @@ -25,7 +25,13 @@ LL | / format!( LL | | r##"foo {{}} LL | | " bar"## LL | | ); - | |______^ help: consider using `.to_string()`: `"foo {}/n/" bar".to_string();` + | |______^ + | +help: consider using `.to_string()` + | +LL | r##"foo {} +LL | " bar"##.to_string(); + | error: useless use of `format!` --> $DIR/format.rs:21:5 From 26e4de9557e5924d001652138ec0f8f40dc40372 Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Fri, 9 Oct 2020 00:49:27 +0200 Subject: [PATCH 56/97] allow refs in our constant handling --- clippy_lints/src/consts.rs | 10 +++++++++- tests/ui/float_cmp.rs | 5 +++++ tests/ui/float_cmp.stderr | 12 ++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/consts.rs b/clippy_lints/src/consts.rs index 062c9bd2d9e6c..c5e33b288a9c7 100644 --- a/clippy_lints/src/consts.rs +++ b/clippy_lints/src/consts.rs @@ -40,6 +40,8 @@ pub enum Constant { Tuple(Vec), /// A raw pointer. RawPtr(u128), + /// A reference + Ref(Box), /// A literal with syntax error. Err(Symbol), } @@ -66,6 +68,7 @@ impl PartialEq for Constant { (&Self::Bool(l), &Self::Bool(r)) => l == r, (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r, (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb, // TODO: are there inter-type equalities? _ => false, } @@ -110,6 +113,9 @@ impl Hash for Constant { Self::RawPtr(u) => { u.hash(state); }, + Self::Ref(ref r) => { + r.hash(state); + }, Self::Err(ref s) => { s.hash(state); }, @@ -144,6 +150,7 @@ impl Constant { x => x, } }, + (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, lb, rb), // TODO: are there any useful inter-type orderings? _ => None, } @@ -239,7 +246,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { ExprKind::Unary(op, ref operand) => self.expr(operand).and_then(|o| match op { UnOp::UnNot => self.constant_not(&o, self.typeck_results.expr_ty(e)), UnOp::UnNeg => self.constant_negate(&o, self.typeck_results.expr_ty(e)), - UnOp::UnDeref => Some(o), + UnOp::UnDeref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::Binary(op, ref left, ref right) => self.binop(op, left, right), ExprKind::Call(ref callee, ref args) => { @@ -269,6 +276,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } }, ExprKind::Index(ref arr, ref index) => self.index(arr, index), + ExprKind::AddrOf(_, _, ref inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), // TODO: add other expressions. _ => None, } diff --git a/tests/ui/float_cmp.rs b/tests/ui/float_cmp.rs index 9fa0e5f5c079b..586784b73e697 100644 --- a/tests/ui/float_cmp.rs +++ b/tests/ui/float_cmp.rs @@ -2,6 +2,7 @@ #![allow( unused, clippy::no_effect, + clippy::op_ref, clippy::unnecessary_operation, clippy::cast_lossless, clippy::many_single_char_names @@ -116,4 +117,8 @@ fn main() { 1.23f64.signum() != x64.signum(); 1.23f64.signum() != -(x64.signum()); 1.23f64.signum() != 3.21f64.signum(); + + // the comparison should also look through references + &0.0 == &ZERO; + &&&&0.0 == &&&&ZERO; } diff --git a/tests/ui/float_cmp.stderr b/tests/ui/float_cmp.stderr index f7c380fc915c0..bb4051c466201 100644 --- a/tests/ui/float_cmp.stderr +++ b/tests/ui/float_cmp.stderr @@ -1,5 +1,5 @@ error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:65:5 + --> $DIR/float_cmp.rs:66:5 | LL | ONE as f64 != 2.0; | ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin` @@ -8,7 +8,7 @@ LL | ONE as f64 != 2.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:70:5 + --> $DIR/float_cmp.rs:71:5 | LL | x == 1.0; | ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin` @@ -16,7 +16,7 @@ LL | x == 1.0; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:73:5 + --> $DIR/float_cmp.rs:74:5 | LL | twice(x) != twice(ONE as f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin` @@ -24,7 +24,7 @@ LL | twice(x) != twice(ONE as f64); = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:93:5 + --> $DIR/float_cmp.rs:94:5 | LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin` @@ -32,7 +32,7 @@ LL | NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` arrays - --> $DIR/float_cmp.rs:98:5 + --> $DIR/float_cmp.rs:99:5 | LL | a1 == a2; | ^^^^^^^^ @@ -40,7 +40,7 @@ LL | a1 == a2; = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin` error: strict comparison of `f32` or `f64` - --> $DIR/float_cmp.rs:99:5 + --> $DIR/float_cmp.rs:100:5 | LL | a1[0] == a2[0]; | ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin` From 6d88803a1c5516fda1d1030e5676d6b15be130fc Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:21:47 +0200 Subject: [PATCH 57/97] Add regression test for ICE 6139 --- tests/ui/crashes/ice-6139.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/ui/crashes/ice-6139.rs diff --git a/tests/ui/crashes/ice-6139.rs b/tests/ui/crashes/ice-6139.rs new file mode 100644 index 0000000000000..f3966e47f5e80 --- /dev/null +++ b/tests/ui/crashes/ice-6139.rs @@ -0,0 +1,7 @@ +trait T<'a> {} + +fn foo(_: Vec>>) {} + +fn main() { + foo(vec![]); +} From a98f9d21fcdad85ae95d00d3931cf438f9d5a9de Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 9 Oct 2020 22:22:21 +0200 Subject: [PATCH 58/97] (Hacky) Fix for ICE #6139 --- clippy_lints/src/types.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 5e83b6c81ec8f..a982b92bb2b3f 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -541,6 +541,11 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); + // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a + // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects + // don't have a known size, this shouldn't introduce FNs. But there + // should be a better solution. + if !matches!(ty_ty.kind(), ty::Dynamic(..)); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; From cf81975d7765bf0cf82fee9f3e991c880f77cd13 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 14:04:14 +0200 Subject: [PATCH 59/97] Fix two ICEs caused by ty.is_{sized,freeze} --- clippy_lints/src/mut_key.rs | 7 ++++++- clippy_lints/src/types.rs | 7 ++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 8a2dbdc50eaea..4525b12689fa7 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,6 +1,7 @@ use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{Adt, Array, RawPtr, Ref, Slice, Tuple, Ty, TypeAndMut}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -120,7 +121,11 @@ fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bo size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span) }, Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)), - Adt(..) => cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx.at(span), cx.param_env), + Adt(..) => { + cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.has_escaping_bound_vars() + && !ty.is_freeze(cx.tcx.at(span), cx.param_env) + }, _ => false, } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index a982b92bb2b3f..9a948af8bfc4e 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -17,6 +17,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, InferTy, Ty, TyCtxt, TyS, TypeckResults}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -541,11 +542,7 @@ impl Types { _ => None, }); let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty); - // HACK(flip1995): This is a fix for an ICE occuring when `ty_ty` is a - // trait object with a lifetime, e.g. `dyn T<'_>`. Since trait objects - // don't have a known size, this shouldn't introduce FNs. But there - // should be a better solution. - if !matches!(ty_ty.kind(), ty::Dynamic(..)); + if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx.at(ty.span), cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); if ty_ty_size <= self.vec_box_size_threshold; From 52e650ae88a63b41686f646f2240de7c870e6ea6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sat, 10 Oct 2020 15:03:49 +0200 Subject: [PATCH 60/97] Add test for ICE #6153 --- tests/ui/crashes/ice-6153.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/ui/crashes/ice-6153.rs diff --git a/tests/ui/crashes/ice-6153.rs b/tests/ui/crashes/ice-6153.rs new file mode 100644 index 0000000000000..9f73f39f10d79 --- /dev/null +++ b/tests/ui/crashes/ice-6153.rs @@ -0,0 +1,9 @@ +pub struct S<'a, 'e>(&'a str, &'e str); + +pub type T<'a, 'e> = std::collections::HashMap, ()>; + +impl<'e, 'a: 'e> S<'a, 'e> { + pub fn foo(_a: &str, _b: &str, _map: &T) {} +} + +fn main() {} From 117877745738f21e851686efe57a09e14606dadc Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 10 Oct 2020 17:36:04 +0200 Subject: [PATCH 61/97] Refactor how SwitchInt stores jump targets --- clippy_lints/src/utils/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/utils/qualify_min_const_fn.rs b/clippy_lints/src/utils/qualify_min_const_fn.rs index 1b4f20342729a..7cb7d0a26b65e 100644 --- a/clippy_lints/src/utils/qualify_min_const_fn.rs +++ b/clippy_lints/src/utils/qualify_min_const_fn.rs @@ -282,7 +282,6 @@ fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Termin TerminatorKind::SwitchInt { discr, switch_ty: _, - values: _, targets: _, } => check_operand(tcx, discr, span, body), From 377d1fab1f1fe104c12cea17f7f24a8e23775942 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Sun, 11 Oct 2020 22:57:22 +0900 Subject: [PATCH 62/97] Remove the generated files by `update-references.sh` if they are empty --- doc/adding_lints.md | 3 ++- tests/ui-cargo/update-references.sh | 8 ++++++++ tests/ui-toml/update-references.sh | 8 ++++++++ tests/ui/update-references.sh | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 2869c3bf7d44b..344bb455aa539 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -104,7 +104,8 @@ every time before running `tests/ui/update-all-references.sh`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit our lint, we need to commit the generated `.stderr` files, too. In general, you should only commit files changed by `tests/ui/update-all-references.sh` for the -specific lint you are creating/editing. +specific lint you are creating/editing. Note that if the generated files are +empty, they should be removed. ### Cargo lints diff --git a/tests/ui-cargo/update-references.sh b/tests/ui-cargo/update-references.sh index 50d42678734e9..2ab51168bcaa6 100755 --- a/tests/ui-cargo/update-references.sh +++ b/tests/ui-cargo/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui-toml/update-references.sh b/tests/ui-toml/update-references.sh index 50d42678734e9..2ab51168bcaa6 100755 --- a/tests/ui-toml/update-references.sh +++ b/tests/ui-toml/update-references.sh @@ -29,10 +29,18 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi done diff --git a/tests/ui/update-references.sh b/tests/ui/update-references.sh index 2c13c327d7980..e16ed600ef814 100755 --- a/tests/ui/update-references.sh +++ b/tests/ui/update-references.sh @@ -30,15 +30,27 @@ while [[ "$1" != "" ]]; do ! (cmp -s -- "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME"); then echo updating "$MYDIR"/"$STDOUT_NAME" cp "$BUILD_DIR"/"$STDOUT_NAME" "$MYDIR"/"$STDOUT_NAME" + if [[ ! -s "$MYDIR"/"$STDOUT_NAME" ]]; then + echo removing "$MYDIR"/"$STDOUT_NAME" + rm "$MYDIR"/"$STDOUT_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$STDERR_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME"); then echo updating "$MYDIR"/"$STDERR_NAME" cp "$BUILD_DIR"/"$STDERR_NAME" "$MYDIR"/"$STDERR_NAME" + if [[ ! -s "$MYDIR"/"$STDERR_NAME" ]]; then + echo removing "$MYDIR"/"$STDERR_NAME" + rm "$MYDIR"/"$STDERR_NAME" + fi fi if [[ -f "$BUILD_DIR"/"$FIXED_NAME" ]] && \ ! (cmp -s -- "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME"); then echo updating "$MYDIR"/"$FIXED_NAME" cp "$BUILD_DIR"/"$FIXED_NAME" "$MYDIR"/"$FIXED_NAME" + if [[ ! -s "$MYDIR"/"$FIXED_NAME" ]]; then + echo removing "$MYDIR"/"$FIXED_NAME" + rm "$MYDIR"/"$FIXED_NAME" + fi fi done From 6021c231599eabcb07b3a8207bddbb3796c93eee Mon Sep 17 00:00:00 2001 From: Andre Bogus Date: Sun, 11 Oct 2020 13:27:20 +0200 Subject: [PATCH 63/97] New lint: result-unit-err --- CHANGELOG.md | 1 + clippy_lints/src/functions.rs | 106 ++++++++++++++++++++++++++---- clippy_lints/src/lib.rs | 3 + src/lintlist/mod.rs | 7 ++ tests/ui/doc_errors.rs | 1 + tests/ui/doc_errors.stderr | 14 ++-- tests/ui/double_must_use.rs | 1 + tests/ui/double_must_use.stderr | 6 +- tests/ui/result_unit_error.rs | 38 +++++++++++ tests/ui/result_unit_error.stderr | 35 ++++++++++ 10 files changed, 189 insertions(+), 23 deletions(-) create mode 100644 tests/ui/result_unit_error.rs create mode 100644 tests/ui/result_unit_error.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb6cf75d96fe..f21768c449880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1918,6 +1918,7 @@ Released 2018-09-13 [`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs [`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option [`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 50b39cf4ea7c0..212a310063707 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -1,8 +1,9 @@ use crate::utils::{ - attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats, match_def_path, - must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help, span_lint_and_then, - trait_ref_of_method, type_is_unsafe_function, + attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats, + last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, + span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function, }; +use if_chain::if_chain; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; +use rustc_typeck::hir_ty_to_ty; declare_clippy_lint! { /// **What it does:** Checks for functions with too many parameters. @@ -169,6 +171,52 @@ declare_clippy_lint! { "function or method that could take a `#[must_use]` attribute" } +declare_clippy_lint! { + /// **What it does:** Checks for public functions that return a `Result` + /// with an `Err` type of `()`. It suggests using a custom type that + /// implements [`std::error::Error`]. + /// + /// **Why is this bad?** Unit does not implement `Error` and carries no + /// further information about what went wrong. + /// + /// **Known problems:** Of course, this lint assumes that `Result` is used + /// for a fallible operation (which is after all the intended use). However + /// code may opt to (mis)use it as a basic two-variant-enum. In that case, + /// the suggestion is misguided, and the code should use a custom enum + /// instead. + /// + /// **Examples:** + /// ```rust + /// pub fn read_u8() -> Result { Err(()) } + /// ``` + /// should become + /// ```rust,should_panic + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct EndOfStream; + /// + /// impl fmt::Display for EndOfStream { + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } + /// } + /// + /// impl std::error::Error for EndOfStream { } + /// + /// pub fn read_u8() -> Result { Err(EndOfStream) } + ///# fn main() { + ///# read_u8().unwrap(); + ///# } + /// ``` + /// + /// Note that there are crates that simplify creating the error type, e.g. + /// [`thiserror`](https://docs.rs/thiserror). + pub RESULT_UNIT_ERR, + style, + "public function returning `Result` with an `Err` type of `()`" +} + #[derive(Copy, Clone)] pub struct Functions { threshold: u64, @@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [ MUST_USE_UNIT, DOUBLE_MUST_USE, MUST_USE_CANDIDATE, + RESULT_UNIT_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attr = must_use_attr(&item.attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); return; } - if cx.access_levels.is_exported(item.hir_id) - && !is_proc_macro(cx.sess(), &item.attrs) - && attr_by_name(&item.attrs, "no_mangle").is_none() - { + if is_public && !is_proc_macro(cx.sess(), &item.attrs) && attr_by_name(&item.attrs, "no_mangle").is_none() { check_must_use_candidate( cx, &sig.decl, @@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public && trait_ref_of_method(cx, item.hir_id).is_none() { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); - } else if cx.access_levels.is_exported(item.hir_id) + } else if is_public && !is_proc_macro(cx.sess(), &item.attrs) && trait_ref_of_method(cx, item.hir_id).is_none() { @@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions { if sig.header.abi == Abi::Rust { self.check_arg_number(cx, &sig.decl, item.span.with_hi(sig.decl.output.span().hi())); } + let is_public = cx.access_levels.is_exported(item.hir_id); + let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); + if is_public { + check_result_unit_err(cx, &sig.decl, item.span, fn_header_span); + } let attr = must_use_attr(&item.attrs); if let Some(attr) = attr { - let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr); } if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir().body(eid); Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id); - if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) - { + if attr.is_none() && is_public && !is_proc_macro(cx.sess(), &item.attrs) { check_must_use_candidate( cx, &sig.decl, @@ -411,6 +468,29 @@ impl<'tcx> Functions { } } +fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) { + if_chain! { + if !in_external_macro(cx.sess(), item_span); + if let hir::FnRetTy::Return(ref ty) = decl.output; + if let hir::TyKind::Path(ref qpath) = ty.kind; + if is_type_diagnostic_item(cx, hir_ty_to_ty(cx.tcx, ty), sym!(result_type)); + if let Some(ref args) = last_path_segment(qpath).args; + if let [_, hir::GenericArg::Type(ref err_ty)] = args.args; + if let hir::TyKind::Tup(t) = err_ty.kind; + if t.is_empty(); + then { + span_lint_and_help( + cx, + RESULT_UNIT_ERR, + fn_header_span, + "This returns a `Result<_, ()>", + None, + "Use a custom Error type instead", + ); + } + } +} + fn check_needless_must_use( cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 097eca0af578c..26a727687b1d1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -582,6 +582,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &functions::MUST_USE_CANDIDATE, &functions::MUST_USE_UNIT, &functions::NOT_UNSAFE_PTR_ARG_DEREF, + &functions::RESULT_UNIT_ERR, &functions::TOO_MANY_ARGUMENTS, &functions::TOO_MANY_LINES, &future_not_send::FUTURE_NOT_SEND, @@ -1327,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), LintId::of(&functions::NOT_UNSAFE_PTR_ARG_DEREF), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&functions::TOO_MANY_ARGUMENTS), LintId::of(&get_last_with_len::GET_LAST_WITH_LEN), LintId::of(&identity_op::IDENTITY_OP), @@ -1558,6 +1560,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&formatting::SUSPICIOUS_UNARY_OP_FORMATTING), LintId::of(&functions::DOUBLE_MUST_USE), LintId::of(&functions::MUST_USE_UNIT), + LintId::of(&functions::RESULT_UNIT_ERR), LintId::of(&if_let_some_result::IF_LET_SOME_RESULT), LintId::of(&inherent_to_string::INHERENT_TO_STRING), LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY), diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 96c9d12d75fbb..d0fc8f0c8a907 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -2005,6 +2005,13 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "map_unit_fn", }, + Lint { + name: "result_unit_err", + group: "style", + desc: "public function returning `Result` with an `Err` type of `()`", + deprecation: None, + module: "functions", + }, Lint { name: "reversed_empty_ranges", group: "correctness", diff --git a/tests/ui/doc_errors.rs b/tests/ui/doc_errors.rs index 445fc8d31d77a..f47b81a450ea9 100644 --- a/tests/ui/doc_errors.rs +++ b/tests/ui/doc_errors.rs @@ -1,5 +1,6 @@ // edition:2018 #![warn(clippy::missing_errors_doc)] +#![allow(clippy::result_unit_err)] use std::io; diff --git a/tests/ui/doc_errors.stderr b/tests/ui/doc_errors.stderr index f44d6693d303b..c7b616e289708 100644 --- a/tests/ui/doc_errors.stderr +++ b/tests/ui/doc_errors.stderr @@ -1,5 +1,5 @@ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:6:1 + --> $DIR/doc_errors.rs:7:1 | LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-errors-doc` implied by `-D warnings` error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:10:1 + --> $DIR/doc_errors.rs:11:1 | LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -17,7 +17,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:15:1 + --> $DIR/doc_errors.rs:16:1 | LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -25,7 +25,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:20:1 + --> $DIR/doc_errors.rs:21:1 | LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> { LL | | unimplemented!(); @@ -33,7 +33,7 @@ LL | | } | |_^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:50:5 + --> $DIR/doc_errors.rs:51:5 | LL | / pub fn pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -41,7 +41,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:55:5 + --> $DIR/doc_errors.rs:56:5 | LL | / pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> { LL | | unimplemented!(); @@ -49,7 +49,7 @@ LL | | } | |_____^ error: docs for function returning `Result` missing `# Errors` section - --> $DIR/doc_errors.rs:84:5 + --> $DIR/doc_errors.rs:85:5 | LL | fn trait_method_missing_errors_header() -> Result<(), ()>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index a48e675e4ea23..05e087b08bc14 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -1,4 +1,5 @@ #![warn(clippy::double_must_use)] +#![allow(clippy::result_unit_err)] #[must_use] pub fn must_use_result() -> Result<(), ()> { diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index bc37785294fca..8290ece1cad18 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,5 +1,5 @@ error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:4:1 + --> $DIR/double_must_use.rs:5:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | pub fn must_use_result() -> Result<(), ()> { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:9:1 + --> $DIR/double_must_use.rs:10:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { = help: either add some descriptive text or remove the attribute error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]` - --> $DIR/double_must_use.rs:14:1 + --> $DIR/double_must_use.rs:15:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/result_unit_error.rs b/tests/ui/result_unit_error.rs new file mode 100644 index 0000000000000..a66f581b2159f --- /dev/null +++ b/tests/ui/result_unit_error.rs @@ -0,0 +1,38 @@ +#[warn(clippy::result_unit_err)] +#[allow(unused)] + +pub fn returns_unit_error() -> Result { + Err(()) +} + +fn private_unit_errors() -> Result { + Err(()) +} + +pub trait HasUnitError { + fn get_that_error(&self) -> Result; + + fn get_this_one_too(&self) -> Result { + Err(()) + } +} + +impl HasUnitError for () { + fn get_that_error(&self) -> Result { + Ok(true) + } +} + +trait PrivateUnitError { + fn no_problem(&self) -> Result; +} + +pub struct UnitErrorHolder; + +impl UnitErrorHolder { + pub fn unit_error(&self) -> Result { + Ok(0) + } +} + +fn main() {} diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr new file mode 100644 index 0000000000000..986d9718acdbf --- /dev/null +++ b/tests/ui/result_unit_error.stderr @@ -0,0 +1,35 @@ +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:4:1 + | +LL | pub fn returns_unit_error() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::result-unit-err` implied by `-D warnings` + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:13:5 + | +LL | fn get_that_error(&self) -> Result; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:15:5 + | +LL | fn get_this_one_too(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: This returns a `Result<_, ()> + --> $DIR/result_unit_error.rs:33:5 + | +LL | pub fn unit_error(&self) -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Use a custom Error type instead + +error: aborting due to 4 previous errors + From 74ae116131696e4385d5b8e5da34deaad0d25ec9 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sun, 11 Oct 2020 22:15:56 +0200 Subject: [PATCH 64/97] Use lowercase in error messages --- clippy_lints/src/functions.rs | 14 +++++++------- tests/ui/result_unit_error.stderr | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/functions.rs b/clippy_lints/src/functions.rs index 212a310063707..fd45a6da61ca6 100644 --- a/clippy_lints/src/functions.rs +++ b/clippy_lints/src/functions.rs @@ -194,19 +194,19 @@ declare_clippy_lint! { /// use std::fmt; /// /// #[derive(Debug)] - /// struct EndOfStream; + /// pub struct EndOfStream; /// /// impl fmt::Display for EndOfStream { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "End of Stream") - /// } + /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// write!(f, "End of Stream") + /// } /// } /// /// impl std::error::Error for EndOfStream { } /// /// pub fn read_u8() -> Result { Err(EndOfStream) } ///# fn main() { - ///# read_u8().unwrap(); + ///# read_u8().unwrap(); ///# } /// ``` /// @@ -483,9 +483,9 @@ fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span cx, RESULT_UNIT_ERR, fn_header_span, - "This returns a `Result<_, ()>", + "this returns a `Result<_, ()>", None, - "Use a custom Error type instead", + "use a custom Error type instead", ); } } diff --git a/tests/ui/result_unit_error.stderr b/tests/ui/result_unit_error.stderr index 986d9718acdbf..b8230032491b6 100644 --- a/tests/ui/result_unit_error.stderr +++ b/tests/ui/result_unit_error.stderr @@ -1,35 +1,35 @@ -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:4:1 | LL | pub fn returns_unit_error() -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::result-unit-err` implied by `-D warnings` - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:13:5 | LL | fn get_that_error(&self) -> Result; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:15:5 | LL | fn get_this_one_too(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead -error: This returns a `Result<_, ()> +error: this returns a `Result<_, ()> --> $DIR/result_unit_error.rs:33:5 | LL | pub fn unit_error(&self) -> Result { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: Use a custom Error type instead + = help: use a custom Error type instead error: aborting due to 4 previous errors From c5774f94efd60b60fc7120ba3d6de7f79b05681b Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 16:50:34 +0200 Subject: [PATCH 65/97] lintlist.rs: Replace lazy_static with once_cell Follow-up to https://github.com/rust-lang/rust-clippy/pull/6120 --- clippy_dev/src/update_lints.rs | 2 +- src/driver.rs | 1 + src/lintlist/mod.rs | 13 +++++++------ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index a9a7092994269..556b67e0b3742 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -29,7 +29,7 @@ pub fn run(update_mode: UpdateMode) { false, update_mode == UpdateMode::Change, || { - format!("pub static ref ALL_LINTS: Vec = vec!{:#?};", sorted_usable_lints) + format!("vec!{:#?}", sorted_usable_lints) .lines() .map(ToString::to_string) .collect::>() diff --git a/src/driver.rs b/src/driver.rs index 377f6d2244635..c9b07855af10d 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,4 +1,5 @@ #![feature(rustc_private)] +#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index d0fc8f0c8a907..624223ff70620 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1,15 +1,16 @@ -//! This file is managed by `cargo dev update_lints`. Do not edit. +//! This file is managed by `cargo dev update_lints`. Do not edit or format this file. -use lazy_static::lazy_static; +use std::lazy::SyncLazy; pub mod lint; pub use lint::Level; pub use lint::Lint; pub use lint::LINT_LEVELS; -lazy_static! { +#[rustfmt::skip] +pub static ALL_LINTS: SyncLazy> = SyncLazy::new(|| { // begin lint list, do not remove this comment, it’s used in `update_lints` -pub static ref ALL_LINTS: Vec = vec![ +vec![ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -2831,6 +2832,6 @@ pub static ref ALL_LINTS: Vec = vec![ deprecation: None, module: "methods", }, -]; +] // end lint list, do not remove this comment, it’s used in `update_lints` -} +}); From 098e4f119595cc199bf09ccf150aeefa6b2c49ac Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 18:29:29 +0200 Subject: [PATCH 66/97] driver.rs: Replace lazy_static with once_cell --- Cargo.toml | 2 -- src/driver.rs | 16 +++++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a3099b8ab0a..13db35f4b0ef0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,13 +34,11 @@ clippy_lints = { version = "0.0.212", path = "clippy_lints" } semver = "0.10" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } -lazy_static = "1.0" [dev-dependencies] cargo_metadata = "0.11.1" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" -lazy_static = "1.0" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } serde = { version = "1.0", features = ["derive"] } derive-new = "0.5" diff --git a/src/driver.rs b/src/driver.rs index c9b07855af10d..e32ba116939b2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -18,13 +18,13 @@ use rustc_interface::interface; use rustc_middle::ty::TyCtxt; use rustc_tools_util::VersionInfo; -use lazy_static::lazy_static; use std::borrow::Cow; use std::env; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; +use std::lazy::SyncLazy; mod lintlist; @@ -231,13 +231,11 @@ You can use tool lints to allow or deny lints from your code, eg.: const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new"; -lazy_static! { - static ref ICE_HOOK: Box) + Sync + Send + 'static> = { - let hook = panic::take_hook(); - panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); - hook - }; -} +static ICE_HOOK: SyncLazy) + Sync + Send + 'static>> = SyncLazy::new(|| { + let hook = panic::take_hook(); + panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL))); + hook +}); fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace @@ -296,7 +294,7 @@ fn toolchain_path(home: Option, toolchain: Option) -> Option = env::args().collect(); From 7b3493c0e95e8cf9656d2cefffc621cb3e5eb726 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Mon, 12 Oct 2020 18:34:06 +0200 Subject: [PATCH 67/97] fmt --- src/driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver.rs b/src/driver.rs index e32ba116939b2..e5d740cecd31c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -20,11 +20,11 @@ use rustc_tools_util::VersionInfo; use std::borrow::Cow; use std::env; +use std::lazy::SyncLazy; use std::ops::Deref; use std::panic; use std::path::{Path, PathBuf}; use std::process::{exit, Command}; -use std::lazy::SyncLazy; mod lintlist; From 32fdb8fb0c15ddc202eed70b82babca8d529e39b Mon Sep 17 00:00:00 2001 From: ThibsG Date: Thu, 8 Oct 2020 23:02:16 +0200 Subject: [PATCH 68/97] Lint on identical variable used as args in `assert_eq!` macro call --- clippy_lints/src/eq_op.rs | 37 ++++++++++++++++++++++++- clippy_lints/src/lib.rs | 2 ++ tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/double_parens.rs | 2 +- tests/ui/eq_op_early.rs | 15 ++++++++++ tests/ui/eq_op_early.stderr | 16 +++++++++++ tests/ui/used_underscore_binding.rs | 2 +- 7 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 tests/ui/eq_op_early.rs create mode 100644 tests/ui/eq_op_early.stderr diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index e16ec783fab79..7126c98a0b438 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,9 +1,13 @@ +use crate::utils::ast_utils::eq_expr; use crate::utils::{ eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, }; +use if_chain::if_chain; +use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -23,6 +27,12 @@ declare_clippy_lint! { /// # let x = 1; /// if x + 1 == x + 1 {} /// ``` + /// or + /// ```rust + /// # let a = 3; + /// # let b = 4; + /// assert_eq!(a, a); + /// ``` pub EQ_OP, correctness, "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)" @@ -52,6 +62,31 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); +impl EarlyLintPass for EqOp { + fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + if_chain! { + if mac.path == sym!(assert_eq); + let tokens = mac.args.inner_tokens(); + let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + if let Ok(left) = parser.parse_expr(); + if parser.eat(&token::Comma); + if let Ok(right) = parser.parse_expr(); + let left_expr = left.into_inner(); + let right_expr = right.into_inner(); + if eq_expr(&left_expr, &right_expr); + + then { + span_lint( + cx, + EQ_OP, + left_expr.span.to(right_expr.span), + "identical args used in this `assert_eq!` macro call", + ); + } + } + } +} + impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index fc4afde9d9e6d..dd99b6b9040c9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,6 +348,7 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); + store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -910,6 +911,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); + store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 3df8be6c23230..e369f62f8bfea 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] +#![allow(clippy::eq_op)] extern crate proc_macro; diff --git a/tests/ui/double_parens.rs b/tests/ui/double_parens.rs index 9c7590c7dd632..ff1dc76ab63b1 100644 --- a/tests/ui/double_parens.rs +++ b/tests/ui/double_parens.rs @@ -1,5 +1,5 @@ #![warn(clippy::double_parens)] -#![allow(dead_code)] +#![allow(dead_code, clippy::eq_op)] #![feature(custom_inner_attributes)] #![rustfmt::skip] diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs new file mode 100644 index 0000000000000..cf5660ea98da0 --- /dev/null +++ b/tests/ui/eq_op_early.rs @@ -0,0 +1,15 @@ +#![warn(clippy::eq_op)] + +fn main() { + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` (see #3574) + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr new file mode 100644 index 0000000000000..9206e9026e95b --- /dev/null +++ b/tests/ui/eq_op_early.stderr @@ -0,0 +1,16 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:8:16 + | +LL | assert_eq!(a, a); + | ^^^^ + | + = note: `-D clippy::eq-op` implied by `-D warnings` + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_early.rs:9:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/used_underscore_binding.rs b/tests/ui/used_underscore_binding.rs index 8e0243c49aaa0..d8bda7e8f48a7 100644 --- a/tests/ui/used_underscore_binding.rs +++ b/tests/ui/used_underscore_binding.rs @@ -3,7 +3,7 @@ #![feature(rustc_private)] #![warn(clippy::all)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::blacklisted_name, clippy::eq_op)] #![warn(clippy::used_underscore_binding)] #[macro_use] From 8c28ba39b573c0d9be2ce7aa3cfc60757f3c81e6 Mon Sep 17 00:00:00 2001 From: Chris Ayoup Date: Mon, 12 Oct 2020 21:51:05 -0400 Subject: [PATCH 69/97] suggest a compatible shell for running setup-toolchain.sh setup-toolchain.sh uses "[[" which is a bash builtin, but the guide suggests running it with sh. On Ubuntu, /bin/sh points to dash and running the script as described fails. --- doc/basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/basics.md b/doc/basics.md index 38959e2331b40..f25edb793e26d 100644 --- a/doc/basics.md +++ b/doc/basics.md @@ -46,7 +46,7 @@ this toolchain, you can just use the `setup-toolchain.sh` script or use `rustup-toolchain-install-master`: ```bash -sh setup-toolchain.sh +bash setup-toolchain.sh # OR cargo install rustup-toolchain-install-master # For better IDE integration also add `-c rustfmt -c rust-src` (optional) From a3e0446afe0ebd7a420f65cd6aec1c56687f0ef5 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 09:31:53 +0200 Subject: [PATCH 70/97] Extend to the `assert` macro family --- clippy_lints/src/eq_op.rs | 17 ++++++++++++++--- tests/ui/eq_op_early.rs | 25 +++++++++++++++++++++++- tests/ui/eq_op_early.stderr | 38 ++++++++++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 7126c98a0b438..a95d71042ee27 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -7,6 +7,7 @@ use rustc_ast::{ast, token}; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_parse::parser; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -64,10 +65,20 @@ declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl EarlyLintPass for EqOp { fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { + let macro_list = [ + sym!(assert_eq), + sym!(assert_ne), + sym!(debug_assert_eq), + sym!(debug_assert_ne), + ]; if_chain! { - if mac.path == sym!(assert_eq); + if !in_external_macro(cx.sess, mac.span()); + if mac.path.segments.len() == 1; + let macro_name = mac.path.segments[0].ident.name; + if macro_list.contains(¯o_name); let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new(&cx.sess.parse_sess, tokens, false, None); + let mut parser = parser::Parser::new( + &cx.sess.parse_sess, tokens, false, None); if let Ok(left) = parser.parse_expr(); if parser.eat(&token::Comma); if let Ok(right) = parser.parse_expr(); @@ -80,7 +91,7 @@ impl EarlyLintPass for EqOp { cx, EQ_OP, left_expr.span.to(right_expr.span), - "identical args used in this `assert_eq!` macro call", + &format!("identical args used in this `{}!` macro call", macro_name), ); } } diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs index cf5660ea98da0..25e1c6ac6b75b 100644 --- a/tests/ui/eq_op_early.rs +++ b/tests/ui/eq_op_early.rs @@ -7,9 +7,32 @@ fn main() { // lint identical args in `assert_eq!` (see #3574) assert_eq!(a, a); assert_eq!(a + 1, a + 1); - // ok assert_eq!(a, b); assert_eq!(a, a + 1); assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); } diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr index 9206e9026e95b..1df094fae1802 100644 --- a/tests/ui/eq_op_early.stderr +++ b/tests/ui/eq_op_early.stderr @@ -12,5 +12,41 @@ error: identical args used in this `assert_eq!` macro call LL | assert_eq!(a + 1, a + 1); | ^^^^^^^^^^^^ -error: aborting due to 2 previous errors +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:16:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_early.rs:17:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:24:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_early.rs:25:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:32:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_early.rs:33:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 8 previous errors From e2124086b8107a59129e163aa120dec50add0f77 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 11:17:51 +0200 Subject: [PATCH 71/97] Fix FP in `same_functions_in_if_condition` lint about condition as macro --- clippy_lints/src/copies.rs | 6 +++++- tests/ui/same_functions_in_if_condition.rs | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 10a64769585e5..6c969c3ead0f0 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,4 @@ -use crate::utils::{eq_expr_value, SpanlessEq, SpanlessHash}; +use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash}; use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind}; @@ -220,6 +220,10 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { }; let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { + // Do not lint if any expr originates from a macro + if in_macro(lhs.span) || in_macro(rhs.span) { + return false; + } // Do not spawn warning if `IFS_SAME_COND` already produced it. if eq_expr_value(cx, lhs, rhs) { return false; diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 686867cf5c6f6..7f28f02579045 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -77,4 +77,14 @@ fn ifs_same_cond_fn() { } } -fn main() {} +fn main() { + // macro as condition (see #6168) + let os = if cfg!(target_os = "macos") { + "macos" + } else if cfg!(target_os = "windows") { + "windows" + } else { + "linux" + }; + println!("{}", os); +} From 121a047645270d5e9ac965d57c324301ea1f21c0 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Tue, 13 Oct 2020 23:46:23 +0200 Subject: [PATCH 72/97] Move linting of `assert` macros from early to late pass --- clippy_lints/src/eq_op.rs | 73 +++++++++++++--------------- clippy_lints/src/lib.rs | 2 - tests/ui/eq_op.rs | 53 +++++++++++++++++++++ tests/ui/eq_op.stderr | 95 ++++++++++++++++++++++++++++++++++++- tests/ui/eq_op_early.rs | 38 --------------- tests/ui/eq_op_early.stderr | 52 -------------------- 6 files changed, 179 insertions(+), 134 deletions(-) delete mode 100644 tests/ui/eq_op_early.rs delete mode 100644 tests/ui/eq_op_early.stderr diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index a95d71042ee27..9653e62cad078 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,14 +1,11 @@ -use crate::utils::ast_utils::eq_expr; use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, + eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + span_lint_and_then, }; use if_chain::if_chain; -use rustc_ast::{ast, token}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_parse::parser; +use rustc_hir::{BinOp, BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -63,44 +60,38 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -impl EarlyLintPass for EqOp { - fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - let macro_list = [ - sym!(assert_eq), - sym!(assert_ne), - sym!(debug_assert_eq), - sym!(debug_assert_ne), - ]; - if_chain! { - if !in_external_macro(cx.sess, mac.span()); - if mac.path.segments.len() == 1; - let macro_name = mac.path.segments[0].ident.name; - if macro_list.contains(¯o_name); - let tokens = mac.args.inner_tokens(); - let mut parser = parser::Parser::new( - &cx.sess.parse_sess, tokens, false, None); - if let Ok(left) = parser.parse_expr(); - if parser.eat(&token::Comma); - if let Ok(right) = parser.parse_expr(); - let left_expr = left.into_inner(); - let right_expr = right.into_inner(); - if eq_expr(&left_expr, &right_expr); - - then { - span_lint( - cx, - EQ_OP, - left_expr.span.to(right_expr.span), - &format!("identical args used in this `{}!` macro call", macro_name), - ); - } - } - } -} +const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Block(ref block, _) = e.kind { + for stmt in block.stmts { + for amn in &ASSERT_MACRO_NAMES { + if_chain! { + if is_expn_of(stmt.span, amn).is_some(); + if let StmtKind::Semi(ref matchexpr) = stmt.kind; + if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; + if let Some(ref matchheader) = matchblock.expr; + if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; + if let ExprKind::Tup(ref conditions) = headerexpr.kind; + if conditions.len() == 2; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if eq_expr_value(cx, lhs, rhs); + + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", amn), + ); + } + } + } + } + } if let ExprKind::Binary(op, ref left, ref right) = e.kind { if e.span.from_expansion() { return; diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index dd99b6b9040c9..fc4afde9d9e6d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -348,7 +348,6 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) { store.register_pre_expansion_pass(|| box write::Write::default()); store.register_pre_expansion_pass(|| box attrs::EarlyAttributes); store.register_pre_expansion_pass(|| box dbg_macro::DbgMacro); - store.register_pre_expansion_pass(|| box eq_op::EqOp); } #[doc(hidden)] @@ -911,7 +910,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let vec_box_size_threshold = conf.vec_box_size_threshold; store.register_late_pass(move || box types::Types::new(vec_box_size_threshold)); store.register_late_pass(|| box booleans::NonminimalBool); - store.register_early_pass(|| box eq_op::EqOp); store.register_late_pass(|| box eq_op::EqOp); store.register_late_pass(|| box enum_clike::UnportableVariant); store.register_late_pass(|| box float_literal::FloatLiteral); diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 272b0900a31c6..3ab4dfc439bca 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,6 +60,8 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; + + check_assert_identical_args(); } #[rustfmt::skip] @@ -85,3 +87,54 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } + +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn check_assert_identical_args() { + // lint also in macro definition + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); +} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 5b80e6078eed7..21a63aec7a171 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,5 +162,98 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: aborting due to 27 previous errors +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:94:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `#[deny(clippy::eq_op)]` on by default + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:95:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:110:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op.rs:111:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:118:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op.rs:119:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:96:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:97:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:126:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op.rs:127:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:134:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op.rs:135:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 39 previous errors diff --git a/tests/ui/eq_op_early.rs b/tests/ui/eq_op_early.rs deleted file mode 100644 index 25e1c6ac6b75b..0000000000000 --- a/tests/ui/eq_op_early.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![warn(clippy::eq_op)] - -fn main() { - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` (see #3574) - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); -} diff --git a/tests/ui/eq_op_early.stderr b/tests/ui/eq_op_early.stderr deleted file mode 100644 index 1df094fae1802..0000000000000 --- a/tests/ui/eq_op_early.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:8:16 - | -LL | assert_eq!(a, a); - | ^^^^ - | - = note: `-D clippy::eq-op` implied by `-D warnings` - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op_early.rs:9:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:16:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op_early.rs:17:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:24:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op_early.rs:25:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:32:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op_early.rs:33:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 8 previous errors - From e82264860dd275fe95c335929ee9a231c3a61236 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Wed, 14 Oct 2020 23:15:01 +1100 Subject: [PATCH 73/97] Add a known problem for transmute_ptr_to_ref lint --- clippy_lints/src/transmute.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index c75adb62f2575..47c650ac27d4c 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -98,7 +98,11 @@ declare_clippy_lint! { /// /// **Why is this bad?** This can always be rewritten with `&` and `*`. /// - /// **Known problems:** None. + /// **Known problems:** + /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0, + /// while dereferencing raw pointer is not stable yet. + /// If you need to do this in those places, + /// you would have to use `transmute` instead. /// /// **Example:** /// ```rust,ignore From 8ba18aeb6963d70767a0880ecb7929864fe14ef9 Mon Sep 17 00:00:00 2001 From: Lotte Steenbrink Date: Wed, 14 Oct 2020 11:58:22 +0200 Subject: [PATCH 74/97] README: sort en/disabling section, fix typos, add note --- README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 62a8be0abf22c..e1b3c84d6917d 100644 --- a/README.md +++ b/README.md @@ -169,12 +169,33 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints: Note: `deny` produces errors instead of warnings. -If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra -flags to Clippy during the run: `cargo clippy -- -A clippy::lint_name` will run Clippy with `lint_name` disabled and -`cargo clippy -- -W clippy::lint_name` will run it with that enabled. This also works with lint groups. For example you -can run Clippy with warnings for all lints enabled: `cargo clippy -- -W clippy::pedantic` +If you do not want to include your lint levels in your code, you can globally enable/disable lints +by passing extra flags to Clippy during the run: + +To disable `lint_name`, run + +```terminal +cargo clippy -- -A clippy::lint_name +``` + +And to enable `lint_name`, run + +```terminal +cargo clippy -- -W clippy::lint_name +``` + +This also works with lint groups. For example you +can run Clippy with warnings for all lints enabled: +```terminal +cargo clippy -- -W clippy::pedantic +``` + If you care only about a single lint, you can allow all others and then explicitly reenable -the lint(s) you are interested in: `cargo clippy -- -Aclippy::all -Wclippy::useless_format -Wclippy::...` +the lint(s) you are interested in: +```terminal +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... +``` +Note that if you've run clippy before, this may only take effect after you've modified a file or ran `cargo clean`. ## Contributing From ab0fc477b8b83cb14c584aca281b16fb5cce4c1a Mon Sep 17 00:00:00 2001 From: hosseind88 Date: Wed, 14 Oct 2020 18:19:26 +0330 Subject: [PATCH 75/97] fix stderr file of clippy/custom_ice_message test --- tests/ui/custom_ice_message.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/custom_ice_message.stderr b/tests/ui/custom_ice_message.stderr index 87cdb7a8b99dd..a1b8e2ee162cf 100644 --- a/tests/ui/custom_ice_message.stderr +++ b/tests/ui/custom_ice_message.stderr @@ -10,4 +10,4 @@ note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy note: Clippy version: foo query stack during panic: -we're just showing a limited slice of the query stack +end of query stack From 71c29b5be8526562c3de8d3b7dc94611647ee120 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Wed, 14 Oct 2020 21:29:53 +0200 Subject: [PATCH 76/97] Add iterator test case for `eq_op` lint --- tests/ui/eq_op.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 3ab4dfc439bca..20613ac6afe96 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -137,4 +137,8 @@ fn check_assert_identical_args() { debug_assert_ne!(a, b); debug_assert_ne!(a, a + 1); debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); } From 07b2da884cda8103af50beb327723dec8204fc61 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 6 Oct 2020 11:49:08 +0200 Subject: [PATCH 77/97] add lint less_concise_than_option_unwrap_or --- CHANGELOG.md | 1 + clippy_lints/src/less_concise_than.rs | 107 ++++++++++++++++++++++++ clippy_lints/src/lib.rs | 4 + clippy_lints/src/option_if_let_else.rs | 53 +----------- clippy_lints/src/utils/eager_or_lazy.rs | 2 +- clippy_lints/src/utils/usage.rs | 50 ++++++++++- src/lintlist/mod.rs | 7 ++ tests/ui/less_concise_than.fixed | 43 ++++++++++ tests/ui/less_concise_than.rs | 55 ++++++++++++ tests/ui/less_concise_than.stderr | 52 ++++++++++++ tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 46 +++++----- 12 files changed, 345 insertions(+), 76 deletions(-) create mode 100644 clippy_lints/src/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.fixed create mode 100644 tests/ui/less_concise_than.rs create mode 100644 tests/ui/less_concise_than.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f21768c449880..93ce6bb85d8a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,6 +1781,7 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero +[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/less_concise_than.rs new file mode 100644 index 0000000000000..097aff4b1786d --- /dev/null +++ b/clippy_lints/src/less_concise_than.rs @@ -0,0 +1,107 @@ +use crate::utils; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// + /// **Why is this bad?** + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// match int_optional { + /// Some(v) => v, + /// None => 1, + /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// int_optional.unwrap_or(1) + /// ``` + pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, + pedantic, + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" +} + +declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); + +impl LateLintPass<'_> for LessConciseThan { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if utils::in_macro(expr.span) { + return; + } + if lint_option_unwrap_or_case(cx, expr) { + return; + } + } +} + +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + #[allow(clippy::needless_bool)] + fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { + if_chain! { + if arms.len() == 2; + if arms.iter().all(|arm| arm.guard.is_none()); + if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| + if_chain! { + if let PatKind::Path(ref qpath) = arm.pat.kind; + if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); + then { true } + else { false } + } + ); + let some_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; + if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME); + if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind; + if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; + if let def::Res::Local(body_path_hir_id) = body_path.res; + if body_path_hir_id == binding_hir_id; + then { Some(none_arm) } + else { None } + } + } + if_chain! { + if !utils::usage::contains_return_break_continue_macro(expr); + if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(match_expr); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, + "this pattern can be more concisely encoded with `Option::unwrap_or`", + "replace with", + format!( + "{}.{}({}{})", + match_expr_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { false} + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 26a727687b1d1..2e9900815d9ad 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,6 +224,7 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; +mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -609,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, + &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -1126,6 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); + store.register_late_pass(|| box less_concise_than::LessConciseThan); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1210,6 +1213,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), + LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 383a62da821e9..eb7624b25a3c9 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -5,10 +5,8 @@ use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -84,53 +82,6 @@ struct OptionIfLetElseOccurence { wrap_braces: bool, } -struct ReturnBreakContinueMacroVisitor { - seen_return_break_continue: bool, -} - -impl ReturnBreakContinueMacroVisitor { - fn new() -> ReturnBreakContinueMacroVisitor { - ReturnBreakContinueMacroVisitor { - seen_return_break_continue: false, - } - } -} - -impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { - type Map = Map<'tcx>; - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::None - } - - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.seen_return_break_continue { - // No need to look farther if we've already seen one of them - return; - } - match &ex.kind { - ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { - self.seen_return_break_continue = true; - }, - // Something special could be done here to handle while or for loop - // desugaring, as this will detect a break if there's a while loop - // or a for loop inside the expression. - _ => { - if utils::in_macro(ex.span) { - self.seen_return_break_continue = true; - } else { - rustc_hir::intravisit::walk_expr(self, ex); - } - }, - } - } -} - -fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { - let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); - recursive_visitor.visit_expr(expression); - recursive_visitor.seen_return_break_continue -} - /// Extracts the body of a given arm. If the arm contains only an expression, /// then it returns the expression. Otherwise, it returns the entire block fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> { @@ -208,8 +159,8 @@ fn detect_option_if_let_else<'tcx>( if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind; if utils::match_qpath(struct_qpath, &paths::OPTION_SOME); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(arms[0].body); - if !contains_return_break_continue_macro(arms[1].body); + if !utils::usage::contains_return_break_continue_macro(arms[0].body); + if !utils::usage::contains_return_break_continue_macro(arms[1].body); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_arm(&arms[0])?; diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 6938d9971d96d..30e812c284b00 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { /// Identify some potentially computationally expensive patterns. /// This function is named so to stress that its implementation is non-exhaustive. /// It returns FNs and FPs. -fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // Searches an expression for method calls or function calls that aren't ctors struct FunCallFinder<'a, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/utils/usage.rs b/clippy_lints/src/utils/usage.rs index ea1dc3be29ba0..2fd6046ebcf5a 100644 --- a/clippy_lints/src/utils/usage.rs +++ b/clippy_lints/src/utils/usage.rs @@ -1,10 +1,11 @@ +use crate::utils; use crate::utils::match_var; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{Expr, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId, Path}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> { intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } + +struct ReturnBreakContinueMacroVisitor { + seen_return_break_continue: bool, +} + +impl ReturnBreakContinueMacroVisitor { + fn new() -> ReturnBreakContinueMacroVisitor { + ReturnBreakContinueMacroVisitor { + seen_return_break_continue: false, + } + } +} + +impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if self.seen_return_break_continue { + // No need to look farther if we've already seen one of them + return; + } + match &ex.kind { + ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => { + self.seen_return_break_continue = true; + }, + // Something special could be done here to handle while or for loop + // desugaring, as this will detect a break if there's a while loop + // or a for loop inside the expression. + _ => { + if utils::in_macro(ex.span) { + self.seen_return_break_continue = true; + } else { + rustc_hir::intravisit::walk_expr(self, ex); + } + }, + } + } +} + +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { + let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new(); + recursive_visitor.visit_expr(expression); + recursive_visitor.seen_return_break_continue +} diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 624223ff70620..6dc95fcfdb289 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,6 +1075,13 @@ vec![ deprecation: None, module: "len_zero", }, + Lint { + name: "less_concise_than_option_unwrap_or", + group: "pedantic", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", + deprecation: None, + module: "less_concise_than", + }, Lint { name: "let_and_return", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/less_concise_than.fixed new file mode 100644 index 0000000000000..52b69ebba3ec5 --- /dev/null +++ b/tests/ui/less_concise_than.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + Some(1).unwrap_or(42); + + // richer none expr + Some(1).unwrap_or_else(|| 1 + 42); + + // multiline case + Some(1).unwrap_or_else(|| { + let a = 1 + 42; + let b = a + 42; + b + 42 + }); + + // string case + Some("Bob").unwrap_or("Alice"); + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.rs b/tests/ui/less_concise_than.rs new file mode 100644 index 0000000000000..bb2a8f2050a93 --- /dev/null +++ b/tests/ui/less_concise_than.rs @@ -0,0 +1,55 @@ +// run-rustfix +#![warn(clippy::less_concise_than_option_unwrap_or)] +#![allow(dead_code)] + +fn unwrap_or() { + // int case + match Some(1) { + Some(i) => i, + None => 42, + }; + + // richer none expr + match Some(1) { + Some(i) => i, + None => 1 + 42, + }; + + // multiline case + match Some(1) { + Some(i) => i, + None => { + let a = 1 + 42; + let b = a + 42; + b + 42 + }, + }; + + // string case + match Some("Bob") { + Some(i) => i, + None => "Alice", + }; + + // don't lint + match Some(1) { + Some(i) => i + 2, + None => 42, + }; + match Some(1) { + Some(i) => i, + None => return, + }; + for j in 0..4 { + match Some(j) { + Some(i) => i, + None => continue, + }; + match Some(j) { + Some(i) => i, + None => break, + }; + } +} + +fn main() {} diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/less_concise_than.stderr new file mode 100644 index 0000000000000..e3e8a406db104 --- /dev/null +++ b/tests/ui/less_concise_than.stderr @@ -0,0 +1,52 @@ +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:7:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + | + = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:13:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => 1 + 42, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:19:5 + | +LL | / match Some(1) { +LL | | Some(i) => i, +LL | | None => { +LL | | let a = 1 + 42; +... | +LL | | }, +LL | | }; + | |_____^ + | +help: replace with + | +LL | Some(1).unwrap_or_else(|| { +LL | let a = 1 + 42; +LL | let b = a + 42; +LL | b + 42 +LL | }); + | + +error: this pattern can be more concisely encoded with `Option::unwrap_or` + --> $DIR/less_concise_than.rs:29:5 + | +LL | / match Some("Bob") { +LL | | Some(i) => i, +LL | | None => "Alice", +LL | | }; + | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index bd91ae4e9340c..e7441293d4572 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,6 +8,7 @@ #![allow( unused_parens, unused_variables, + clippy::less_concise_than_option_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index 8a831375b412d..7c1ad2949e91b 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,135 +1,135 @@ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:26:5 + --> $DIR/shadow.rs:27:5 | LL | let x = &mut x; | ^^^^^^^^^^^^^^^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:25:13 + --> $DIR/shadow.rs:26:13 | LL | let mut x = 1; | ^ error: `x` is shadowed by itself in `{ x }` - --> $DIR/shadow.rs:27:5 + --> $DIR/shadow.rs:28:5 | LL | let x = { x }; | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:26:9 + --> $DIR/shadow.rs:27:9 | LL | let x = &mut x; | ^ error: `x` is shadowed by itself in `(&*x)` - --> $DIR/shadow.rs:28:5 + --> $DIR/shadow.rs:29:5 | LL | let x = (&*x); | ^^^^^^^^^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:27:9 + --> $DIR/shadow.rs:28:9 | LL | let x = { x }; | ^ error: `x` is shadowed by `{ *x + 1 }` which reuses the original value - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:29:13 + --> $DIR/shadow.rs:30:13 | LL | let x = { *x + 1 }; | ^^^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:28:9 + --> $DIR/shadow.rs:29:9 | LL | let x = (&*x); | ^ error: `x` is shadowed by `id(x)` which reuses the original value - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = id(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:30:13 + --> $DIR/shadow.rs:31:13 | LL | let x = id(x); | ^^^^^ note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = { *x + 1 }; | ^ error: `x` is shadowed by `(1, x)` which reuses the original value - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:31:13 + --> $DIR/shadow.rs:32:13 | LL | let x = (1, x); | ^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = id(x); | ^ error: `x` is shadowed by `first(x)` which reuses the original value - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ | note: initialization happens here - --> $DIR/shadow.rs:32:13 + --> $DIR/shadow.rs:33:13 | LL | let x = first(x); | ^^^^^^^^ note: previous binding is here - --> $DIR/shadow.rs:31:9 + --> $DIR/shadow.rs:32:9 | LL | let x = (1, x); | ^ error: `x` is being shadowed - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: initialization happens here - --> $DIR/shadow.rs:34:13 + --> $DIR/shadow.rs:35:13 | LL | let x = y; | ^ note: previous binding is here - --> $DIR/shadow.rs:32:9 + --> $DIR/shadow.rs:33:9 | LL | let x = first(x); | ^ error: `x` shadows a previous declaration - --> $DIR/shadow.rs:36:5 + --> $DIR/shadow.rs:37:5 | LL | let x; | ^^^^^^ | note: previous binding is here - --> $DIR/shadow.rs:34:9 + --> $DIR/shadow.rs:35:9 | LL | let x = y; | ^ From 9c9327980becadc15a68307705b3a06c28116ae1 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:42:45 +0200 Subject: [PATCH 78/97] manual-unwrap-or / rename files --- clippy_lints/src/{less_concise_than.rs => manual_unwrap_or.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{less_concise_than.rs => manual_unwrap_or.rs} (100%) diff --git a/clippy_lints/src/less_concise_than.rs b/clippy_lints/src/manual_unwrap_or.rs similarity index 100% rename from clippy_lints/src/less_concise_than.rs rename to clippy_lints/src/manual_unwrap_or.rs From 6d4eeeabcda6d6d25738e1e8e2b64580daefc4b9 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Sun, 11 Oct 2020 22:55:05 +0200 Subject: [PATCH 79/97] manual-unwrap-or / pr remarks --- CHANGELOG.md | 2 +- clippy_lints/src/lib.rs | 9 +- clippy_lints/src/manual_unwrap_or.rs | 113 +++++++++--------- src/lintlist/mod.rs | 14 +-- ...cise_than.fixed => manual_unwrap_or.fixed} | 4 +- ...ss_concise_than.rs => manual_unwrap_or.rs} | 7 +- ...se_than.stderr => manual_unwrap_or.stderr} | 29 +++-- tests/ui/shadow.rs | 2 +- 8 files changed, 101 insertions(+), 79 deletions(-) rename tests/ui/{less_concise_than.fixed => manual_unwrap_or.fixed} (93%) rename tests/ui/{less_concise_than.rs => manual_unwrap_or.rs} (90%) rename tests/ui/{less_concise_than.stderr => manual_unwrap_or.stderr} (54%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93ce6bb85d8a3..d82f970b8bf20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1781,7 +1781,6 @@ Released 2018-09-13 [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero -[`less_concise_than_option_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#less_concise_than_option_unwrap_or [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use @@ -1797,6 +1796,7 @@ Released 2018-09-13 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 2e9900815d9ad..d4d2f92a6a695 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -224,7 +224,6 @@ mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; mod len_zero; -mod less_concise_than; mod let_if_seq; mod let_underscore; mod lifetimes; @@ -235,6 +234,7 @@ mod main_recursion; mod manual_async_fn; mod manual_non_exhaustive; mod manual_strip; +mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_identity; @@ -610,7 +610,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &large_stack_arrays::LARGE_STACK_ARRAYS, &len_zero::LEN_WITHOUT_IS_EMPTY, &len_zero::LEN_ZERO, - &less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR, &let_if_seq::USELESS_LET_IF_SEQ, &let_underscore::LET_UNDERSCORE_LOCK, &let_underscore::LET_UNDERSCORE_MUST_USE, @@ -642,6 +641,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &manual_async_fn::MANUAL_ASYNC_FN, &manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, &manual_strip::MANUAL_STRIP, + &manual_unwrap_or::MANUAL_UNWRAP_OR, &map_clone::MAP_CLONE, &map_err_ignore::MAP_ERR_IGNORE, &map_identity::MAP_IDENTITY, @@ -1128,7 +1128,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box repeat_once::RepeatOnce); store.register_late_pass(|| box unwrap_in_result::UnwrapInResult); store.register_late_pass(|| box self_assignment::SelfAssignment); - store.register_late_pass(|| box less_concise_than::LessConciseThan); + store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr); store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs); store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync); store.register_late_pass(|| box manual_strip::ManualStrip); @@ -1213,7 +1213,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&infinite_iter::MAYBE_INFINITE_ITER), LintId::of(&items_after_statements::ITEMS_AFTER_STATEMENTS), LintId::of(&large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(&less_concise_than::LESS_CONCISE_THAN_OPTION_UNWRAP_OR), LintId::of(&literal_representation::LARGE_DIGIT_GROUPS), LintId::of(&literal_representation::UNREADABLE_LITERAL), LintId::of(&loops::EXPLICIT_INTO_ITER_LOOP), @@ -1371,6 +1370,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&manual_async_fn::MANUAL_ASYNC_FN), LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_clone::MAP_CLONE), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), @@ -1666,6 +1666,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&loops::MUT_RANGE_BOUND), LintId::of(&loops::WHILE_LET_LOOP), LintId::of(&manual_strip::MANUAL_STRIP), + LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(&map_identity::MAP_IDENTITY), LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN), diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 097aff4b1786d..9d8fc863424c9 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -2,12 +2,14 @@ use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath}; +use rustc_lint::LintContext; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// Finds patterns that can be encoded more concisely with `Option::unwrap_or`. + /// Finds patterns that reimplement `Option::unwrap_or`. /// /// **Why is this bad?** /// Concise code helps focusing on behavior instead of boilerplate. @@ -16,7 +18,7 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_optional { + /// match int_option { /// Some(v) => v, /// None => 1, /// } @@ -24,39 +26,35 @@ declare_clippy_lint! { /// /// Use instead: /// ```rust - /// int_optional.unwrap_or(1) + /// int_option.unwrap_or(1) /// ``` - pub LESS_CONCISE_THAN_OPTION_UNWRAP_OR, - pedantic, - "finds patterns that can be encoded more concisely with `Option::unwrap_or`" + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" } -declare_lint_pass!(LessConciseThan => [LESS_CONCISE_THAN_OPTION_UNWRAP_OR]); +declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for LessConciseThan { +impl LateLintPass<'_> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if utils::in_macro(expr.span) { - return; - } - if lint_option_unwrap_or_case(cx, expr) { + if in_external_macro(cx.sess(), expr.span) { return; } + lint_option_unwrap_or_case(cx, expr); } } fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - #[allow(clippy::needless_bool)] fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; if arms.iter().all(|arm| arm.guard.is_none()); if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)| - if_chain! { - if let PatKind::Path(ref qpath) = arm.pat.kind; - if utils::match_qpath(qpath, &utils::paths::OPTION_NONE); - then { true } - else { false } - } + if let PatKind::Path(ref qpath) = arm.pat.kind { + utils::match_qpath(qpath, &utils::paths::OPTION_NONE) + } else { + false + } ); let some_arm = &arms[1 - idx]; if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind; @@ -65,43 +63,50 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind; if let def::Res::Local(body_path_hir_id) = body_path.res; if body_path_hir_id == binding_hir_id; - then { Some(none_arm) } - else { None } + if !utils::usage::contains_return_break_continue_macro(none_arm.body); + then { + Some(none_arm) + } + else { + None + } } } + if_chain! { - if !utils::usage::contains_return_break_continue_macro(expr); - if let ExprKind::Match (match_expr, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(match_expr); - if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); - if let Some(none_arm) = applicable_none_arm(match_arms); - if let Some(match_expr_snippet) = utils::snippet_opt(cx, match_expr.span); - if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); - if let Some(indent) = utils::indent_of(cx, expr.span); - then { - let reindented_none_body = - utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; - utils::span_lint_and_sugg( - cx, - LESS_CONCISE_THAN_OPTION_UNWRAP_OR, expr.span, - "this pattern can be more concisely encoded with `Option::unwrap_or`", - "replace with", - format!( - "{}.{}({}{})", - match_expr_snippet, - method, - if eager_eval { ""} else { "|| " }, - reindented_none_body - ), - Applicability::MachineApplicable, - ); - true - } else { false} + if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; + let ty = cx.typeck_results().expr_ty(scrutinee); + if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)); + if let Some(none_arm) = applicable_none_arm(match_arms); + if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); + if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); + if let Some(indent) = utils::indent_of(cx, expr.span); + then { + let reindented_none_body = + utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); + let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let method = if eager_eval { + "unwrap_or" + } else { + "unwrap_or_else" + }; + utils::span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `Option::{}`", &method), + "replace with", + format!( + "{}.{}({}{})", + scrutinee_snippet, + method, + if eager_eval { ""} else { "|| " }, + reindented_none_body + ), + Applicability::MachineApplicable, + ); + true + } else { + false + } } } diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 6dc95fcfdb289..debd3c31d8bff 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1075,13 +1075,6 @@ vec![ deprecation: None, module: "len_zero", }, - Lint { - name: "less_concise_than_option_unwrap_or", - group: "pedantic", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", - deprecation: None, - module: "less_concise_than", - }, Lint { name: "let_and_return", group: "style", @@ -1187,6 +1180,13 @@ vec![ deprecation: None, module: "swap", }, + Lint { + name: "manual_unwrap_or", + group: "complexity", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + deprecation: None, + module: "manual_unwrap_or", + }, Lint { name: "many_single_char_names", group: "style", diff --git a/tests/ui/less_concise_than.fixed b/tests/ui/manual_unwrap_or.fixed similarity index 93% rename from tests/ui/less_concise_than.fixed rename to tests/ui/manual_unwrap_or.fixed index 52b69ebba3ec5..99d30360db1a1 100644 --- a/tests/ui/less_concise_than.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -1,11 +1,13 @@ // run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] #![allow(dead_code)] fn unwrap_or() { // int case Some(1).unwrap_or(42); + // int case reversed + Some(1).unwrap_or(42); + // richer none expr Some(1).unwrap_or_else(|| 1 + 42); diff --git a/tests/ui/less_concise_than.rs b/tests/ui/manual_unwrap_or.rs similarity index 90% rename from tests/ui/less_concise_than.rs rename to tests/ui/manual_unwrap_or.rs index bb2a8f2050a93..5d03d9db16392 100644 --- a/tests/ui/less_concise_than.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -1,5 +1,4 @@ // run-rustfix -#![warn(clippy::less_concise_than_option_unwrap_or)] #![allow(dead_code)] fn unwrap_or() { @@ -9,6 +8,12 @@ fn unwrap_or() { None => 42, }; + // int case reversed + match Some(1) { + None => 42, + Some(i) => i, + }; + // richer none expr match Some(1) { Some(i) => i, diff --git a/tests/ui/less_concise_than.stderr b/tests/ui/manual_unwrap_or.stderr similarity index 54% rename from tests/ui/less_concise_than.stderr rename to tests/ui/manual_unwrap_or.stderr index e3e8a406db104..03da118a0c420 100644 --- a/tests/ui/less_concise_than.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -1,5 +1,5 @@ -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:7:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:6:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -7,10 +7,19 @@ LL | | None => 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` | - = note: `-D clippy::less-concise-than-option-unwrap-or` implied by `-D warnings` + = note: `-D clippy::manual-unwrap-or` implied by `-D warnings` -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:13:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:12:5 + | +LL | / match Some(1) { +LL | | None => 42, +LL | | Some(i) => i, +LL | | }; + | |_____^ help: replace with: `Some(1).unwrap_or(42)` + +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -18,8 +27,8 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:19:5 +error: this pattern reimplements `Option::unwrap_or_else` + --> $DIR/manual_unwrap_or.rs:24:5 | LL | / match Some(1) { LL | | Some(i) => i, @@ -39,8 +48,8 @@ LL | b + 42 LL | }); | -error: this pattern can be more concisely encoded with `Option::unwrap_or` - --> $DIR/less_concise_than.rs:29:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:34:5 | LL | / match Some("Bob") { LL | | Some(i) => i, @@ -48,5 +57,5 @@ LL | | None => "Alice", LL | | }; | |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index e7441293d4572..e366c75335c20 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -8,7 +8,7 @@ #![allow( unused_parens, unused_variables, - clippy::less_concise_than_option_unwrap_or, + clippy::manual_unwrap_or, clippy::missing_docs_in_private_items, clippy::single_match )] From fc846c37fcc720c4a5c2e2075102c1957433e703 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Mon, 12 Oct 2020 00:06:21 +0200 Subject: [PATCH 80/97] manual_unwrap_or / use consts::constant_simple helper --- clippy_lints/src/manual_unwrap_or.rs | 11 +++++++---- tests/ui/manual_unwrap_or.fixed | 2 +- tests/ui/manual_unwrap_or.stderr | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 9d8fc863424c9..ced941fac1a48 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -1,3 +1,4 @@ +use crate::consts::constant_simple; use crate::utils; use if_chain::if_chain; use rustc_errors::Applicability; @@ -18,15 +19,17 @@ declare_clippy_lint! { /// /// **Example:** /// ```rust - /// match int_option { + /// let foo: Option = None; + /// match foo { /// Some(v) => v, /// None => 1, - /// } + /// }; /// ``` /// /// Use instead: /// ```rust - /// int_option.unwrap_or(1) + /// let foo: Option = None; + /// foo.unwrap_or(1); /// ``` pub MANUAL_UNWRAP_OR, complexity, @@ -84,7 +87,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = utils::eager_or_lazy::is_eagerness_candidate(cx, none_arm.body); + let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); let method = if eager_eval { "unwrap_or" } else { diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index 99d30360db1a1..a9cc8678c9d17 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -9,7 +9,7 @@ fn unwrap_or() { Some(1).unwrap_or(42); // richer none expr - Some(1).unwrap_or_else(|| 1 + 42); + Some(1).unwrap_or(1 + 42); // multiline case Some(1).unwrap_or_else(|| { diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 03da118a0c420..8f6835ed78d29 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -18,14 +18,14 @@ LL | | Some(i) => i, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(42)` -error: this pattern reimplements `Option::unwrap_or_else` +error: this pattern reimplements `Option::unwrap_or` --> $DIR/manual_unwrap_or.rs:18:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => 1 + 42, LL | | }; - | |_____^ help: replace with: `Some(1).unwrap_or_else(|| 1 + 42)` + | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` error: this pattern reimplements `Option::unwrap_or_else` --> $DIR/manual_unwrap_or.rs:24:5 From a8fb69f065a427f5d3fc7222b834cad9a2a7a712 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Tue, 13 Oct 2020 10:24:00 +0200 Subject: [PATCH 81/97] manual-unwrap-or / more pr remarks --- clippy_lints/src/manual_unwrap_or.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index ced941fac1a48..719a8b91f6690 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -47,7 +47,7 @@ impl LateLintPass<'_> for ManualUnwrapOr { } } -fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { +fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> { if_chain! { if arms.len() == 2; @@ -69,8 +69,7 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if !utils::usage::contains_return_break_continue_macro(none_arm.body); then { Some(none_arm) - } - else { + } else { None } } @@ -102,14 +101,11 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc "{}.{}({}{})", scrutinee_snippet, method, - if eager_eval { ""} else { "|| " }, + if eager_eval { "" } else { "|| " }, reindented_none_body ), Applicability::MachineApplicable, ); - true - } else { - false } } } From 690a6a6c0eff1a3edeb5f4c2dcbf9994760c3184 Mon Sep 17 00:00:00 2001 From: Tim Nielens Date: Wed, 14 Oct 2020 22:09:28 +0200 Subject: [PATCH 82/97] manual-unwrap-or / remove unwrap_or_else suggestion due to ownership issues --- clippy_lints/src/manual_unwrap_or.rs | 17 +++++---------- src/lintlist/mod.rs | 2 +- tests/ui/manual_unwrap_or.fixed | 31 ++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.rs | 31 ++++++++++++++++++++++++---- tests/ui/manual_unwrap_or.stderr | 18 ++++++++-------- 5 files changed, 69 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index 719a8b91f6690..ddb8cc25077e1 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ``` pub MANUAL_UNWRAP_OR, complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`" + "finds patterns that can be encoded more concisely with `Option::unwrap_or`" } declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); @@ -83,26 +83,19 @@ fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tc if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span); if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span); if let Some(indent) = utils::indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); then { let reindented_none_body = utils::reindent_multiline(none_body_snippet.into(), true, Some(indent)); - let eager_eval = constant_simple(cx, cx.typeck_results(), none_arm.body).is_some(); - let method = if eager_eval { - "unwrap_or" - } else { - "unwrap_or_else" - }; utils::span_lint_and_sugg( cx, MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `Option::{}`", &method), + "this pattern reimplements `Option::unwrap_or`", "replace with", format!( - "{}.{}({}{})", + "{}.unwrap_or({})", scrutinee_snippet, - method, - if eager_eval { "" } else { "|| " }, - reindented_none_body + reindented_none_body, ), Applicability::MachineApplicable, ); diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index debd3c31d8bff..6301d623a2b12 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -1183,7 +1183,7 @@ vec![ Lint { name: "manual_unwrap_or", group: "complexity", - desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or(_else)`", + desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`", deprecation: None, module: "manual_unwrap_or", }, diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed index a9cc8678c9d17..a8736f1e6efe1 100644 --- a/tests/ui/manual_unwrap_or.fixed +++ b/tests/ui/manual_unwrap_or.fixed @@ -12,10 +12,11 @@ fn unwrap_or() { Some(1).unwrap_or(1 + 42); // multiline case - Some(1).unwrap_or_else(|| { - let a = 1 + 42; - let b = a + 42; - b + 42 + #[rustfmt::skip] + Some(1).unwrap_or({ + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 }); // string case @@ -40,6 +41,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs index 5d03d9db16392..bede8cffc326e 100644 --- a/tests/ui/manual_unwrap_or.rs +++ b/tests/ui/manual_unwrap_or.rs @@ -21,13 +21,14 @@ fn unwrap_or() { }; // multiline case + #[rustfmt::skip] match Some(1) { Some(i) => i, None => { - let a = 1 + 42; - let b = a + 42; - b + 42 - }, + 42 + 42 + + 42 + 42 + 42 + + 42 + 42 + 42 + } }; // string case @@ -55,6 +56,28 @@ fn unwrap_or() { None => break, }; } + + // cases where the none arm isn't a constant expression + // are not linted due to potential ownership issues + + // ownership issue example, don't lint + struct NonCopyable; + let mut option: Option = None; + match option { + Some(x) => x, + None => { + option = Some(NonCopyable); + // some more code ... + option.unwrap() + }, + }; + + // ownership issue example, don't lint + let option: Option<&str> = None; + match option { + Some(s) => s, + None => &format!("{} {}!", "hello", "world"), + }; } fn main() {} diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr index 8f6835ed78d29..674f2952635f6 100644 --- a/tests/ui/manual_unwrap_or.stderr +++ b/tests/ui/manual_unwrap_or.stderr @@ -27,29 +27,29 @@ LL | | None => 1 + 42, LL | | }; | |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)` -error: this pattern reimplements `Option::unwrap_or_else` - --> $DIR/manual_unwrap_or.rs:24:5 +error: this pattern reimplements `Option::unwrap_or` + --> $DIR/manual_unwrap_or.rs:25:5 | LL | / match Some(1) { LL | | Some(i) => i, LL | | None => { -LL | | let a = 1 + 42; +LL | | 42 + 42 ... | -LL | | }, +LL | | } LL | | }; | |_____^ | help: replace with | -LL | Some(1).unwrap_or_else(|| { -LL | let a = 1 + 42; -LL | let b = a + 42; -LL | b + 42 +LL | Some(1).unwrap_or({ +LL | 42 + 42 +LL | + 42 + 42 + 42 +LL | + 42 + 42 + 42 LL | }); | error: this pattern reimplements `Option::unwrap_or` - --> $DIR/manual_unwrap_or.rs:34:5 + --> $DIR/manual_unwrap_or.rs:35:5 | LL | / match Some("Bob") { LL | | Some(i) => i, From 2da121d97fa2a1839d703e8c584d5bdf989b8117 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Wed, 14 Oct 2020 23:26:48 +0200 Subject: [PATCH 83/97] Fix remark linting on checkboxes --- .github/PULL_REQUEST_TEMPLATE.md | 12 ++++++------ doc/adding_lints.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 137a73630940a..6c92e10522c99 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -12,12 +12,12 @@ your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. -- [ ] Followed [lint naming conventions][lint_naming] -- [ ] Added passing UI tests (including committed `.stderr` file) -- [ ] `cargo test` passes locally -- [ ] Executed `cargo dev update_lints` -- [ ] Added lint documentation -- [ ] Run `cargo dev fmt` +- \[ ] Followed [lint naming conventions][lint_naming] +- \[ ] Added passing UI tests (including committed `.stderr` file) +- \[ ] `cargo test` passes locally +- \[ ] Executed `cargo dev update_lints` +- \[ ] Added lint documentation +- \[ ] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 344bb455aa539..ab8ff7117967b 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -454,12 +454,12 @@ Before submitting your PR make sure you followed all of the basic requirements: -- [ ] Followed [lint naming conventions][lint_naming] -- [ ] Added passing UI tests (including committed `.stderr` file) -- [ ] `cargo test` passes locally -- [ ] Executed `cargo dev update_lints` -- [ ] Added lint documentation -- [ ] Run `cargo dev fmt` +- \[ ] Followed [lint naming conventions][lint_naming] +- \[ ] Added passing UI tests (including committed `.stderr` file) +- \[ ] `cargo test` passes locally +- \[ ] Executed `cargo dev update_lints` +- \[ ] Added lint documentation +- \[ ] Run `cargo dev fmt` ## Cheatsheet From 2c1e8cfc622652ebfb1f0256aa8d2afae91bf416 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 14 Oct 2020 18:42:13 +0200 Subject: [PATCH 84/97] Remove rustc_session::config::Config The wrapper type led to tons of target.target across the compiler. Its ptr_width field isn't required any more, as target_pointer_width is already present in parsed form. --- clippy_lints/src/trivially_copy_pass_by_ref.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index 1f06d2dbe9144..d92eb86fb2eb6 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -9,10 +9,10 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_session::config::Config as SessionConfig; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_target::abi::LayoutOf; +use rustc_target::spec::Target; use rustc_target::spec::abi::Abi; declare_clippy_lint! { @@ -60,9 +60,9 @@ pub struct TriviallyCopyPassByRef { } impl<'tcx> TriviallyCopyPassByRef { - pub fn new(limit: Option, target: &SessionConfig) -> Self { + pub fn new(limit: Option, target: &Target) -> Self { let limit = limit.unwrap_or_else(|| { - let bit_width = u64::from(target.ptr_width); + let bit_width = u64::from(target.pointer_width); // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); From ef91de640294e6d0fbf881082196ba83379ea447 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 15 Oct 2020 22:37:53 -0700 Subject: [PATCH 85/97] Run cargo dev fmt Signed-off-by: Joe Richey --- clippy_lints/src/future_not_send.rs | 9 ++------- clippy_lints/src/trivially_copy_pass_by_ref.rs | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index d2a322e1223c6..71a30d1c33d4f 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -92,13 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { |db| { cx.tcx.infer_ctxt().enter(|infcx| { for FulfillmentError { obligation, .. } in send_errors { - infcx.maybe_note_obligation_cause_for_async_await( - db, - &obligation, - ); - if let Trait(trait_pred, _) = - obligation.predicate.skip_binders() - { + infcx.maybe_note_obligation_cause_for_async_await(db, &obligation); + if let Trait(trait_pred, _) = obligation.predicate.skip_binders() { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/trivially_copy_pass_by_ref.rs b/clippy_lints/src/trivially_copy_pass_by_ref.rs index d92eb86fb2eb6..e90ea0fc200a9 100644 --- a/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -12,8 +12,8 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_target::abi::LayoutOf; -use rustc_target::spec::Target; use rustc_target::spec::abi::Abi; +use rustc_target::spec::Target; declare_clippy_lint! { /// **What it does:** Checks for functions taking arguments by reference, where From 6d358d29b0eb4e6f21526ccfb29636dea20d8993 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 16 Oct 2020 14:23:17 +0200 Subject: [PATCH 86/97] Update semver 0.10 -> 0.11 --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c7a3099b8ab0a..e67aba19b7e8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ path = "src/driver.rs" # begin automatic update clippy_lints = { version = "0.0.212", path = "clippy_lints" } # end automatic update -semver = "0.10" +semver = "0.11" rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } lazy_static = "1.0" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index fcf817b82c89e..cd9363a857243 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -27,7 +27,7 @@ serde = { version = "1.0", features = ["derive"] } smallvec = { version = "1", features = ["union"] } toml = "0.5.3" unicode-normalization = "0.1" -semver = "0.10.0" +semver = "0.11" # NOTE: cargo requires serde feat in its url dep # see url = { version = "2.1.0", features = ["serde"] } From 701c7e2fbac1f05064519f0800128ea92491689a Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 16 Oct 2020 22:11:37 +0200 Subject: [PATCH 87/97] bump cargo_metadata version --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 96655e7f5b93a..1ddcd18598dea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} tempfile = { version = "3.1.0", optional = true } [dev-dependencies] -cargo_metadata = "0.11.1" +cargo_metadata = "0.12" compiletest_rs = { version = "0.5.0", features = ["tmp"] } tester = "0.7" clippy-mini-macro-test = { version = "0.2", path = "mini-macro" } diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index cd9363a857243..d9471d2519744 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -17,7 +17,7 @@ keywords = ["clippy", "lint", "plugin"] edition = "2018" [dependencies] -cargo_metadata = "0.11.1" +cargo_metadata = "0.12" if_chain = "1.0.0" itertools = "0.9" pulldown-cmark = { version = "0.8", default-features = false } From 0af467ebf2da4994aee56b2b70c59028170a88ba Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 6 Oct 2020 18:51:31 -0300 Subject: [PATCH 88/97] Handle ExprKind::ConstBlock on clippy --- clippy_lints/src/loops.rs | 1 + clippy_lints/src/utils/author.rs | 5 +++++ clippy_lints/src/utils/eager_or_lazy.rs | 2 +- clippy_lints/src/utils/hir_utils.rs | 3 +++ clippy_lints/src/utils/inspector.rs | 5 +++++ clippy_lints/src/utils/sugg.rs | 2 ++ 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/loops.rs b/clippy_lints/src/loops.rs index 61b63597b1633..4fdcaca8f6039 100644 --- a/clippy_lints/src/loops.rs +++ b/clippy_lints/src/loops.rs @@ -742,6 +742,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Closure(_, _, _, _, _) | ExprKind::LlvmInlineAsm(_) | ExprKind::Path(_) + | ExprKind::ConstBlock(_) | ExprKind::Lit(_) | ExprKind::Err => NeverLoopResult::Otherwise, } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 6eda6d1fa8340..89425437eeead 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -506,6 +506,11 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor { println!(" if {}.len() == {};", fields_pat, fields.len()); println!(" // unimplemented: field checks"); }, + ExprKind::ConstBlock(_) => { + let value_pat = self.next("value"); + println!("Const({})", value_pat); + self.current = value_pat; + }, // FIXME: compute length (needs type info) ExprKind::Repeat(ref value, _) => { let value_pat = self.next("value"); diff --git a/clippy_lints/src/utils/eager_or_lazy.rs b/clippy_lints/src/utils/eager_or_lazy.rs index 6938d9971d96d..27e9567740d17 100644 --- a/clippy_lints/src/utils/eager_or_lazy.rs +++ b/clippy_lints/src/utils/eager_or_lazy.rs @@ -23,7 +23,7 @@ use rustc_middle::hir::map::Map; /// This function is named so to stress that it isn't exhaustive and returns FNs. fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool { match expr.kind { - ExprKind::Lit(..) | ExprKind::Path(..) | ExprKind::Field(..) => true, + ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true, ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr), ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)), ExprKind::Struct(_, fields, expr) => { diff --git a/clippy_lints/src/utils/hir_utils.rs b/clippy_lints/src/utils/hir_utils.rs index c7263f48965a5..c9e639e8728fd 100644 --- a/clippy_lints/src/utils/hir_utils.rs +++ b/clippy_lints/src/utils/hir_utils.rs @@ -559,6 +559,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(path.ident.name); self.hash_exprs(args); }, + ExprKind::ConstBlock(ref l_id) => { + self.hash_body(l_id.body); + }, ExprKind::Repeat(ref e, ref l_id) => { self.hash_expr(e); self.hash_body(l_id.body); diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index 4701a3f26e6f7..93bd82994466a 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -338,6 +338,11 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, base, indent + 1); } }, + hir::ExprKind::ConstBlock(ref anon_const) => { + println!("{}ConstBlock", ind); + println!("{}anon_const:", ind); + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, hir::ExprKind::Repeat(ref val, ref anon_const) => { println!("{}Repeat", ind); println!("{}value:", ind); diff --git a/clippy_lints/src/utils/sugg.rs b/clippy_lints/src/utils/sugg.rs index ec8b7e59b5976..a2a1d109c9ac8 100644 --- a/clippy_lints/src/utils/sugg.rs +++ b/clippy_lints/src/utils/sugg.rs @@ -110,6 +110,7 @@ impl<'a> Sugg<'a> { | hir::ExprKind::Index(..) | hir::ExprKind::InlineAsm(..) | hir::ExprKind::LlvmInlineAsm(..) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Lit(..) | hir::ExprKind::Loop(..) | hir::ExprKind::MethodCall(..) @@ -157,6 +158,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Index(..) | ast::ExprKind::InlineAsm(..) | ast::ExprKind::LlvmInlineAsm(..) + | ast::ExprKind::ConstBlock(..) | ast::ExprKind::Lit(..) | ast::ExprKind::Loop(..) | ast::ExprKind::MacCall(..) From 5a13217ea9c07121e7d3cdcfb0ddd2aa52b90f12 Mon Sep 17 00:00:00 2001 From: ThibsG Date: Fri, 16 Oct 2020 17:58:26 +0200 Subject: [PATCH 89/97] Assert macro args extractor as a common function in higher --- clippy_lints/src/eq_op.rs | 12 ++-- clippy_lints/src/mutable_debug_assertion.rs | 66 ++++----------------- clippy_lints/src/utils/higher.rs | 54 +++++++++++++++++ 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 9653e62cad078..3201adbf9a0b6 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,5 +1,5 @@ use crate::utils::{ - eq_expr_value, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, + eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of, multispan_sugg, snippet, span_lint, span_lint_and_then, }; use if_chain::if_chain; @@ -71,13 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { if_chain! { if is_expn_of(stmt.span, amn).is_some(); if let StmtKind::Semi(ref matchexpr) = stmt.kind; - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind; - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind; + if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); + if macro_args.len() == 2; + let (lhs, rhs) = (macro_args[0], macro_args[1]); if eq_expr_value(cx, lhs, rhs); then { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index cc635c2a202f6..76417aa7ed09d 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,7 +1,6 @@ -use crate::utils::{is_direct_expn_of, span_lint}; -use if_chain::if_chain; +use crate::utils::{higher, is_direct_expn_of, span_lint}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; -use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability, StmtKind, UnOp}; +use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::ty; @@ -39,66 +38,23 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { for dmn in &DEBUG_MACRO_NAMES { if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(span) = extract_call(cx, e) { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } - } -} - -//HACK(hellow554): remove this when #4694 is implemented -fn extract_call<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option { - if_chain! { - if let ExprKind::Block(ref block, _) = e.kind; - if block.stmts.len() == 1; - if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind; - then { - // debug_assert - if_chain! { - if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; - if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; - if let ExprKind::Unary(UnOp::UnNot, ref condition) = droptmp.kind; - then { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(condition); - return visitor.expr_span(); - } - } - - // debug_assert_{eq,ne} - if_chain! { - if let ExprKind::Block(ref matchblock, _) = matchexpr.kind; - if let Some(ref matchheader) = matchblock.expr; - if let ExprKind::Match(ref headerexpr, _, _) = matchheader.kind; - if let ExprKind::Tup(ref conditions) = headerexpr.kind; - if conditions.len() == 2; - then { - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref lhs) = conditions[0].kind { + if let Some(macro_args) = higher::extract_assert_macro_args(e) { + for arg in macro_args { let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(lhs); + visitor.visit_expr(arg); if let Some(span) = visitor.expr_span() { - return Some(span); - } - } - if let ExprKind::AddrOf(BorrowKind::Ref, _, ref rhs) = conditions[1].kind { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(rhs); - if let Some(span) = visitor.expr_span() { - return Some(span); + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!("do not call a function with mutable arguments inside of `{}!`", dmn), + ); } } } } } } - - None } struct MutArgVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/utils/higher.rs b/clippy_lints/src/utils/higher.rs index 8563b469a30dd..6d7c5058b4f30 100644 --- a/clippy_lints/src/utils/higher.rs +++ b/clippy_lints/src/utils/higher.rs @@ -7,6 +7,7 @@ use crate::utils::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast; use rustc_hir as hir; +use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp}; use rustc_lint::LateContext; /// Converts a hir binary operator to the corresponding `ast` type. @@ -241,3 +242,56 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option(e: &'tcx Expr<'tcx>) -> Option>> { + /// Try to match the AST for a pattern that contains a match, for example when two args are + /// compared + fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option>> { + if_chain! { + if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind; + if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind; + then { + return Some(vec![lhs, rhs]); + } + } + None + } + + if let ExprKind::Block(ref block, _) = e.kind { + if block.stmts.len() == 1 { + if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind { + // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`) + if_chain! { + if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind; + if let ExprKind::DropTemps(ref droptmp) = ifclause.kind; + if let ExprKind::Unary(UnOp::UnNot, condition) = droptmp.kind; + then { + return Some(vec![condition]); + } + } + + // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + if_chain! { + if let ExprKind::Block(ref matchblock,_) = matchexpr.kind; + if let Some(ref matchblock_expr) = matchblock.expr; + then { + return ast_matchblock(matchblock_expr); + } + } + } + } else if let Some(matchblock_expr) = block.expr { + // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`) + return ast_matchblock(&matchblock_expr); + } + } + None +} From 29392a1728df1b334b48115ad24cc592f04ca15e Mon Sep 17 00:00:00 2001 From: Jacob Hughes Date: Sat, 17 Oct 2020 13:45:40 -0400 Subject: [PATCH 90/97] Appease the almightly lord clippy, hallowed be thy name --- clippy_lints/src/utils/paths.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 277da9d3f3a27..5e769c690a690 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -14,7 +14,7 @@ pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "Bina pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"]; pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"]; pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"]; -pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"]; +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"]; pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"]; pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"]; pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"]; From 67bc11bd0480b5657c8c8db233e55f9b16ed664a Mon Sep 17 00:00:00 2001 From: Ryan Wiedemann Date: Sun, 18 Oct 2020 17:01:57 -0600 Subject: [PATCH 91/97] Add more infomation about LintStore registration Backstory: I somehow missed the fact that I needed to register a lint pass in order for it to run, and I spent some time confused until I figured it out. So I wanted to make it clear that a missing `register_(early|late)_pass` call is a likely cause of a lint not running. --- doc/adding_lints.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/adding_lints.md b/doc/adding_lints.md index ab8ff7117967b..2572833b8debf 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -225,6 +225,17 @@ automate everything. We will have to register our lint pass manually in the store.register_early_pass(|| box foo_functions::FooFunctions); ``` +As one may expect, there is a corresponding `register_late_pass` method +available as well. Without a call to one of `register_early_pass` or +`register_late_pass`, the lint pass in question will not be run. + +One reason that `cargo dev` does not automate this step is that multiple lints +can use the same lint pass, so registering the lint pass may already be done +when adding a new lint. Another reason that this step is not automated is that +the order that the passes are registered determines the order the passes +actually run, which in turn affects the order that any emitted lints are output +in. + [declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60 [example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints From 16b5f37b5a23f475d0d94efea764c57e4572f63f Mon Sep 17 00:00:00 2001 From: ThibsG Date: Mon, 19 Oct 2020 17:31:41 +0200 Subject: [PATCH 92/97] Split `eq_op` ui tests to avoid file limit error in CI --- tests/ui/eq_op.rs | 57 ---------------------- tests/ui/eq_op.stderr | 95 +----------------------------------- tests/ui/eq_op_macros.rs | 56 +++++++++++++++++++++ tests/ui/eq_op_macros.stderr | 95 ++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 151 deletions(-) create mode 100644 tests/ui/eq_op_macros.rs create mode 100644 tests/ui/eq_op_macros.stderr diff --git a/tests/ui/eq_op.rs b/tests/ui/eq_op.rs index 20613ac6afe96..272b0900a31c6 100644 --- a/tests/ui/eq_op.rs +++ b/tests/ui/eq_op.rs @@ -60,8 +60,6 @@ fn main() { const B: u32 = 10; const C: u32 = A / B; // ok, different named constants const D: u32 = A / A; - - check_assert_identical_args(); } #[rustfmt::skip] @@ -87,58 +85,3 @@ fn check_ignore_macro() { // checks if the lint ignores macros with `!` operator !bool_macro!(1) && !bool_macro!(""); } - -macro_rules! assert_in_macro_def { - () => { - let a = 42; - assert_eq!(a, a); - assert_ne!(a, a); - debug_assert_eq!(a, a); - debug_assert_ne!(a, a); - }; -} - -// lint identical args in assert-like macro invocations (see #3574) -fn check_assert_identical_args() { - // lint also in macro definition - assert_in_macro_def!(); - - let a = 1; - let b = 2; - - // lint identical args in `assert_eq!` - assert_eq!(a, a); - assert_eq!(a + 1, a + 1); - // ok - assert_eq!(a, b); - assert_eq!(a, a + 1); - assert_eq!(a + 1, b + 1); - - // lint identical args in `assert_ne!` - assert_ne!(a, a); - assert_ne!(a + 1, a + 1); - // ok - assert_ne!(a, b); - assert_ne!(a, a + 1); - assert_ne!(a + 1, b + 1); - - // lint identical args in `debug_assert_eq!` - debug_assert_eq!(a, a); - debug_assert_eq!(a + 1, a + 1); - // ok - debug_assert_eq!(a, b); - debug_assert_eq!(a, a + 1); - debug_assert_eq!(a + 1, b + 1); - - // lint identical args in `debug_assert_ne!` - debug_assert_ne!(a, a); - debug_assert_ne!(a + 1, a + 1); - // ok - debug_assert_ne!(a, b); - debug_assert_ne!(a, a + 1); - debug_assert_ne!(a + 1, b + 1); - - let my_vec = vec![1; 5]; - let mut my_iter = my_vec.iter(); - assert_ne!(my_iter.next(), my_iter.next()); -} diff --git a/tests/ui/eq_op.stderr b/tests/ui/eq_op.stderr index 21a63aec7a171..5b80e6078eed7 100644 --- a/tests/ui/eq_op.stderr +++ b/tests/ui/eq_op.stderr @@ -162,98 +162,5 @@ error: equal expressions as operands to `/` LL | const D: u32 = A / A; | ^^^^^ -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:94:20 - | -LL | assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: `#[deny(clippy::eq_op)]` on by default - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:95:20 - | -LL | assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:110:16 - | -LL | assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `assert_eq!` macro call - --> $DIR/eq_op.rs:111:16 - | -LL | assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:118:16 - | -LL | assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `assert_ne!` macro call - --> $DIR/eq_op.rs:119:16 - | -LL | assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:96:26 - | -LL | debug_assert_eq!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:97:26 - | -LL | debug_assert_ne!(a, a); - | ^^^^ -... -LL | assert_in_macro_def!(); - | ----------------------- in this macro invocation - | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:126:22 - | -LL | debug_assert_eq!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_eq!` macro call - --> $DIR/eq_op.rs:127:22 - | -LL | debug_assert_eq!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:134:22 - | -LL | debug_assert_ne!(a, a); - | ^^^^ - -error: identical args used in this `debug_assert_ne!` macro call - --> $DIR/eq_op.rs:135:22 - | -LL | debug_assert_ne!(a + 1, a + 1); - | ^^^^^^^^^^^^ - -error: aborting due to 39 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/eq_op_macros.rs b/tests/ui/eq_op_macros.rs new file mode 100644 index 0000000000000..6b5b31a1a2ef1 --- /dev/null +++ b/tests/ui/eq_op_macros.rs @@ -0,0 +1,56 @@ +#![warn(clippy::eq_op)] + +// lint also in macro definition +macro_rules! assert_in_macro_def { + () => { + let a = 42; + assert_eq!(a, a); + assert_ne!(a, a); + debug_assert_eq!(a, a); + debug_assert_ne!(a, a); + }; +} + +// lint identical args in assert-like macro invocations (see #3574) +fn main() { + assert_in_macro_def!(); + + let a = 1; + let b = 2; + + // lint identical args in `assert_eq!` + assert_eq!(a, a); + assert_eq!(a + 1, a + 1); + // ok + assert_eq!(a, b); + assert_eq!(a, a + 1); + assert_eq!(a + 1, b + 1); + + // lint identical args in `assert_ne!` + assert_ne!(a, a); + assert_ne!(a + 1, a + 1); + // ok + assert_ne!(a, b); + assert_ne!(a, a + 1); + assert_ne!(a + 1, b + 1); + + // lint identical args in `debug_assert_eq!` + debug_assert_eq!(a, a); + debug_assert_eq!(a + 1, a + 1); + // ok + debug_assert_eq!(a, b); + debug_assert_eq!(a, a + 1); + debug_assert_eq!(a + 1, b + 1); + + // lint identical args in `debug_assert_ne!` + debug_assert_ne!(a, a); + debug_assert_ne!(a + 1, a + 1); + // ok + debug_assert_ne!(a, b); + debug_assert_ne!(a, a + 1); + debug_assert_ne!(a + 1, b + 1); + + let my_vec = vec![1; 5]; + let mut my_iter = my_vec.iter(); + assert_ne!(my_iter.next(), my_iter.next()); +} diff --git a/tests/ui/eq_op_macros.stderr b/tests/ui/eq_op_macros.stderr new file mode 100644 index 0000000000000..fb9378108b98c --- /dev/null +++ b/tests/ui/eq_op_macros.stderr @@ -0,0 +1,95 @@ +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:7:20 + | +LL | assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: `-D clippy::eq-op` implied by `-D warnings` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:8:20 + | +LL | assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:22:16 + | +LL | assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `assert_eq!` macro call + --> $DIR/eq_op_macros.rs:23:16 + | +LL | assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:30:16 + | +LL | assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `assert_ne!` macro call + --> $DIR/eq_op_macros.rs:31:16 + | +LL | assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:9:26 + | +LL | debug_assert_eq!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:10:26 + | +LL | debug_assert_ne!(a, a); + | ^^^^ +... +LL | assert_in_macro_def!(); + | ----------------------- in this macro invocation + | + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:38:22 + | +LL | debug_assert_eq!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_eq!` macro call + --> $DIR/eq_op_macros.rs:39:22 + | +LL | debug_assert_eq!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:46:22 + | +LL | debug_assert_ne!(a, a); + | ^^^^ + +error: identical args used in this `debug_assert_ne!` macro call + --> $DIR/eq_op_macros.rs:47:22 + | +LL | debug_assert_ne!(a + 1, a + 1); + | ^^^^^^^^^^^^ + +error: aborting due to 12 previous errors + From 496dbb6fe80c8102e36c5927c117fb085b199ac7 Mon Sep 17 00:00:00 2001 From: Takayuki Nakata Date: Thu, 22 Oct 2020 08:51:03 +0900 Subject: [PATCH 93/97] Fix links to labeled issues --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 100c9edb36724..6494695606c3a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -316,8 +316,8 @@ If you have @bors permissions, you can find an overview of the available commands [here][homu_instructions]. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash%20%3Aboom%3A -[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug%20%3Abeetle%3A +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug [homu]: https://github.com/rust-lang/homu [homu_instructions]: https://buildbot2.rust-lang.org/homu/ [homu_queue]: https://buildbot2.rust-lang.org/homu/queue/clippy From fcde7683fe7ca10c83e5bc17f0969d2284affcd2 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 22 Oct 2020 13:23:14 +0100 Subject: [PATCH 94/97] Fix clippy tests --- tests/ui/auxiliary/proc_macro_attr.rs | 1 + tests/ui/auxiliary/proc_macro_derive.rs | 1 + tests/ui/crashes/auxiliary/proc_macro_crash.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index de670cdfc31f1..e370a98df1acf 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_hygiene, proc_macro_quote, box_patterns)] +#![allow(incomplete_features)] #![allow(clippy::useless_conversion)] extern crate proc_macro; diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 3df8be6c23230..cd5a5ae0aa75a 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -3,6 +3,7 @@ #![crate_type = "proc-macro"] #![feature(repr128, proc_macro_quote)] +#![allow(incomplete_features)] extern crate proc_macro; diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index 619d11cefc46d..ed8e7a708a5e2 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -6,6 +6,7 @@ // contain a proc-macro. #![feature(repr128)] +#![allow(incomplete_features)] #![crate_type = "proc-macro"] extern crate proc_macro; From e407cc521236677c9bc9bc546a6b8efe21a71423 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 23 Oct 2020 23:45:37 +0200 Subject: [PATCH 95/97] Remove duplicate import of `Target` --- src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs index c2c86aa474adc..e90ea0fc200a9 100644 --- a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs +++ b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs @@ -12,7 +12,6 @@ use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; use rustc_target::abi::LayoutOf; -use rustc_target::spec::Target; use rustc_target::spec::abi::Abi; use rustc_target::spec::Target; From ca11eeb563399b78896dbf40e827594d53a84b7f Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Fri, 23 Oct 2020 23:47:58 +0200 Subject: [PATCH 96/97] Update Cargo.lock --- Cargo.lock | 47 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94fbd52bcf0d7..533ca39f9b29d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,6 +418,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "cargo_metadata" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345" +dependencies = [ + "semver 0.11.0", + "serde", + "serde_json", +] + [[package]] name = "cargotest2" version = "0.1.0" @@ -530,15 +541,14 @@ dependencies = [ name = "clippy" version = "0.0.212" dependencies = [ - "cargo_metadata 0.11.1", + "cargo_metadata 0.12.0", "clippy-mini-macro-test", "clippy_lints", "compiletest_rs", "derive-new", - "lazy_static", "rustc-workspace-hack", "rustc_tools_util 0.2.0", - "semver 0.10.0", + "semver 0.11.0", "serde", "tempfile", "tester", @@ -552,14 +562,14 @@ version = "0.2.0" name = "clippy_lints" version = "0.0.212" dependencies = [ - "cargo_metadata 0.11.1", + "cargo_metadata 0.12.0", "if_chain", "itertools 0.9.0", "pulldown-cmark 0.8.0", "quine-mc_cluskey", "quote", "regex-syntax", - "semver 0.10.0", + "semver 0.11.0", "serde", "smallvec 1.4.2", "syn", @@ -4373,7 +4383,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", "serde", ] @@ -4383,7 +4393,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", + "serde", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.1", "serde", ] @@ -4393,6 +4413,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.115" @@ -4424,9 +4453,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", From a5139196029c8e9bc6e431e11c5e8780c6ee7f62 Mon Sep 17 00:00:00 2001 From: Eduardo Broto Date: Sat, 24 Oct 2020 22:59:44 +0200 Subject: [PATCH 97/97] Add unbounded_depth to serde_json (clippy-driver) --- src/tools/rustc-workspace-hack/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml index 11d61606ff521..213cb07fddf18 100644 --- a/src/tools/rustc-workspace-hack/Cargo.toml +++ b/src/tools/rustc-workspace-hack/Cargo.toml @@ -66,7 +66,7 @@ crossbeam-utils = { version = "0.7.2", features = ["nightly"] } proc-macro2 = { version = "1", features = ["default"] } quote = { version = "1", features = ["default"] } serde = { version = "1.0.82", features = ['derive'] } -serde_json = { version = "1.0.31", features = ["raw_value"] } +serde_json = { version = "1.0.31", features = ["raw_value", "unbounded_depth"] } smallvec-0_6 = { package = "smallvec", version = "0.6", features = ['union', 'may_dangle'] } smallvec = { version = "1.0", features = ['union', 'may_dangle'] } syn = { version = "1", features = ['fold', 'full', 'extra-traits', 'visit', 'visit-mut'] }