From 7975d41a915b7e2c0b5ce2e2ca47ab31b96d978a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 26 May 2022 13:57:05 -0400 Subject: [PATCH 01/78] Rework `branches_sharing_code` --- clippy_lints/src/copies.rs | 675 ++++++++---------- clippy_utils/src/hir_utils.rs | 14 +- clippy_utils/src/lib.rs | 4 +- .../branches_sharing_code/false_positives.rs | 15 +- .../shared_at_bottom.stderr | 28 +- .../shared_at_top.stderr | 18 +- .../shared_at_top_and_bottom.stderr | 48 +- 7 files changed, 355 insertions(+), 447 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index e6a0162fd0272..b18eba1156a7c 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,18 +1,18 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt}; use clippy_utils::{ - both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed, - search_same, ContainsName, SpanlessEq, SpanlessHash, + eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, + search_same, ContainsName, HirEqInterExpr, SpanlessEq, }; -use if_chain::if_chain; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::hir::nested_filter; +use core::iter; +use rustc_errors::Applicability; +use rustc_hir::intravisit; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{source_map::Span, symbol::Symbol, BytePos}; +use rustc_span::hygiene::walk_chain; +use rustc_span::source_map::SourceMap; +use rustc_span::{BytePos, Span, Symbol}; use std::borrow::Cow; declare_clippy_lint! { @@ -165,243 +165,315 @@ declare_lint_pass!(CopyAndPaste => [ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !expr.span.from_expansion() { - if let ExprKind::If(_, _, _) = expr.kind { - // skip ifs directly in else, it will be checked in the parent if - if let Some(&Expr { - kind: ExprKind::If(_, _, Some(else_expr)), - .. - }) = get_parent_expr(cx, expr) - { - if else_expr.hir_id == expr.hir_id { - return; - } - } - - let (conds, blocks) = if_sequence(expr); - // Conditions - lint_same_cond(cx, &conds); - lint_same_fns_in_if_cond(cx, &conds); - // Block duplication - lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr); + if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { + let (conds, blocks) = if_sequence(expr); + lint_same_cond(cx, &conds); + lint_same_fns_in_if_cond(cx, &conds); + let all_same = + !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks); + if !all_same && conds.len() != blocks.len() { + lint_branches_sharing_code(cx, &conds, &blocks, expr); } } } } -/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal. -fn lint_same_then_else<'tcx>( +/// Checks if the given expression is a let chain. +fn contains_let(e: &Expr<'_>) -> bool { + match e.kind { + ExprKind::Let(..) => true, + ExprKind::Binary(op, lhs, rhs) if op.node == BinOpKind::And => { + matches!(lhs.kind, ExprKind::Let(..)) || contains_let(rhs) + }, + _ => false, + } +} + +fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> bool { + let mut eq = SpanlessEq::new(cx); + blocks + .array_windows::<2>() + .enumerate() + .fold(true, |all_eq, (i, &[lhs, rhs])| { + if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) { + span_lint_and_note( + cx, + IF_SAME_THEN_ELSE, + lhs.span, + "this `if` has identical blocks", + Some(rhs.span), + "same as this", + ); + all_eq + } else { + false + } + }) +} + +fn lint_branches_sharing_code<'tcx>( cx: &LateContext<'tcx>, conds: &[&'tcx Expr<'_>], blocks: &[&Block<'tcx>], - has_conditional_else: bool, expr: &'tcx Expr<'_>, ) { // We only lint ifs with multiple blocks - if blocks.len() < 2 || is_else_clause(cx.tcx, expr) { - return; - } - - // Check if each block has shared code - let has_expr = blocks[0].expr.is_some(); - - let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) { - (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq) - } else { + let &[first_block, ref blocks @ ..] = blocks else { return; }; - - // BRANCHES_SHARING_CODE prerequisites - if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) { + let &[.., last_block] = blocks else { return; - } - - // Only the start is the same - if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) { - let block = blocks[0]; - let start_stmts = block.stmts.split_at(start_eq).0; - - let mut start_walker = UsedValueFinderVisitor::new(cx); - for stmt in start_stmts { - intravisit::walk_stmt(&mut start_walker, stmt); - } + }; - emit_branches_sharing_code_lint( - cx, - start_eq, - 0, - false, - check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr), - blocks, - expr, - ); - } else if end_eq != 0 || (has_expr && expr_eq) { - let block = blocks[blocks.len() - 1]; - let (start_stmts, block_stmts) = block.stmts.split_at(start_eq); - let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq); + let res = scan_block_for_eq(cx, conds, first_block, blocks); + let sm = cx.tcx.sess.source_map(); + let start_suggestion = res.start_span(first_block, sm).map(|span| { + let first_line_span = first_line_of_span(cx, expr.span); + let replace_span = first_line_span.with_hi(span.hi()); + let cond_span = first_line_span.until(first_block.span); + let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); + let cond_indent = indent_of(cx, cond_span); + let moved_snippet = reindent_multiline(snippet(cx, span, "_"), true, None); + let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); + (replace_span, suggestion.to_string()) + }); + let end_suggestion = res.end_span(last_block, sm).map(|span| { + let moved_snipped = reindent_multiline(snippet(cx, span, "_"), true, None); + let indent = indent_of(cx, expr.span.shrink_to_hi()); + let suggestion = "}\n".to_string() + &moved_snipped; + let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - // Scan start - let mut start_walker = UsedValueFinderVisitor::new(cx); - for stmt in start_stmts { - intravisit::walk_stmt(&mut start_walker, stmt); + let span = span.with_hi(last_block.span.hi()); + // Improve formatting if the inner block has indention (i.e. normal Rust formatting) + let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent()); + let span = if snippet_opt(cx, test_span).map_or(false, |snip| snip == " ") { + span.with_lo(test_span.lo()) + } else { + span + }; + (span, suggestion.to_string()) + }); + + let (span, msg, end_span) = match (&start_suggestion, &end_suggestion) { + (&Some((span, _)), &Some((end_span, _))) => ( + span, + "all if blocks contain the same code at both the start and the end", + Some(end_span), + ), + (&Some((span, _)), None) => (span, "all if blocks contain the same code at the start", None), + (None, &Some((span, _))) => (span, "all if blocks contain the same code at the end", None), + (None, None) => return, + }; + span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg, |diag| { + if let Some(span) = end_span { + diag.span_note(span, "this code is shared at the end"); } - let mut moved_syms = start_walker.def_symbols; - - // Scan block - let mut block_walker = UsedValueFinderVisitor::new(cx); - for stmt in block_stmts { - intravisit::walk_stmt(&mut block_walker, stmt); + if let Some((span, sugg)) = start_suggestion { + diag.span_suggestion( + span, + "consider moving these statements before the if", + sugg, + Applicability::Unspecified, + ); } - let mut block_defs = block_walker.defs; - - // Scan moved stmts - let mut moved_start: Option = None; - let mut end_walker = UsedValueFinderVisitor::new(cx); - for (index, stmt) in end_stmts.iter().enumerate() { - intravisit::walk_stmt(&mut end_walker, stmt); - - for value in &end_walker.uses { - // Well we can't move this and all prev statements. So reset - if block_defs.contains(value) { - moved_start = Some(index + 1); - end_walker.defs.drain().for_each(|x| { - block_defs.insert(x); - }); - - end_walker.def_symbols.clear(); - } + if let Some((span, sugg)) = end_suggestion { + diag.span_suggestion( + span, + "consider moving these statements after the if", + sugg, + Applicability::Unspecified, + ); + if !cx.typeck_results().expr_ty(expr).is_unit() { + diag.note("the end suggestion probably needs some adjustments to use the expression result correctly"); } - - end_walker.uses.clear(); } - - if let Some(moved_start) = moved_start { - end_eq -= moved_start; + if check_for_warn_of_moved_symbol(cx, &res.moved_locals, expr) { + diag.warn("some moved values might need to be renamed to avoid wrong references"); } + }); +} - let end_linable = block.expr.map_or_else( - || end_eq != 0, - |expr| { - intravisit::walk_expr(&mut end_walker, expr); - end_walker.uses.iter().any(|x| !block_defs.contains(x)) - }, - ); - - if end_linable { - end_walker.def_symbols.drain().for_each(|x| { - moved_syms.insert(x); - }); +struct BlockEq { + /// The end of the range of equal stmts at the start. + start_end_eq: usize, + /// The start of the range of equal stmts at the end. + end_begin_eq: Option, + /// The name and id of every local which can be moved at the beginning and the end. + moved_locals: Vec<(HirId, Symbol)>, +} +impl BlockEq { + fn start_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match &b.stmts[..self.start_end_eq] { + [first, .., last] => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + [s] => Some(sm.stmt_span(s.span, b.span)), + [] => None, } + } - emit_branches_sharing_code_lint( - cx, - start_eq, - end_eq, - end_linable, - check_for_warn_of_moved_symbol(cx, &moved_syms, expr), - blocks, - expr, - ); + fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option { + match (&b.stmts[b.stmts.len() - self.end_begin_eq?..], b.expr) { + ([first, .., last], None) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([first, ..], Some(last)) => Some(sm.stmt_span(first.span, b.span).to(sm.stmt_span(last.span, b.span))), + ([s], None) => Some(sm.stmt_span(s.span, b.span)), + ([], Some(e)) => Some(walk_chain(e.span, b.span.ctxt())), + ([], None) => None, + } } } -struct BlockEqual { - /// The amount statements that are equal from the start - start_eq: usize, - /// The amount statements that are equal from the end - end_eq: usize, - /// An indication if the block expressions are the same. This will also be true if both are - /// `None` - expr_eq: bool, +/// If the statement is a local, checks if the bound names match the expected list of names. +fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool { + if let StmtKind::Local(l) = s.kind { + let mut i = 0usize; + let mut res = true; + l.pat.each_binding_or_first(&mut |_, _, _, name| { + if names.get(i).map_or(false, |&(_, n)| n == name.name) { + i += 1; + } else { + res = false; + } + }); + res && i == names.len() + } else { + false + } } -/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to -/// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option { - let mut start_eq = usize::MAX; - let mut end_eq = usize::MAX; - let mut expr_eq = true; - let mut iter = blocks.windows(2).enumerate(); - while let Some((i, &[block0, block1])) = iter.next() { - let l_stmts = block0.stmts; - let r_stmts = block1.stmts; - - // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752. - // The comparison therefore needs to be done in a way that builds the correct context. - let mut evaluator = SpanlessEq::new(cx); - let mut evaluator = evaluator.inter_expr(); - - let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r)); +/// Checks if the given statement should be considered equal to the statement in the same position +/// for each block. +fn eq_stmts( + stmt: &Stmt<'_>, + blocks: &[&Block<'_>], + get_stmt: impl for<'a> Fn(&'a Block<'a>) -> Option<&'a Stmt<'a>>, + eq: &mut HirEqInterExpr<'_, '_, '_>, + moved_bindings: &mut Vec<(HirId, Symbol)>, +) -> bool { + (if let StmtKind::Local(l) = stmt.kind { + let old_count = moved_bindings.len(); + l.pat.each_binding_or_first(&mut |_, id, _, name| { + moved_bindings.push((id, name.name)); + }); + let new_bindings = &moved_bindings[old_count..]; + blocks + .iter() + .all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings))) + } else { + true + }) && blocks + .iter() + .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt))) +} - let current_end_eq = { - // We skip the middle statements which can't be equal - let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq; - let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count); - let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count); - it1.zip(it2) - .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 }) +fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq { + let mut eq = SpanlessEq::new(cx); + let mut eq = eq.inter_expr(); + let mut moved_locals = Vec::new(); + + let start_end_eq = block + .stmts + .iter() + .enumerate() + .find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)) + .map_or(block.stmts.len(), |(i, _)| i); + + // Walk backwards through the final expression/statements so long as their hashes are equal. Note + // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block + // to match those in other blocks. e.g. If each block ends with the following the hash value will be + // the same even though each `x` binding will have a different `HirId`: + // let x = foo(); + // x + 50 + let expr_hash_eq = if let Some(e) = block.expr { + let hash = hash_expr(cx, e); + blocks + .iter() + .all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash)) + } else { + blocks.iter().all(|b| b.expr.is_none()) + }; + if !expr_hash_eq { + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, }; - let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r)); - - // IF_SAME_THEN_ELSE - if_chain! { - if block_expr_eq; - if l_stmts.len() == r_stmts.len(); - if l_stmts.len() == current_start_eq; - // `conds` may have one last item than `blocks`. - // Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration. - if !matches!(conds[i].kind, ExprKind::Let(..)); - if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..))); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id); - if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id); - then { - span_lint_and_note( - cx, - IF_SAME_THEN_ELSE, - block0.span, - "this `if` has identical blocks", - Some(block1.span), - "same as this", - ); - - return None; + } + let end_search_start = block.stmts[start_end_eq..] + .iter() + .rev() + .enumerate() + .find(|&(offset, stmt)| { + let hash = hash_stmt(cx, stmt); + blocks.iter().any(|b| { + b.stmts + // the bounds check will catch the underflow + .get(b.stmts.len().wrapping_sub(offset + 1)) + .map_or(true, |s| hash != hash_stmt(cx, s)) + }) + }) + .map_or(block.stmts.len() - start_end_eq, |(i, _)| i); + + let moved_locals_at_start = moved_locals.len(); + let mut i = end_search_start; + let end_begin_eq = block.stmts[block.stmts.len() - end_search_start..] + .iter() + .zip(iter::repeat_with(move || { + let x = i; + i -= 1; + x + })) + .fold(end_search_start, |init, (stmt, offset)| { + if eq_stmts( + stmt, + blocks, + |b| b.stmts.get(b.stmts.len() - offset), + &mut eq, + &mut moved_locals, + ) { + init + } else { + // Clear out all locals seen at the end so far. None of them can be moved. + let stmts = &blocks[0].stmts; + for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] { + if let StmtKind::Local(l) = stmt.kind { + l.pat.each_binding_or_first(&mut |_, id, _, _| { + eq.locals.remove(&id); + }); + } + } + moved_locals.truncate(moved_locals_at_start); + offset - 1 + } + }); + if let Some(e) = block.expr { + for block in blocks { + if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) { + moved_locals.truncate(moved_locals_at_start); + return BlockEq { + start_end_eq, + end_begin_eq: None, + moved_locals, + }; } } - - start_eq = start_eq.min(current_start_eq); - end_eq = end_eq.min(current_end_eq); - expr_eq &= block_expr_eq; - } - - if !expr_eq { - end_eq = 0; } - // Check if the regions are overlapping. Set `end_eq` to prevent the overlap - let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap(); - if (start_eq + end_eq) > min_block_size { - end_eq = min_block_size - start_eq; + BlockEq { + start_end_eq, + end_begin_eq: Some(end_begin_eq), + moved_locals, } - - Some(BlockEqual { - start_eq, - end_eq, - expr_eq, - }) } -fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet, if_expr: &Expr<'_>) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); symbols .iter() - .filter(|sym| !sym.as_str().starts_with('_')) - .any(move |sym| { - let mut walker = ContainsName { - name: *sym, - result: false, - }; + .filter(|&&(_, name)| !name.as_str().starts_with('_')) + .any(|&(_, name)| { + let mut walker = ContainsName { name, result: false }; // Scan block block @@ -419,194 +491,9 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet, - start_stmts: usize, - end_stmts: usize, - lint_end: bool, - warn_about_moved_symbol: bool, - blocks: &[&Block<'_>], - if_expr: &Expr<'_>, -) { - if start_stmts == 0 && !lint_end { - return; - } - - // (help, span, suggestion) - let mut suggestions: Vec<(&str, Span, String)> = vec![]; - let mut add_expr_note = false; - - // Construct suggestions - let sm = cx.sess().source_map(); - if start_stmts > 0 { - let block = blocks[0]; - let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo(); - let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span); - - let cond_span = first_line_of_span(cx, if_expr.span).until(block.span); - let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None); - let cond_indent = indent_of(cx, cond_span); - let moved_span = block.stmts[0].span.source_callsite().to(span_end); - let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None); - let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{"; - let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent); - - let span = span_start.to(span_end); - suggestions.push(("start", span, suggestion.to_string())); - } - - if lint_end { - let block = blocks[blocks.len() - 1]; - let span_end = block.span.shrink_to_hi(); - - let moved_start = if end_stmts == 0 && block.expr.is_some() { - block.expr.unwrap().span.source_callsite() - } else { - sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span) - }; - let moved_end = block.expr.map_or_else( - || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span), - |expr| expr.span.source_callsite(), - ); - - let moved_span = moved_start.to(moved_end); - let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None); - let indent = indent_of(cx, if_expr.span.shrink_to_hi()); - let suggestion = "}\n".to_string() + &moved_snipped; - let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent); - - let mut span = moved_start.to(span_end); - // Improve formatting if the inner block has indention (i.e. normal Rust formatting) - let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent()); - if snippet_opt(cx, test_span) - .map(|snip| snip == " ") - .unwrap_or_default() - { - span = span.with_lo(test_span.lo()); - } - - suggestions.push(("end", span, suggestion.to_string())); - add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit(); - } - - let add_optional_msgs = |diag: &mut Diagnostic| { - if add_expr_note { - diag.note("The end suggestion probably needs some adjustments to use the expression result correctly"); - } - - if warn_about_moved_symbol { - diag.warn("Some moved values might need to be renamed to avoid wrong references"); - } - }; - - // Emit lint - if suggestions.len() == 1 { - let (place_str, span, sugg) = suggestions.pop().unwrap(); - let msg = format!("all if blocks contain the same code at the {}", place_str); - let help = format!("consider moving the {} statements out like this", place_str); - span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| { - diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified); - - add_optional_msgs(diag); - }); - } else if suggestions.len() == 2 { - let (_, end_span, end_sugg) = suggestions.pop().unwrap(); - let (_, start_span, start_sugg) = suggestions.pop().unwrap(); - span_lint_and_then( - cx, - BRANCHES_SHARING_CODE, - start_span, - "all if blocks contain the same code at the start and the end. Here at the start", - move |diag| { - diag.span_note(end_span, "and here at the end"); - - diag.span_suggestion( - start_span, - "consider moving the start statements out like this", - start_sugg, - Applicability::Unspecified, - ); - - diag.span_suggestion( - end_span, - "and consider moving the end statements out like this", - end_sugg, - Applicability::Unspecified, - ); - - add_optional_msgs(diag); - }, - ); - } -} - -/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values. -struct UsedValueFinderVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - - /// The `HirId`s of defined values in the scanned statements - defs: FxHashSet, - - /// The Symbols of the defined symbols in the scanned statements - def_symbols: FxHashSet, - - /// The `HirId`s of the used values - uses: FxHashSet, -} - -impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - UsedValueFinderVisitor { - cx, - defs: FxHashSet::default(), - def_symbols: FxHashSet::default(), - uses: FxHashSet::default(), - } - } -} - -impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } - - fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) { - let local_id = l.pat.hir_id; - self.defs.insert(local_id); - - if let Some(sym) = l.pat.simple_ident() { - self.def_symbols.insert(sym.name); - } - - if let Some(expr) = l.init { - intravisit::walk_expr(self, expr); - } - } - - fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) { - if let rustc_hir::QPath::Resolved(_, path) = *qpath { - if path.segments.len() == 1 { - if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) { - self.uses.insert(var); - } - } - } - } -} - /// Implementation of `IFS_SAME_COND`. fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(expr); - h.finish() - }; - - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) }; - - for (i, j) in search_same(conds, hash, eq) { + for (i, j) in search_same(conds, |e| hash_expr(cx, e), |lhs, rhs| eq_expr_value(cx, lhs, rhs)) { span_lint_and_note( cx, IFS_SAME_COND, @@ -620,12 +507,6 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { /// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`. fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { - let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 { - let mut h = SpanlessHash::new(cx); - h.hash_expr(expr); - h.finish() - }; - let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { // Do not lint if any expr originates from a macro if lhs.span.from_expansion() || rhs.span.from_expansion() { @@ -638,7 +519,7 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) { SpanlessEq::new(cx).eq_expr(lhs, rhs) }; - for (i, j) in search_same(conds, hash, eq) { + for (i, j) in search_same(conds, |e| hash_expr(cx, e), eq) { span_lint_and_note( cx, SAME_FUNCTIONS_IN_IF_CONDITION, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c440793b90e0e..16c5c728dba5a 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -91,7 +91,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> { // When binding are declared, the binding ID in the left expression is mapped to the one on the // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`, // these blocks are considered equal since `x` is mapped to `y`. - locals: HirIdMap, + pub locals: HirIdMap, } impl HirEqInterExpr<'_, '_, '_> { @@ -998,3 +998,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } } + +pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_stmt(s); + h.finish() +} + +pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 { + let mut h = SpanlessHash::new(cx); + h.hash_expr(e); + h.finish() +} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index adb37cc9d7510..bcaa9571a0582 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -58,7 +58,9 @@ pub mod usage; pub mod visitors; pub use self::attrs::*; -pub use self::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash}; +pub use self::hir_utils::{ + both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash, +}; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; diff --git a/tests/ui/branches_sharing_code/false_positives.rs b/tests/ui/branches_sharing_code/false_positives.rs index 7f42df463411d..064482009517a 100644 --- a/tests/ui/branches_sharing_code/false_positives.rs +++ b/tests/ui/branches_sharing_code/false_positives.rs @@ -25,4 +25,17 @@ impl FooBar { fn baz(&mut self) {} } -fn main() {} +fn foo(x: u32, y: u32) -> u32 { + x / y +} + +fn main() { + let x = (1, 2); + let _ = if true { + let (x, y) = x; + foo(x, y) + } else { + let (y, x) = x; + foo(x, y) + }; +} diff --git a/tests/ui/branches_sharing_code/shared_at_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_bottom.stderr index e3c1bbee99423..5e1a68d216ea8 100644 --- a/tests/ui/branches_sharing_code/shared_at_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_bottom.stderr @@ -12,8 +12,8 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL + let result = false; @@ -28,7 +28,7 @@ LL | / println!("Same end of block"); LL | | } | |_____^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!("Same end of block"); @@ -44,7 +44,7 @@ LL | | ); LL | | } | |_____^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!( @@ -60,7 +60,7 @@ LL | / println!("Hello World"); LL | | } | |_________^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + println!("Hello World"); @@ -75,8 +75,8 @@ LL | | // I'm expecting a note about this LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements after the if | LL ~ } LL + let later_used_value = "A string value"; @@ -91,8 +91,8 @@ LL | | println!("This is the new simple_example: {}", simple_examples); LL | | } | |_____^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the end statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements after the if | LL ~ } LL + let simple_examples = "I now identify as a &str :)"; @@ -106,8 +106,8 @@ LL | / x << 2 LL | | }; | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL ~ x << 2; @@ -120,8 +120,8 @@ LL | / x * 4 LL | | } | |_____^ | - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the end statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements after the if | LL ~ } LL + x * 4 @@ -133,7 +133,7 @@ error: all if blocks contain the same code at the end LL | if x == 17 { b = 1; a = 0x99; } else { a = 0x99; } | ^^^^^^^^^^^ | -help: consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ if x == 17 { b = 1; a = 0x99; } else { } LL + a = 0x99; diff --git a/tests/ui/branches_sharing_code/shared_at_top.stderr b/tests/ui/branches_sharing_code/shared_at_top.stderr index 8d78fa5de7e49..d890b12ecbb4c 100644 --- a/tests/ui/branches_sharing_code/shared_at_top.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top.stderr @@ -10,7 +10,7 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ println!("Hello World!"); LL + if true { @@ -25,8 +25,8 @@ LL | | println!("The value y was set to: `{}`", y); LL | | let _z = y; | |___________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let y = 9; LL + println!("The value y was set to: `{}`", y); @@ -41,7 +41,7 @@ LL | / let _ = if x == 7 { LL | | let y = 16; | |___________________^ | -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ let y = 16; LL + let _ = if x == 7 { @@ -55,8 +55,8 @@ LL | | let used_value_name = "Different type"; LL | | println!("Str: {}", used_value_name); | |_____________________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let used_value_name = "Different type"; LL + println!("Str: {}", used_value_name); @@ -71,8 +71,8 @@ LL | | let can_be_overridden = "Move me"; LL | | println!("I'm also moveable"); | |______________________________________^ | - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let can_be_overridden = "Move me"; LL + println!("I'm also moveable"); @@ -87,7 +87,7 @@ LL | | println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint LL | | println!("Because `IF_SAME_THEN_ELSE` is allowed here"); | |________________________________________________________________^ | -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ println!("This should trigger the `SHARED_CODE_IN_IF_BLOCKS` lint."); LL + println!("Because `IF_SAME_THEN_ELSE` is allowed here"); diff --git a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr index 1db2343d3fe97..11843cc03d8d1 100644 --- a/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr +++ b/tests/ui/branches_sharing_code/shared_at_top_and_bottom.stderr @@ -1,4 +1,4 @@ -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:16:5 | LL | / if x == 7 { @@ -12,26 +12,26 @@ note: the lint level is defined here | LL | #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:28:5 | LL | / let _u = 9; LL | | } | |_____^ -help: consider moving the start statements out like this +help: consider moving these statements before the if | LL ~ let t = 7; LL + let _overlap_start = t * 2; LL + let _overlap_end = 2 * t; LL + if x == 7 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let _u = 9; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:32:5 | LL | / if x == 99 { @@ -40,29 +40,29 @@ LL | | let _overlap_start = r; LL | | let _overlap_middle = r * r; | |____________________________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:43:5 | LL | / let _overlap_end = r * r * r; LL | | let z = "end"; LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let r = 7; LL + let _overlap_start = r; LL + let _overlap_middle = r * r; LL + if x == 99 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let _overlap_end = r * r * r; LL + let z = "end"; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:61:5 | LL | / if (x > 7 && y < 13) || (x + y) % 2 == 1 { @@ -71,7 +71,7 @@ LL | | let b = 0xffff00ff; LL | | let e_id = gen_id(a, b); | |________________________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:81:5 | LL | / let pack = DataPack { @@ -82,15 +82,15 @@ LL | | }; LL | | process_data(pack); LL | | } | |_____^ - = warning: Some moved values might need to be renamed to avoid wrong references -help: consider moving the start statements out like this + = warning: some moved values might need to be renamed to avoid wrong references +help: consider moving these statements before the if | LL ~ let a = 0xcafe; LL + let b = 0xffff00ff; LL + let e_id = gen_id(a, b); LL + if (x > 7 && y < 13) || (x + y) % 2 == 1 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + let pack = DataPack { @@ -100,51 +100,51 @@ LL + some_data: vec![0x12, 0x34, 0x56, 0x78, 0x90], LL + }; ... -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:94:5 | LL | / let _ = if x == 7 { LL | | let _ = 19; | |___________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:103:5 | LL | / x << 2 LL | | }; | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements before the if | LL ~ let _ = 19; LL + let _ = if x == 7 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL ~ x << 2; | -error: all if blocks contain the same code at the start and the end. Here at the start +error: all if blocks contain the same code at both the start and the end --> $DIR/shared_at_top_and_bottom.rs:106:5 | LL | / if x == 9 { LL | | let _ = 17; | |___________________^ | -note: and here at the end +note: this code is shared at the end --> $DIR/shared_at_top_and_bottom.rs:115:5 | LL | / x * 4 LL | | } | |_____^ - = note: The end suggestion probably needs some adjustments to use the expression result correctly -help: consider moving the start statements out like this + = note: the end suggestion probably needs some adjustments to use the expression result correctly +help: consider moving these statements before the if | LL ~ let _ = 17; LL + if x == 9 { | -help: and consider moving the end statements out like this +help: consider moving these statements after the if | LL ~ } LL + x * 4 From 6fd2c6de90423ff6096da4005229df3b2110629d Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Sat, 28 May 2022 14:33:06 +0900 Subject: [PATCH 02/78] fix(lint): check const context https://github.com/rust-lang/rust-clippy/issues/8898 --- clippy_lints/src/checked_conversions.rs | 3 ++- tests/ui/checked_conversions.fixed | 5 +++++ tests/ui/checked_conversions.rs | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 7eeaaa0192147..df3485533b3a4 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, meets_msrv, msrvs, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -61,6 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions { } let result = if_chain! { + if !in_constant(cx, item.hir_id); if !in_external_macro(cx.sess(), item.span); if let ExprKind::Binary(op, left, right) = &item.kind; diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 0983d393b560e..cb7100bc9efae 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -71,4 +71,9 @@ pub fn i8_to_u8(value: i8) { let _ = value >= 0; } +// Do not lint +pub const fn issue_8898(i: u32) -> bool { + i <= i32::MAX as u32 +} + fn main() {} diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 7d26ace47fdf5..ed4e0692388a5 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -71,4 +71,9 @@ pub fn i8_to_u8(value: i8) { let _ = value >= 0; } +// Do not lint +pub const fn issue_8898(i: u32) -> bool { + i <= i32::MAX as u32 +} + fn main() {} From 007fae10ed8c8fa51b396bcc02071fe792f2c288 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Thu, 2 Jun 2022 00:57:08 +0900 Subject: [PATCH 03/78] fix(manual_find_map and manual_filter_map): check clone method --- clippy_lints/src/methods/filter_map.rs | 10 +++++++++- tests/ui/manual_filter_map.fixed | 22 ++++++++++++++++++++++ tests/ui/manual_filter_map.rs | 23 +++++++++++++++++++++++ tests/ui/manual_filter_map.stderr | 16 +++++++++++++++- tests/ui/manual_find_map.fixed | 20 ++++++++++++++++++++ tests/ui/manual_find_map.rs | 20 ++++++++++++++++++++ tests/ui/manual_find_map.stderr | 14 +++++++++++++- 7 files changed, 122 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 558cb6bd64e72..04ca295dc69cd 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -155,7 +155,15 @@ pub(super) fn check<'tcx>( } false }; - if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg); + + if match map_arg.kind { + ExprKind::MethodCall(clone, [original_arg], _) => { + clone.ident.name == sym::clone + && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg) + }, + _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg) + }; + then { let span = filter_span.with_hi(expr.span.hi()); let (filter_name, lint) = if is_find { diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed index fc8f58f8ea5cd..c88a4f43de209 100644 --- a/tests/ui/manual_filter_map.fixed +++ b/tests/ui/manual_filter_map.fixed @@ -35,3 +35,25 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct OptionFoo { + field: Option, +} + +struct ResultFoo { + field: Result, +} + +fn issue_8920() { + let vec = vec![OptionFoo { + field: Some(String::from("str")), + }]; + let _ = vec + .iter() + .filter_map(|f| f.field.clone()); + + let vec = vec![ResultFoo { + field: Ok(String::from("str")), + }]; + let _ = vec.iter().filter_map(|f| f.field.clone().ok()); +} diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs index 3af4bbee3bf82..bb859ebe31582 100644 --- a/tests/ui/manual_filter_map.rs +++ b/tests/ui/manual_filter_map.rs @@ -35,3 +35,26 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct OptionFoo { + field: Option, +} + +struct ResultFoo { + field: Result, +} + +fn issue_8920() { + let vec = vec![OptionFoo { + field: Some(String::from("str")), + }]; + let _ = vec + .iter() + .filter(|f| f.field.is_some()) + .map(|f| f.field.clone().unwrap()); + + let vec = vec![ResultFoo { + field: Ok(String::from("str")), + }]; + let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); +} diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr index 4d4e2d5c12fe9..a78343e882f98 100644 --- a/tests/ui/manual_filter_map.stderr +++ b/tests/ui/manual_filter_map.stderr @@ -18,5 +18,19 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` -error: aborting due to 3 previous errors +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:53:10 + | +LL | .filter(|f| f.field.is_some()) + | __________^ +LL | | .map(|f| f.field.clone().unwrap()); + | |__________________________________________^ help: try: `filter_map(|f| f.field.clone())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:59:24 + | +LL | let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|f| f.field.clone().ok())` + +error: aborting due to 5 previous errors diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed index 95e97c4fd1ff4..d78d72d8e8fa8 100644 --- a/tests/ui/manual_find_map.fixed +++ b/tests/ui/manual_find_map.fixed @@ -35,3 +35,23 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct OptionFoo { + field: Option, +} + +struct ResultFoo { + field: Result, +} + +fn issue_8920() { + let vec = vec![OptionFoo { + field: Some(String::from("str")), + }]; + let _ = vec.iter().find_map(|f| f.field.clone()); + + let vec = vec![ResultFoo { + field: Ok(String::from("str")), + }]; + let _ = vec.iter().find_map(|f| f.field.clone().ok()); +} diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs index cd3c82e3b25ab..74e8e52ed1627 100644 --- a/tests/ui/manual_find_map.rs +++ b/tests/ui/manual_find_map.rs @@ -35,3 +35,23 @@ fn to_opt(_: T) -> Option { fn to_res(_: T) -> Result { unimplemented!() } + +struct OptionFoo { + field: Option, +} + +struct ResultFoo { + field: Result, +} + +fn issue_8920() { + let vec = vec![OptionFoo { + field: Some(String::from("str")), + }]; + let _ = vec.iter().find(|f| f.field.is_some()).map(|f| f.field.clone().unwrap()); + + let vec = vec![ResultFoo { + field: Ok(String::from("str")), + }]; + let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); +} diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr index 9e7f798df4573..0b1465a842295 100644 --- a/tests/ui/manual_find_map.stderr +++ b/tests/ui/manual_find_map.stderr @@ -18,5 +18,17 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` -error: aborting due to 3 previous errors +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:51:24 + | +LL | let _ = vec.iter().find(|f| f.field.is_some()).map(|f| f.field.clone().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.clone())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:56:24 + | +LL | let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.clone().ok())` + +error: aborting due to 5 previous errors From 1fad95309bcac3d28f75afdd45ded51a890c1940 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:13:25 -0400 Subject: [PATCH 04/78] Fully stabilize NLL --- tests/ui/crashes/ice-6256.stderr | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/tests/ui/crashes/ice-6256.stderr b/tests/ui/crashes/ice-6256.stderr index ae4e6cad3328b..9cfcccf1e3cd9 100644 --- a/tests/ui/crashes/ice-6256.stderr +++ b/tests/ui/crashes/ice-6256.stderr @@ -1,18 +1,14 @@ -error[E0308]: mismatched types - --> $DIR/ice-6256.rs:13:28 +error[E0521]: borrowed data escapes outside of closure + --> $DIR/ice-6256.rs:13:26 | LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types - | ^^^^ lifetime mismatch - | - = note: expected reference `&(dyn TT + 'static)` - found reference `&dyn TT` -note: the anonymous lifetime #1 defined here... - --> $DIR/ice-6256.rs:13:13 - | -LL | let f = |x: &dyn TT| x.func(); //[default]~ ERROR: mismatched types - | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...does not necessarily outlive the static lifetime + | - - ^^^^^^^^ + | | | | + | | | `x` escapes the closure body here + | | | argument requires that `'1` must outlive `'static` + | | let's call the lifetime of this reference `'1` + | `x` is a reference that is only valid in the closure body error: aborting due to previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0521`. From 990f8bf5a6354d98a7e0b628f9f1b810644c3ad8 Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:55:33 +0900 Subject: [PATCH 05/78] refactor: Add some methods --- clippy_lints/src/methods/filter_map.rs | 19 ++++++++++-- tests/ui/manual_filter_map.fixed | 22 +++++++++++++- tests/ui/manual_filter_map.rs | 27 ++++++++++++++++- tests/ui/manual_filter_map.stderr | 42 +++++++++++++++++++++++++- tests/ui/manual_find_map.fixed | 20 +++++++++++- tests/ui/manual_find_map.rs | 24 ++++++++++++++- tests/ui/manual_find_map.stderr | 40 +++++++++++++++++++++++- 7 files changed, 185 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 04ca295dc69cd..6cabe74fdef4e 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -6,7 +6,7 @@ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_span::source_map::Span; use rustc_span::symbol::{sym, Symbol}; @@ -157,8 +157,8 @@ pub(super) fn check<'tcx>( }; if match map_arg.kind { - ExprKind::MethodCall(clone, [original_arg], _) => { - clone.ident.name == sym::clone + ExprKind::MethodCall(method, [original_arg], _) => { + acceptable_methods(method) && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg) }, _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg) @@ -179,3 +179,16 @@ pub(super) fn check<'tcx>( } } } + +fn acceptable_methods(method: &PathSegment<'_>) -> bool { + let methods: [Symbol; 6] = [ + sym::clone, + sym::as_ref, + sym!(as_deref), + sym!(as_mut), + sym!(as_deref_mut), + sym!(to_owned), + ]; + + methods.contains(&method.ident.name) +} diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed index c88a4f43de209..9de7040000a03 100644 --- a/tests/ui/manual_filter_map.fixed +++ b/tests/ui/manual_filter_map.fixed @@ -52,8 +52,28 @@ fn issue_8920() { .iter() .filter_map(|f| f.field.clone()); - let vec = vec![ResultFoo { + let mut vec = vec![ResultFoo { field: Ok(String::from("str")), }]; let _ = vec.iter().filter_map(|f| f.field.clone().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.field.as_ref().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.field.as_deref().ok()); + + let _ = vec + .iter_mut() + .filter_map(|f| f.field.as_mut().ok()); + + let _ = vec + .iter_mut() + .filter_map(|f| f.field.as_deref_mut().ok()); + + let _ = vec + .iter() + .filter_map(|f| f.field.to_owned().ok()); } diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs index bb859ebe31582..6766078d694a5 100644 --- a/tests/ui/manual_filter_map.rs +++ b/tests/ui/manual_filter_map.rs @@ -53,8 +53,33 @@ fn issue_8920() { .filter(|f| f.field.is_some()) .map(|f| f.field.clone().unwrap()); - let vec = vec![ResultFoo { + let mut vec = vec![ResultFoo { field: Ok(String::from("str")), }]; let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.field.is_ok()) + .map(|f| f.field.as_ref().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.field.is_ok()) + .map(|f| f.field.as_deref().unwrap()); + + let _ = vec + .iter_mut() + .filter(|f| f.field.is_ok()) + .map(|f| f.field.as_mut().unwrap()); + + let _ = vec + .iter_mut() + .filter(|f| f.field.is_ok()) + .map(|f| f.field.as_deref_mut().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.field.is_ok()) + .map(|f| f.field.to_owned().unwrap()); } diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr index a78343e882f98..5e67ca348fab0 100644 --- a/tests/ui/manual_filter_map.stderr +++ b/tests/ui/manual_filter_map.stderr @@ -32,5 +32,45 @@ error: `filter(..).map(..)` can be simplified as `filter_map(..)` LL | let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|f| f.field.clone().ok())` -error: aborting due to 5 previous errors +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:63:10 + | +LL | .filter(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_ref().unwrap()); + | |___________________________________________^ help: try: `filter_map(|f| f.field.as_ref().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:68:10 + | +LL | .filter(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_deref().unwrap()); + | |_____________________________________________^ help: try: `filter_map(|f| f.field.as_deref().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:73:10 + | +LL | .filter(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_mut().unwrap()); + | |___________________________________________^ help: try: `filter_map(|f| f.field.as_mut().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:78:10 + | +LL | .filter(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_deref_mut().unwrap()); + | |_________________________________________________^ help: try: `filter_map(|f| f.field.as_deref_mut().ok())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:83:10 + | +LL | .filter(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.to_owned().unwrap()); + | |_____________________________________________^ help: try: `filter_map(|f| f.field.to_owned().ok())` + +error: aborting due to 10 previous errors diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed index d78d72d8e8fa8..e7b5081ea59a5 100644 --- a/tests/ui/manual_find_map.fixed +++ b/tests/ui/manual_find_map.fixed @@ -50,8 +50,26 @@ fn issue_8920() { }]; let _ = vec.iter().find_map(|f| f.field.clone()); - let vec = vec![ResultFoo { + let mut vec = vec![ResultFoo { field: Ok(String::from("str")), }]; let _ = vec.iter().find_map(|f| f.field.clone().ok()); + + let _ = vec.iter().find_map(|f| f.field.as_ref().ok()); + + let _ = vec + .iter() + .find_map(|f| f.field.as_deref().ok()); + + let _ = vec + .iter_mut() + .find_map(|f| f.field.as_mut().ok()); + + let _ = vec + .iter_mut() + .find_map(|f| f.field.as_deref_mut().ok()); + + let _ = vec + .iter() + .find_map(|f| f.field.to_owned().ok()); } diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs index 74e8e52ed1627..46badbb9a18ea 100644 --- a/tests/ui/manual_find_map.rs +++ b/tests/ui/manual_find_map.rs @@ -50,8 +50,30 @@ fn issue_8920() { }]; let _ = vec.iter().find(|f| f.field.is_some()).map(|f| f.field.clone().unwrap()); - let vec = vec![ResultFoo { + let mut vec = vec![ResultFoo { field: Ok(String::from("str")), }]; let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + + let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.as_ref().unwrap()); + + let _ = vec + .iter() + .find(|f| f.field.is_ok()) + .map(|f| f.field.as_deref().unwrap()); + + let _ = vec + .iter_mut() + .find(|f| f.field.is_ok()) + .map(|f| f.field.as_mut().unwrap()); + + let _ = vec + .iter_mut() + .find(|f| f.field.is_ok()) + .map(|f| f.field.as_deref_mut().unwrap()); + + let _ = vec + .iter() + .find(|f| f.field.is_ok()) + .map(|f| f.field.to_owned().unwrap()); } diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr index 0b1465a842295..2b6955a17df83 100644 --- a/tests/ui/manual_find_map.stderr +++ b/tests/ui/manual_find_map.stderr @@ -30,5 +30,43 @@ error: `find(..).map(..)` can be simplified as `find_map(..)` LL | let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.clone().ok())` -error: aborting due to 5 previous errors +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:58:24 + | +LL | let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.as_ref().unwrap()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.as_ref().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:62:10 + | +LL | .find(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_deref().unwrap()); + | |_____________________________________________^ help: try: `find_map(|f| f.field.as_deref().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:67:10 + | +LL | .find(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_mut().unwrap()); + | |___________________________________________^ help: try: `find_map(|f| f.field.as_mut().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:72:10 + | +LL | .find(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.as_deref_mut().unwrap()); + | |_________________________________________________^ help: try: `find_map(|f| f.field.as_deref_mut().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:77:10 + | +LL | .find(|f| f.field.is_ok()) + | __________^ +LL | | .map(|f| f.field.to_owned().unwrap()); + | |_____________________________________________^ help: try: `find_map(|f| f.field.to_owned().ok())` + +error: aborting due to 10 previous errors From 5a49918f36adc9fb176a9be59b6fc2afd46c3572 Mon Sep 17 00:00:00 2001 From: DevAccentor Date: Sat, 4 Jun 2022 11:03:11 +0200 Subject: [PATCH 06/78] improve for_loops_over_fallibles to detect the usage of iter, iter_mut and into_iterator --- .../src/loops/for_loops_over_fallibles.rs | 40 +++++++++++++----- clippy_lints/src/loops/mod.rs | 12 +++++- tests/ui/for_loops_over_fallibles.rs | 17 +++++++- tests/ui/for_loops_over_fallibles.stderr | 40 ++++++++++++++---- tests/ui/manual_map_option.fixed | 1 + tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 42 +++++++++---------- 7 files changed, 110 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/loops/for_loops_over_fallibles.rs b/clippy_lints/src/loops/for_loops_over_fallibles.rs index 90530ebf0031f..77de90fd7b94a 100644 --- a/clippy_lints/src/loops/for_loops_over_fallibles.rs +++ b/clippy_lints/src/loops/for_loops_over_fallibles.rs @@ -7,9 +7,22 @@ use rustc_lint::LateContext; use rustc_span::symbol::sym; /// Checks for `for` loops over `Option`s and `Result`s. -pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) { let ty = cx.typeck_results().expr_ty(arg); if is_type_diagnostic_item(cx, ty, sym::Option) { + let help_string = if let Some(method_name) = method_name { + format!( + "consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + } else { + format!( + "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + }; span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -20,13 +33,22 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), None, - &format!( - "consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), + &help_string, ); } else if is_type_diagnostic_item(cx, ty, sym::Result) { + let help_string = if let Some(method_name) = method_name { + format!( + "consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + } else { + format!( + "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", + snippet(cx, pat.span, "_"), + snippet(cx, arg.span, "_") + ) + }; span_lint_and_help( cx, FOR_LOOPS_OVER_FALLIBLES, @@ -37,11 +59,7 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { snippet(cx, arg.span, "_") ), None, - &format!( - "consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`", - snippet(cx, pat.span, "_"), - snippet(cx, arg.span, "_") - ), + &help_string, ); } } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index d61be78895ffc..e32b228e104c2 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -188,6 +188,10 @@ declare_clippy_lint! { /// for x in &res { /// // .. /// } + /// + /// for x in res.iter() { + /// // .. + /// } /// ``` /// /// Use instead: @@ -695,10 +699,14 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { let method_name = method.ident.as_str(); // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x match method_name { - "iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name), + "iter" | "iter_mut" => { + explicit_iter_loop::check(cx, self_arg, arg, method_name); + for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); + }, "into_iter" => { explicit_iter_loop::check(cx, self_arg, arg, method_name); explicit_into_iter_loop::check(cx, self_arg, arg); + for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name)); }, "next" => { next_loop_linted = iter_next_loop::check(cx, arg); @@ -708,6 +716,6 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { } if !next_loop_linted { - for_loops_over_fallibles::check(cx, pat, arg); + for_loops_over_fallibles::check(cx, pat, arg, None); } } diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index 1b9dde87cd5a2..bde78a9fd4ae9 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -2,7 +2,7 @@ fn for_loops_over_fallibles() { let option = Some(1); - let result = option.ok_or("x not found"); + let mut result = option.ok_or("x not found"); let v = vec![0, 1, 2]; // check over an `Option` @@ -10,11 +10,26 @@ fn for_loops_over_fallibles() { println!("{}", x); } + // check over an `Option` + for x in option.iter() { + println!("{}", x); + } + // check over a `Result` for x in result { println!("{}", x); } + // check over a `Result` + for x in result.into_iter() { + println!("{}", x); + } + + // check over a `Result` + for x in result.iter_mut() { + println!("{}", x); + } + for x in option.ok_or("x not found") { println!("{}", x); } diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 52b94875aec4d..635e08182d5b5 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -7,16 +7,40 @@ LL | for x in option { = note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings` = help: consider replacing `for x in option` with `if let Some(x) = option` -error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement +error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:14:14 | +LL | for x in option.iter() { + | ^^^^^^ + | + = help: consider replacing `for x in option.iter()` with `if let Some(x) = option` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:19:14 + | LL | for x in result { | ^^^^^^ | = help: consider replacing `for x in result` with `if let Ok(x) = result` +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:24:14 + | +LL | for x in result.into_iter() { + | ^^^^^^ + | + = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` + +error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement + --> $DIR/for_loops_over_fallibles.rs:29:14 + | +LL | for x in result.iter_mut() { + | ^^^^^^ + | + = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` + error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:18:14 + --> $DIR/for_loops_over_fallibles.rs:33:14 | LL | for x in option.ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +48,7 @@ LL | for x in option.ok_or("x not found") { = help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")` error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want - --> $DIR/for_loops_over_fallibles.rs:24:14 + --> $DIR/for_loops_over_fallibles.rs:39:14 | LL | for x in v.iter().next() { | ^^^^^^^^^^^^^^^ @@ -32,7 +56,7 @@ LL | for x in v.iter().next() { = note: `#[deny(clippy::iter_next_loop)]` on by default error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:29:14 + --> $DIR/for_loops_over_fallibles.rs:44:14 | LL | for x in v.iter().next().and(Some(0)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +64,7 @@ LL | for x in v.iter().next().and(Some(0)) { = help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))` error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement - --> $DIR/for_loops_over_fallibles.rs:33:14 + --> $DIR/for_loops_over_fallibles.rs:48:14 | LL | for x in v.iter().next().ok_or("x not found") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +72,7 @@ LL | for x in v.iter().next().ok_or("x not found") { = help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")` error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:45:5 + --> $DIR/for_loops_over_fallibles.rs:60:5 | LL | / while let Some(x) = option { LL | | println!("{}", x); @@ -59,7 +83,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> $DIR/for_loops_over_fallibles.rs:51:5 + --> $DIR/for_loops_over_fallibles.rs:66:5 | LL | / while let Ok(x) = result { LL | | println!("{}", x); @@ -67,5 +91,5 @@ LL | | break; LL | | } | |_____^ -error: aborting due to 8 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index fc6a7abca0ebd..a59da4ae10bce 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -7,6 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 16508270f64d9..0bdbefa51e8b4 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -7,6 +7,7 @@ clippy::unit_arg, clippy::match_ref_pats, clippy::redundant_pattern_matching, + clippy::for_loops_over_fallibles, dead_code )] diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 0036b8151ded0..cdc2c0e62a9b9 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -1,5 +1,5 @@ error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:14:5 + --> $DIR/manual_map_option.rs:15:5 | LL | / match Some(0) { LL | | Some(_) => Some(2), @@ -10,7 +10,7 @@ LL | | }; = note: `-D clippy::manual-map` implied by `-D warnings` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:19:5 + --> $DIR/manual_map_option.rs:20:5 | LL | / match Some(0) { LL | | Some(x) => Some(x + 1), @@ -19,7 +19,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + 1)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:24:5 + --> $DIR/manual_map_option.rs:25:5 | LL | / match Some("") { LL | | Some(x) => Some(x.is_empty()), @@ -28,7 +28,7 @@ LL | | }; | |_____^ help: try this: `Some("").map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:29:5 + --> $DIR/manual_map_option.rs:30:5 | LL | / if let Some(x) = Some(0) { LL | | Some(!x) @@ -38,7 +38,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| !x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:36:5 + --> $DIR/manual_map_option.rs:37:5 | LL | / match Some(0) { LL | | Some(x) => { Some(std::convert::identity(x)) } @@ -47,7 +47,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(std::convert::identity)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:41:5 + --> $DIR/manual_map_option.rs:42:5 | LL | / match Some(&String::new()) { LL | | Some(x) => Some(str::len(x)), @@ -56,7 +56,7 @@ LL | | }; | |_____^ help: try this: `Some(&String::new()).map(|x| str::len(x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:51:5 + --> $DIR/manual_map_option.rs:52:5 | LL | / match &Some([0, 1]) { LL | | Some(x) => Some(x[0]), @@ -65,7 +65,7 @@ LL | | }; | |_____^ help: try this: `Some([0, 1]).as_ref().map(|x| x[0])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:56:5 + --> $DIR/manual_map_option.rs:57:5 | LL | / match &Some(0) { LL | | &Some(x) => Some(x * 2), @@ -74,7 +74,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x * 2)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:61:5 + --> $DIR/manual_map_option.rs:62:5 | LL | / match Some(String::new()) { LL | | Some(ref x) => Some(x.is_empty()), @@ -83,7 +83,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:66:5 + --> $DIR/manual_map_option.rs:67:5 | LL | / match &&Some(String::new()) { LL | | Some(x) => Some(x.len()), @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:71:5 + --> $DIR/manual_map_option.rs:72:5 | LL | / match &&Some(0) { LL | | &&Some(x) => Some(x + x), @@ -101,7 +101,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| x + x)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:84:9 + --> $DIR/manual_map_option.rs:85:9 | LL | / match &mut Some(String::new()) { LL | | Some(x) => Some(x.push_str("")), @@ -110,7 +110,7 @@ LL | | }; | |_________^ help: try this: `Some(String::new()).as_mut().map(|x| x.push_str(""))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:90:5 + --> $DIR/manual_map_option.rs:91:5 | LL | / match &mut Some(String::new()) { LL | | Some(ref x) => Some(x.len()), @@ -119,7 +119,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.len())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:95:5 + --> $DIR/manual_map_option.rs:96:5 | LL | / match &mut &Some(String::new()) { LL | | Some(x) => Some(x.is_empty()), @@ -128,7 +128,7 @@ LL | | }; | |_____^ help: try this: `Some(String::new()).as_ref().map(|x| x.is_empty())` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:100:5 + --> $DIR/manual_map_option.rs:101:5 | LL | / match Some((0, 1, 2)) { LL | | Some((x, y, z)) => Some(x + y + z), @@ -137,7 +137,7 @@ LL | | }; | |_____^ help: try this: `Some((0, 1, 2)).map(|(x, y, z)| x + y + z)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:105:5 + --> $DIR/manual_map_option.rs:106:5 | LL | / match Some([1, 2, 3]) { LL | | Some([first, ..]) => Some(first), @@ -146,7 +146,7 @@ LL | | }; | |_____^ help: try this: `Some([1, 2, 3]).map(|[first, ..]| first)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:110:5 + --> $DIR/manual_map_option.rs:111:5 | LL | / match &Some((String::new(), "test")) { LL | | Some((x, y)) => Some((y, x)), @@ -155,7 +155,7 @@ LL | | }; | |_____^ help: try this: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:168:5 + --> $DIR/manual_map_option.rs:169:5 | LL | / match Some(0) { LL | | Some(x) => Some(vec![x]), @@ -164,7 +164,7 @@ LL | | }; | |_____^ help: try this: `Some(0).map(|x| vec![x])` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:173:5 + --> $DIR/manual_map_option.rs:174:5 | LL | / match option_env!("") { LL | | Some(x) => Some(String::from(x)), @@ -173,7 +173,7 @@ LL | | }; | |_____^ help: try this: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:193:12 + --> $DIR/manual_map_option.rs:194:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -184,7 +184,7 @@ LL | | }; | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> $DIR/manual_map_option.rs:201:12 + --> $DIR/manual_map_option.rs:202:12 | LL | } else if let Some(x) = Some(0) { | ____________^ From f067783461aff336e1273c9948d620d4009aedc4 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 4 Jun 2022 13:34:07 +0200 Subject: [PATCH 07/78] Merge commit 'd9ddce8a223cb9916389c039777b6966ea448dc8' into clippyup --- CHANGELOG.md | 10 +- Cargo.toml | 5 +- clippy_dev/Cargo.toml | 2 +- clippy_dev/src/main.rs | 156 ++++---- clippy_dev/src/serve.rs | 10 +- .../src/almost_complete_letter_range.rs | 100 +++++ clippy_lints/src/approx_const.rs | 2 +- clippy_lints/src/as_conversions.rs | 9 +- clippy_lints/src/as_underscore.rs | 74 ++++ clippy_lints/src/assign_ops.rs | 10 +- clippy_lints/src/attrs.rs | 59 +-- clippy_lints/src/blocks_in_if_conditions.rs | 12 +- clippy_lints/src/booleans.rs | 19 +- clippy_lints/src/borrow_deref_ref.rs | 118 ++++++ clippy_lints/src/bytecount.rs | 8 +- .../src/casts/cast_abs_to_unsigned.rs | 46 +-- clippy_lints/src/checked_conversions.rs | 4 +- clippy_lints/src/cognitive_complexity.rs | 2 +- clippy_lints/src/collapsible_if.rs | 2 +- clippy_lints/src/comparison_chain.rs | 2 +- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/crate_in_macro_def.rs | 4 +- clippy_lints/src/dbg_macro.rs | 21 +- clippy_lints/src/derivable_impls.rs | 2 +- clippy_lints/src/derive.rs | 47 ++- clippy_lints/src/doc_link_with_quotes.rs | 60 +++ clippy_lints/src/double_comparison.rs | 2 +- clippy_lints/src/double_parens.rs | 14 +- clippy_lints/src/duration_subsec.rs | 18 +- clippy_lints/src/else_if_without_else.rs | 2 +- clippy_lints/src/empty_enum.rs | 3 +- clippy_lints/src/entry.rs | 2 +- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/eq_op.rs | 6 +- clippy_lints/src/equatable_if_let.rs | 2 +- clippy_lints/src/escape.rs | 6 +- clippy_lints/src/excessive_bools.rs | 3 +- clippy_lints/src/explicit_write.rs | 10 +- clippy_lints/src/fallible_impl_from.rs | 3 +- clippy_lints/src/float_literal.rs | 5 +- clippy_lints/src/floating_point_arithmetic.rs | 3 +- clippy_lints/src/format.rs | 7 +- clippy_lints/src/formatting.rs | 16 +- clippy_lints/src/get_first.rs | 69 ++++ clippy_lints/src/get_last_with_len.rs | 107 ------ clippy_lints/src/identity_op.rs | 125 ++++--- clippy_lints/src/large_enum_variant.rs | 88 +++-- clippy_lints/src/let_if_seq.rs | 2 +- clippy_lints/src/lib.register_all.rs | 18 +- clippy_lints/src/lib.register_complexity.rs | 6 +- clippy_lints/src/lib.register_correctness.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 29 +- clippy_lints/src/lib.register_nursery.rs | 3 +- clippy_lints/src/lib.register_pedantic.rs | 4 +- clippy_lints/src/lib.register_restriction.rs | 7 +- clippy_lints/src/lib.register_style.rs | 5 +- clippy_lints/src/lib.register_suspicious.rs | 5 +- clippy_lints/src/lib.rs | 39 +- clippy_lints/src/loops/mod.rs | 15 +- clippy_lints/src/loops/needless_range_loop.rs | 57 ++- clippy_lints/src/loops/never_loop.rs | 2 +- clippy_lints/src/manual_map.rs | 316 ---------------- clippy_lints/src/manual_non_exhaustive.rs | 2 +- clippy_lints/src/manual_unwrap_or.rs | 123 ------ clippy_lints/src/match_on_vec_items.rs | 104 ------ .../src/{ => matches}/collapsible_match.rs | 70 +--- clippy_lints/src/matches/manual_map.rs | 306 +++++++++++++++ clippy_lints/src/matches/manual_unwrap_or.rs | 83 +++++ clippy_lints/src/matches/match_bool.rs | 4 +- .../src/matches/match_like_matches.rs | 34 +- .../src/matches/match_on_vec_items.rs | 61 +++ .../{ => matches}/match_str_case_mismatch.rs | 75 +--- clippy_lints/src/matches/mod.rs | 351 +++++++++++++++++- clippy_lints/src/matches/needless_match.rs | 30 +- .../src/matches/redundant_pattern_match.rs | 20 +- .../significant_drop_in_scrutinee.rs | 216 ++++------- clippy_lints/src/matches/try_err.rs | 145 ++++++++ clippy_lints/src/methods/get_last_with_len.rs | 55 +++ clippy_lints/src/methods/iter_next_slice.rs | 9 +- clippy_lints/src/methods/mod.rs | 157 +++++--- clippy_lints/src/methods/no_effect_replace.rs | 47 +++ .../src/methods/option_map_or_none.rs | 4 +- .../src/methods/unnecessary_to_owned.rs | 2 +- clippy_lints/src/minmax.rs | 8 +- clippy_lints/src/misc.rs | 14 +- .../src/mismatching_type_param_order.rs | 116 ++++++ .../src/mixed_read_write_in_expression.rs | 2 +- clippy_lints/src/mut_key.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 7 +- clippy_lints/src/needless_late_init.rs | 13 +- .../{arithmetic.rs => numeric_arithmetic.rs} | 6 +- clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/ranges.rs | 18 +- clippy_lints/src/rc_clone_in_vec_init.rs | 56 ++- clippy_lints/src/redundant_clone.rs | 6 +- .../src/redundant_static_lifetimes.rs | 9 +- clippy_lints/src/returns.rs | 14 +- clippy_lints/src/shadow.rs | 53 +-- clippy_lints/src/size_of_in_element_count.rs | 2 +- clippy_lints/src/swap_ptr_to_ref.rs | 80 ++++ clippy_lints/src/trait_bounds.rs | 10 +- clippy_lints/src/transmute/mod.rs | 2 +- .../src/transmute/useless_transmute.rs | 47 +-- clippy_lints/src/try_err.rs | 186 ---------- clippy_lints/src/unused_rounding.rs | 69 ++++ clippy_lints/src/use_self.rs | 18 +- clippy_lints/src/utils/conf.rs | 4 + clippy_lints/src/utils/internal_lints.rs | 8 +- .../internal_lints/metadata_collector.rs | 58 ++- clippy_utils/src/consts.rs | 2 +- clippy_utils/src/higher.rs | 2 +- clippy_utils/src/hir_utils.rs | 6 +- clippy_utils/src/lib.rs | 19 +- clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/paths.rs | 2 + clippy_utils/src/qualify_min_const_fn.rs | 21 +- clippy_utils/src/source.rs | 23 +- clippy_utils/src/ty.rs | 22 +- doc/adding_lints.md | 11 +- doc/common_tools_writing_lints.md | 3 +- lintcheck/Cargo.toml | 2 +- lintcheck/src/config.rs | 22 +- rust-toolchain | 2 +- tests/compile-test.rs | 95 ++++- .../ui-internal/unnecessary_symbol_str.fixed | 1 + tests/ui-internal/unnecessary_symbol_str.rs | 1 + .../ui-internal/unnecessary_symbol_str.stderr | 10 +- tests/ui-toml/dbg_macro/clippy.toml | 1 + tests/ui-toml/dbg_macro/dbg_macro.rs | 39 ++ tests/ui-toml/dbg_macro/dbg_macro.stderr | 102 +++++ .../toml_unknown_key/conf_unknown_key.stderr | 1 + tests/ui-toml/unwrap_used/unwrap_used.rs | 2 +- tests/ui/almost_complete_letter_range.fixed | 66 ++++ tests/ui/almost_complete_letter_range.rs | 66 ++++ tests/ui/almost_complete_letter_range.stderr | 100 +++++ tests/ui/as_underscore.fixed | 13 + tests/ui/as_underscore.rs | 13 + tests/ui/as_underscore.stderr | 20 + tests/ui/bind_instead_of_map_multipart.fixed | 62 ++++ tests/ui/bind_instead_of_map_multipart.rs | 1 + tests/ui/bind_instead_of_map_multipart.stderr | 12 +- tests/ui/borrow_deref_ref.fixed | 59 +++ tests/ui/borrow_deref_ref.rs | 59 +++ tests/ui/borrow_deref_ref.stderr | 22 ++ tests/ui/borrow_deref_ref_unfixable.rs | 10 + tests/ui/borrow_deref_ref_unfixable.stderr | 18 + tests/ui/cast_abs_to_unsigned.fixed | 21 ++ tests/ui/cast_abs_to_unsigned.rs | 21 ++ tests/ui/cast_abs_to_unsigned.stderr | 92 ++++- tests/ui/collapsible_match2.rs | 2 +- tests/ui/collapsible_match2.stderr | 4 +- tests/ui/crashes/ice-8850.rs | 27 ++ tests/ui/crashes/ice-8850.stderr | 45 +++ tests/ui/dbg_macro.stderr | 35 +- tests/ui/debug_assert_with_mut_call.rs | 2 +- tests/ui/deref_by_slicing.fixed | 1 + tests/ui/deref_by_slicing.rs | 1 + tests/ui/deref_by_slicing.stderr | 18 +- tests/ui/derive_partial_eq_without_eq.fixed | 6 + tests/ui/derive_partial_eq_without_eq.rs | 6 + tests/ui/doc_link_with_quotes.rs | 12 + tests/ui/doc_link_with_quotes.stderr | 10 + tests/ui/empty_line_after_outer_attribute.rs | 10 + .../empty_line_after_outer_attribute.stderr | 12 +- tests/ui/explicit_deref_methods.fixed | 7 +- tests/ui/explicit_deref_methods.rs | 7 +- tests/ui/explicit_deref_methods.stderr | 24 +- tests/ui/forget_ref.rs | 1 + tests/ui/forget_ref.stderr | 36 +- tests/ui/get_first.fixed | 42 +++ tests/ui/get_first.rs | 42 +++ tests/ui/get_first.stderr | 22 ++ tests/ui/get_last_with_len.fixed | 28 +- tests/ui/get_last_with_len.rs | 28 +- tests/ui/get_last_with_len.stderr | 36 +- tests/ui/get_unwrap.fixed | 2 +- tests/ui/get_unwrap.rs | 2 +- tests/ui/identity_op.fixed | 119 ++++++ tests/ui/identity_op.rs | 63 ++-- tests/ui/identity_op.stderr | 260 +++++++------ tests/ui/implicit_clone.fixed | 118 ++++++ tests/ui/implicit_clone.rs | 3 +- tests/ui/implicit_clone.stderr | 24 +- tests/ui/issue_2356.fixed | 26 ++ tests/ui/issue_2356.rs | 2 + tests/ui/issue_2356.stderr | 4 +- tests/ui/iter_next_slice.fixed | 8 +- tests/ui/iter_next_slice.rs | 4 +- tests/ui/iter_next_slice.stderr | 4 +- tests/ui/large_enum_variant.rs | 32 ++ tests/ui/large_enum_variant.stderr | 68 +++- tests/ui/map_flatten_fixable.fixed | 37 ++ tests/ui/map_flatten_fixable.rs | 36 ++ tests/ui/map_flatten_fixable.stderr | 28 +- tests/ui/match_ref_pats.fixed | 118 ++++++ tests/ui/match_ref_pats.rs | 3 +- tests/ui/match_ref_pats.stderr | 10 +- tests/ui/match_str_case_mismatch.fixed | 186 ++++++++++ tests/ui/match_str_case_mismatch.rs | 2 + tests/ui/match_str_case_mismatch.stderr | 14 +- tests/ui/mismatching_type_param_order.rs | 60 +++ tests/ui/mismatching_type_param_order.stderr | 83 +++++ tests/ui/modulo_one.rs | 2 +- tests/ui/modulo_one.stderr | 10 +- tests/ui/needless_late_init.fixed | 273 ++++++++++++++ tests/ui/needless_late_init.rs | 44 ++- tests/ui/needless_late_init.stderr | 93 ++++- tests/ui/needless_late_init_fixable.fixed | 19 - tests/ui/needless_late_init_fixable.rs | 19 - tests/ui/needless_late_init_fixable.stderr | 70 ---- tests/ui/needless_lifetimes.rs | 3 +- tests/ui/needless_lifetimes.stderr | 62 ++-- tests/ui/needless_return.fixed | 10 +- tests/ui/needless_return.rs | 6 + tests/ui/needless_return.stderr | 20 +- tests/ui/no_effect_replace.rs | 51 +++ tests/ui/no_effect_replace.stderr | 52 +++ tests/ui/nonminimal_bool_methods.fixed | 111 ++++++ tests/ui/nonminimal_bool_methods.rs | 1 + tests/ui/nonminimal_bool_methods.stderr | 26 +- tests/ui/range_contains.fixed | 7 + tests/ui/range_contains.rs | 7 + tests/ui/range_contains.stderr | 26 +- tests/ui/rc_buffer.fixed | 28 ++ tests/ui/rc_buffer.rs | 2 + tests/ui/rc_buffer.stderr | 16 +- tests/ui/rc_buffer_arc.fixed | 27 ++ tests/ui/rc_buffer_arc.rs | 2 + tests/ui/rc_buffer_arc.stderr | 16 +- tests/ui/rc_clone_in_vec_init/arc.stderr | 16 +- tests/ui/rc_clone_in_vec_init/rc.stderr | 16 +- tests/ui/rc_clone_in_vec_init/weak.rs | 83 +++++ tests/ui/rc_clone_in_vec_init/weak.stderr | 201 ++++++++++ tests/ui/recursive_format_impl.rs | 3 +- tests/ui/recursive_format_impl.stderr | 20 +- tests/ui/shadow.rs | 7 + tests/ui/significant_drop_in_scrutinee.rs | 39 ++ tests/ui/significant_drop_in_scrutinee.stderr | 60 +-- tests/ui/suspicious_operation_groupings.fixed | 209 +++++++++++ tests/ui/suspicious_operation_groupings.rs | 3 +- .../ui/suspicious_operation_groupings.stderr | 52 +-- tests/ui/swap_ptr_to_ref.fixed | 24 ++ tests/ui/swap_ptr_to_ref.rs | 24 ++ tests/ui/swap_ptr_to_ref.stderr | 28 ++ tests/ui/swap_ptr_to_ref_unfixable.rs | 18 + tests/ui/swap_ptr_to_ref_unfixable.stderr | 22 ++ tests/ui/transmute.rs | 19 +- tests/ui/transmute.stderr | 88 ++--- tests/ui/transmute_undefined_repr.rs | 2 +- tests/ui/unicode.fixed | 36 ++ tests/ui/unicode.rs | 1 + tests/ui/unicode.stderr | 14 +- tests/ui/unit_arg_empty_blocks.fixed | 30 ++ tests/ui/unit_arg_empty_blocks.rs | 1 + tests/ui/unit_arg_empty_blocks.stderr | 8 +- tests/ui/unnecessary_cast.fixed | 91 +++++ tests/ui/unnecessary_cast.rs | 54 ++- tests/ui/unnecessary_cast.stderr | 120 +++++- tests/ui/unnecessary_cast_fixable.fixed | 50 --- tests/ui/unnecessary_cast_fixable.rs | 50 --- tests/ui/unnecessary_cast_fixable.stderr | 106 ------ tests/ui/unused_rounding.fixed | 9 + tests/ui/unused_rounding.rs | 9 + tests/ui/unused_rounding.stderr | 22 ++ tests/ui/use_self.fixed | 66 ++++ tests/ui/use_self.rs | 66 ++++ tests/ui/use_self.stderr | 62 +++- util/gh-pages/script.js | 37 +- 269 files changed, 7465 insertions(+), 2628 deletions(-) create mode 100644 clippy_lints/src/almost_complete_letter_range.rs create mode 100644 clippy_lints/src/as_underscore.rs create mode 100644 clippy_lints/src/borrow_deref_ref.rs create mode 100644 clippy_lints/src/doc_link_with_quotes.rs create mode 100644 clippy_lints/src/get_first.rs delete mode 100644 clippy_lints/src/get_last_with_len.rs delete mode 100644 clippy_lints/src/manual_map.rs delete mode 100644 clippy_lints/src/manual_unwrap_or.rs delete mode 100644 clippy_lints/src/match_on_vec_items.rs rename clippy_lints/src/{ => matches}/collapsible_match.rs (71%) create mode 100644 clippy_lints/src/matches/manual_map.rs create mode 100644 clippy_lints/src/matches/manual_unwrap_or.rs create mode 100644 clippy_lints/src/matches/match_on_vec_items.rs rename clippy_lints/src/{ => matches}/match_str_case_mismatch.rs (64%) rename clippy_lints/src/{ => matches}/significant_drop_in_scrutinee.rs (64%) create mode 100644 clippy_lints/src/matches/try_err.rs create mode 100644 clippy_lints/src/methods/get_last_with_len.rs create mode 100644 clippy_lints/src/methods/no_effect_replace.rs create mode 100644 clippy_lints/src/mismatching_type_param_order.rs rename clippy_lints/src/{arithmetic.rs => numeric_arithmetic.rs} (97%) create mode 100644 clippy_lints/src/swap_ptr_to_ref.rs delete mode 100644 clippy_lints/src/try_err.rs create mode 100644 clippy_lints/src/unused_rounding.rs create mode 100644 tests/ui-toml/dbg_macro/clippy.toml create mode 100644 tests/ui-toml/dbg_macro/dbg_macro.rs create mode 100644 tests/ui-toml/dbg_macro/dbg_macro.stderr create mode 100644 tests/ui/almost_complete_letter_range.fixed create mode 100644 tests/ui/almost_complete_letter_range.rs create mode 100644 tests/ui/almost_complete_letter_range.stderr create mode 100644 tests/ui/as_underscore.fixed create mode 100644 tests/ui/as_underscore.rs create mode 100644 tests/ui/as_underscore.stderr create mode 100644 tests/ui/bind_instead_of_map_multipart.fixed create mode 100644 tests/ui/borrow_deref_ref.fixed create mode 100644 tests/ui/borrow_deref_ref.rs create mode 100644 tests/ui/borrow_deref_ref.stderr create mode 100644 tests/ui/borrow_deref_ref_unfixable.rs create mode 100644 tests/ui/borrow_deref_ref_unfixable.stderr create mode 100644 tests/ui/crashes/ice-8850.rs create mode 100644 tests/ui/crashes/ice-8850.stderr create mode 100644 tests/ui/doc_link_with_quotes.rs create mode 100644 tests/ui/doc_link_with_quotes.stderr create mode 100644 tests/ui/get_first.fixed create mode 100644 tests/ui/get_first.rs create mode 100644 tests/ui/get_first.stderr create mode 100644 tests/ui/identity_op.fixed create mode 100644 tests/ui/implicit_clone.fixed create mode 100644 tests/ui/issue_2356.fixed create mode 100644 tests/ui/match_ref_pats.fixed create mode 100644 tests/ui/match_str_case_mismatch.fixed create mode 100644 tests/ui/mismatching_type_param_order.rs create mode 100644 tests/ui/mismatching_type_param_order.stderr create mode 100644 tests/ui/needless_late_init.fixed delete mode 100644 tests/ui/needless_late_init_fixable.fixed delete mode 100644 tests/ui/needless_late_init_fixable.rs delete mode 100644 tests/ui/needless_late_init_fixable.stderr create mode 100644 tests/ui/no_effect_replace.rs create mode 100644 tests/ui/no_effect_replace.stderr create mode 100644 tests/ui/nonminimal_bool_methods.fixed create mode 100644 tests/ui/rc_buffer.fixed create mode 100644 tests/ui/rc_buffer_arc.fixed create mode 100644 tests/ui/rc_clone_in_vec_init/weak.rs create mode 100644 tests/ui/rc_clone_in_vec_init/weak.stderr create mode 100644 tests/ui/suspicious_operation_groupings.fixed create mode 100644 tests/ui/swap_ptr_to_ref.fixed create mode 100644 tests/ui/swap_ptr_to_ref.rs create mode 100644 tests/ui/swap_ptr_to_ref.stderr create mode 100644 tests/ui/swap_ptr_to_ref_unfixable.rs create mode 100644 tests/ui/swap_ptr_to_ref_unfixable.stderr create mode 100644 tests/ui/unicode.fixed create mode 100644 tests/ui/unit_arg_empty_blocks.fixed create mode 100644 tests/ui/unnecessary_cast.fixed delete mode 100644 tests/ui/unnecessary_cast_fixable.fixed delete mode 100644 tests/ui/unnecessary_cast_fixable.rs delete mode 100644 tests/ui/unnecessary_cast_fixable.stderr create mode 100644 tests/ui/unused_rounding.fixed create mode 100644 tests/ui/unused_rounding.rs create mode 100644 tests/ui/unused_rounding.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ef00fcbafa5..6ef338b819d4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3276,9 +3276,11 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops @@ -3296,6 +3298,7 @@ Released 2018-09-13 [`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison [`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison [`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref [`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const [`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box [`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection @@ -3362,6 +3365,7 @@ Released 2018-09-13 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`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 [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use @@ -3435,6 +3439,7 @@ Released 2018-09-13 [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 [`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap [`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion @@ -3556,6 +3561,7 @@ Released 2018-09-13 [`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max [`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute [`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order [`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items @@ -3613,6 +3619,7 @@ Released 2018-09-13 [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default [`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace [`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions @@ -3729,7 +3736,6 @@ Released 2018-09-13 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive -[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign @@ -3750,6 +3756,7 @@ Released 2018-09-13 [`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings [`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn [`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref [`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments [`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment [`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr @@ -3820,6 +3827,7 @@ Released 2018-09-13 [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings diff --git a/Cargo.toml b/Cargo.toml index 373e720b0d5c0..d23d681df00c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,7 @@ tempfile = { version = "3.2", optional = true } termize = "0.1" [dev-dependencies] -compiletest_rs = { version = "0.7.1", features = ["tmp"] } +compiletest_rs = { version = "0.8", features = ["tmp"] } tester = "0.9" regex = "1.5" # This is used by the `collect-metadata` alias. @@ -40,6 +40,7 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies +clap = { version = "3.1", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" @@ -48,7 +49,7 @@ quote = "1.0" serde = { version = "1.0.125", features = ["derive"] } syn = { version = "1.0", features = ["full"] } futures = "0.3" -parking_lot = "0.11.2" +parking_lot = "0.12" tokio = { version = "1", features = ["io-util"] } rustc-semver = "1.1" diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 2cfbcea5034e6..b0d470a2124d8 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] aho-corasick = "0.7" -clap = "2.33" +clap = "3.1" indoc = "1.0" itertools = "0.10.1" opener = "0.5" diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index d5cd7ca96c0c0..ee535b1d3be8b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -2,20 +2,20 @@ // warn on lints, that are included in `rust-lang/rust`s bootstrap #![warn(rust_2018_idioms, unused_lifetimes)] -use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +use clap::{Arg, ArgMatches, Command}; use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints}; use indoc::indoc; fn main() { let matches = get_clap_config(); match matches.subcommand() { - ("bless", Some(matches)) => { + Some(("bless", matches)) => { bless::bless(matches.is_present("ignore-timestamp")); }, - ("fmt", Some(matches)) => { + Some(("fmt", matches)) => { fmt::run(matches.is_present("check"), matches.is_present("verbose")); }, - ("update_lints", Some(matches)) => { + Some(("update_lints", matches)) => { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { @@ -24,7 +24,7 @@ fn main() { update_lints::update(update_lints::UpdateMode::Change); } }, - ("new_lint", Some(matches)) => { + Some(("new_lint", matches)) => { match new_lint::create( matches.value_of("pass"), matches.value_of("name"), @@ -35,8 +35,8 @@ fn main() { Err(e) => eprintln!("Unable to create lint: {}", e), } }, - ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => { + Some(("setup", sub_command)) => match sub_command.subcommand() { + Some(("intellij", matches)) => { if matches.is_present("remove") { setup::intellij::remove_rustc_src(); } else { @@ -47,14 +47,14 @@ fn main() { ); } }, - ("git-hook", Some(matches)) => { + Some(("git-hook", matches)) => { if matches.is_present("remove") { setup::git_hook::remove_hook(); } else { setup::git_hook::install_hook(matches.is_present("force-override")); } }, - ("vscode-tasks", Some(matches)) => { + Some(("vscode-tasks", matches)) => { if matches.is_present("remove") { setup::vscode::remove_tasks(); } else { @@ -63,23 +63,23 @@ fn main() { }, _ => {}, }, - ("remove", Some(sub_command)) => match sub_command.subcommand() { - ("git-hook", Some(_)) => setup::git_hook::remove_hook(), - ("intellij", Some(_)) => setup::intellij::remove_rustc_src(), - ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(), + Some(("remove", sub_command)) => match sub_command.subcommand() { + Some(("git-hook", _)) => setup::git_hook::remove_hook(), + Some(("intellij", _)) => setup::intellij::remove_rustc_src(), + Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(), _ => {}, }, - ("serve", Some(matches)) => { + Some(("serve", matches)) => { let port = matches.value_of("port").unwrap().parse().unwrap(); let lint = matches.value_of("lint"); serve::run(port, lint); }, - ("lint", Some(matches)) => { + Some(("lint", matches)) => { let path = matches.value_of("path").unwrap(); let args = matches.values_of("args").into_iter().flatten(); lint::run(path, args); }, - ("rename_lint", Some(matches)) => { + Some(("rename_lint", matches)) => { let old_name = matches.value_of("old_name").unwrap(); let new_name = matches.value_of("new_name").unwrap_or(old_name); let uplift = matches.is_present("uplift"); @@ -89,35 +89,24 @@ fn main() { } } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("Clippy developer tooling") - .setting(AppSettings::ArgRequiredElseHelp) +fn get_clap_config() -> ArgMatches { + Command::new("Clippy developer tooling") + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("bless") - .about("bless the test output changes") - .arg( - Arg::with_name("ignore-timestamp") - .long("ignore-timestamp") - .help("Include files updated before clippy was built"), - ), + Command::new("bless").about("bless the test output changes").arg( + Arg::new("ignore-timestamp") + .long("ignore-timestamp") + .help("Include files updated before clippy was built"), + ), ) .subcommand( - SubCommand::with_name("fmt") + Command::new("fmt") .about("Run rustfmt on all projects and tests") - .arg( - Arg::with_name("check") - .long("check") - .help("Use the rustfmt --check option"), - ) - .arg( - Arg::with_name("verbose") - .short("v") - .long("verbose") - .help("Echo commands run"), - ), + .arg(Arg::new("check").long("check").help("Use the rustfmt --check option")) + .arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")), ) .subcommand( - SubCommand::with_name("update_lints") + Command::new("update_lints") .about("Updates lint registration and information from the source code") .long_about( "Makes sure that:\n \ @@ -127,23 +116,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) - .arg(Arg::with_name("print-only").long("print-only").help( + .arg(Arg::new("print-only").long("print-only").help( "Print a table of lints to STDOUT. \ This does not include deprecated and internal lints. \ (Does not modify any files)", )) .arg( - Arg::with_name("check") + Arg::new("check") .long("check") .help("Checks that `cargo dev update_lints` has been run. Used on CI."), ), ) .subcommand( - SubCommand::with_name("new_lint") + Command::new("new_lint") .about("Create new lint and run `cargo dev update_lints`") .arg( - Arg::with_name("pass") - .short("p") + Arg::new("pass") + .short('p') .long("pass") .help("Specify whether the lint runs during the early or late pass") .takes_value(true) @@ -151,16 +140,16 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .required(true), ) .arg( - Arg::with_name("name") - .short("n") + Arg::new("name") + .short('n') .long("name") .help("Name of the new lint in snake case, ex: fn_too_long") .takes_value(true) .required(true), ) .arg( - Arg::with_name("category") - .short("c") + Arg::new("category") + .short('c') .long("category") .help("What category the lint belongs to") .default_value("nursery") @@ -179,29 +168,25 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ]) .takes_value(true), ) - .arg( - Arg::with_name("msrv") - .long("msrv") - .help("Add MSRV config code to the lint"), - ), + .arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")), ) .subcommand( - SubCommand::with_name("setup") + Command::new("setup") .about("Support for setting up your personal development environment") - .setting(AppSettings::ArgRequiredElseHelp) + .arg_required_else_help(true) .subcommand( - SubCommand::with_name("intellij") + Command::new("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the dependencies added with 'cargo dev setup intellij'") .required(false), ) .arg( - Arg::with_name("rustc-repo-path") + Arg::new("rustc-repo-path") .long("repo-path") - .short("r") + .short('r') .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") @@ -210,66 +195,65 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { ), ) .subcommand( - SubCommand::with_name("git-hook") + Command::new("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of an existing git pre-commit hook") .required(false), ), ) .subcommand( - SubCommand::with_name("vscode-tasks") + Command::new("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") .arg( - Arg::with_name("remove") + Arg::new("remove") .long("remove") .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") .required(false), ) .arg( - Arg::with_name("force-override") + Arg::new("force-override") .long("force-override") - .short("f") + .short('f') .help("Forces the override of existing vscode tasks") .required(false), ), ), ) .subcommand( - SubCommand::with_name("remove") + Command::new("remove") .about("Support for undoing changes done by the setup command") - .setting(AppSettings::ArgRequiredElseHelp) - .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook")) - .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks")) + .arg_required_else_help(true) + .subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook")) + .subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks")) .subcommand( - SubCommand::with_name("intellij") - .about("Removes rustc source paths added via `cargo dev setup intellij`"), + Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"), ), ) .subcommand( - SubCommand::with_name("serve") + Command::new("serve") .about("Launch a local 'ALL the Clippy Lints' website in a browser") .arg( - Arg::with_name("port") + Arg::new("port") .long("port") - .short("p") + .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)")), + .arg(Arg::new("lint").help("Which lint's page to load initially (optional)")), ) .subcommand( - SubCommand::with_name("lint") + Command::new("lint") .about("Manually run clippy on a file or package") .after_help(indoc! {" EXAMPLES @@ -288,33 +272,33 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( - Arg::with_name("path") + Arg::new("path") .required(true) .help("The path to a file or package directory to lint"), ) .arg( - Arg::with_name("args") - .multiple(true) + Arg::new("args") + .multiple_occurrences(true) .help("Pass extra arguments to cargo/clippy-driver"), ), ) .subcommand( - SubCommand::with_name("rename_lint") + Command::new("rename_lint") .about("Renames the given lint") .arg( - Arg::with_name("old_name") + Arg::new("old_name") .index(1) .required(true) .help("The name of the lint to rename"), ) .arg( - Arg::with_name("new_name") + Arg::new("new_name") .index(2) - .required_unless("uplift") + .required_unless_present("uplift") .help("The new name of the lint"), ) .arg( - Arg::with_name("uplift") + Arg::new("uplift") .long("uplift") .help("This lint will be uplifted into rustc"), ), diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs index b36e2a28ee454..d55b1a354d003 100644 --- a/clippy_dev/src/serve.rs +++ b/clippy_dev/src/serve.rs @@ -1,4 +1,5 @@ -use std::ffi::{OsStr, OsString}; +use std::ffi::OsStr; +use std::num::ParseIntError; use std::path::Path; use std::process::Command; use std::thread; @@ -59,9 +60,6 @@ fn mtime(path: impl AsRef) -> SystemTime { } #[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())), - } +pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> { + arg.to_string_lossy().parse::().map(|_| ()) } diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs new file mode 100644 index 0000000000000..b364a370efab5 --- /dev/null +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -0,0 +1,100 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{trim_span, walk_span_to_context}; +use clippy_utils::{meets_msrv, msrvs}; +use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but + /// don't because they're a half open range. + /// + /// ### Why is this bad? + /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters. + /// + /// ### Example + /// ```rust + /// let _ = 'a'..'z'; + /// ``` + /// Use instead: + /// ```rust + /// let _ = 'a'..='z'; + /// ``` + #[clippy::version = "1.63.0"] + pub ALMOST_COMPLETE_LETTER_RANGE, + suspicious, + "almost complete letter range" +} +impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); + +pub struct AlmostCompleteLetterRange { + msrv: Option, +} +impl AlmostCompleteLetterRange { + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +impl EarlyLintPass for AlmostCompleteLetterRange { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind { + let ctxt = e.span.ctxt(); + let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) + && let Some(end) = walk_span_to_context(end.span, ctxt) + && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + { + Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) + } else { + None + }; + check_range(cx, e.span, start, end, sugg); + } + } + + fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) { + if let PatKind::Range(Some(start), Some(end), kind) = &p.kind + && matches!(kind.node, RangeEnd::Excluded) + { + let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + "..=" + } else { + "..." + }; + check_range(cx, p.span, start, end, Some((kind.span, sugg))); + } + } + + extract_msrv_attr!(EarlyContext); +} + +fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) { + if let ExprKind::Lit(start_lit) = &start.peel_parens().kind + && let ExprKind::Lit(end_lit) = &end.peel_parens().kind + && matches!( + (&start_lit.kind, &end_lit.kind), + (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z')) + | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z')) + ) + { + span_lint_and_then( + cx, + ALMOST_COMPLETE_LETTER_RANGE, + span, + "almost complete ascii letter range", + |diag| { + if let Some((span, sugg)) = sugg { + diag.span_suggestion( + span, + "use an inclusive range", + sugg.to_owned(), + Applicability::MaybeIncorrect, + ); + } + } + ); + } +} diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index da1b646f4777a..159f3b0cd014a 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -28,7 +28,7 @@ declare_clippy_lint! { /// let x = 3.14; /// let y = 1_f64 / x; /// ``` - /// Use predefined constants instead: + /// Use instead: /// ```rust /// let x = std::f32::consts::PI; /// let y = std::f64::consts::FRAC_1_PI; diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs index 88b91d589074d..6e5c8f445818e 100644 --- a/clippy_lints/src/as_conversions.rs +++ b/clippy_lints/src/as_conversions.rs @@ -29,15 +29,14 @@ declare_clippy_lint! { /// f(a as u16); /// ``` /// - /// Usually better represents the semantics you expect: + /// Use instead: /// ```rust,ignore /// f(a.try_into()?); - /// ``` - /// or - /// ```rust,ignore + /// + /// // or + /// /// f(a.try_into().expect("Unexpected u16 overflow in f")); /// ``` - /// #[clippy::version = "1.41.0"] pub AS_CONVERSIONS, restriction, diff --git a/clippy_lints/src/as_underscore.rs b/clippy_lints/src/as_underscore.rs new file mode 100644 index 0000000000000..464be4218dd4d --- /dev/null +++ b/clippy_lints/src/as_underscore.rs @@ -0,0 +1,74 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Check for the usage of `as _` conversion using inferred type. + /// + /// ### Why is this bad? + /// The conversion might include lossy conversion and dangerous cast that might go + /// undetected du to the type being inferred. + /// + /// The lint is allowed by default as using `_` is less wordy than always specifying the type. + /// + /// ### Example + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as _); + /// ``` + /// Use instead: + /// ```rust + /// fn foo(n: usize) {} + /// let n: u16 = 256; + /// foo(n as usize); + /// ``` + #[clippy::version = "1.63.0"] + pub AS_UNDERSCORE, + restriction, + "detects `as _` conversion" +} +declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]); + +impl<'tcx> LateLintPass<'tcx> for AsUnderscore { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if in_external_macro(cx.sess(), expr.span) { + return; + } + + if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind { + + let ty_resolved = cx.typeck_results().expr_ty(expr); + if let ty::Error(_) = ty_resolved.kind() { + span_lint_and_help( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + None, + "consider giving the type explicitly", + ); + } else { + span_lint_and_then( + cx, + AS_UNDERSCORE, + expr.span, + "using `as _` conversion", + |diag| { + diag.span_suggestion( + ty.span, + "consider giving the type explicitly", + format!("{}", ty_resolved), + Applicability::MachineApplicable, + ); + } + ); + } + } + } +} diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 4c2d3366483af..f81da2d422333 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -27,10 +27,16 @@ declare_clippy_lint! { /// let mut a = 5; /// let b = 0; /// // ... - /// // Bad + /// /// a = a + b; + /// ``` + /// + /// Use instead: + /// ```rust + /// let mut a = 5; + /// let b = 0; + /// // ... /// - /// // Good /// a += b; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 3de91f3d24a91..770cb6a3d7b8b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -89,13 +89,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad /// #[deny(dead_code)] /// extern crate foo; /// #[forbid(dead_code)] /// use foo::bar; + /// ``` /// - /// // Ok + /// Use instead: + /// ```rust,ignore /// #[allow(unused_imports)] /// use foo::baz; /// #[allow(unused_imports)] @@ -146,15 +147,19 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() { } + /// ``` + /// + /// Use instead: + /// ```rust /// // Good (as inner attribute) /// #![allow(dead_code)] /// /// fn this_is_fine() { } /// - /// // Bad - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } + /// // or /// /// // Good (as outer attribute) /// #[allow(dead_code)] @@ -175,12 +180,11 @@ declare_clippy_lint! { /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. /// /// ### Example - /// Bad: /// ```rust /// #![deny(clippy::restriction)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![deny(clippy::as_conversions)] /// ``` @@ -205,13 +209,12 @@ declare_clippy_lint! { /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) /// /// ### Example - /// Bad: /// ```rust /// #[cfg_attr(rustfmt, rustfmt_skip)] /// fn main() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #[rustfmt::skip] /// fn main() { } @@ -231,20 +234,20 @@ declare_clippy_lint! { /// by the conditional compilation engine. /// /// ### Example - /// Bad: /// ```rust /// #[cfg(linux)] /// fn conditional() { } /// ``` /// - /// Good: + /// Use instead: /// ```rust + /// # mod hidden { /// #[cfg(target_os = "linux")] /// fn conditional() { } - /// ``` + /// # } + /// + /// // or /// - /// Or: - /// ```rust /// #[cfg(unix)] /// fn conditional() { } /// ``` @@ -266,14 +269,13 @@ declare_clippy_lint! { /// ensure that others understand the reasoning /// /// ### Example - /// Bad: /// ```rust /// #![feature(lint_reasons)] /// /// #![allow(clippy::some_lint)] /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(lint_reasons)] /// @@ -585,15 +587,21 @@ impl EarlyLintPass for EarlyAttributes { } fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - for attr in &item.attrs { + let mut iter = item.attrs.iter().peekable(); + while let Some(attr) = iter.next() { if matches!(attr.kind, AttrKind::Normal(..)) && attr.style == AttrStyle::Outer && is_present_in_source(cx, attr.span) { let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); + let end_of_attr_to_next_attr_or_item = Span::new( + attr.span.hi(), + iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), + item.span.ctxt(), + item.span.parent(), + ); - if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) { + if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) { let lines = snippet.split('\n').collect::>(); let lines = without_block_comments(lines); @@ -623,8 +631,15 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Opti if feature_item.has_name(sym::rustfmt); // check for `rustfmt_skip` and `rustfmt::skip` if let Some(skip_item) = &items[1].meta_item(); - if skip_item.has_name(sym!(rustfmt_skip)) || - skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip; + if skip_item.has_name(sym!(rustfmt_skip)) + || skip_item + .path + .segments + .last() + .expect("empty path in attribute") + .ident + .name + == sym::skip; // Only lint outer attributes, because custom inner attributes are unstable // Tracking issue: https://github.com/rust-lang/rust/issues/54726 if attr.style == AttrStyle::Outer; diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 4c4dd85d518a6..5bd7a342389fe 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -22,21 +22,17 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// // Bad + /// # fn somefunc() -> bool { true }; /// if { true } { /* ... */ } /// - /// // Good - /// if true { /* ... */ } + /// if { let x = somefunc(); x } { /* ... */ } /// ``` /// - /// // or - /// + /// Use instead: /// ```rust /// # fn somefunc() -> bool { true }; - /// // Bad - /// if { let x = somefunc(); x } { /* ... */ } + /// if true { /* ... */ } /// - /// // Good /// let res = { let x = somefunc(); x }; /// if res { /* ... */ } /// ``` diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 0adb6327164e7..e4e122ba6eb59 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -27,8 +27,14 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// if a && true // should be: if a - /// if !(a == b) // should be: if a != b + /// if a && true {} + /// if !(a == b) {} + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// if a != b {} /// ``` #[clippy::version = "pre 1.29.0"] pub NONMINIMAL_BOOL, @@ -48,10 +54,15 @@ declare_clippy_lint! { /// Ignores short circuiting behavior. /// /// ### Example - /// ```ignore + /// ```rust,ignore + /// // The `b` is unnecessary, the expression is equivalent to `if a`. /// if a && b || a { ... } /// ``` - /// The `b` is unnecessary, the expression is equivalent to `if a`. + /// + /// Use instead: + /// ```rust,ignore + /// if a {} + /// ``` #[clippy::version = "pre 1.29.0"] pub LOGIC_BUG, correctness, diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs new file mode 100644 index 0000000000000..ec2f31cf67374 --- /dev/null +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -0,0 +1,118 @@ +use crate::reference::DEREF_ADDROF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_parent_expr, is_lint_allowed}; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::Mutability; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `&*(&T)`. + /// + /// ### Why is this bad? + /// Dereferencing and then borrowing a reference value has no effect in most cases. + /// + /// ### Known problems + /// false negative on such code: + /// ``` + /// let x = &12; + /// let addr_x = &x as *const _ as usize; + /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd. + /// // But if we fix it, assert will fail. + /// assert_ne!(addr_x, addr_y); + /// ``` + /// + /// ### Example + /// ```rust + /// let s = &String::new(); + /// + /// // Bad + /// let a: &String = &* s; + /// foo(&*s); + /// + /// // Good + /// let a: &String = s; + /// foo(&**s); + /// + /// fn foo(_: &str){ } + /// ``` + #[clippy::version = "1.59.0"] + pub BORROW_DEREF_REF, + complexity, + "deref on an immutable reference returns the same type as itself" +} + +declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); + +impl LateLintPass<'_> for BorrowDerefRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) { + if_chain! { + if !e.span.from_expansion(); + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; + if !addrof_target.span.from_expansion(); + if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; + if !deref_target.span.from_expansion(); + if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); + let ref_ty = cx.typeck_results().expr_ty(deref_target); + if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); + then{ + + if let Some(parent_expr) = get_parent_expr(cx, e){ + if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && + !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) { + return; + } + + // modification to `&mut &*x` is different from `&mut x` + if matches!(deref_target.kind, ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..)) + && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { + return; + } + } + + span_lint_and_then( + cx, + BORROW_DEREF_REF, + e.span, + "deref on an immutable reference", + |diag| { + diag.span_suggestion( + e.span, + "if you would like to reborrow, try removing `&*`", + snippet_opt(cx, deref_target.span).unwrap(), + Applicability::MachineApplicable + ); + + // has deref trait -> give 2 help + // doesn't have deref trait -> give 1 help + if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ + if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { + return; + } + } + + diag.span_suggestion( + e.span, + "if you would like to deref, try using `&**`", + format!( + "&**{}", + &snippet_opt(cx, deref_target.span).unwrap(), + ), + Applicability::MaybeIncorrect + ); + + } + ); + + } + } + } +} diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index 02d97bf43df83..bfdbaf2413a25 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -28,7 +28,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let vec = vec![1_u8]; - /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead + /// let count = vec.iter().filter(|x| **x == 0u8).count(); + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// # let vec = vec![1_u8]; + /// let count = bytecount::count(&vec, 0u8); /// ``` #[clippy::version = "pre 1.29.0"] pub NAIVE_BYTECOUNT, diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 6bac6bf83f8e5..64ea326b75a0d 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -18,25 +17,28 @@ pub(super) fn check( cast_to: Ty<'_>, msrv: Option, ) { - if_chain! { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS); - if cast_from.is_integral(); - if cast_to.is_integral(); - if cast_from.is_signed(); - if !cast_to.is_signed(); - if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind; - if let method_name = method_path.ident.name.as_str(); - if method_name == "abs"; - then { - span_lint_and_sugg( - cx, - CAST_ABS_TO_UNSIGNED, - expr.span, - &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to), - "replace with", - format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), - Applicability::MachineApplicable, - ); - } + if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + && let ty::Int(from) = cast_from.kind() + && let ty::Uint(to) = cast_to.kind() + && let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind + && method_path.ident.name.as_str() == "abs" + { + let span = if from.bit_width() == to.bit_width() { + expr.span + } else { + // if the result of `.unsigned_abs` would be a different type, keep the cast + // e.g. `i64 -> usize`, `i16 -> u8` + cast_expr.span + }; + + span_lint_and_sugg( + cx, + CAST_ABS_TO_UNSIGNED, + span, + &format!("casting the result of `{cast_from}::abs()` to {cast_to}"), + "replace with", + format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")), + Applicability::MachineApplicable, + ); } } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 7eeaaa0192147..1010340c71213 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -318,7 +318,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: if let QPath::TypeRelative(ty, path) = &path; if path.ident.name.as_str() == function; if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; - if let [int] = &*tp.segments; + if let [int] = tp.segments; then { let name = int.ident.name.as_str(); candidates.iter().find(|c| &name == *c).copied() @@ -332,7 +332,7 @@ fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { if_chain! { if let QPath::Resolved(_, path) = *path; - if let [ty] = &*path.segments; + if let [ty] = path.segments; then { let name = ty.ident.name.as_str(); INTS.iter().find(|c| &name == *c).copied() diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 317c4bfb3226e..33c44f8b2dba9 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// complexity. /// /// ### Example - /// No. You'll see it when you get the warning. + /// You'll see it when you get the warning. #[clippy::version = "1.35.0"] pub COGNITIVE_COMPLEXITY, nursery, diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3227e6e86af4e..3eceb848822e9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// /// ``` /// - /// Should be written: + /// Use instead: /// /// ```rust,ignore /// if x && y { diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 399d11472b09b..913e081af3bda 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -34,7 +34,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust,ignore /// use std::cmp::Ordering; diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index e6a0162fd0272..1e9a115301100 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -141,7 +141,7 @@ declare_clippy_lint! { /// }; /// ``` /// - /// Could be written as: + /// Use instead: /// ```ignore /// println!("Hello World"); /// let foo = if … { diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index fc141b4a6e3af..9b8a481b6eab7 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -90,7 +90,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { while let Some(curr) = cursor.next() { if_chain! { if !prev_is_dollar; - if let Some(span) = is_crate_keyword(&curr); + if let Some(span) = is_crate_keyword(curr); if let Some(next) = cursor.look_ahead(0); if is_token(next, &TokenKind::ModSep); then { @@ -103,7 +103,7 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option { return span; } } - prev_is_dollar = is_token(&curr, &TokenKind::Dollar); + prev_is_dollar = is_token(curr, &TokenKind::Dollar); } None } diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index f99d793c201d7..17deccf8c3930 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::sym; declare_clippy_lint! { @@ -30,14 +30,27 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -declare_lint_pass!(DbgMacro => [DBG_MACRO]); +#[derive(Copy, Clone)] +pub struct DbgMacro { + allow_dbg_in_tests: bool, +} + +impl_lint_pass!(DbgMacro => [DBG_MACRO]); + +impl DbgMacro { + pub fn new(allow_dbg_in_tests: bool) -> Self { + DbgMacro { allow_dbg_in_tests } + } +} impl LateLintPass<'_> for DbgMacro { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) { - // we make an exception for test code - if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) { + // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml + if self.allow_dbg_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) + { return; } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 34a5f8444dea0..e98691fd5bb0a 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -21,7 +21,7 @@ declare_clippy_lint! { /// bar: bool /// } /// - /// impl std::default::Default for Foo { + /// impl Default for Foo { /// fn default() -> Self { /// Self { /// bar: false diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index fe99f4a8d55d1..99347ebadc602 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,16 +1,17 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; -use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -224,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_hash_peq<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, hash_is_automatically_derived: bool, ) { @@ -277,7 +278,7 @@ fn check_hash_peq<'tcx>( fn check_ord_partial_ord<'tcx>( cx: &LateContext<'tcx>, span: Span, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ord_is_automatically_derived: bool, ) { @@ -328,7 +329,7 @@ fn check_ord_partial_ord<'tcx>( } /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -378,7 +379,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &T fn check_unsafe_derive_deserialize<'tcx>( cx: &LateContext<'tcx>, item: &Item<'_>, - trait_ref: &TraitRef<'_>, + trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, ) { fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { @@ -455,13 +456,41 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, substs) = ty.kind(); if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - if !implements_trait(cx, ty, eq_trait_def_id, substs); + // New `ParamEnv` replacing `T: PartialEq` with `T: Eq` + let param_env = ParamEnv::new( + cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| { + let kind = p.kind(); + match kind.skip_binder() { + PredicateKind::Trait(p) + if p.trait_ref.def_id == peq_trait_def_id + && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1) + && matches!(p.trait_ref.self_ty().kind(), ty::Param(_)) + && p.constness == BoundConstness::NotConst + && p.polarity == ImplPolarity::Positive => + { + cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate { + trait_ref: TraitRef::new( + eq_trait_def_id, + cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()), + ), + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }))) + }, + _ => p, + } + })), + cx.param_env.reveal(), + cx.param_env.constness(), + ); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs); then { // If all of our fields implement `Eq`, we can implement `Eq` too for variant in adt.variants() { diff --git a/clippy_lints/src/doc_link_with_quotes.rs b/clippy_lints/src/doc_link_with_quotes.rs new file mode 100644 index 0000000000000..cb07f57e87006 --- /dev/null +++ b/clippy_lints/src/doc_link_with_quotes.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint; +use itertools::Itertools; +use rustc_ast::{AttrKind, Attribute}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks) + /// outside of code blocks + /// ### Why is this bad? + /// It is likely a typo when defining an intra-doc link + /// + /// ### Example + /// ```rust + /// /// See also: ['foo'] + /// fn bar() {} + /// ``` + /// Use instead: + /// ```rust + /// /// See also: [`foo`] + /// fn bar() {} + /// ``` + #[clippy::version = "1.60.0"] + pub DOC_LINK_WITH_QUOTES, + pedantic, + "possible typo for an intra-doc link" +} +declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]); + +impl EarlyLintPass for DocLinkWithQuotes { + fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) { + if let AttrKind::DocComment(_, symbol) = attr.kind { + if contains_quote_link(symbol.as_str()) { + span_lint( + ctx, + DOC_LINK_WITH_QUOTES, + attr.span, + "possible intra-doc link using quotes instead of backticks", + ); + } + } + } +} + +fn contains_quote_link(s: &str) -> bool { + let mut in_backticks = false; + let mut found_opening = false; + + for c in s.chars().tuple_windows::<(char, char)>() { + match c { + ('`', _) => in_backticks = !in_backticks, + ('[', '\'') if !in_backticks => found_opening = true, + ('\'', ']') if !in_backticks && found_opening => return true, + _ => {}, + } + } + + false +} diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index be95375789d5b..ee0440e52ff85 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// if x == y || x < y {} /// ``` /// - /// Could be written as: + /// Use instead: /// /// ```rust /// # let x = 1; diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index e10f740d24a41..a33ef5ce6e37c 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -13,23 +13,21 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn simple_double_parens() -> i32 { /// ((0)) /// } /// - /// // Good + /// # fn foo(bar: usize) {} + /// foo((0)); + /// ``` + /// + /// Use instead: + /// ```rust /// fn simple_no_parens() -> i32 { /// 0 /// } /// - /// // or - /// /// # fn foo(bar: usize) {} - /// // Bad - /// foo((0)); - /// - /// // Good /// foo(0); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs index 09318f74527c2..d85ace3a279b3 100644 --- a/clippy_lints/src/duration_subsec.rs +++ b/clippy_lints/src/duration_subsec.rs @@ -22,15 +22,17 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # use std::time::Duration; - /// let dur = Duration::new(5, 0); - /// - /// // Bad - /// let _micros = dur.subsec_nanos() / 1_000; - /// let _millis = dur.subsec_nanos() / 1_000_000; + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_nanos() / 1_000; + /// let millis = duration.subsec_nanos() / 1_000_000; + /// ``` /// - /// // Good - /// let _micros = dur.subsec_micros(); - /// let _millis = dur.subsec_millis(); + /// Use instead: + /// ```rust + /// # use std::time::Duration; + /// # let duration = Duration::new(5, 0); + /// let micros = duration.subsec_micros(); + /// let millis = duration.subsec_millis(); /// ``` #[clippy::version = "pre 1.29.0"] pub DURATION_SUBSEC, diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index 0b9f54231c59b..bf4488570eaf2 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written: + /// Use instead: /// /// ```rust /// # fn a() {} diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index b5d6b3c7524ba..bbebc02441412 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -23,12 +23,11 @@ declare_clippy_lint! { /// /// /// ### Example - /// Bad: /// ```rust /// enum Test {} /// ``` /// - /// Good: + /// Use instead: /// ```rust /// #![feature(never_type)] /// diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index c5a987842c3f1..27743a0ebec7e 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { /// map.insert(k, v); /// } /// ``` - /// can both be rewritten as: + /// Use instead: /// ```rust /// # use std::collections::HashMap; /// # let mut map = HashMap::new(); diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index e029b8e85379f..263a5b573c9cf 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -32,7 +32,7 @@ declare_clippy_lint! { /// BattenbergCake, /// } /// ``` - /// Could be written as: + /// Use instead: /// ```rust /// enum Cake { /// BlackForest, diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index afb5d32f95334..c3176d987c637 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -30,9 +30,9 @@ declare_clippy_lint! { /// ```rust /// # let x = 1; /// if x + 1 == x + 1 {} - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let a = 3; /// # let b = 4; /// assert_eq!(a, a); diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index cf47e581ccb48..ef1216358dd97 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// do_thing(); /// } /// ``` - /// Should be written + /// Use instead: /// ```rust,ignore /// if x == Some(2) { /// do_thing(); diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 807ecd2ddd16e..9d21dd71e0e8d 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -31,12 +31,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # fn foo(bar: usize) {} - /// // Bad /// let x = Box::new(1); /// foo(*x); /// println!("{}", *x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(bar: usize) {} /// let x = 1; /// foo(x); /// println!("{}", x); diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 7a81fb37e841c..a2af10e2ba5ea 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -18,7 +18,6 @@ declare_clippy_lint! { /// readability and API. /// /// ### Example - /// Bad: /// ```rust /// struct S { /// is_pending: bool, @@ -27,7 +26,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Good: + /// Use instead: /// ```rust /// enum S { /// Pending, diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index d8f765b288a6c..12d636cf41014 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -22,8 +22,16 @@ declare_clippy_lint! { /// ```rust /// # use std::io::Write; /// # let bar = "furchtbar"; - /// // this would be clearer as `eprintln!("foo: {:?}", bar);` /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap(); + /// writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap(); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::io::Write; + /// # let bar = "furchtbar"; + /// eprintln!("foo: {:?}", bar); + /// println!("foo: {:?}", bar); /// ``` #[clippy::version = "pre 1.29.0"] pub EXPLICIT_WRITE, diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 9f868df3ad063..b88e53aeca693 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -20,7 +20,6 @@ declare_clippy_lint! { /// ```rust /// struct Foo(i32); /// - /// // Bad /// impl From for Foo { /// fn from(s: String) -> Self { /// Foo(s.parse().unwrap()) @@ -28,8 +27,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// struct Foo(i32); /// /// impl TryFrom for Foo { diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 7a4397a7b7467..f850ea31f4d6e 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -19,11 +19,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let v: f32 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let v: f64 = 0.123_456_789_9; /// println!("{}", v); // 0.123_456_789_9 /// ``` diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 42503c26de1d1..df9b41d2c98be 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -35,8 +35,7 @@ declare_clippy_lint! { /// let _ = a.exp() - 1.0; /// ``` /// - /// is better expressed as - /// + /// Use instead: /// ```rust /// let a = 3f32; /// let _ = a.cbrt(); diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 64c41b565878b..3084c70589fa3 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -25,12 +25,13 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// - /// // Bad /// let foo = "foo"; /// format!("{}", foo); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// let foo = "foo"; /// foo.to_owned(); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 57964b8d48ea9..db0166da57f0e 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -36,12 +36,18 @@ declare_clippy_lint! { /// This is either a typo in the binary operator or confusing. /// /// ### Example - /// ```rust,ignore - /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator - /// } + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// // &&! looks like a different operator + /// if foo &&! bar {} + /// ``` /// - /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator - /// } + /// Use instead: + /// ```rust + /// # let foo = true; + /// # let bar = false; + /// if foo && !bar {} /// ``` #[clippy::version = "1.40.0"] pub SUSPICIOUS_UNARY_OP_FORMATTING, diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs new file mode 100644 index 0000000000000..0748ab45252ad --- /dev/null +++ b/clippy_lints/src/get_first.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{is_slice_of_primitives, match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(0)` instead of + /// `x.first()`. + /// + /// ### Why is this bad? + /// Using `x.first()` is easier to read and has the same + /// result. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let first_element = x.get(0); + /// ``` + /// Use instead: + /// ```rust + /// // Good + /// let x = vec![2, 3, 5]; + /// let first_element = x.first(); + /// ``` + #[clippy::version = "1.63.0"] + pub GET_FIRST, + style, + "Using `x.get(0)` when `x.first()` is simpler" +} +declare_lint_pass!(GetFirst => [GET_FIRST]); + +impl<'tcx> LateLintPass<'tcx> for GetFirst { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::SLICE_GET); + + if let Some(_) = is_slice_of_primitives(cx, struct_calling_on); + if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind; + + then { + let mut applicability = Applicability::MachineApplicable; + let slice_name = snippet_with_applicability( + cx, + struct_calling_on.span, "..", + &mut applicability, + ); + span_lint_and_sugg( + cx, + GET_FIRST, + expr.span, + &format!("accessing first element with `{0}.get(0)`", slice_name), + "try", + format!("{}.first()", slice_name), + applicability, + ); + } + } + } +} diff --git a/clippy_lints/src/get_last_with_len.rs b/clippy_lints/src/get_last_with_len.rs deleted file mode 100644 index df29d9308e712..0000000000000 --- a/clippy_lints/src/get_last_with_len.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! lint on using `x.get(x.len() - 1)` instead of `x.last()` - -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::SpanlessEq; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for using `x.get(x.len() - 1)` instead of - /// `x.last()`. - /// - /// ### Why is this bad? - /// Using `x.last()` is easier to read and has the same - /// result. - /// - /// Note that using `x[x.len() - 1]` is semantically different from - /// `x.last()`. Indexing into the array will panic on out-of-bounds - /// accesses, while `x.get()` and `x.last()` will return `None`. - /// - /// There is another lint (get_unwrap) that covers the case of using - /// `x.get(index).unwrap()` instead of `x[index]`. - /// - /// ### Example - /// ```rust - /// // Bad - /// let x = vec![2, 3, 5]; - /// let last_element = x.get(x.len() - 1); - /// - /// // Good - /// let x = vec![2, 3, 5]; - /// let last_element = x.last(); - /// ``` - #[clippy::version = "1.37.0"] - pub GET_LAST_WITH_LEN, - complexity, - "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" -} - -declare_lint_pass!(GetLastWithLen => [GET_LAST_WITH_LEN]); - -impl<'tcx> LateLintPass<'tcx> for GetLastWithLen { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - // Is a method call - if let ExprKind::MethodCall(path, args, _) = expr.kind; - - // Method name is "get" - if path.ident.name == sym!(get); - - // Argument 0 (the struct we're calling the method on) is a vector - if let Some(struct_calling_on) = args.get(0); - let struct_ty = cx.typeck_results().expr_ty(struct_calling_on); - if is_type_diagnostic_item(cx, struct_ty, sym::Vec); - - // Argument to "get" is a subtraction - if let Some(get_index_arg) = args.get(1); - if let ExprKind::Binary( - Spanned { - node: BinOpKind::Sub, - .. - }, - lhs, - rhs, - ) = &get_index_arg.kind; - - // LHS of subtraction is "x.len()" - if let ExprKind::MethodCall(arg_lhs_path, lhs_args, _) = &lhs.kind; - if arg_lhs_path.ident.name == sym::len; - if let Some(arg_lhs_struct) = lhs_args.get(0); - - // The two vectors referenced (x in x.get(...) and in x.len()) - if SpanlessEq::new(cx).eq_expr(struct_calling_on, arg_lhs_struct); - - // RHS of subtraction is 1 - if let ExprKind::Lit(rhs_lit) = &rhs.kind; - if let LitKind::Int(1, ..) = rhs_lit.node; - - then { - let mut applicability = Applicability::MachineApplicable; - let vec_name = snippet_with_applicability( - cx, - struct_calling_on.span, "vec", - &mut applicability, - ); - - span_lint_and_sugg( - cx, - GET_LAST_WITH_LEN, - expr.span, - &format!("accessing last element with `{0}.get({0}.len() - 1)`", vec_name), - "try", - format!("{}.last()", vec_name), - applicability, - ); - } - } - } -} diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 40cc5cd4bcf9d..419ea5a6811b8 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,15 +1,14 @@ -use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{clip, unsext}; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{clip, unsext}; - declare_clippy_lint! { /// ### What it does /// Checks for identity operations, e.g., `x + 0`. @@ -23,11 +22,6 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` - /// - /// ### Known problems - /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to - /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. - /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -45,31 +39,22 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if !is_allowed(cx, *cmp, left, right) { match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 0, expr.span, right.span); - } - check(cx, right, 0, expr.span, left.span); + check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check(cx, right, 0, expr.span, left.span); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Mul => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 1, expr.span, right.span); - } - check(cx, right, 1, expr.span, left.span); + check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 1, expr.span, left.span, Parens::Unneeded); }, - BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded), BinOpKind::BitAnd => { - if reducible_to_right(cx, expr, right) { - check(cx, left, -1, expr.span, right.span); - } - check(cx, right, -1, expr.span, left.span); - }, - BinOpKind::Rem => { - // Don't call reducible_to_right because N % N is always reducible to 1 - check_remainder(cx, left, right, expr.span, left.span); + check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, -1, expr.span, left.span, Parens::Unneeded); }, + BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), } } @@ -77,24 +62,50 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { } } -/// Checks if `left op ..right` can be actually reduced to `right` -/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +#[derive(Copy, Clone)] +enum Parens { + Needed, + Unneeded, +} + +/// Checks if `left op right` needs parenthesis when reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced +/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be +/// interpreted as a statement +/// /// See #8724 -fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { - if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { - is_toplevel_binary(cx, binary) - } else { - true +fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens { + match right.kind { + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => { + // ensure we're checking against the leftmost expression of `right` + // + // ~~~ `lhs` + // 0 + {4} * 2 + // ~~~~~~~ `right` + return needs_parenthesis(cx, binary, lhs); + }, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {}, + _ => return Parens::Unneeded, } -} -fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { - false - } else { - true + let mut prev_id = binary.hir_id; + for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) { + if let Node::Expr(expr) = node + && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind + && lhs.hir_id == prev_id + { + // keep going until we find a node that encompasses left of `binary` + prev_id = expr.hir_id; + continue; + } + + match node { + Node::Block(_) | Node::Stmt(_) => break, + _ => return Parens::Unneeded, + }; } + + Parens::Needed } fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { @@ -115,11 +126,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, Parens::Unneeded); } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -132,19 +143,27 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, parens); } } } -fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { - span_lint( +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) { + let mut applicability = Applicability::MachineApplicable; + let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability); + + let suggestion = match parens { + Parens::Needed => format!("({expr_snippet})"), + Parens::Unneeded => expr_snippet.into_owned(), + }; + + span_lint_and_sugg( cx, IDENTITY_OP, span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), + "this operation has no effect", + "consider reducing it to", + suggestion, + applicability, ); } diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 0f3889a293618..63ac092dfaf12 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,13 @@ //! lint when there is a large size difference between variants on an enum -use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -26,6 +27,15 @@ declare_clippy_lint! { /// the overhead is negligible and the boxing is counter-productive. Always /// measure the change this lint suggests. /// + /// For types that implement `Copy`, the suggestion to `Box` a variant's + /// data would require removing the trait impl. The types can of course + /// still be `Clone`, but that is worse ergonomically. Depending on the + /// use case it may be possible to store the large data in an auxillary + /// structure (e.g. Arena or ECS). + /// + /// The lint will ignore generic types if the layout depends on the + /// generics, even if the size difference will be large anyway. + /// /// ### Example /// ```rust /// // Bad @@ -74,7 +84,7 @@ struct VariantInfo { impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) { if in_external_macro(cx.tcx.sess, item.span) { return; } @@ -132,37 +142,43 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let fields = def.variants[variants_size[0].ind].data.fields(); variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); let mut applicability = Applicability::MaybeIncorrect; - let sugg: Vec<(Span, String)> = variants_size[0] - .fields_size - .iter() - .rev() - .map_while(|val| { - if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); - Some(( - fields[val.ind].ty.span, - format!( - "Box<{}>", - snippet_with_applicability( - cx, - fields[val.ind].ty.span, - "..", - &mut applicability - ) - .into_owned() - ), - )) - } else { - None - } - }) - .collect(); + if is_copy(cx, ty) || maybe_copy(cx, ty) { + diag.span_note( + item.ident.span, + "boxing a variant would require the type no longer be `Copy`", + ); + } else { + let sugg: Vec<(Span, String)> = variants_size[0] + .fields_size + .iter() + .rev() + .map_while(|val| { + if difference > self.maximum_size_difference_allowed { + difference = difference.saturating_sub(val.size); + Some(( + fields[val.ind].ty.span, + format!( + "Box<{}>", + snippet_with_applicability( + cx, + fields[val.ind].ty.span, + "..", + &mut applicability + ) + .into_owned() + ), + )) + } else { + None + } + }) + .collect(); - if !sugg.is_empty() { - diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); - return; + if !sugg.is_empty() { + diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect); + return; + } } - diag.span_help(def.variants[variants_size[0].ind].span, help_text); }, ); @@ -170,3 +186,13 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { } } } + +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Adt(_def, substs) = ty.kind() + && substs.types().next().is_some() + && let Some(copy_trait) = cx.tcx.lang_items().copy_trait() + { + return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some(); + } + false +} diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index db09d00d7303f..56bbbbbc819e5 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; if !is_local_used(cx, *cond, canonical_id); if let hir::ExprKind::Block(then, _) = then.kind; - if let Some(value) = check_assign(cx, canonical_id, &*then); + if let Some(value) = check_assign(cx, canonical_id, then); if !is_local_used(cx, value, canonical_id); then { let span = stmt.span.to(if_.span); diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index be5c478900fac..d4ec046d0bb08 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -4,6 +4,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS), + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(approx_const::APPROX_CONSTANT), LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), LintId::of(assign_ops::ASSIGN_OP_PATTERN), @@ -24,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), @@ -36,7 +38,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(casts::UNNECESSARY_CAST), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), @@ -91,7 +92,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ 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(get_first::GET_FIRST), LintId::of(identity_op::IDENTITY_OP), LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), @@ -132,23 +133,25 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), 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_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), LintId::of(matches::MATCH_SINGLE_BINDING), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(matches::NEEDLESS_MATCH), LintId::of(matches::REDUNDANT_PATTERN_MATCHING), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(matches::SINGLE_MATCH), LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), @@ -166,6 +169,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::IS_DIGIT_ASCII_RADIX), @@ -189,6 +193,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::OK_EXPECT), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -278,7 +283,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(self_assignment::SELF_ASSIGNMENT), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), @@ -289,6 +293,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(swap::ALMOST_SWAPPED), LintId::of(swap::MANUAL_SWAP), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), @@ -302,6 +307,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmuting_null::TRANSMUTING_NULL), LintId::of(types::BORROWED_BOX), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index b15c979d0c70e..4f1c3673f853c 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(borrow_deref_ref::BORROW_DEREF_REF), LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), @@ -15,7 +16,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(explicit_write::EXPLICIT_WRITE), LintId::of(format::USELESS_FORMAT), LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(get_last_with_len::GET_LAST_WITH_LEN), LintId::of(identity_op::IDENTITY_OP), LintId::of(int_plus_one::INT_PLUS_ONE), LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), @@ -25,9 +25,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(loops::SINGLE_ELEMENT_LOOP), LintId::of(loops::WHILE_LET_LOOP), LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR), LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), + LintId::of(matches::MANUAL_UNWRAP_OR), LintId::of(matches::MATCH_AS_REF), LintId::of(matches::MATCH_SINGLE_BINDING), LintId::of(matches::NEEDLESS_MATCH), @@ -37,6 +37,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), + LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), @@ -90,6 +91,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_PTR_TO_REF), + LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(types::BORROWED_BOX), LintId::of(types::TYPE_COMPLEXITY), LintId::of(types::VEC_BOX), diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs index 6bf2c4bbaedc0..50cdd0af92305 100644 --- a/clippy_lints/src/lib.register_correctness.rs +++ b/clippy_lints/src/lib.register_correctness.rs @@ -39,7 +39,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve LintId::of(loops::ITER_NEXT_LOOP), LintId::of(loops::NEVER_LOOP), LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH), + LintId::of(matches::MATCH_STR_CASE_MISMATCH), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::ITERATOR_STEP_BY_ZERO), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5552ea8aa80ac..b927ba3b17c0e 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -34,10 +34,10 @@ store.register_lints(&[ #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, + almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, approx_const::APPROX_CONSTANT, - arithmetic::FLOAT_ARITHMETIC, - arithmetic::INTEGER_ARITHMETIC, as_conversions::AS_CONVERSIONS, + as_underscore::AS_UNDERSCORE, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, assertions_on_constants::ASSERTIONS_ON_CONSTANTS, @@ -64,6 +64,7 @@ store.register_lints(&[ booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, + borrow_deref_ref::BORROW_DEREF_REF, bytecount::NAIVE_BYTECOUNT, bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, @@ -93,7 +94,6 @@ store.register_lints(&[ cognitive_complexity::COGNITIVE_COMPLEXITY, collapsible_if::COLLAPSIBLE_ELSE_IF, collapsible_if::COLLAPSIBLE_IF, - collapsible_match::COLLAPSIBLE_MATCH, comparison_chain::COMPARISON_CHAIN, copies::BRANCHES_SHARING_CODE, copies::IFS_SAME_COND, @@ -124,6 +124,7 @@ store.register_lints(&[ doc::MISSING_PANICS_DOC, doc::MISSING_SAFETY_DOC, doc::NEEDLESS_DOCTEST_MAIN, + doc_link_with_quotes::DOC_LINK_WITH_QUOTES, double_comparison::DOUBLE_COMPARISONS, double_parens::DOUBLE_PARENS, drop_forget_ref::DROP_COPY, @@ -183,7 +184,7 @@ store.register_lints(&[ functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_LINES, future_not_send::FUTURE_NOT_SEND, - get_last_with_len::GET_LAST_WITH_LEN, + get_first::GET_FIRST, identity_op::IDENTITY_OP, if_let_mutex::IF_LET_MUTEX, if_not_else::IF_NOT_ELSE, @@ -250,33 +251,36 @@ store.register_lints(&[ manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, manual_bits::MANUAL_BITS, - manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, manual_strip::MANUAL_STRIP, - manual_unwrap_or::MANUAL_UNWRAP_OR, map_clone::MAP_CLONE, map_err_ignore::MAP_ERR_IGNORE, map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN, - match_on_vec_items::MATCH_ON_VEC_ITEMS, match_result_ok::MATCH_RESULT_OK, - match_str_case_mismatch::MATCH_STR_CASE_MISMATCH, + matches::COLLAPSIBLE_MATCH, matches::INFALLIBLE_DESTRUCTURING_MATCH, + matches::MANUAL_MAP, + matches::MANUAL_UNWRAP_OR, matches::MATCH_AS_REF, matches::MATCH_BOOL, matches::MATCH_LIKE_MATCHES_MACRO, + matches::MATCH_ON_VEC_ITEMS, matches::MATCH_OVERLAPPING_ARM, matches::MATCH_REF_PATS, matches::MATCH_SAME_ARMS, matches::MATCH_SINGLE_BINDING, + matches::MATCH_STR_CASE_MISMATCH, matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, matches::MATCH_WILD_ERR_ARM, matches::NEEDLESS_MATCH, matches::REDUNDANT_PATTERN_MATCHING, matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, + matches::SIGNIFICANT_DROP_IN_SCRUTINEE, matches::SINGLE_MATCH, matches::SINGLE_MATCH_ELSE, + matches::TRY_ERR, matches::WILDCARD_ENUM_MATCH_ARM, matches::WILDCARD_IN_OR_PATTERNS, mem_forget::MEM_FORGET, @@ -302,6 +306,7 @@ store.register_lints(&[ methods::FLAT_MAP_IDENTITY, methods::FLAT_MAP_OPTION, methods::FROM_ITER_INSTEAD_OF_COLLECT, + methods::GET_LAST_WITH_LEN, methods::GET_UNWRAP, methods::IMPLICIT_CLONE, methods::INEFFICIENT_TO_STRING, @@ -330,6 +335,7 @@ store.register_lints(&[ methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, + methods::NO_EFFECT_REPLACE, methods::OK_EXPECT, methods::OPTION_AS_REF_DEREF, methods::OPTION_FILTER_MAP, @@ -377,6 +383,7 @@ store.register_lints(&[ misc_early::UNNEEDED_WILDCARD_PATTERN, misc_early::UNSEPARATED_LITERAL_SUFFIX, misc_early::ZERO_PREFIXED_LITERAL, + mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, missing_const_for_fn::MISSING_CONST_FOR_FN, missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, @@ -418,6 +425,8 @@ store.register_lints(&[ non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, + numeric_arithmetic::FLOAT_ARITHMETIC, + numeric_arithmetic::INTEGER_ARITHMETIC, octal_escapes::OCTAL_ESCAPES, only_used_in_recursion::ONLY_USED_IN_RECURSION, open_options::NONSENSICAL_OPEN_OPTIONS, @@ -473,7 +482,6 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, - significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, @@ -493,6 +501,7 @@ store.register_lints(&[ suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, swap::ALMOST_SWAPPED, swap::MANUAL_SWAP, + swap_ptr_to_ref::SWAP_PTR_TO_REF, tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, temporary_assignment::TEMPORARY_ASSIGNMENT, to_digit_is_some::TO_DIGIT_IS_SOME, @@ -514,7 +523,6 @@ store.register_lints(&[ transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, transmuting_null::TRANSMUTING_NULL, - try_err::TRY_ERR, types::BORROWED_BOX, types::BOX_COLLECTION, types::LINKEDLIST, @@ -544,6 +552,7 @@ store.register_lints(&[ unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, + unused_rounding::UNUSED_ROUNDING, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, unwrap::PANICKING_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 34d1555049da5..642d629971d90 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,11 +25,10 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(transmute::USELESS_TRANSMUTE), + LintId::of(unused_rounding::UNUSED_ROUNDING), LintId::of(use_self::USE_SELF), ]) diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63232fd411305..48de92ae94523 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -26,6 +26,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(doc::DOC_MARKDOWN), LintId::of(doc::MISSING_ERRORS_DOC), LintId::of(doc::MISSING_PANICS_DOC), + LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES), LintId::of(empty_enum::EMPTY_ENUM), LintId::of(enum_variants::MODULE_NAME_REPETITIONS), LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), @@ -50,8 +51,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(macro_use::MACRO_USE_IMPORTS), LintId::of(manual_assert::MANUAL_ASSERT), LintId::of(manual_ok_or::MANUAL_OK_OR), - LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_BOOL), + LintId::of(matches::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_SAME_ARMS), LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILD_ERR_ARM), @@ -66,6 +67,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(methods::UNNECESSARY_JOIN), LintId::of(misc::FLOAT_CMP), LintId::of(misc::USED_UNDERSCORE_BINDING), + LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), LintId::of(mut_mut::MUT_MUT), LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL), LintId::of(needless_continue::NEEDLESS_CONTINUE), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index a6d3a06dc16e3..3695012f55238 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -3,9 +3,8 @@ // Manual edits will be overwritten. 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(as_underscore::AS_UNDERSCORE), LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), @@ -32,6 +31,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), + LintId::of(matches::TRY_ERR), LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), LintId::of(mem_forget::MEM_FORGET), LintId::of(methods::CLONE_ON_REF_PTR), @@ -50,6 +50,8 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(module_style::MOD_MODULE_FILES), LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), + LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC), + LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(panic_unimplemented::PANIC), LintId::of(panic_unimplemented::TODO), @@ -67,7 +69,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STR_TO_STRING), - LintId::of(try_err::TRY_ERR), LintId::of(types::RC_BUFFER), LintId::of(types::RC_MUTEX), LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 62f26d821a0d6..35575351784a2 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -12,7 +12,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), @@ -31,6 +30,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(functions::DOUBLE_MUST_USE), LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::RESULT_UNIT_ERR), + LintId::of(get_first::GET_FIRST), LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(len_zero::COMPARISON_TO_EMPTY), @@ -45,11 +45,12 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), LintId::of(match_result_ok::MATCH_RESULT_OK), + LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), + LintId::of(matches::MANUAL_MAP), LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), LintId::of(matches::MATCH_OVERLAPPING_ARM), LintId::of(matches::MATCH_REF_PATS), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 2de49f1624a42..7b13713c36e59 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -3,6 +3,7 @@ // Manual edits will be overwritten. store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ + LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), @@ -23,11 +24,13 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(loops::EMPTY_LOOP), LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES), LintId::of(loops::MUT_RANGE_BOUND), + LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), + LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), + LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4ac834f72405b..ee0416fc0ff5e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -168,9 +168,10 @@ mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; +mod almost_complete_letter_range; mod approx_const; -mod arithmetic; mod as_conversions; +mod as_underscore; mod asm_syntax; mod assertions_on_constants; mod assign_ops; @@ -183,6 +184,7 @@ mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; +mod borrow_deref_ref; mod bytecount; mod bytes_count_to_len; mod cargo; @@ -191,7 +193,6 @@ mod casts; mod checked_conversions; mod cognitive_complexity; mod collapsible_if; -mod collapsible_match; mod comparison_chain; mod copies; mod copy_iterator; @@ -208,6 +209,7 @@ mod disallowed_methods; mod disallowed_script_idents; mod disallowed_types; mod doc; +mod doc_link_with_quotes; mod double_comparison; mod double_parens; mod drop_forget_ref; @@ -242,7 +244,7 @@ mod from_over_into; mod from_str_radix_10; mod functions; mod future_not_send; -mod get_last_with_len; +mod get_first; mod identity_op; mod if_let_mutex; mod if_not_else; @@ -278,17 +280,13 @@ mod main_recursion; mod manual_assert; mod manual_async_fn; mod manual_bits; -mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; mod manual_strip; -mod manual_unwrap_or; mod map_clone; mod map_err_ignore; mod map_unit_fn; -mod match_on_vec_items; mod match_result_ok; -mod match_str_case_mismatch; mod matches; mod mem_forget; mod mem_replace; @@ -296,6 +294,7 @@ mod methods; mod minmax; mod misc; mod misc_early; +mod mismatching_type_param_order; mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; @@ -328,6 +327,7 @@ mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; +mod numeric_arithmetic; mod octal_escapes; mod only_used_in_recursion; mod open_options; @@ -367,7 +367,6 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; -mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -378,6 +377,7 @@ mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; mod swap; +mod swap_ptr_to_ref; mod tabs_in_doc_comments; mod temporary_assignment; mod to_digit_is_some; @@ -385,7 +385,6 @@ mod trailing_empty_array; mod trait_bounds; mod transmute; mod transmuting_null; -mod try_err; mod types; mod undocumented_unsafe_blocks; mod unicode; @@ -402,6 +401,7 @@ mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; mod unused_io_amount; +mod unused_rounding; mod unused_self; mod unused_unit; mod unwrap; @@ -562,7 +562,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(len_zero::LenZero)); store.register_late_pass(|| Box::new(attrs::Attributes)); store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions)); - store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch)); store.register_late_pass(|| Box::new(unicode::Unicode)); store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); store.register_late_pass(|| Box::new(unit_hash::UnitHash)); @@ -636,6 +635,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(mutex_atomic::Mutex)); store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate)); store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); + store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|| Box::new(no_effect::NoEffect)); store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(|| Box::new(transmute::Transmute)); @@ -652,7 +652,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|| Box::new(derive::Derive)); store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls)); - store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons)); @@ -678,7 +677,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone()))); store.register_late_pass(|| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|| Box::new(mem_forget::MemForget)); - store.register_late_pass(|| Box::new(arithmetic::Arithmetic::default())); + store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default())); store.register_late_pass(|| Box::new(assign_ops::AssignOps)); store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); @@ -700,7 +699,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: ); store.register_late_pass(move || Box::new(pass_by_ref_or_value)); store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef)); - store.register_late_pass(|| Box::new(try_err::TryErr)); store.register_late_pass(|| Box::new(bytecount::ByteCount)); store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody)); @@ -812,7 +810,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock)); - store.register_late_pass(|| Box::new(match_on_vec_items::MatchOnVecItems)); store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero)); store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn)); @@ -830,7 +827,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(repeat_once::RepeatOnce)); store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|| Box::new(self_assignment::SelfAssignment)); - store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr)); store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr)); store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs)); store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); @@ -849,7 +845,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(|| Box::new(manual_map::ManualMap)); store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); @@ -875,7 +870,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch)); store.register_late_pass(move || Box::new(format_args::FormatArgs)); store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); @@ -886,9 +880,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); + store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); - store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); - store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); + let allow_dbg_in_tests = conf.allow_dbg_in_tests; + store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { Box::new(cargo::Cargo { @@ -906,6 +901,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); + store.register_late_pass(|| Box::new(get_first::GetFirst)); + store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef)); + store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); + store.register_late_pass(|| Box::new(as_underscore::AsUnderscore)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 75d771f992a8c..d61be78895ffc 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -180,29 +180,24 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let opt = Some(1); - /// - /// // Bad + /// # let res: Result = Ok(1); /// for x in opt { /// // .. /// } /// - /// // Good - /// if let Some(x) = opt { + /// for x in &res { /// // .. /// } /// ``` /// - /// // or - /// + /// Use instead: /// ```rust + /// # let opt = Some(1); /// # let res: Result = Ok(1); - /// - /// // Bad - /// for x in &res { + /// if let Some(x) = opt { /// // .. /// } /// - /// // Good /// if let Ok(x) = res { /// // .. /// } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 4f85364965b69..a348bb465c884 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,9 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{ - contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq, -}; +use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -29,7 +27,12 @@ pub(super) fn check<'tcx>( body: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, ) { - if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) { + if let Some(higher::Range { + start: Some(start), + ref end, + limits, + }) = higher::Range::hir(arg) + { // the var must be a single name if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind { let mut visitor = VarVisitor { @@ -104,22 +107,19 @@ pub(super) fn check<'tcx>( } } - if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) - { + 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) - { + } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) { return; } else { match limits { ast::RangeLimits::Closed => { let take_expr = sugg::Sugg::hir(cx, take_expr, ""); format!(".take({})", take_expr + sugg::ONE) - } + }, ast::RangeLimits::HalfOpen => { format!(".take({})", snippet(cx, take_expr.span, "..")) - } + }, } } } else { @@ -145,10 +145,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!( - "the loop variable `{}` is used to index `{}`", - ident.name, indexed - ), + &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( diag, @@ -157,10 +154,7 @@ pub(super) fn check<'tcx>( (pat.span, format!("({}, )", ident.name)), ( arg.span, - format!( - "{}.{}().enumerate(){}{}", - indexed, method, method_1, method_2 - ), + format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2), ), ], ); @@ -177,10 +171,7 @@ pub(super) fn check<'tcx>( cx, NEEDLESS_RANGE_LOOP, arg.span, - &format!( - "the loop variable `{}` is only used to index `{}`", - ident.name, indexed - ), + &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( diag, @@ -257,12 +248,7 @@ struct VarVisitor<'a, 'tcx> { } impl<'a, 'tcx> VarVisitor<'a, 'tcx> { - fn check( - &mut self, - idx: &'tcx Expr<'_>, - seqexpr: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, - ) -> bool { + fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { if_chain! { // the indexed container is referenced by a name if let ExprKind::Path(ref seqpath) = seqexpr.kind; @@ -351,13 +337,13 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { self.visit_expr(lhs); self.prefer_mutable = false; self.visit_expr(rhs); - } + }, ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => { if mutbl == Mutability::Mut { self.prefer_mutable = true; } self.visit_expr(expr); - } + }, ExprKind::Call(f, args) => { self.visit_expr(f); for expr in args { @@ -370,11 +356,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - } + }, ExprKind::MethodCall(_, args, _) => { let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap(); - for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) - { + for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) { self.prefer_mutable = false; if let ty::Ref(_, _, mutbl) = *ty.kind() { if mutbl == Mutability::Mut { @@ -383,11 +368,11 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { } self.visit_expr(expr); } - } + }, ExprKind::Closure(_, _, body_id, ..) => { let body = self.cx.tcx.hir().body(body_id); self.visit_expr(&body.value); - } + }, _ => walk_expr(self, expr), } self.prefer_mutable = old; diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 70a118d6b3539..c025f5972d519 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -146,7 +146,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); combine_seq(e, arms) } }, diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs deleted file mode 100644 index 230ae029ed9d2..0000000000000 --- a/clippy_lints/src/manual_map.rs +++ /dev/null @@ -1,316 +0,0 @@ -use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; -use clippy_utils::{ - can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, -}; -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{ - def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, - QPath, UnsafeSource, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, SyntaxContext}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `match` which could be implemented using `map` - /// - /// ### Why is this bad? - /// Using the `map` method is clearer and more concise. - /// - /// ### Example - /// ```rust - /// match Some(0) { - /// Some(x) => Some(x + 1), - /// None => None, - /// }; - /// ``` - /// Use instead: - /// ```rust - /// Some(0).map(|x| x + 1); - /// ``` - #[clippy::version = "1.52.0"] - pub MANUAL_MAP, - style, - "reimplementation of `map`" -} - -declare_lint_pass!(ManualMap => [MANUAL_MAP]); - -impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[expect(clippy::too_many_lines)] - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { - Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), - Some(IfLetOrMatch::Match( - scrutinee, - [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], - _, - )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body), - _ => return, - }; - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) - { - return; - } - - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { - (else_body, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { - (then_body, pattern, ref_count, false) - }, - _ => return, - }; - - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } - - let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { - Some(expr) => expr, - None => return, - }; - - // These two lints will go back and forth with each other. - if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit - && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) - { - return; - } - - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { - return; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); - - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; - - match can_move_expr_to_closure(cx, some_expr.expr) { - Some(captures) => { - // Check if captures the closure will need conflict with borrows made in the scrutinee. - // TODO: check all the references made in the scrutinee expression. This will require interacting - // with the borrow checker. Currently only `[.]*` is checked for. - if let Some(binding_ref_mutability) = binding_ref { - let e = peel_hir_expr_while(scrutinee, |e| match e.kind { - ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), - _ => None, - }); - if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { - match captures.get(l) { - Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, - Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { - return; - }, - Some(CaptureKind::Ref(Mutability::Not)) | None => (), - } - } - } - }, - None => return, - }; - - let mut app = Applicability::MachineApplicable; - - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrutinee).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = - if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; - - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - if_chain! { - if !some_expr.needs_unsafe_block; - if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); - if func.span.ctxt() == some_expr.expr.span.ctxt(); - then { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - } else { - if path_to_local_id(some_expr.expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } - - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) - } else { - format!("|{}{}| {}", annotation, some_binding, expr_snip) - } - } - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; - let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; - if some_expr.needs_unsafe_block { - format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) - } else { - format!("|{}| {}", pat_snip, expr_snip) - } - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if else_pat.is_none() && is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) - } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) - }, - app, - ); - } -} - -// Checks whether the expression could be passed as a function, or whether a closure is needed. -// Returns the function to be passed to `map` if it exists. -fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - match expr.kind { - ExprKind::Call(func, [arg]) - if path_to_local_id(arg, binding) - && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => - { - Some(func) - }, - _ => None, - } -} - -enum OptionPat<'a> { - Wild, - None, - Some { - // The pattern contained in the `Some` tuple. - pattern: &'a Pat<'a>, - // The number of references before the `Some` tuple. - // e.g. `&&Some(_)` has a ref count of 2. - ref_count: usize, - }, -} - -struct SomeExpr<'tcx> { - expr: &'tcx Expr<'tcx>, - needs_unsafe_block: bool, -} - -// Try to parse into a recognized `Option` pattern. -// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { - fn f<'tcx>( - cx: &LateContext<'tcx>, - pat: &'tcx Pat<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat.kind { - PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) - if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => - { - Some(OptionPat::Some { pattern, ref_count }) - }, - _ => None, - } - } - f(cx, pat, 0, ctxt) -} - -// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - needs_unsafe_block: bool, - ctxt: SyntaxContext, -) -> Option> { - // TODO: Allow more complex expressions. - match expr.kind { - ExprKind::Call( - Expr { - kind: ExprKind::Path(ref qpath), - .. - }, - [arg], - ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { - expr: arg, - needs_unsafe_block, - }), - ExprKind::Block( - Block { - stmts: [], - expr: Some(expr), - rules, - .. - }, - _, - ) => get_some_expr( - cx, - expr, - needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), - ctxt, - ), - _ => None, - } -} - -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) -} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 80845ace3f940..14f5faafd7cb9 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -113,7 +113,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { let mut iter = fields.iter().filter_map(|f| match f.vis.kind { VisibilityKind::Public => None, VisibilityKind::Inherited => Some(Ok(f)), - _ => Some(Err(())), + VisibilityKind::Restricted { .. } => Some(Err(())), }); if let Some(Ok(field)) = iter.next() && iter.next().is_none() diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs deleted file mode 100644 index b3a91d9f18f5d..0000000000000 --- a/clippy_lints/src/manual_unwrap_or.rs +++ /dev/null @@ -1,123 +0,0 @@ -use clippy_utils::consts::constant_simple; -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{in_constant, is_lang_ctor, path_to_local_id, sugg}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::{Arm, Expr, ExprKind, PatKind}; -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}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. - /// - /// ### Why is this bad? - /// Concise code helps focusing on behavior instead of boilerplate. - /// - /// ### Example - /// ```rust - /// let foo: Option = None; - /// match foo { - /// Some(v) => v, - /// None => 1, - /// }; - /// ``` - /// - /// Use instead: - /// ```rust - /// let foo: Option = None; - /// foo.unwrap_or(1); - /// ``` - #[clippy::version = "1.49.0"] - pub MANUAL_UNWRAP_OR, - complexity, - "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" -} - -declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); - -impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - lint_manual_unwrap_or(cx, expr); - } -} - -fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - fn applicable_or_arm<'a>(cx: &LateContext<'_>, 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, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { - match arm.pat.kind { - PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), - PatKind::TupleStruct(ref qpath, [pat], _) => - matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), - _ => false, - } - }); - let unwrap_arm = &arms[1 - idx]; - if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; - if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); - if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; - if path_to_local_id(unwrap_arm.body, binding_hir_id); - if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); - if !contains_return_break_continue_macro(or_arm.body); - then { - Some(or_arm) - } else { - None - } - } - } - - if_chain! { - if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind; - let ty = cx.typeck_results().expr_ty(scrutinee); - if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { - Some("Option") - } else if is_type_diagnostic_item(cx, ty, sym::Result) { - Some("Result") - } else { - None - }; - if let Some(or_arm) = applicable_or_arm(cx, match_arms); - if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); - if let Some(indent) = indent_of(cx, expr.span); - if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); - then { - let reindented_or_body = - reindent_multiline(or_body_snippet.into(), true, Some(indent)); - - let suggestion = if scrutinee.span.from_expansion() { - // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` - sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") - } - else { - sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() - }; - - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR, expr.span, - &format!("this pattern reimplements `{}::unwrap_or`", ty_name), - "replace with", - format!( - "{}.unwrap_or({})", - suggestion, - reindented_or_body, - ), - Applicability::MachineApplicable, - ); - } - } -} diff --git a/clippy_lints/src/match_on_vec_items.rs b/clippy_lints/src/match_on_vec_items.rs deleted file mode 100644 index 583b577ffe25d..0000000000000 --- a/clippy_lints/src/match_on_vec_items.rs +++ /dev/null @@ -1,104 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `match vec[idx]` or `match vec[n..m]`. - /// - /// ### Why is this bad? - /// This can panic at runtime. - /// - /// ### Example - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Bad - /// match arr[idx] { - /// 0 => println!("{}", 0), - /// 1 => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust, no_run - /// let arr = vec![0, 1, 2, 3]; - /// let idx = 1; - /// - /// // Good - /// match arr.get(idx) { - /// Some(0) => println!("{}", 0), - /// Some(1) => println!("{}", 3), - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.45.0"] - pub MATCH_ON_VEC_ITEMS, - pedantic, - "matching on vector elements can panic" -} - -declare_lint_pass!(MatchOnVecItems => [MATCH_ON_VEC_ITEMS]); - -impl<'tcx> LateLintPass<'tcx> for MatchOnVecItems { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Match(match_expr, _, MatchSource::Normal) = expr.kind; - if let Some(idx_expr) = is_vec_indexing(cx, match_expr); - if let ExprKind::Index(vec, idx) = idx_expr.kind; - - then { - // FIXME: could be improved to suggest surrounding every pattern with Some(_), - // but only when `or_patterns` are stabilized. - span_lint_and_sugg( - cx, - MATCH_ON_VEC_ITEMS, - match_expr.span, - "indexing into a vector may panic", - "try this", - format!( - "{}.get({})", - snippet(cx, vec.span, ".."), - snippet(cx, idx.span, "..") - ), - Applicability::MaybeIncorrect - ); - } - } - } -} - -fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if_chain! { - if let ExprKind::Index(array, index) = expr.kind; - if is_vector(cx, array); - if !is_full_range(cx, index); - - then { - return Some(expr); - } - } - - None -} - -fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_diagnostic_item(cx, ty, sym::Vec) -} - -fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); - let ty = ty.peel_refs(); - is_type_lang_item(cx, ty, LangItem::RangeFull) -} diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs similarity index 71% rename from clippy_lints/src/collapsible_match.rs rename to clippy_lints/src/matches/collapsible_match.rs index ec55009f347d3..07021f1bcad8d 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -6,68 +6,28 @@ use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::LateContext; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together - /// without adding any branches. - /// - /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only - /// cases where merging would most likely make the code more readable. - /// - /// ### Why is this bad? - /// It is unnecessarily verbose and complex. - /// - /// ### Example - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(n) => match n { - /// Ok(n) => n, - /// _ => return, - /// } - /// None => return, - /// }; - /// } - /// ``` - /// Use instead: - /// ```rust - /// fn func(opt: Option>) { - /// let n = match opt { - /// Some(Ok(n)) => n, - /// _ => return, - /// }; - /// } - /// ``` - #[clippy::version = "1.50.0"] - pub COLLAPSIBLE_MATCH, - style, - "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." -} - -declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]); +use super::COLLAPSIBLE_MATCH; -impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - match IfLetOrMatch::parse(cx, expr) { - Some(IfLetOrMatch::Match(_, arms, _)) => { - if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { - for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); - } - } - }, - Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { - check_arm(cx, false, pat, body, None, els); - }, - None => {}, +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { + if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { + for arm in arms { + check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); } } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + body: &'tcx Expr<'_>, + else_expr: Option<&'tcx Expr<'_>>, +) { + check_arm(cx, false, pat, body, None, else_expr); +} + fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/matches/manual_map.rs b/clippy_lints/src/matches/manual_map.rs new file mode 100644 index 0000000000000..542905a2d763c --- /dev/null +++ b/clippy_lints/src/matches/manual_map.rs @@ -0,0 +1,306 @@ +use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::{ + can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks, + peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, +}; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, + QPath, UnsafeSource, +}; +use rustc_lint::LateContext; +use rustc_span::{sym, SyntaxContext}; + +use super::MANUAL_MAP; + +pub(super) fn check_match<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + arms: &'tcx [Arm<'_>], +) { + if let [arm1, arm2] = arms + && arm1.guard.is_none() + && arm2.guard.is_none() + { + check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body); + } +} + +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + check(cx, expr, let_expr, let_pat, then_expr, None, else_expr); +} + +#[expect(clippy::too_many_lines)] +fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + scrutinee: &'tcx Expr<'_>, + then_pat: &'tcx Pat<'_>, + then_body: &'tcx Expr<'_>, + else_pat: Option<&'tcx Pat<'_>>, + else_body: &'tcx Expr<'_>, +) { + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) + { + return; + } + + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return, + }; + + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } + + let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) { + Some(expr) => expr, + None => return, + }; + + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } + + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() { + return; + } + + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; + + match can_move_expr_to_closure(cx, some_expr.expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return, + }; + + let mut app = Applicability::MachineApplicable; + + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; + + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + if_chain! { + if !some_expr.needs_unsafe_block; + if let Some(func) = can_pass_as_func(cx, id, some_expr.expr); + if func.span.ctxt() == some_expr.expr.span.ctxt(); + then { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + } else { + if path_to_local_id(some_expr.expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } + + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip) + } else { + format!("|{}{}| {}", annotation, some_binding, expr_snip) + } + } + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0; + let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0; + if some_expr.needs_unsafe_block { + format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip) + } else { + format!("|{}| {}", pat_snip, expr_snip) + } + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if else_pat.is_none() && is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) + }, + app, + ); +} + +// Checks whether the expression could be passed as a function, or whether a closure is needed. +// Returns the function to be passed to `map` if it exists. +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + match expr.kind { + ExprKind::Call(func, [arg]) + if path_to_local_id(arg, binding) + && cx.typeck_results().expr_adjustments(arg).is_empty() + && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + { + Some(func) + }, + _ => None, + } +} + +enum OptionPat<'a> { + Wild, + None, + Some { + // The pattern contained in the `Some` tuple. + pattern: &'a Pat<'a>, + // The number of references before the `Some` tuple. + // e.g. `&&Some(_)` has a ref count of 2. + ref_count: usize, + }, +} + +struct SomeExpr<'tcx> { + expr: &'tcx Expr<'tcx>, + needs_unsafe_block: bool, +} + +// Try to parse into a recognized `Option` pattern. +// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option> { + match pat.kind { + PatKind::Wild => Some(OptionPat::Wild), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), + PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + { + Some(OptionPat::Some { pattern, ref_count }) + }, + _ => None, + } + } + f(cx, pat, 0, ctxt) +} + +// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. +fn get_some_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + needs_unsafe_block: bool, + ctxt: SyntaxContext, +) -> Option> { + // TODO: Allow more complex expressions. + match expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref qpath), + .. + }, + [arg], + ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr { + expr: arg, + needs_unsafe_block, + }), + ExprKind::Block( + Block { + stmts: [], + expr: Some(expr), + rules, + .. + }, + _, + ) => get_some_expr( + cx, + expr, + needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + ctxt, + ), + _ => None, + } +} + +// Checks for the `None` value. +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) +} diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs new file mode 100644 index 0000000000000..e1111c80f2fe2 --- /dev/null +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -0,0 +1,83 @@ +use clippy_utils::consts::constant_simple; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::usage::contains_return_break_continue_macro; +use clippy_utils::{is_lang_ctor, path_to_local_id, sugg}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::{Arm, Expr, PatKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_UNWRAP_OR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + let ty = cx.typeck_results().expr_ty(scrutinee); + if_chain! { + if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) { + Some("Option") + } else if is_type_diagnostic_item(cx, ty, sym::Result) { + Some("Result") + } else { + None + }; + if let Some(or_arm) = applicable_or_arm(cx, arms); + if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span); + if let Some(indent) = indent_of(cx, expr.span); + if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some(); + then { + let reindented_or_body = + reindent_multiline(or_body_snippet.into(), true, Some(indent)); + + let suggestion = if scrutinee.span.from_expansion() { + // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)` + sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..") + } + else { + sugg::Sugg::hir(cx, scrutinee, "..").maybe_par() + }; + + span_lint_and_sugg( + cx, + MANUAL_UNWRAP_OR, expr.span, + &format!("this pattern reimplements `{}::unwrap_or`", ty_name), + "replace with", + format!( + "{}.unwrap_or({})", + suggestion, + reindented_or_body, + ), + Applicability::MachineApplicable, + ); + } + } +} + +fn applicable_or_arm<'a>(cx: &LateContext<'_>, 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, or_arm)) = arms.iter().enumerate().find(|(_, arm)| { + match arm.pat.kind { + PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone), + PatKind::TupleStruct(ref qpath, [pat], _) => + matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr), + _ => false, + } + }); + let unwrap_arm = &arms[1 - idx]; + if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind; + if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk); + if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind; + if path_to_local_id(unwrap_arm.body, binding_hir_id); + if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty(); + if !contains_return_break_continue_macro(or_arm.body); + then { + Some(or_arm) + } else { + None + } + } +} diff --git a/clippy_lints/src/matches/match_bool.rs b/clippy_lints/src/matches/match_bool.rs index 90c50b994d2bf..1c216e135704c 100644 --- a/clippy_lints/src/matches/match_bool.rs +++ b/clippy_lints/src/matches/match_bool.rs @@ -24,8 +24,8 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind { if let ExprKind::Lit(ref lit) = arm_bool.kind { match lit.node { - LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)), - LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)), + LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), + LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), _ => None, } } else { diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index 2e1f7646eb400..a68eec842abc5 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_wild; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{higher, is_wild}; use rustc_ast::{Attribute, LitKind}; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; @@ -11,22 +11,24 @@ use rustc_span::source_map::Spanned; use super::MATCH_LIKE_MATCHES_MACRO; /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!` -pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - let_pat, +pub(crate) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + let_pat: &'tcx Pat<'_>, + let_expr: &'tcx Expr<'_>, + then_expr: &'tcx Expr<'_>, + else_expr: &'tcx Expr<'_>, +) { + find_matches_sugg( + cx, let_expr, - if_then, - if_else: Some(if_else), - }) = higher::IfLet::hir(cx, expr) - { - find_matches_sugg( - cx, - let_expr, - IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]), - expr, - true, - ); - } + IntoIterator::into_iter([ + (&[][..], Some(let_pat), then_expr, None), + (&[][..], None, else_expr, None), + ]), + expr, + true, + ); } pub(super) fn check_match<'tcx>( diff --git a/clippy_lints/src/matches/match_on_vec_items.rs b/clippy_lints/src/matches/match_on_vec_items.rs new file mode 100644 index 0000000000000..2917f85c45f53 --- /dev/null +++ b/clippy_lints/src/matches/match_on_vec_items.rs @@ -0,0 +1,61 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MATCH_ON_VEC_ITEMS; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { + if_chain! { + if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); + if let ExprKind::Index(vec, idx) = idx_expr.kind; + + then { + // FIXME: could be improved to suggest surrounding every pattern with Some(_), + // but only when `or_patterns` are stabilized. + span_lint_and_sugg( + cx, + MATCH_ON_VEC_ITEMS, + scrutinee.span, + "indexing into a vector may panic", + "try this", + format!( + "{}.get({})", + snippet(cx, vec.span, ".."), + snippet(cx, idx.span, "..") + ), + Applicability::MaybeIncorrect + ); + } + } +} + +fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if_chain! { + if let ExprKind::Index(array, index) = expr.kind; + if is_vector(cx, array); + if !is_full_range(cx, index); + + then { + return Some(expr); + } + } + + None +} + +fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_diagnostic_item(cx, ty, sym::Vec) +} + +fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr); + let ty = ty.peel_refs(); + is_type_lang_item(cx, ty, LangItem::RangeFull) +} diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs similarity index 64% rename from clippy_lints/src/match_str_case_mismatch.rs rename to clippy_lints/src/matches/match_str_case_mismatch.rs index 85aec93670b95..8302ce426e570 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -3,48 +3,13 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; +use rustc_hir::{Arm, Expr, ExprKind, PatKind}; +use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; -declare_clippy_lint! { - /// ### What it does - /// Checks for `match` expressions modifying the case of a string with non-compliant arms - /// - /// ### Why is this bad? - /// The arm is unreachable, which is likely a mistake - /// - /// ### Example - /// ```rust - /// # let text = "Foo"; - /// - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "Bar" => {}, - /// _ => {}, - /// } - /// ``` - /// Use instead: - /// ```rust - /// # let text = "Foo"; - /// - /// match &*text.to_ascii_lowercase() { - /// "foo" => {}, - /// "bar" => {}, - /// _ => {}, - /// } - /// ``` - #[clippy::version = "1.58.0"] - pub MATCH_STR_CASE_MISMATCH, - correctness, - "creation of a case altering match expression with non-compliant arms" -} - -declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]); +use super::MATCH_STR_CASE_MISMATCH; #[derive(Debug)] enum CaseMethod { @@ -54,25 +19,21 @@ enum CaseMethod { AsciiUppercase, } -impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind; - if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind(); - if let ty::Str = ty.kind(); - then { - let mut visitor = MatchExprVisitor { - cx, - case_method: None, - }; - - visitor.visit_expr(match_expr); - - if let Some(case_method) = visitor.case_method { - if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { - lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); - } +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) { + if_chain! { + if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind(); + if let ty::Str = ty.kind(); + then { + let mut visitor = MatchExprVisitor { + cx, + case_method: None, + }; + + visitor.visit_expr(scrutinee); + + if let Some(case_method) = visitor.case_method { + if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) { + lint(cx, &case_method, bad_case_span, bad_case_sym.as_str()); } } } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 3d8391bce2b28..d1e42f39e470d 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,26 +1,34 @@ use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; +mod collapsible_match; mod infallible_destructuring_match; +mod manual_map; +mod manual_unwrap_or; mod match_as_ref; mod match_bool; mod match_like_matches; +mod match_on_vec_items; mod match_ref_pats; mod match_same_arms; mod match_single_binding; +mod match_str_case_mismatch; mod match_wild_enum; mod match_wild_err_arm; mod needless_match; mod overlapping_arms; mod redundant_pattern_match; mod rest_pat_in_fully_bound_struct; +mod significant_drop_in_scrutinee; mod single_match; +mod try_err; mod wild_in_or_pats; declare_clippy_lint! { @@ -610,6 +618,274 @@ declare_clippy_lint! { "`match` or match-like `if let` that are unnecessary" } +declare_clippy_lint! { + /// ### What it does + /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together + /// without adding any branches. + /// + /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only + /// cases where merging would most likely make the code more readable. + /// + /// ### Why is this bad? + /// It is unnecessarily verbose and complex. + /// + /// ### Example + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(n) => match n { + /// Ok(n) => n, + /// _ => return, + /// } + /// None => return, + /// }; + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn func(opt: Option>) { + /// let n = match opt { + /// Some(Ok(n)) => n, + /// _ => return, + /// }; + /// } + /// ``` + #[clippy::version = "1.50.0"] + pub COLLAPSIBLE_MATCH, + style, + "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together." +} + +declare_clippy_lint! { + /// ### What it does + /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`. + /// + /// ### Why is this bad? + /// Concise code helps focusing on behavior instead of boilerplate. + /// + /// ### Example + /// ```rust + /// let foo: Option = None; + /// match foo { + /// Some(v) => v, + /// None => 1, + /// }; + /// ``` + /// + /// Use instead: + /// ```rust + /// let foo: Option = None; + /// foo.unwrap_or(1); + /// ``` + #[clippy::version = "1.49.0"] + pub MANUAL_UNWRAP_OR, + complexity, + "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match vec[idx]` or `match vec[n..m]`. + /// + /// ### Why is this bad? + /// This can panic at runtime. + /// + /// ### Example + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Bad + /// match arr[idx] { + /// 0 => println!("{}", 0), + /// 1 => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust, no_run + /// let arr = vec![0, 1, 2, 3]; + /// let idx = 1; + /// + /// // Good + /// match arr.get(idx) { + /// Some(0) => println!("{}", 0), + /// Some(1) => println!("{}", 3), + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.45.0"] + pub MATCH_ON_VEC_ITEMS, + pedantic, + "matching on vector elements can panic" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for `match` expressions modifying the case of a string with non-compliant arms + /// + /// ### Why is this bad? + /// The arm is unreachable, which is likely a mistake + /// + /// ### Example + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "Bar" => {}, + /// _ => {}, + /// } + /// ``` + /// Use instead: + /// ```rust + /// # let text = "Foo"; + /// match &*text.to_ascii_lowercase() { + /// "foo" => {}, + /// "bar" => {}, + /// _ => {}, + /// } + /// ``` + #[clippy::version = "1.58.0"] + pub MATCH_STR_CASE_MISMATCH, + correctness, + "creation of a case altering match expression with non-compliant arms" +} + +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust.ignore + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + suspicious, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Err(x)?`. + /// + /// ### Why is this bad? + /// The `?` operator is designed to allow calls that + /// can fail to be easily chained. For example, `foo()?.bar()` or + /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will + /// always return), it is more clear to write `return Err(x)`. + /// + /// ### Example + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// Err("failed")?; + /// } + /// Ok(0) + /// } + /// ``` + /// Could be written: + /// + /// ```rust + /// fn foo(fail: bool) -> Result { + /// if fail { + /// return Err("failed".into()); + /// } + /// Ok(0) + /// } + /// ``` + #[clippy::version = "1.38.0"] + pub TRY_ERR, + restriction, + "return errors explicitly rather than hiding them behind a `?`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `match` which could be implemented using `map` + /// + /// ### Why is this bad? + /// Using the `map` method is clearer and more concise. + /// + /// ### Example + /// ```rust + /// match Some(0) { + /// Some(x) => Some(x + 1), + /// None => None, + /// }; + /// ``` + /// Use instead: + /// ```rust + /// Some(0).map(|x| x + 1); + /// ``` + #[clippy::version = "1.52.0"] + pub MANUAL_MAP, + style, + "reimplementation of `map`" +} + #[derive(Default)] pub struct Matches { msrv: Option, @@ -644,19 +920,42 @@ impl_lint_pass!(Matches => [ MATCH_LIKE_MATCHES_MACRO, MATCH_SAME_ARMS, NEEDLESS_MATCH, + COLLAPSIBLE_MATCH, + MANUAL_UNWRAP_OR, + MATCH_ON_VEC_ITEMS, + MATCH_STR_CASE_MISMATCH, + SIGNIFICANT_DROP_IN_SCRUTINEE, + TRY_ERR, + MANUAL_MAP, ]); impl<'tcx> LateLintPass<'tcx> for Matches { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if expr.span.from_expansion() { + if in_external_macro(cx.sess(), expr.span) { return; } + let from_expansion = expr.span.from_expansion(); if let ExprKind::Match(ex, arms, source) = expr.kind { - if !span_starts_with(cx, expr.span, "match") { + if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") { return; } - if !contains_cfg_arm(cx, expr, ex, arms) { + if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) { + significant_drop_in_scrutinee::check(cx, expr, ex, source); + } + + collapsible_match::check_match(cx, arms); + if !from_expansion { + // These don't depend on a relationship between multiple arms + match_wild_err_arm::check(cx, ex, arms); + wild_in_or_pats::check(cx, arms); + } + + if source == MatchSource::TryDesugar { + try_err::check(cx, expr, ex); + } + + if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) @@ -671,6 +970,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_enum::check(cx, ex, arms); match_as_ref::check(cx, ex, arms, expr); needless_match::check_match(cx, ex, arms, expr); + match_on_vec_items::check(cx, ex); + match_str_case_mismatch::check(cx, ex, arms); + + if !in_constant(cx, expr.hir_id) { + manual_unwrap_or::check(cx, expr, ex, arms); + manual_map::check_match(cx, expr, ex, arms); + } if self.infallible_destructuring_match_linted { self.infallible_destructuring_match_linted = false; @@ -680,16 +986,35 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } - - // These don't depend on a relationship between multiple arms - match_wild_err_arm::check(cx, ex, arms); - wild_in_or_pats::check(cx, arms); - } else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { - match_like_matches::check(cx, expr); + } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { + collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); + if !from_expansion { + if let Some(else_expr) = if_let.if_else { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + match_like_matches::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); + } + if !in_constant(cx, expr.hir_id) { + manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr); + } + } + redundant_pattern_match::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_else.is_some(), + ); + needless_match::check_if_let(cx, expr, &if_let); } + } else if !from_expansion { redundant_pattern_match::check(cx, expr); - needless_match::check(cx, expr); } } diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index f920ad4651f9d..fa19cddd35ec7 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -47,20 +47,18 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], /// some_enum /// } /// ``` -pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) { - if let Some(ref if_let) = higher::IfLet::hir(cx, ex) { - if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) { - let mut applicability = Applicability::MachineApplicable; - span_lint_and_sugg( - cx, - NEEDLESS_MATCH, - ex.span, - "this if-let expression is unnecessary", - "replace it with", - snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), - applicability, - ); - } +pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) { + if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_MATCH, + ex.span, + "this if-let expression is unnecessary", + "replace it with", + snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(), + applicability, + ); } } @@ -77,7 +75,7 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) true } -fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { +fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let Some(if_else) = if_let.if_else { if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) { return false; @@ -85,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { - return check_if_let(cx, nested_if_let); + return check_if_let_inner(cx, nested_if_let); } if matches!(if_else.kind, ExprKind::Block(..)) { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 1a8b9d15f370f..095cd43ea13fb 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -18,19 +18,21 @@ use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; use rustc_span::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - if_else, - let_pat, - let_expr, - .. - }) = higher::IfLet::hir(cx, expr) - { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()); - } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { + if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false); } } +pub(super) fn check_if_let<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + pat: &'tcx Pat<'_>, + scrutinee: &'tcx Expr<'_>, + has_else: bool, +) { + find_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else); +} + // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs similarity index 64% rename from clippy_lints/src/significant_drop_in_scrutinee.rs rename to clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 424b361a905ce..a211dc18f9e1e 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,103 +4,25 @@ use clippy_utils::get_attr; use clippy_utils::source::{indent_of, snippet}; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, ExprKind, MatchSource}; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{Ty, TypeAndMut}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Check for temporaries returned from function calls in a match scrutinee that have the - /// `clippy::has_significant_drop` attribute. - /// - /// ### Why is this bad? - /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have - /// an important side-effect, such as unlocking a mutex, making it important for users to be - /// able to accurately understand their lifetimes. When a temporary is returned in a function - /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may - /// be surprising. - /// - /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a - /// function call that returns a `MutexGuard` and then tries to lock again in one of the match - /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of - /// the match block and thus will not unlock. - /// - /// ### Example - /// ```rust.ignore - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// - /// let mutex = Mutex::new(State {}); - /// - /// match mutex.lock().unwrap().foo() { - /// true => { - /// mutex.lock().unwrap().bar(); // Deadlock! - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// - /// ``` - /// Use instead: - /// ```rust - /// # use std::sync::Mutex; - /// - /// # struct State {} - /// - /// # impl State { - /// # fn foo(&self) -> bool { - /// # true - /// # } - /// - /// # fn bar(&self) {} - /// # } - /// - /// let mutex = Mutex::new(State {}); - /// - /// let is_foo = mutex.lock().unwrap().foo(); - /// match is_foo { - /// true => { - /// mutex.lock().unwrap().bar(); - /// } - /// false => {} - /// }; - /// - /// println!("All done!"); - /// ``` - #[clippy::version = "1.60.0"] - pub SIGNIFICANT_DROP_IN_SCRUTINEE, - suspicious, - "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" -} - -declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); +use super::SIGNIFICANT_DROP_IN_SCRUTINEE; -impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { - for found in suggestions { - span_lint_and_then( - cx, - SIGNIFICANT_DROP_IN_SCRUTINEE, - found.found_span, - "temporary with significant drop in match scrutinee", - |diag| set_diagnostic(diag, cx, expr, found), - ); - } +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + scrutinee: &'tcx Expr<'_>, + source: MatchSource, +) { + if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) { + for found in suggestions { + span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| { + set_diagnostic(diag, cx, expr, found); + }); } } } @@ -152,13 +74,18 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// may have a surprising lifetime. fn has_significant_drop_in_scrutinee<'tcx, 'a>( cx: &'a LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, -) -> Option> { + scrutinee: &'tcx Expr<'tcx>, + source: MatchSource, +) -> Option<(Vec, &'static str)> { let mut helper = SigDropHelper::new(cx); - match expr.kind { - ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), - _ => None, - } + helper.find_sig_drop(scrutinee).map(|drops| { + let message = if source == MatchSource::Normal { + "temporary with significant drop in match scrutinee" + } else { + "temporary with significant drop in for loop" + }; + (drops, message) + }) } struct SigDropHelper<'a, 'tcx> { @@ -213,6 +140,19 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { self.sig_drop_spans.take() } + fn replace_current_sig_drop( + &mut self, + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, + ) { + self.current_sig_drop.replace(FoundSigDrop { + found_span, + is_unit_return_val, + lint_suggestion, + }); + } + /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. @@ -229,25 +169,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // but let's avoid any chance of an ICE if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndDerefToCopy, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy); } else if allow_move_and_clone { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveAndClone, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } } else if ty.is_trivially_pure_clone_copy() { - self.current_sig_drop.replace(FoundSigDrop { - found_span: expr.span, - is_unit_return_val: false, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly); + } else if allow_move_and_clone { + self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone); } } @@ -279,11 +209,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { // If either side had a significant drop, suggest moving the entire scrutinee to avoid // unnecessary copies and to simplify cases where both sides have significant drops. if self.has_significant_drop { - self.current_sig_drop.replace(FoundSigDrop { - found_span: span, - is_unit_return_val, - lint_suggestion: LintSuggestion::MoveOnly, - }); + self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly); } self.special_handling_for_binary_op = false; @@ -363,34 +289,34 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { } } ExprKind::Box(..) | - ExprKind::Array(..) | - ExprKind::Call(..) | - ExprKind::Unary(..) | - ExprKind::If(..) | - ExprKind::Match(..) | - ExprKind::Field(..) | - ExprKind::Index(..) | - ExprKind::Ret(..) | - ExprKind::Repeat(..) | - ExprKind::Yield(..) | - ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), ExprKind::AddrOf(_, _, _) | - ExprKind::Block(_, _) | - ExprKind::Break(_, _) | - ExprKind::Cast(_, _) | - // Don't want to check the closure itself, only invocation, which is covered by MethodCall - ExprKind::Closure(_, _, _, _, _) | - ExprKind::ConstBlock(_) | - ExprKind::Continue(_) | - ExprKind::DropTemps(_) | - ExprKind::Err | - ExprKind::InlineAsm(_) | - ExprKind::Let(_) | - ExprKind::Lit(_) | - ExprKind::Loop(_, _, _, _) | - ExprKind::Path(_) | - ExprKind::Struct(_, _, _) | - ExprKind::Type(_, _) => { + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { return; } } diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs new file mode 100644 index 0000000000000..0491a0679f37a --- /dev/null +++ b/clippy_lints/src/matches/try_err.rs @@ -0,0 +1,145 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{hygiene, sym}; + +use super::TRY_ERR; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) { + // Looks for a structure like this: + // match ::std::ops::Try::into_result(Err(5)) { + // ::std::result::Result::Err(err) => + // #[allow(unreachable_code)] + // return ::std::ops::Try::from_error(::std::convert::From::from(err)), + // ::std::result::Result::Ok(val) => + // #[allow(unreachable_code)] + // val, + // }; + if_chain! { + if let ExprKind::Call(match_fun, try_args) = scrutinee.kind; + if let ExprKind::Path(ref match_fun_path) = match_fun.kind; + if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); + if let Some(try_arg) = try_args.get(0); + if let ExprKind::Call(err_fun, err_args) = try_arg.kind; + if let Some(err_arg) = err_args.get(0); + if let ExprKind::Path(ref err_fun_path) = err_fun.kind; + if is_lang_ctor(cx, err_fun_path, ResultErr); + if let Some(return_ty) = find_return_type(cx, &expr.kind); + then { + let prefix; + let suffix; + let err_ty; + + if let Some(ty) = result_error_type(cx, return_ty) { + prefix = "Err("; + suffix = ")"; + err_ty = ty; + } else if let Some(ty) = poll_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Err("; + suffix = "))"; + err_ty = ty; + } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { + prefix = "Poll::Ready(Some(Err("; + suffix = ")))"; + err_ty = ty; + } else { + return; + }; + + let expr_err_ty = cx.typeck_results().expr_ty(err_arg); + let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); + let mut applicability = Applicability::MachineApplicable; + let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); + let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { + "" // already returns + } else { + "return " + }; + let suggestion = if err_ty == expr_err_ty { + format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) + } else { + format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) + }; + + span_lint_and_sugg( + cx, + TRY_ERR, + expr.span, + "returning an `Err(_)` with the `?` operator", + "try this", + suggestion, + applicability, + ); + } + } +} + +/// Finds function return type by examining return expressions in match arms. +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { + if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { + for arm in arms.iter() { + if let ExprKind::Ret(Some(ret)) = arm.body.kind { + return Some(cx.typeck_results().expr_ty(ret)); + } + } + } + None +} + +/// Extracts the error type from Result. +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(_, subst) = ty.kind(); + if is_type_diagnostic_item(cx, ty, sym::Result); + then { + Some(subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>. +fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); + then { + Some(ready_subst.type_at(1)) + } else { + None + } + } +} + +/// Extracts the error type from Poll>>. +fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + if_chain! { + if let ty::Adt(def, subst) = ty.kind(); + if match_def_path(cx, def.did(), &paths::POLL); + let ready_ty = subst.type_at(0); + + if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); + let some_ty = ready_subst.type_at(0); + + if let ty::Adt(some_def, some_subst) = some_ty.kind(); + if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); + then { + Some(some_subst.type_at(1)) + } else { + None + } + } +} diff --git a/clippy_lints/src/methods/get_last_with_len.rs b/clippy_lints/src/methods/get_last_with_len.rs new file mode 100644 index 0000000000000..23368238ef5cc --- /dev/null +++ b/clippy_lints/src/methods/get_last_with_len.rs @@ -0,0 +1,55 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::SpanlessEq; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::source_map::Spanned; +use rustc_span::sym; + +use super::GET_LAST_WITH_LEN; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { + // Argument to "get" is a subtraction + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = arg.kind + + // LHS of subtraction is "x.len()" + && let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind + && lhs_path.ident.name == sym::len + + // RHS of subtraction is 1 + && let ExprKind::Lit(rhs_lit) = &rhs.kind + && let LitKind::Int(1, ..) = rhs_lit.node + + // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)` + && SpanlessEq::new(cx).eq_expr(recv, lhs_recv) + && !recv.can_have_side_effects() + { + let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() { + ty::Adt(def, _) if cx.tcx.is_diagnostic_item(sym::VecDeque, def.did()) => "back", + ty::Slice(_) => "last", + _ => return, + }; + + let mut applicability = Applicability::MachineApplicable; + let recv_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); + + span_lint_and_sugg( + cx, + GET_LAST_WITH_LEN, + expr.span, + &format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"), + "try", + format!("{recv_snippet}.{method}()"), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/iter_next_slice.rs b/clippy_lints/src/methods/iter_next_slice.rs index d053ff567565c..b8d1dabe00764 100644 --- a/clippy_lints/src/methods/iter_next_slice.rs +++ b/clippy_lints/src/methods/iter_next_slice.rs @@ -34,13 +34,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if let ast::LitKind::Int(start_idx, _) = start_lit.node; then { let mut applicability = Applicability::MachineApplicable; + let suggest = if start_idx == 0 { + format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability)) + } else { + format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx) + }; span_lint_and_sugg( cx, ITER_NEXT_SLICE, expr.span, "using `.iter().next()` on a Slice without end index", "try calling", - format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx), + suggest, applicability, ); } @@ -55,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal "using `.iter().next()` on an array", "try calling", format!( - "{}.get(0)", + "{}.first()", snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability) ), applicability, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3bf48e18019df..7308e74c323e3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -21,6 +21,7 @@ mod filter_next; mod flat_map_identity; mod flat_map_option; mod from_iter_instead_of_collect; +mod get_last_with_len; mod get_unwrap; mod implicit_clone; mod inefficient_to_string; @@ -44,6 +45,7 @@ mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; mod needless_option_take; +mod no_effect_replace; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -192,25 +194,18 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let opt = Some(1); - /// - /// // Bad - /// opt.unwrap(); - /// - /// // Good - /// opt.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.unwrap(); + /// result.unwrap(); /// ``` /// - /// // or - /// + /// Use instead: /// ```rust - /// # let res: Result = Ok(1); - /// - /// // Bad - /// res.unwrap(); - /// - /// // Good - /// res.expect("more helpful message"); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("more helpful message"); + /// result.expect("more helpful message"); /// ``` #[clippy::version = "1.45.0"] pub UNWRAP_USED, @@ -233,27 +228,21 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust,ignore - /// # let opt = Some(1); - /// - /// // Bad - /// opt.expect("one"); - /// - /// // Good - /// let opt = Some(1); - /// opt?; + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option.expect("one"); + /// result.expect("one"); /// ``` /// - /// // or - /// - /// ```rust - /// # let res: Result = Ok(1); + /// Use instead: + /// ```rust,ignore + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// option?; /// - /// // Bad - /// res.expect("one"); + /// // or /// - /// // Good - /// res?; - /// # Ok::<(), ()>(()) + /// result?; /// ``` #[clippy::version = "1.45.0"] pub EXPECT_USED, @@ -429,26 +418,20 @@ declare_clippy_lint! { /// /// ### Examples /// ```rust - /// # let x = Some(1); - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or(0); - /// - /// // Good - /// x.map_or(0, |a| a + 1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); + /// # fn some_function(foo: ()) -> usize { 1 } + /// option.map(|a| a + 1).unwrap_or(0); + /// result.map(|a| a + 1).unwrap_or_else(some_function); /// ``` /// - /// // or - /// + /// Use instead: /// ```rust - /// # let x: Result = Ok(1); + /// # let option = Some(1); + /// # let result: Result = Ok(1); /// # fn some_function(foo: ()) -> usize { 1 } - /// - /// // Bad - /// x.map(|a| a + 1).unwrap_or_else(some_function); - /// - /// // Good - /// x.map_or_else(some_function, |a| a + 1); + /// option.map_or(0, |a| a + 1); + /// result.map_or_else(some_function, |a| a + 1); /// ``` #[clippy::version = "1.45.0"] pub MAP_UNWRAP_OR, @@ -791,13 +774,14 @@ declare_clippy_lint! { /// # let foo = Some(String::new()); /// foo.unwrap_or(String::new()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// foo.unwrap_or_else(String::new); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); /// foo.unwrap_or_default(); /// ``` @@ -861,15 +845,14 @@ declare_clippy_lint! { /// # let err_code = "418"; /// # let err_msg = "I'm a teapot"; /// foo.expect(&format!("Err {}: {}", err_code, err_msg)); - /// ``` - /// or - /// ```rust + /// + /// // or + /// /// # let foo = Some(String::new()); - /// # let err_code = "418"; - /// # let err_msg = "I'm a teapot"; /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str()); /// ``` - /// this can instead be written: + /// + /// Use instead: /// ```rust /// # let foo = Some(String::new()); /// # let err_code = "418"; @@ -1209,6 +1192,38 @@ declare_clippy_lint! { "replace `.drain(..)` with `.into_iter()`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for using `x.get(x.len() - 1)` instead of + /// `x.last()`. + /// + /// ### Why is this bad? + /// Using `x.last()` is easier to read and has the same + /// result. + /// + /// Note that using `x[x.len() - 1]` is semantically different from + /// `x.last()`. Indexing into the array will panic on out-of-bounds + /// accesses, while `x.get()` and `x.last()` will return `None`. + /// + /// There is another lint (get_unwrap) that covers the case of using + /// `x.get(index).unwrap()` instead of `x[index]`. + /// + /// ### Example + /// ```rust + /// // Bad + /// let x = vec![2, 3, 5]; + /// let last_element = x.get(x.len() - 1); + /// + /// // Good + /// let x = vec![2, 3, 5]; + /// let last_element = x.last(); + /// ``` + #[clippy::version = "1.37.0"] + pub GET_LAST_WITH_LEN, + complexity, + "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler" +} + declare_clippy_lint! { /// ### What it does /// Checks for use of `.get().unwrap()` (or @@ -2195,6 +2210,24 @@ declare_clippy_lint! { "using `.as_ref().take()` on a temporary value" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `replace` statements which have no effect. + /// + /// ### Why is this bad? + /// It's either a mistake or confusing. + /// + /// ### Example + /// ```rust + /// "1234".replace("12", "12"); + /// "1234".replacen("12", "12", 1); + /// ``` + #[clippy::version = "1.62.0"] + pub NO_EFFECT_REPLACE, + suspicious, + "replace with no effect" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2264,6 +2297,7 @@ impl_lint_pass!(Methods => [ BYTES_NTH, ITER_SKIP_NEXT, GET_UNWRAP, + GET_LAST_WITH_LEN, STRING_EXTEND_CHARS, ITER_CLONED_COLLECT, ITER_WITH_DRAIN, @@ -2294,6 +2328,7 @@ impl_lint_pass!(Methods => [ NEEDLESS_OPTION_AS_DEREF, IS_DIGIT_ASCII_RADIX, NEEDLESS_OPTION_TAKE, + NO_EFFECT_REPLACE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2590,6 +2625,7 @@ impl Methods { inspect_for_each::check(cx, expr, span2); } }, + ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg), ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), @@ -2705,6 +2741,9 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); }, }, + ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => { + no_effect_replace::check(cx, expr, arg1, arg2); + }, _ => {}, } } diff --git a/clippy_lints/src/methods/no_effect_replace.rs b/clippy_lints/src/methods/no_effect_replace.rs new file mode 100644 index 0000000000000..a76341855b6db --- /dev/null +++ b/clippy_lints/src/methods/no_effect_replace.rs @@ -0,0 +1,47 @@ +use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::SpanlessEq; +use if_chain::if_chain; +use rustc_ast::LitKind; +use rustc_hir::ExprKind; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NO_EFFECT_REPLACE; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx rustc_hir::Expr<'_>, + arg1: &'tcx rustc_hir::Expr<'_>, + arg2: &'tcx rustc_hir::Expr<'_>, +) { + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) { + return; + } + + if_chain! { + if let ExprKind::Lit(spanned) = &arg1.kind; + if let Some(param1) = lit_string_value(&spanned.node); + + if let ExprKind::Lit(spanned) = &arg2.kind; + if let LitKind::Str(param2, _) = &spanned.node; + if param1 == param2.as_str(); + + then { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } + } + + if SpanlessEq::new(cx).eq_expr(arg1, arg2) { + span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself"); + } +} + +fn lit_string_value(node: &LitKind) -> Option { + match node { + LitKind::Char(value) => Some(value.to_string()), + LitKind::Str(value, _) => Some(value.as_str().to_owned()), + _ => None, + } +} diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 76bc9466ed818..8989db54f6c5f 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( let func_snippet = snippet(cx, map_arg.span, ".."); let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ `and_then(..)` instead"; - return span_lint_and_sugg( + span_lint_and_sugg( cx, OPTION_MAP_OR_NONE, expr.span, @@ -110,7 +110,7 @@ pub(super) fn check<'tcx>( let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \ `ok()` instead"; let self_snippet = snippet(cx, recv.span, ".."); - return span_lint_and_sugg( + span_lint_and_sugg( cx, RESULT_MAP_OR_INTO_OPTION, expr.span, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 97c4feb3122a0..b4c6bfb31ed1c 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -415,7 +415,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool { - is_clone_like(cx, &*method_name.as_str(), method_def_id) + is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string(cx, method_name, method_def_id) } diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs index 65d1f440b7639..a081cde85725b 100644 --- a/clippy_lints/src/minmax.rs +++ b/clippy_lints/src/minmax.rs @@ -18,11 +18,11 @@ declare_clippy_lint! { /// the least it hurts readability of the code. /// /// ### Example - /// ```ignore + /// ```rust,ignore /// min(0, max(100, x)) - /// ``` - /// or - /// ```ignore + /// + /// // or + /// /// x.max(100).min(0) /// ``` /// It will always be equal to `0`. Probably the author meant to clamp the value diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 7fdc28c5a062d..5566569945322 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -103,11 +103,14 @@ declare_clippy_lint! { /// let x = 1.2331f64; /// let y = 1.2332f64; /// - /// // Bad /// if y == 1.23f64 { } /// if y != x {} // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1.2331f64; + /// # let y = 1.2332f64; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; @@ -258,10 +261,13 @@ declare_clippy_lint! { /// let x: f64 = 1.0; /// const ONE: f64 = 1.00; /// - /// // Bad /// if x == ONE { } // where both are floats + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x: f64 = 1.0; + /// # const ONE: f64 = 1.00; /// let error_margin = f64::EPSILON; // Use an epsilon for comparison /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead. /// // let error_margin = std::f64::EPSILON; diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs new file mode 100644 index 0000000000000..d466d54a6ba5d --- /dev/null +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -0,0 +1,116 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for type parameters which are positioned inconsistently between + /// a type definition and impl block. Specifically, a paramater in an impl + /// block which has the same name as a parameter in the type def, but is in + /// a different place. + /// + /// ### Why is this bad? + /// Type parameters are determined by their position rather than name. + /// Naming type parameters inconsistently may cause you to refer to the + /// wrong type parameter. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// // inside the impl, B refers to Foo::A + /// impl Foo {} + /// ``` + /// Use instead: + /// ```rust + /// struct Foo { + /// x: A, + /// y: B, + /// } + /// impl Foo {} + /// ``` + #[clippy::version = "1.62.0"] + pub MISMATCHING_TYPE_PARAM_ORDER, + pedantic, + "type parameter positioned inconsistently between type def and impl block" +} +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); + +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if_chain! { + if !item.span.from_expansion(); + if let ItemKind::Impl(imp) = &item.kind; + if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind; + if let Some(segment) = path.segments.iter().next(); + if let Some(generic_args) = segment.args; + if !generic_args.args.is_empty(); + then { + // get the name and span of the generic parameters in the Impl + let impl_params = generic_args.args.iter() + .filter_map(|p| + match p { + GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) => + Some((path.segments[0].ident.to_string(), path.span)), + _ => None, + } + ); + + // find the type that the Impl is for + // only lint on struct/enum/union for now + let defid = match path.res { + Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid, + _ => return, + }; + + // get the names of the generic parameters in the type + let type_params = &cx.tcx.generics_of(defid).params; + let type_param_names: Vec<_> = type_params.iter() + .filter_map(|p| + match p.kind { + GenericParamDefKind::Type {..} => Some(p.name.to_string()), + _ => None, + } + ).collect(); + // hashmap of name -> index for mismatch_param_name + let type_param_names_hashmap: FxHashMap<&String, usize> = + type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect(); + + let type_name = segment.ident; + for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() { + if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) { + let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order", + type_name, impl_param_name); + let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params", + type_param_names[i], type_name); + span_lint_and_help( + cx, + MISMATCHING_TYPE_PARAM_ORDER, + impl_param_span, + &msg, + None, + &help + ); + } + } + } + } + } +} + +// Checks if impl_param_name is the same as one of type_param_names, +// and is in a different position +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool { + if let Some(j) = type_param_names.get(impl_param_name) { + if i != *j { + return true; + } + } + false +} diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 405fc23e8de0a..024bd0760715e 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -120,7 +120,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { self.visit_expr(if_expr); } // make sure top level arm expressions aren't linted - self.maybe_walk_expr(&*arm.body); + self.maybe_walk_expr(arm.body); } }, _ => walk_expr(self, e), diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index cba54e14212d0..7e2531c7ca5f0 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat)); + check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index bb6d820b08cde..44fdf84c6df79 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -22,9 +22,12 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// debug_assert_eq!(vec![3].pop(), Some(3)); + /// /// // or - /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } - /// debug_assert!(take_a_mut_parameter(&mut 5)); + /// + /// # let mut x = 5; + /// # fn takes_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() } + /// debug_assert!(takes_a_mut_parameter(&mut x)); /// ``` #[clippy::version = "1.40.0"] pub DEBUG_ASSERT_WITH_MUT_CALL, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index b70871b38beab..26c694a71fedd 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -194,14 +194,15 @@ fn assignment_suggestions<'tcx>( })) .collect::>>()?; - let applicability = if suggestions.len() > 1 { + match suggestions.len() { + // All of `exprs` are never types + // https://github.com/rust-lang/rust-clippy/issues/8911 + 0 => None, + 1 => Some((Applicability::MachineApplicable, suggestions)), // multiple suggestions don't work with rustfix in multipart_suggest // https://github.com/rust-lang/rustfix/issues/141 - Applicability::Unspecified - } else { - Applicability::MachineApplicable - }; - Some((applicability, suggestions)) + _ => Some((Applicability::Unspecified, suggestions)), + } } struct Usage<'tcx> { diff --git a/clippy_lints/src/arithmetic.rs b/clippy_lints/src/numeric_arithmetic.rs similarity index 97% rename from clippy_lints/src/arithmetic.rs rename to clippy_lints/src/numeric_arithmetic.rs index c5948707c8124..5c4de3381496c 100644 --- a/clippy_lints/src/arithmetic.rs +++ b/clippy_lints/src/numeric_arithmetic.rs @@ -51,16 +51,16 @@ declare_clippy_lint! { } #[derive(Copy, Clone, Default)] -pub struct Arithmetic { +pub struct NumericArithmetic { expr_span: Option, /// This field is used to check whether expressions are constants, such as in enum discriminants /// and consts const_span: Option, } -impl_lint_pass!(Arithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); +impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]); -impl<'tcx> LateLintPass<'tcx> for Arithmetic { +impl<'tcx> LateLintPass<'tcx> for NumericArithmetic { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if self.expr_span.is_some() { return; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index b7a56970b3355..21acf003d92b2 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -61,7 +61,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir expr_visitor_no_bodies(|expr| { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; if matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" ) { panics.push(macro_call.span); diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index e3ded716341f6..5a93431f25a98 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -233,7 +233,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue { } if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind { - self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None); + self.check_poly_fn(cx, item.def_id, method_sig.decl, None); } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 26dc88a406e02..eea036178b837 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { }, ExprKind::Binary(ref op, l, r) => { if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { - check_possible_range_contains(cx, op.node, l, r, expr); + check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } }, _ => {}, @@ -213,12 +213,12 @@ fn check_possible_range_contains( left: &Expr<'_>, right: &Expr<'_>, expr: &Expr<'_>, + span: Span, ) { if in_constant(cx, expr.hir_id) { return; } - let span = expr.span; let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -294,6 +294,20 @@ fn check_possible_range_contains( ); } } + + // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have + // the same operator precedence + if_chain! { + if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind; + if op == lhs_op.node; + let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()); + if let Some(snip) = &snippet_opt(cx, new_span); + // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong + if snip.matches('(').count() == snip.matches(')').count(); + then { + check_possible_range_contains(cx, op, new_lhs, right, expr, new_span); + } + } } struct RangeBounds<'a> { diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 110f58f3734df..8db8c4e9b7870 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -2,7 +2,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::paths; use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::ty::match_type; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -11,10 +13,11 @@ use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does - /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`) + /// in `vec![elem; len]` /// /// ### Why is this bad? - /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak` /// is a bit misleading, as it will create references to the same pointer, rather /// than different instances. /// @@ -26,7 +29,6 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// /// // Initialize each value separately: /// let mut data = Vec::with_capacity(100); /// for _ in 0..100 { @@ -42,7 +44,7 @@ declare_clippy_lint! { #[clippy::version = "1.62.0"] pub RC_CLONE_IN_VEC_INIT, suspicious, - "initializing `Arc` or `Rc` in `vec![elem; len]`" + "initializing reference-counted pointer in `vec![elem; len]`" } declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); @@ -50,26 +52,12 @@ impl LateLintPass<'_> for RcCloneInVecInit { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; - let Some(symbol) = new_reference_call(cx, elem) else { return; }; + let Some((symbol, func_span)) = ref_init(cx, elem) else { return; }; - emit_lint(cx, symbol, macro_call.span, elem, len); + emit_lint(cx, symbol, macro_call.span, elem, len, func_span); } } -fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { - let elem_snippet = snippet(cx, elem.span, "..").to_string(); - if elem_snippet.contains('\n') { - // This string must be found in `elem_snippet`, otherwise we won't be constructing - // the snippet in the first place. - let reference_creation = format!("{symbol_name}::new"); - let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); - - return format!("{code_until_reference_creation}{reference_creation}(..)"); - } - - elem_snippet -} - fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( r#"{{ @@ -89,17 +77,17 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { ) } -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) { let symbol_name = symbol.as_str(); span_lint_and_then( cx, RC_CLONE_IN_VEC_INIT, lint_span, - &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + "initializing a reference-counted pointer in `vec![elem; len]`", |diag| { let len_snippet = snippet(cx, len.span, ".."); - let elem_snippet = elem_snippet(cx, elem, symbol_name); + let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), "..")); let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); @@ -109,7 +97,7 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< lint_span, format!("consider initializing each `{symbol_name}` element individually"), loop_init_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); diag.span_suggestion( lint_span, @@ -117,23 +105,33 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" ), extract_suggestion, - Applicability::Unspecified, + Applicability::HasPlaceholders, ); }, ); } -/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` -fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak` +fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> { if_chain! { if let ExprKind::Call(func, _args) = expr.kind; if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; if let TyKind::Path(ref ty_path) = ty.kind; if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); - if last_path_segment(func_path).ident.name == sym::new; then { - return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + if last_path_segment(func_path).ident.name == sym::new + && let Some(symbol) = cx + .tcx + .get_diagnostic_name(def_id) + .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) { + return Some((symbol, func.span)); + } + + let ty_path = cx.typeck_results().expr_ty(expr); + if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) { + return Some((Symbol::intern("Weak"), func.span)); + } } } diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index ab16fe47d4dfb..249f11f985082 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -288,8 +288,8 @@ fn is_call_with_ref_arg<'tcx>( if let mir::TerminatorKind::Call { func, args, destination, .. } = kind; if args.len() == 1; if let mir::Operand::Move(mir::Place { local, .. }) = &args[0]; - if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind(); - if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx)); + if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind(); + if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx)); if !is_copy(cx, inner_ty); then { Some((def_id, *local, inner_ty, destination.as_local()?)) @@ -318,7 +318,7 @@ fn find_stmt_assigns_to<'tcx>( None })?; - match (by_ref, &*rvalue) { + match (by_ref, rvalue) { (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => { Some(base_local_and_movability(cx, mir, *place)) }, diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 2d26c49252fa5..0825f00f421c5 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -51,12 +51,12 @@ impl RedundantStaticLifetimes { fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) { match ty.kind { // Be careful of nested structures (arrays and tuples) - TyKind::Array(ref ty, _) => { - self.visit_type(&*ty, cx, reason); + TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => { + self.visit_type(ty, cx, reason); }, TyKind::Tup(ref tup) => { for tup_ty in tup { - self.visit_type(&*tup_ty, cx, reason); + self.visit_type(tup_ty, cx, reason); } }, // This is what we are looking for ! @@ -89,9 +89,6 @@ impl RedundantStaticLifetimes { } self.visit_type(&*borrow_type.ty, cx, reason); }, - TyKind::Slice(ref ty) => { - self.visit_type(ty, cx, reason); - }, _ => {}, } } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 8068fa22d9ccf..e525eba53e2aa 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::snippet_opt; +use clippy_utils::source::{snippet_opt, snippet_with_context}; use clippy_utils::{fn_def_id, path_to_local_id}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -226,14 +226,10 @@ fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option { - if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() { - return; - } - + let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| { - if let Some(snippet) = snippet_opt(cx, inner_span) { - diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable); - } + let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability); + diag.span_suggestion(ret_span, "remove `return`", snippet, applicability); }); }, None => match replacement { @@ -287,7 +283,7 @@ struct BorrowVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.borrows { + if self.borrows || expr.span.from_expansion() { return; } diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 2a80e6f918de2..4f74c1e44c26d 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -5,9 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; use rustc_hir::def_id::LocalDefId; use rustc_hir::hir_id::ItemLocalId; -use rustc_hir::{ - Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp, -}; +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, Symbol}; @@ -141,31 +139,34 @@ impl<'tcx> LateLintPass<'tcx> for Shadow { fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) - { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.push(FxHashMap::default()); } } fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) { let hir = cx.tcx.hir(); - if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure) - { + if !matches!( + hir.body_owner_kind(hir.body_owner_def_id(body.id())), + BodyOwnerKind::Closure + ) { self.bindings.pop(); } } } -fn is_shadow( - cx: &LateContext<'_>, - owner: LocalDefId, - first: ItemLocalId, - second: ItemLocalId, -) -> bool { - let scope_tree = cx.tcx.region_scope_tree(owner); - let first_scope = scope_tree.var_scope(first).unwrap(); - let second_scope = scope_tree.var_scope(second).unwrap(); - scope_tree.is_subscope_of(second_scope, first_scope) +fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool { + let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id()); + if let Some(first_scope) = scope_tree.var_scope(first) { + if let Some(second_scope) = scope_tree.var_scope(second) { + return scope_tree.is_subscope_of(second_scope, first_scope); + } + } + + false } fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) { @@ -177,16 +178,15 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) snippet(cx, expr.span, "..") ); (SHADOW_SAME, msg) - } + }, Some(expr) if is_local_used(cx, expr, shadowed) => { let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_")); (SHADOW_REUSE, msg) - } + }, _ => { - let msg = - format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); + let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_")); (SHADOW_UNRELATED, msg) - } + }, }; span_lint_and_note( cx, @@ -215,7 +215,14 @@ fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_ expr = match expr.kind { ExprKind::Box(e) | ExprKind::AddrOf(_, _, e) - | ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _) + | ExprKind::Block( + &Block { + stmts: [], + expr: Some(e), + .. + }, + _, + ) | ExprKind::Unary(UnOp::Deref, e) => e, ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id), _ => break false, diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index 3d7dc49b406a6..bfb9f0d01e1dc 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -110,7 +110,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods if let ExprKind::MethodCall(method_path, [ptr_self, .., count], _) = expr.kind; let method_ident = method_path.ident.as_str(); - if METHODS.iter().any(|m| *m == &*method_ident); + if METHODS.iter().any(|m| *m == method_ident); // Get the pointee type if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) = diff --git a/clippy_lints/src/swap_ptr_to_ref.rs b/clippy_lints/src/swap_ptr_to_ref.rs new file mode 100644 index 0000000000000..75d3b040c968f --- /dev/null +++ b/clippy_lints/src/swap_ptr_to_ref.rs @@ -0,0 +1,80 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer + /// + /// ### Why is this bad? + /// When at least one parameter to `swap` is derived from a pointer it may overlap with the + /// other. This would then lead to undefined behavior. + /// + /// ### Example + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::mem::swap(&mut *x, &mut *y); + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) { + /// for (&x, &y) in x.iter().zip(y) { + /// core::ptr::swap(x, y); + /// } + /// } + /// ``` + #[clippy::version = "1.63.0"] + pub SWAP_PTR_TO_REF, + suspicious, + "call to `mem::swap` using pointer derived references" +} +declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]); + +impl LateLintPass<'_> for SwapPtrToRef { + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind + && let Some(fn_id) = path_def_id(cx, fn_expr) + && match_def_path(cx, fn_id, &paths::MEM_SWAP) + && let ctxt = e.span.ctxt() + && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt) + && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt) + && (from_ptr1 || from_ptr2) + { + span_lint_and_then( + cx, + SWAP_PTR_TO_REF, + e.span, + "call to `core::mem::swap` with a parameter derived from a raw pointer", + |diag| { + if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) { + let mut app = Applicability::MachineApplicable; + let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0; + let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0; + diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app); + } + } + ); + } + } +} + +/// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also +/// returns the span of the pointer expression if it's suitable for making a suggestion. +fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option) { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind + && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind + && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr() + { + (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span)) + } else { + (false, None) + } +} diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 911da3997ae45..1e5b646f5f055 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -54,14 +54,14 @@ declare_clippy_lint! { /// fn func(arg: T) where T: Clone + Default {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust + /// # mod hidden { /// fn func(arg: T) {} - /// ``` - /// or + /// # } + /// + /// // or /// - /// ```rust /// fn func(arg: T) where T: Clone + Default {} /// ``` #[clippy::version = "1.47.0"] diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index d2a040beb0cf7..cbe1406728bc7 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -59,7 +59,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub USELESS_TRANSMUTE, - nursery, + complexity, "transmutes that have the same to and from types or could be a cast/coercion" } diff --git a/clippy_lints/src/transmute/useless_transmute.rs b/clippy_lints/src/transmute/useless_transmute.rs index 3cc3d40a143dc..a0d104e239042 100644 --- a/clippy_lints/src/transmute/useless_transmute.rs +++ b/clippy_lints/src/transmute/useless_transmute.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; /// Checks for `useless_transmute` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -16,7 +16,7 @@ pub(super) fn check<'tcx>( arg: &'tcx Expr<'_>, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { - _ if from_ty == to_ty => { + _ if from_ty == to_ty && !from_ty.has_erased_regions() => { span_lint( cx, USELESS_TRANSMUTE, @@ -26,28 +26,31 @@ pub(super) fn check<'tcx>( true }, (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => { - span_lint_and_then( - cx, - USELESS_TRANSMUTE, - e.span, - "transmute from a reference to a pointer", - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let rty_and_mut = ty::TypeAndMut { - ty: *rty, - mutbl: *rty_mutbl, - }; + // No way to give the correct suggestion here. Avoid linting for now. + if !rty.has_erased_regions() { + span_lint_and_then( + cx, + USELESS_TRANSMUTE, + e.span, + "transmute from a reference to a pointer", + |diag| { + if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { + let rty_and_mut = ty::TypeAndMut { + ty: *rty, + mutbl: *rty_mutbl, + }; - let sugg = if *ptr_ty == rty_and_mut { - arg.as_ty(to_ty) - } else { - arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) - }; + let sugg = if *ptr_ty == rty_and_mut { + arg.as_ty(to_ty) + } else { + arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty) + }; - diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); - } - }, - ); + diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified); + } + }, + ); + } true }, (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => { diff --git a/clippy_lints/src/try_err.rs b/clippy_lints/src/try_err.rs deleted file mode 100644 index e108f7be12e6a..0000000000000 --- a/clippy_lints/src/try_err.rs +++ /dev/null @@ -1,186 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths}; -use if_chain::if_chain; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{hygiene, sym}; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usages of `Err(x)?`. - /// - /// ### Why is this bad? - /// The `?` operator is designed to allow calls that - /// can fail to be easily chained. For example, `foo()?.bar()` or - /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will - /// always return), it is more clear to write `return Err(x)`. - /// - /// ### Example - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// Err("failed")?; - /// } - /// Ok(0) - /// } - /// ``` - /// Could be written: - /// - /// ```rust - /// fn foo(fail: bool) -> Result { - /// if fail { - /// return Err("failed".into()); - /// } - /// Ok(0) - /// } - /// ``` - #[clippy::version = "1.38.0"] - pub TRY_ERR, - restriction, - "return errors explicitly rather than hiding them behind a `?`" -} - -declare_lint_pass!(TryErr => [TRY_ERR]); - -impl<'tcx> LateLintPass<'tcx> for TryErr { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Looks for a structure like this: - // match ::std::ops::Try::into_result(Err(5)) { - // ::std::result::Result::Err(err) => - // #[allow(unreachable_code)] - // return ::std::ops::Try::from_error(::std::convert::From::from(err)), - // ::std::result::Result::Ok(val) => - // #[allow(unreachable_code)] - // val, - // }; - if_chain! { - if !in_external_macro(cx.tcx.sess, expr.span); - if let ExprKind::Match(match_arg, _, MatchSource::TryDesugar) = expr.kind; - if let ExprKind::Call(match_fun, try_args) = match_arg.kind; - if let ExprKind::Path(ref match_fun_path) = match_fun.kind; - if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..)); - if let Some(try_arg) = try_args.get(0); - if let ExprKind::Call(err_fun, err_args) = try_arg.kind; - if let Some(err_arg) = err_args.get(0); - if let ExprKind::Path(ref err_fun_path) = err_fun.kind; - if is_lang_ctor(cx, err_fun_path, ResultErr); - if let Some(return_ty) = find_return_type(cx, &expr.kind); - then { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; - } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; - } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; - } else { - return; - }; - - let expr_err_ty = cx.typeck_results().expr_ty(err_arg); - let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt()); - let mut applicability = Applicability::MachineApplicable; - let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability); - let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) { - "" // already returns - } else { - "return " - }; - let suggestion = if err_ty == expr_err_ty { - format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix) - } else { - format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix) - }; - - span_lint_and_sugg( - cx, - TRY_ERR, - expr.span, - "returning an `Err(_)` with the `?` operator", - "try this", - suggestion, - applicability, - ); - } - } - } -} - -/// Finds function return type by examining return expressions in match arms. -fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option> { - if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr { - for arm in arms.iter() { - if let ExprKind::Ret(Some(ret)) = arm.body.kind { - return Some(cx.typeck_results().expr_ty(ret)); - } - } - } - None -} - -/// Extracts the error type from Result. -fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(_, subst) = ty.kind(); - if is_type_diagnostic_item(cx, ty, sym::Result); - then { - Some(subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>. -fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()); - then { - Some(ready_subst.type_at(1)) - } else { - None - } - } -} - -/// Extracts the error type from Poll>>. -fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if_chain! { - if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); - let ready_ty = subst.type_at(0); - - if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()); - let some_ty = ready_subst.type_at(0); - - if let ty::Adt(some_def, some_subst) = some_ty.kind(); - if cx.tcx.is_diagnostic_item(sym::Result, some_def.did()); - then { - Some(some_subst.type_at(1)) - } else { - None - } - } -} diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs new file mode 100644 index 0000000000000..306afe4414847 --- /dev/null +++ b/clippy_lints/src/unused_rounding.rs @@ -0,0 +1,69 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases where a whole-number literal float is being rounded, using + /// the `floor`, `ceil`, or `round` methods. + /// + /// ### Why is this bad? + /// + /// This is unnecessary and confusing to the reader. Doing this is probably a mistake. + /// + /// ### Example + /// ```rust + /// let x = 1f32.ceil(); + /// ``` + /// Use instead: + /// ```rust + /// let x = 1f32; + /// ``` + #[clippy::version = "1.62.0"] + pub UNUSED_ROUNDING, + nursery, + "Uselessly rounding a whole number floating-point literal" +} +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); + +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { + if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind + && let method_name = name_ident.ident.name.as_str() + && (method_name == "ceil" || method_name == "round" || method_name == "floor") + && !args.is_empty() + && let ExprKind::Lit(spanned) = &args[0].kind + && let LitKind::Float(symbol, ty) = spanned.kind { + let f = symbol.as_str().parse::().unwrap(); + let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty { + ty.name_str() + } else { + "" + }; + if f.fract() == 0.0 { + Some((method_name, f_str)) + } else { + None + } + } else { + None + } +} + +impl EarlyLintPass for UnusedRounding { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + if let Some((method_name, float)) = is_useless_rounding(expr) { + span_lint_and_sugg( + cx, + UNUSED_ROUNDING, + expr.span, + &format!("used the `{}` method with a whole number float", method_name), + &format!("remove the `{}` method call", method_name), + float, + Applicability::MachineApplicable, + ); + } + } +} diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 66f7748e9e089..486ea5e5ccfa9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -258,13 +258,21 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if !pat.span.from_expansion(); if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); - if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; - if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); + // get the path from the pattern + if let PatKind::Path(QPath::Resolved(_, path)) + | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) + | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); - if let [first, ..] = path.segments; - if let Some(hir_id) = first.hir_id; then { - span_lint(cx, cx.tcx.hir().span(hir_id)); + match path.res { + Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { + CtorOf::Variant => lint_path_to_variant(cx, path), + CtorOf::Struct => span_lint(cx, path.span), + }, + Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), + Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), + _ => () + } } } } diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index cd4d16fe95f75..b5c5d35135f90 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -340,6 +340,10 @@ define_Conf! { /// /// Whether `unwrap` should be allowed in test functions (allow_unwrap_in_tests: bool = false), + /// Lint: DBG_MACRO. + /// + /// Whether `dbg!` should be allowed in test functions + (allow_dbg_in_tests: bool = false), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 0e8f40e92101a..60f9887699498 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -422,7 +422,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { if !matches!( - &*cx.tcx.item_name(macro_call.def_id).as_str(), + cx.tcx.item_name(macro_call.def_id).as_str(), "impl_lint_pass" | "declare_lint_pass" ) { return; @@ -504,7 +504,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' return; } - if RustcVersion::parse(&*value.as_str()).is_err() { + if RustcVersion::parse(value.as_str()).is_err() { span_lint_and_help( cx, INVALID_CLIPPY_VERSION_ATTRIBUTE, @@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { if_chain! { if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; let fn_name = path.ident; - if let Some(sugg) = self.map.get(&*fn_name.as_str()); + if let Some(sugg) = self.map.get(fn_name.as_str()); let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); @@ -679,7 +679,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls { then { let and_then_snippets = get_and_then_snippets(cx, and_then_args); let mut sle = SpanlessEq::new(cx).deny_side_effects(); - match &*ps.ident.as_str() { + match ps.ident.as_str() { "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => { suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args)); }, diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 8c1910b3b2af8..cf2de6a42af36 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -7,6 +7,7 @@ //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such //! a simple mistake) +use crate::renamed_lints::RENAMED_LINTS; use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type}; use clippy_utils::diagnostics::span_lint; @@ -26,6 +27,7 @@ use rustc_span::{sym, Loc, Span, Symbol}; use serde::{ser::SerializeStruct, Serialize, Serializer}; use std::collections::BinaryHeap; use std::fmt; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; @@ -85,6 +87,21 @@ macro_rules! CONFIGURATION_VALUE_TEMPLATE { }; } +macro_rules! RENAMES_SECTION_TEMPLATE { + () => { + r#" +### Past names + +{names} +"# + }; +} +macro_rules! RENAME_VALUE_TEMPLATE { + () => { + "* `{name}`\n" + }; +} + const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [ &["clippy_utils", "diagnostics", "span_lint"], &["clippy_utils", "diagnostics", "span_lint_and_help"], @@ -198,9 +215,10 @@ impl Drop for MetadataCollector { // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - lints - .iter_mut() - .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default())); + collect_renames(&mut lints); + for x in &mut lints { + x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); + } // Outputting if Path::new(OUTPUT_FILE).exists() { @@ -527,12 +545,11 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - let mut docs = String::from(&*lines.next()?.as_str()); + let mut docs = String::from(lines.next()?.as_str()); let mut in_code_block = false; let mut is_code_block_rust = false; for line in lines { let line = line.as_str(); - let line = &*line; // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) if is_code_block_rust && line.trim_start().starts_with("# ") { @@ -643,6 +660,37 @@ fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } +fn collect_renames(lints: &mut Vec) { + for lint in lints { + let mut collected = String::new(); + let mut names = vec![lint.id.clone()]; + + loop { + if let Some(lint_name) = names.pop() { + for (k, v) in RENAMED_LINTS { + if_chain! { + if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + if name == lint_name; + if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX); + then { + write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap(); + names.push(past_name.to_string()); + } + } + } + + continue; + } + + break; + } + + if !collected.is_empty() { + write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap(); + } + } +} + // ================================================================== // Lint emission // ================================================================== diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9f162a117b2d1..d487868cafe50 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -478,7 +478,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option { if let Some(Constant::Bool(b)) = self.expr(cond) { if b { - self.expr(&*then) + self.expr(then) } else { otherwise.as_ref().and_then(|expr| self.expr(expr)) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 1e0fc789af243..4604ae5c2c7f0 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -35,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> { if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind; if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind; if let hir::ExprKind::Loop(block, ..) = arm.body.kind; - if let [stmt] = &*block.stmts; + if let [stmt] = block.stmts; if let hir::StmtKind::Expr(e) = stmt.kind; if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind; if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index fc1a4e1f60255..0603471c3431b 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -411,10 +411,10 @@ impl HirEqInterExpr<'_, '_, '_> { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl), (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => { - l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty) + l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty) }, (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => { - l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty) + l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty) }, (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r), (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)), @@ -608,7 +608,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_name(i.ident.name); } if let Some(j) = *j { - self.hash_expr(&*j); + self.hash_expr(j); } }, ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => { diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 833f8cde63aba..5f051e3f444c0 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1443,7 +1443,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { }, PatKind::Tuple(pats, _) => are_refutable(cx, pats), PatKind::Struct(ref qpath, fields, _) => { - is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat)) + is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat)) }, PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats), PatKind::Slice(head, middle, tail) => { @@ -1658,7 +1658,7 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, let mut blocks: Vec<&Block<'_>> = Vec::new(); while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { - conds.push(&*cond); + conds.push(cond); if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { @@ -1916,7 +1916,17 @@ pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { .. }, .., - ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(), + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + cx.typeck_results().qpath_res(qpath, *path_hir_id) + { + Some(id) + } else { + None + } + }, _ => None, } } @@ -2073,7 +2083,8 @@ static TEST_ITEM_NAMES_CACHE: SyncOnceCell(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default())); let mut map: MutexGuard<'_, FxHashMap>> = cache.lock().unwrap(); - match map.entry(module) { + let value = map.entry(module); + match value { Entry::Occupied(entry) => f(entry.get()), Entry::Vacant(entry) => { let mut names = Vec::new(); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 134fd1ce505a0..b9ec2c19fdd31 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -30,6 +30,7 @@ msrv_aliases! { 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,28,0 { FROM_BOOL } + 1,26,0 { RANGE_INCLUSIVE } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } 1,24,0 { IS_ASCII_DIGIT } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 9b9cbff2d1462..89789c3d85135 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -73,6 +73,7 @@ pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; #[cfg(feature = "internal")] pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"]; pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; /// Preferably use the diagnostic item `sym::Option` where possible @@ -141,6 +142,7 @@ pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"]; pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"]; +pub const SLICE_GET: [&str; 4] = ["core", "slice", "", "get"]; pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "", "into_vec"]; pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"]; pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 58abef38ea8be..498dcbb89006d 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -121,24 +121,18 @@ fn check_rvalue<'tcx>( ) -> 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::Cast(CastKind::PointerExposeAddress, _, _) => { - Err((span, "casting pointers to ints is unstable in const fn".into())) - }, - Rvalue::Cast(CastKind::Misc, operand, _) => { - check_operand(tcx, operand, span, body) - }, - Rvalue::Cast( + Rvalue::Repeat(operand, _) + | Rvalue::Use(operand) + | Rvalue::Cast( CastKind::PointerFromExposedAddress + | CastKind::Misc | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, - _ - ) => { - check_operand(tcx, operand, span, body) - }, + _, + ) => check_operand(tcx, operand, span, body), Rvalue::Cast( CastKind::Pointer( PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer, @@ -163,6 +157,9 @@ fn check_rvalue<'tcx>( Err((span, "unsizing casts are not allowed in const fn".into())) } }, + Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => { + Err((span, "casting pointers to ints is unstable in const fn".into())) + }, // binops are fine on integers Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => { check_operand(tcx, lhs, span, body)?; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index 04ef2f57447c6..f88a92fb11c11 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; use rustc_span::source_map::SourceMap; -use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext}; use std::borrow::Cow; /// Checks if the span starts with the given text. This will return false if the span crosses @@ -389,6 +389,27 @@ pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { without } +/// Trims the whitespace from the start and the end of the span. +pub fn trim_span(sm: &SourceMap, span: Span) -> Span { + let data = span.data(); + let sf: &_ = &sm.lookup_source_file(data.lo); + let Some(src) = sf.src.as_deref() else { + return span; + }; + let Some(snip) = &src.get((data.lo - sf.start_pos).to_usize()..(data.hi - sf.start_pos).to_usize()) else { + return span; + }; + let trim_start = snip.len() - snip.trim_start().len(); + let trim_end = snip.len() - snip.trim_end().len(); + SpanData { + lo: data.lo + BytePos::from_usize(trim_start), + hi: data.hi - BytePos::from_usize(trim_end), + ctxt: data.ctxt, + parent: data.parent, + } + .span() +} + #[cfg(test)] mod test { use super::{reindent_multiline, without_block_comments}; diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 75d27d3b59482..a10515d2fec63 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,8 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, + VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -151,18 +152,29 @@ pub fn implements_trait<'tcx>( ty: Ty<'tcx>, trait_id: DefId, ty_params: &[GenericArg<'tcx>], +) -> bool { + implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params) +} + +/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context. +pub fn implements_trait_with_env<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ParamEnv<'tcx>, + ty: Ty<'tcx>, + trait_id: DefId, + ty_params: &[GenericArg<'tcx>], ) -> bool { // Clippy shouldn't have infer types assert!(!ty.needs_infer()); - let ty = cx.tcx.erase_regions(ty); + let ty = tcx.erase_regions(ty); if ty.has_escaping_bound_vars() { return false; } - let ty_params = cx.tcx.mk_substs(ty_params.iter()); - cx.tcx.infer_ctxt().enter(|infcx| { + let ty_params = tcx.mk_substs(ty_params.iter()); + tcx.infer_ctxt().enter(|infcx| { infcx - .type_implements_trait(trait_id, ty, ty_params, cx.param_env) + .type_implements_trait(trait_id, ty, ty_params, param_env) .must_apply_modulo_regions() }) } diff --git a/doc/adding_lints.md b/doc/adding_lints.md index e8f0c338fd58a..3e0b1c5c4f782 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -516,11 +516,12 @@ declare_clippy_lint! { /// ### Example /// /// ```rust,ignore - /// // Bad - /// Insert a short example of code that triggers the lint - /// - /// // Good - /// Insert a short example of improved code that doesn't trigger the lint + /// // A short example of code that triggers the lint + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// // A short example of improved code that doesn't trigger the lint /// ``` #[clippy::version = "1.29.0"] pub FOO_FUNCTIONS, diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 131ac3c3611e8..1d1aee0da2cc7 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -159,7 +159,8 @@ A list of defined paths for Clippy can be found in [paths.rs][paths] To check if our type defines a method called `some_method`: ```rust -use clippy_utils::{is_type_diagnostic_item, return_ty}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::return_ty; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index e63f65ce2f75d..504d58b5197a1 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -11,7 +11,7 @@ publish = false [dependencies] cargo_metadata = "0.14" -clap = "2.33" +clap = "3.1" flate2 = "1.0" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs index de32b48436019..a6f93d2a1c0a6 100644 --- a/lintcheck/src/config.rs +++ b/lintcheck/src/config.rs @@ -1,47 +1,47 @@ -use clap::{App, Arg, ArgMatches}; +use clap::{Arg, ArgMatches, Command}; use std::env; use std::path::PathBuf; -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") +fn get_clap_config() -> ArgMatches { + Command::new("lintcheck") .about("run clippy on a set of crates and check output") .arg( - Arg::with_name("only") + Arg::new("only") .takes_value(true) .value_name("CRATE") .long("only") .help("Only process a single crate of the list"), ) .arg( - Arg::with_name("crates-toml") + Arg::new("crates-toml") .takes_value(true) .value_name("CRATES-SOURCES-TOML-PATH") .long("crates-toml") .help("Set the path for a crates.toml where lintcheck should read the sources from"), ) .arg( - Arg::with_name("threads") + Arg::new("threads") .takes_value(true) .value_name("N") - .short("j") + .short('j') .long("jobs") .help("Number of threads to use, 0 automatic choice"), ) .arg( - Arg::with_name("fix") + Arg::new("fix") .long("--fix") .help("Runs cargo clippy --fix and checks if all suggestions apply"), ) .arg( - Arg::with_name("filter") + Arg::new("filter") .long("--filter") .takes_value(true) - .multiple(true) + .multiple_occurrences(true) .value_name("clippy_lint_name") .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), ) .arg( - Arg::with_name("markdown") + Arg::new("markdown") .long("--markdown") .help("Change the reports table to use markdown links"), ) diff --git a/rust-toolchain b/rust-toolchain index 997e7ba9382b2..2386a751f04f6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-05-19" +channel = "nightly-2022-06-04" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c9710e3db8e8d..7d21983572324 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,6 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] +#![feature(is_sorted)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -22,6 +23,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ + "clap", "clippy_utils", "derive_new", "futures", @@ -40,6 +42,8 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) #[allow(unused_extern_crates)] +extern crate clap; +#[allow(unused_extern_crates)] extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; @@ -109,8 +113,9 @@ static EXTERN_FLAGS: SyncLazy = SyncLazy::new(|| { not_found.is_empty(), "dependencies not found in depinfo: {:?}\n\ help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\ - help: Try adding to dev-dependencies in Cargo.toml", - not_found + help: Try adding to dev-dependencies in Cargo.toml\n\ + help: Be sure to also add `extern crate ...;` to tests/compile-test.rs", + not_found, ); crates .into_iter() @@ -162,7 +167,8 @@ fn base_config(test_dir: &str) -> compiletest::Config { } fn run_ui() { - let config = base_config("ui"); + let mut config = base_config("ui"); + config.rustfix_coverage = true; // use tests/clippy.toml let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap()); let _threads = VarGuard::set( @@ -175,6 +181,7 @@ fn run_ui() { }), ); compiletest::run_tests(&config); + check_rustfix_coverage(); } fn run_internal_tests() { @@ -337,6 +344,88 @@ fn compile_test() { run_internal_tests(); } +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[ + "assign_ops2.rs", + "borrow_deref_ref_unfixable.rs", + "cast_size_32bit.rs", + "char_lit_as_u8.rs", + "cmp_owned/without_suggestion.rs", + "dbg_macro.rs", + "deref_addrof_double_trigger.rs", + "doc/unbalanced_ticks.rs", + "eprint_with_newline.rs", + "explicit_counter_loop.rs", + "iter_skip_next_unfixable.rs", + "let_and_return.rs", + "literals.rs", + "map_flatten.rs", + "map_unwrap_or.rs", + "match_bool.rs", + "mem_replace_macro.rs", + "needless_arbitrary_self_type_unfixable.rs", + "needless_borrow_pat.rs", + "needless_for_each_unfixable.rs", + "nonminimal_bool.rs", + "print_literal.rs", + "print_with_newline.rs", + "redundant_static_lifetimes_multiple.rs", + "ref_binding_to_reference.rs", + "repl_uninit.rs", + "result_map_unit_fn_unfixable.rs", + "search_is_some.rs", + "single_component_path_imports_nested_first.rs", + "string_add.rs", + "toplevel_ref_arg_non_rustfix.rs", + "unit_arg.rs", + "unnecessary_clone.rs", + "unnecessary_lazy_eval_unfixable.rs", + "write_literal.rs", + "write_literal_2.rs", + "write_with_newline.rs", +]; + +fn check_rustfix_coverage() { + let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt"); + + if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) { + assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new)); + + for rs_path in missing_coverage_contents.lines() { + if Path::new(rs_path).starts_with("tests/ui/crashes") { + continue; + } + let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap(); + assert!( + RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS + .binary_search_by_key(&filename, Path::new) + .is_ok(), + "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \ + Please either add `// run-rustfix` at the top of the file or add the file to \ + `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.", + rs_path, + ); + } + } +} + +#[test] +fn rustfix_coverage_known_exceptions_accuracy() { + for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS { + let rs_path = Path::new("tests/ui").join(filename); + assert!( + rs_path.exists(), + "`{}` does not exists", + rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + let fixed_path = rs_path.with_extension("fixed"); + assert!( + !fixed_path.exists(), + "`{}` exists", + fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display() + ); + } +} + /// Restores an env var on drop #[must_use] struct VarGuard { diff --git a/tests/ui-internal/unnecessary_symbol_str.fixed b/tests/ui-internal/unnecessary_symbol_str.fixed index 4f5336663a8f7..6033d06e4f637 100644 --- a/tests/ui-internal/unnecessary_symbol_str.fixed +++ b/tests/ui-internal/unnecessary_symbol_str.fixed @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.rs b/tests/ui-internal/unnecessary_symbol_str.rs index 894aa1d3bc6ea..1bb5d55f0b60c 100644 --- a/tests/ui-internal/unnecessary_symbol_str.rs +++ b/tests/ui-internal/unnecessary_symbol_str.rs @@ -2,6 +2,7 @@ #![feature(rustc_private)] #![deny(clippy::internal)] #![allow( + clippy::borrow_deref_ref, clippy::unnecessary_operation, unused_must_use, clippy::missing_clippy_version_attribute diff --git a/tests/ui-internal/unnecessary_symbol_str.stderr b/tests/ui-internal/unnecessary_symbol_str.stderr index 75367bf4bc519..a1f507f331d26 100644 --- a/tests/ui-internal/unnecessary_symbol_str.stderr +++ b/tests/ui-internal/unnecessary_symbol_str.stderr @@ -1,5 +1,5 @@ error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:15:5 + --> $DIR/unnecessary_symbol_str.rs:16:5 | LL | Symbol::intern("foo").as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy` @@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)] = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:16:5 + --> $DIR/unnecessary_symbol_str.rs:17:5 | LL | Symbol::intern("foo").to_string() == "self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:17:5 + --> $DIR/unnecessary_symbol_str.rs:18:5 | LL | Symbol::intern("foo").to_ident_string() != "Self"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:18:5 + --> $DIR/unnecessary_symbol_str.rs:19:5 | LL | &*Ident::empty().as_str() == "clippy"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy` error: unnecessary `Symbol` to string conversion - --> $DIR/unnecessary_symbol_str.rs:19:5 + --> $DIR/unnecessary_symbol_str.rs:20:5 | LL | "clippy" == Ident::empty().to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name` diff --git a/tests/ui-toml/dbg_macro/clippy.toml b/tests/ui-toml/dbg_macro/clippy.toml new file mode 100644 index 0000000000000..4296655a040fa --- /dev/null +++ b/tests/ui-toml/dbg_macro/clippy.toml @@ -0,0 +1 @@ +allow-dbg-in-tests = true diff --git a/tests/ui-toml/dbg_macro/dbg_macro.rs b/tests/ui-toml/dbg_macro/dbg_macro.rs new file mode 100644 index 0000000000000..5d9ce18f631b6 --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.rs @@ -0,0 +1,39 @@ +// compile-flags: --test +#![warn(clippy::dbg_macro)] + +fn foo(n: u32) -> u32 { + if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } +} + +fn factorial(n: u32) -> u32 { + if dbg!(n <= 1) { + dbg!(1) + } else { + dbg!(n * factorial(n - 1)) + } +} + +fn main() { + dbg!(42); + dbg!(dbg!(dbg!(42))); + foo(3) + dbg!(factorial(4)); + dbg!(1, 2, dbg!(3, 4)); + dbg!(1, 2, 3, 4, 5); +} + +#[test] +pub fn issue8481() { + dbg!(1); +} + +#[cfg(test)] +fn foo2() { + dbg!(1); +} + +#[cfg(test)] +mod mod1 { + fn func() { + dbg!(1); + } +} diff --git a/tests/ui-toml/dbg_macro/dbg_macro.stderr b/tests/ui-toml/dbg_macro/dbg_macro.stderr new file mode 100644 index 0000000000000..46efb86dcfc5f --- /dev/null +++ b/tests/ui-toml/dbg_macro/dbg_macro.stderr @@ -0,0 +1,102 @@ +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:5:22 + | +LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n } + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::dbg-macro` implied by `-D warnings` +help: ensure to avoid having uses of it in version control + | +LL | if let Some(n) = n.checked_sub(4) { n } else { n } + | ~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:9:8 + | +LL | if dbg!(n <= 1) { + | ^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | if n <= 1 { + | ~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:10:9 + | +LL | dbg!(1) + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1 + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:12:9 + | +LL | dbg!(n * factorial(n - 1)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | n * factorial(n - 1) + | + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:17:5 + | +LL | dbg!(42); + | ^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 42; + | ~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:18:5 + | +LL | dbg!(dbg!(dbg!(42))); + | ^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | dbg!(dbg!(42)); + | ~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:19:14 + | +LL | foo(3) + dbg!(factorial(4)); + | ^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | foo(3) + factorial(4); + | ~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:20:5 + | +LL | dbg!(1, 2, dbg!(3, 4)); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, dbg!(3, 4)); + | ~~~~~~~~~~~~~~~~~~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:21:5 + | +LL | dbg!(1, 2, 3, 4, 5); + | ^^^^^^^^^^^^^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | (1, 2, 3, 4, 5); + | ~~~~~~~~~~~~~~~ + +error: aborting due to 9 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 92838aa57dfda..1d87fd91a2532 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,5 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of + allow-dbg-in-tests allow-expect-in-tests allow-unwrap-in-tests allowed-scripts diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index 74d0d7c2650dd..0e82fb20e4558 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -1,6 +1,6 @@ // compile-flags: --test -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed new file mode 100644 index 0000000000000..39f8f0c29495b --- /dev/null +++ b/tests/ui/almost_complete_letter_range.fixed @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..='z'; + let _ = 'A' ..= ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..=(b'z'); + let _ = b'A'..=b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..='z'; + + let _ = match 0u8 { + b'a'..=b'z' if true => 1, + b'A'..=b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..='z' if true => 1, + 'A'..='Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'...'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..='z'; + let _ = match 'a' { + 'a'..='z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs new file mode 100644 index 0000000000000..3dc0219925760 --- /dev/null +++ b/tests/ui/almost_complete_letter_range.rs @@ -0,0 +1,66 @@ +// run-rustfix +// edition:2018 + +#![feature(custom_inner_attributes)] +#![feature(exclusive_range_pattern)] +#![feature(stmt_expr_attributes)] +#![warn(clippy::almost_complete_letter_range)] +#![allow(ellipsis_inclusive_range_patterns)] + +macro_rules! a { + () => { + 'a' + }; +} + +fn main() { + #[rustfmt::skip] + { + let _ = ('a') ..'z'; + let _ = 'A' .. ('Z'); + } + + let _ = 'b'..'z'; + let _ = 'B'..'Z'; + + let _ = (b'a')..(b'z'); + let _ = b'A'..b'Z'; + + let _ = b'b'..b'z'; + let _ = b'B'..b'Z'; + + let _ = a!()..'z'; + + let _ = match 0u8 { + b'a'..b'z' if true => 1, + b'A'..b'Z' if true => 2, + b'b'..b'z' => 3, + b'B'..b'Z' => 4, + _ => 5, + }; + + let _ = match 'x' { + 'a'..'z' if true => 1, + 'A'..'Z' if true => 2, + 'b'..'z' => 3, + 'B'..'Z' => 4, + _ => 5, + }; +} + +fn _under_msrv() { + #![clippy::msrv = "1.25"] + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} + +fn _meets_msrv() { + #![clippy::msrv = "1.26"] + let _ = 'a'..'z'; + let _ = match 'a' { + 'a'..'z' => 1, + _ => 2, + }; +} diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr new file mode 100644 index 0000000000000..74980ec1a923f --- /dev/null +++ b/tests/ui/almost_complete_letter_range.stderr @@ -0,0 +1,100 @@ +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:19:17 + | +LL | let _ = ('a') ..'z'; + | ^^^^^^--^^^ + | | + | help: use an inclusive range: `..=` + | + = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:20:17 + | +LL | let _ = 'A' .. ('Z'); + | ^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:26:13 + | +LL | let _ = (b'a')..(b'z'); + | ^^^^^^--^^^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:27:13 + | +LL | let _ = b'A'..b'Z'; + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:32:13 + | +LL | let _ = a!()..'z'; + | ^^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:35:9 + | +LL | b'a'..b'z' if true => 1, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:36:9 + | +LL | b'A'..b'Z' if true => 2, + | ^^^^--^^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:43:9 + | +LL | 'a'..'z' if true => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:44:9 + | +LL | 'A'..'Z' if true => 2, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:54:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `...` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:61:13 + | +LL | let _ = 'a'..'z'; + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: almost complete ascii letter range + --> $DIR/almost_complete_letter_range.rs:63:9 + | +LL | 'a'..'z' => 1, + | ^^^--^^^ + | | + | help: use an inclusive range: `..=` + +error: aborting due to 12 previous errors + diff --git a/tests/ui/as_underscore.fixed b/tests/ui/as_underscore.fixed new file mode 100644 index 0000000000000..948f6d8e6b10b --- /dev/null +++ b/tests/ui/as_underscore.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as usize); + + let n = 0_u128; + let _n: u8 = n as u8; +} diff --git a/tests/ui/as_underscore.rs b/tests/ui/as_underscore.rs new file mode 100644 index 0000000000000..97785ed08a8d0 --- /dev/null +++ b/tests/ui/as_underscore.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#![warn(clippy::as_underscore)] + +fn foo(_n: usize) {} + +fn main() { + let n: u16 = 256; + foo(n as _); + + let n = 0_u128; + let _n: u8 = n as _; +} diff --git a/tests/ui/as_underscore.stderr b/tests/ui/as_underscore.stderr new file mode 100644 index 0000000000000..d7cd58d965acb --- /dev/null +++ b/tests/ui/as_underscore.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> $DIR/as_underscore.rs:9:9 + | +LL | foo(n as _); + | ^^^^^- + | | + | help: consider giving the type explicitly: `usize` + | + = note: `-D clippy::as-underscore` implied by `-D warnings` + +error: using `as _` conversion + --> $DIR/as_underscore.rs:12:18 + | +LL | let _n: u8 = n as _; + | ^^^^^- + | | + | help: consider giving the type explicitly: `u8` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/bind_instead_of_map_multipart.fixed b/tests/ui/bind_instead_of_map_multipart.fixed new file mode 100644 index 0000000000000..e1589843226c4 --- /dev/null +++ b/tests/ui/bind_instead_of_map_multipart.fixed @@ -0,0 +1,62 @@ +// run-rustfix +#![deny(clippy::bind_instead_of_map)] +#![allow(clippy::blocks_in_if_conditions)] + +pub fn main() { + let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) }); + + let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); + let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) }); + + let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() }); + let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) }); + + hard_example(); + macro_example(); +} + +fn hard_example() { + Some("42").map(|s| { + if { + if s == "43" { + return 43; + } + s == "42" + } { + return 45; + } + match s.len() { + 10 => 2, + 20 => { + if foo() { + return { + if foo() { + return 20; + } + println!("foo"); + 3 + }; + } + 20 + }, + 40 => 30, + _ => 1, + } + }); +} + +fn foo() -> bool { + true +} + +macro_rules! m { + () => { + Some(10) + }; +} + +fn macro_example() { + let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) }); + let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) }); +} diff --git a/tests/ui/bind_instead_of_map_multipart.rs b/tests/ui/bind_instead_of_map_multipart.rs index 91d9d11e3c110..49944403f6ddd 100644 --- a/tests/ui/bind_instead_of_map_multipart.rs +++ b/tests/ui/bind_instead_of_map_multipart.rs @@ -1,3 +1,4 @@ +// run-rustfix #![deny(clippy::bind_instead_of_map)] #![allow(clippy::blocks_in_if_conditions)] diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr index e4f605a4de305..f822b6f49fa35 100644 --- a/tests/ui/bind_instead_of_map_multipart.stderr +++ b/tests/ui/bind_instead_of_map_multipart.stderr @@ -1,11 +1,11 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:5:13 + --> $DIR/bind_instead_of_map_multipart.rs:6:13 | LL | let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/bind_instead_of_map_multipart.rs:1:9 + --> $DIR/bind_instead_of_map_multipart.rs:2:9 | LL | #![deny(clippy::bind_instead_of_map)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() }); | ~~~ ~ ~~~~~~~ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:8:13 + --> $DIR/bind_instead_of_map_multipart.rs:9:13 | LL | let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() | ~~~ ~ ~~~~~~~ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:11:13 + --> $DIR/bind_instead_of_map_multipart.rs:12:13 | LL | let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } | ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:19:5 + --> $DIR/bind_instead_of_map_multipart.rs:20:5 | LL | / Some("42").and_then(|s| { LL | | if { @@ -59,7 +59,7 @@ LL | s == "42" ... error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)` - --> $DIR/bind_instead_of_map_multipart.rs:60:13 + --> $DIR/bind_instead_of_map_multipart.rs:61:13 | LL | let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed new file mode 100644 index 0000000000000..bf4691c5bc97e --- /dev/null +++ b/tests/ui/borrow_deref_ref.fixed @@ -0,0 +1,59 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn one_help() { + let a = &12; + let b = a; + + let b = &mut bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs new file mode 100644 index 0000000000000..28c005fdbef70 --- /dev/null +++ b/tests/ui/borrow_deref_ref.rs @@ -0,0 +1,59 @@ +// run-rustfix + +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn one_help() { + let a = &12; + let b = &*a; + + let b = &mut &*bar(&12); + } + + fn bar(x: &u32) -> &u32 { + x + } +} + +// this mod explains why we should not lint `&mut &* (&T)` +mod should_not_lint1 { + fn foo(x: &mut &u32) { + *x = &1; + } + + fn main() { + let mut x = &0; + foo(&mut &*x); // should not lint + assert_eq!(*x, 0); + + foo(&mut x); + assert_eq!(*x, 1); + } +} + +// similar to should_not_lint1 +mod should_not_lint2 { + struct S<'a> { + a: &'a u32, + b: u32, + } + + fn main() { + let s = S { a: &1, b: 1 }; + let x = &mut &*s.a; + *x = &2; + } +} + +// this mod explains why we should not lint `& &* (&T)` +mod false_negative { + fn foo() { + let x = &12; + let addr_x = &x as *const _ as usize; + let addr_y = &&*x as *const _ as usize; // assert ok + // let addr_y = &x as *const _ as usize; // assert fail + assert_ne!(addr_x, addr_y); + } +} diff --git a/tests/ui/borrow_deref_ref.stderr b/tests/ui/borrow_deref_ref.stderr new file mode 100644 index 0000000000000..d72de37c69ff5 --- /dev/null +++ b/tests/ui/borrow_deref_ref.stderr @@ -0,0 +1,22 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:10:17 + | +LL | let b = &*a; + | ^^^ help: if you would like to reborrow, try removing `&*`: `a` + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:12:22 + | +LL | let b = &mut &*bar(&12); + | ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)` + +error: deref on an immutable reference + --> $DIR/borrow_deref_ref.rs:55:23 + | +LL | let addr_y = &&*x as *const _ as usize; // assert ok + | ^^^ help: if you would like to reborrow, try removing `&*`: `x` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/borrow_deref_ref_unfixable.rs b/tests/ui/borrow_deref_ref_unfixable.rs new file mode 100644 index 0000000000000..a8e2bbfef0f5a --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.rs @@ -0,0 +1,10 @@ +#![allow(dead_code, unused_variables)] + +fn main() {} + +mod should_lint { + fn two_helps() { + let s = &String::new(); + let x: &str = &*s; + } +} diff --git a/tests/ui/borrow_deref_ref_unfixable.stderr b/tests/ui/borrow_deref_ref_unfixable.stderr new file mode 100644 index 0000000000000..738b01e7ec1ee --- /dev/null +++ b/tests/ui/borrow_deref_ref_unfixable.stderr @@ -0,0 +1,18 @@ +error: deref on an immutable reference + --> $DIR/borrow_deref_ref_unfixable.rs:8:23 + | +LL | let x: &str = &*s; + | ^^^ + | + = note: `-D clippy::borrow-deref-ref` implied by `-D warnings` +help: if you would like to reborrow, try removing `&*` + | +LL | let x: &str = s; + | ~ +help: if you would like to deref, try using `&**` + | +LL | let x: &str = &**s; + | ~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed index 4ec2465be06db..a68b32b097e85 100644 --- a/tests/ui/cast_abs_to_unsigned.fixed +++ b/tests/ui/cast_abs_to_unsigned.fixed @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.unsigned_abs(); println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.unsigned_abs() as usize; + let _: usize = a.unsigned_abs() as _; + let _ = a.unsigned_abs() as usize; + + let a: i64 = -3; + let _ = a.unsigned_abs() as usize; + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u128; + + let a: isize = -3; + let _ = a.unsigned_abs(); + let _ = a.unsigned_abs() as u8; + let _ = a.unsigned_abs() as u16; + let _ = a.unsigned_abs() as u32; + let _ = a.unsigned_abs() as u64; + let _ = a.unsigned_abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs index 59b9c8c367883..110fbc6c2dfb6 100644 --- a/tests/ui/cast_abs_to_unsigned.rs +++ b/tests/ui/cast_abs_to_unsigned.rs @@ -5,4 +5,25 @@ fn main() { let x: i32 = -42; let y: u32 = x.abs() as u32; println!("The absolute value of {} is {}", x, y); + + let a: i32 = -3; + let _: usize = a.abs() as usize; + let _: usize = a.abs() as _; + let _ = a.abs() as usize; + + let a: i64 = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; + + let a: isize = -3; + let _ = a.abs() as usize; + let _ = a.abs() as u8; + let _ = a.abs() as u16; + let _ = a.abs() as u32; + let _ = a.abs() as u64; + let _ = a.abs() as u128; } diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr index eb12857357a44..02c24e10659ac 100644 --- a/tests/ui/cast_abs_to_unsigned.stderr +++ b/tests/ui/cast_abs_to_unsigned.stderr @@ -6,5 +6,95 @@ LL | let y: u32 = x.abs() as u32; | = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings` -error: aborting due to previous error +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:10:20 + | +LL | let _: usize = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:11:20 + | +LL | let _: usize = a.abs() as _; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i32::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:12:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:15:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:16:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:17:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:18:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:19:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `i64::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:20:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to usize + --> $DIR/cast_abs_to_unsigned.rs:23:13 + | +LL | let _ = a.abs() as usize; + | ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u8 + --> $DIR/cast_abs_to_unsigned.rs:24:13 + | +LL | let _ = a.abs() as u8; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u16 + --> $DIR/cast_abs_to_unsigned.rs:25:13 + | +LL | let _ = a.abs() as u16; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u32 + --> $DIR/cast_abs_to_unsigned.rs:26:13 + | +LL | let _ = a.abs() as u32; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u64 + --> $DIR/cast_abs_to_unsigned.rs:27:13 + | +LL | let _ = a.abs() as u64; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: casting the result of `isize::abs()` to u128 + --> $DIR/cast_abs_to_unsigned.rs:28:13 + | +LL | let _ = a.abs() as u128; + | ^^^^^^^ help: replace with: `a.unsigned_abs()` + +error: aborting due to 16 previous errors diff --git a/tests/ui/collapsible_match2.rs b/tests/ui/collapsible_match2.rs index 542e69484276d..c8fb0a39e954c 100644 --- a/tests/ui/collapsible_match2.rs +++ b/tests/ui/collapsible_match2.rs @@ -57,7 +57,7 @@ fn lint_cases(opt_opt: Option>, res_opt: Result, String> // ref pattern and deref match Some(&[1]) { - Some(ref s) => match &*s { + Some(ref s) => match s { [n] => foo(n), _ => (), }, diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 46b645aea135c..fe64e4693792d 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -78,7 +78,7 @@ LL | [n] => foo(n), error: this `match` can be collapsed into the outer `match` --> $DIR/collapsible_match2.rs:60:24 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ________________________^ LL | | [n] => foo(n), LL | | _ => (), @@ -88,7 +88,7 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> $DIR/collapsible_match2.rs:60:14 | -LL | Some(ref s) => match &*s { +LL | Some(ref s) => match s { | ^^^^^ replace this binding LL | [n] => foo(n), | ^^^ with this pattern diff --git a/tests/ui/crashes/ice-8850.rs b/tests/ui/crashes/ice-8850.rs new file mode 100644 index 0000000000000..f2747ab2239a6 --- /dev/null +++ b/tests/ui/crashes/ice-8850.rs @@ -0,0 +1,27 @@ +fn fn_pointer_static() -> usize { + static FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn fn_pointer_const() -> usize { + const FN: fn() -> usize = || 1; + let res = FN() + 1; + res +} + +fn deref_to_dyn_fn() -> usize { + struct Derefs; + impl std::ops::Deref for Derefs { + type Target = dyn Fn() -> usize; + + fn deref(&self) -> &Self::Target { + &|| 2 + } + } + static FN: Derefs = Derefs; + let res = FN() + 1; + res +} + +fn main() {} diff --git a/tests/ui/crashes/ice-8850.stderr b/tests/ui/crashes/ice-8850.stderr new file mode 100644 index 0000000000000..620fd1edaf790 --- /dev/null +++ b/tests/ui/crashes/ice-8850.stderr @@ -0,0 +1,45 @@ +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:4:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | + = note: `-D clippy::let-and-return` implied by `-D warnings` +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:10:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: returning the result of a `let` binding from a block + --> $DIR/ice-8850.rs:24:5 + | +LL | let res = FN() + 1; + | ------------------- unnecessary `let` binding +LL | res + | ^^^ + | +help: return the expression directly + | +LL ~ +LL ~ FN() + 1 + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/dbg_macro.stderr b/tests/ui/dbg_macro.stderr index a3e7a7162e51d..e6a65b46d975c 100644 --- a/tests/ui/dbg_macro.stderr +++ b/tests/ui/dbg_macro.stderr @@ -109,5 +109,38 @@ help: ensure to avoid having uses of it in version control LL | 2; | ~ -error: aborting due to 10 previous errors +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:47:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:52:5 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: `dbg!` macro is intended as a debugging tool + --> $DIR/dbg_macro.rs:58:9 + | +LL | dbg!(1); + | ^^^^^^^ + | +help: ensure to avoid having uses of it in version control + | +LL | 1; + | ~ + +error: aborting due to 13 previous errors diff --git a/tests/ui/debug_assert_with_mut_call.rs b/tests/ui/debug_assert_with_mut_call.rs index c5de412556567..46faa0a7b9117 100644 --- a/tests/ui/debug_assert_with_mut_call.rs +++ b/tests/ui/debug_assert_with_mut_call.rs @@ -1,7 +1,7 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] #![warn(clippy::debug_assert_with_mut_call)] -#![allow(clippy::redundant_closure_call)] +#![allow(clippy::redundant_closure_call, clippy::get_first)] struct S; diff --git a/tests/ui/deref_by_slicing.fixed b/tests/ui/deref_by_slicing.fixed index b26276218b78c..257393e56ff0f 100644 --- a/tests/ui/deref_by_slicing.fixed +++ b/tests/ui/deref_by_slicing.fixed @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.rs b/tests/ui/deref_by_slicing.rs index 6aa1408ba1769..e288046f927fd 100644 --- a/tests/ui/deref_by_slicing.rs +++ b/tests/ui/deref_by_slicing.rs @@ -1,6 +1,7 @@ // run-rustfix #![warn(clippy::deref_by_slicing)] +#![allow(clippy::borrow_deref_ref)] use std::io::Read; diff --git a/tests/ui/deref_by_slicing.stderr b/tests/ui/deref_by_slicing.stderr index ffd76de378df1..8f042ef47ebe3 100644 --- a/tests/ui/deref_by_slicing.stderr +++ b/tests/ui/deref_by_slicing.stderr @@ -1,5 +1,5 @@ error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:9:13 + --> $DIR/deref_by_slicing.rs:10:13 | LL | let _ = &vec[..]; | ^^^^^^^^ help: dereference the original value instead: `&*vec` @@ -7,49 +7,49 @@ LL | let _ = &vec[..]; = note: `-D clippy::deref-by-slicing` implied by `-D warnings` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:10:13 + --> $DIR/deref_by_slicing.rs:11:13 | LL | let _ = &mut vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:13:13 + --> $DIR/deref_by_slicing.rs:14:13 | LL | let _ = &ref_vec[..]; | ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:14:21 + --> $DIR/deref_by_slicing.rs:15:21 | LL | let mut_slice = &mut ref_vec[..]; | ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:15:13 + --> $DIR/deref_by_slicing.rs:16:13 | LL | let _ = &mut mut_slice[..]; // Err, re-borrows slice | ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:18:13 + --> $DIR/deref_by_slicing.rs:19:13 | LL | let _ = &s[..]; | ^^^^^^ help: dereference the original value instead: `&*s` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:21:18 + --> $DIR/deref_by_slicing.rs:22:18 | LL | let _ = &mut &S[..]; // Err, re-borrows slice | ^^^^^^ help: reborrow the original value instead: `&*S` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:25:13 + --> $DIR/deref_by_slicing.rs:26:13 | LL | let _ = &slice_ref[..]; // Err, derefs slice | ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref` error: slicing when dereferencing would work - --> $DIR/deref_by_slicing.rs:28:13 + --> $DIR/deref_by_slicing.rs:29:13 | LL | let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice | ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)` diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 7d4d1b3b64906..012780258fc3a 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Eq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index ab4e1df1ca408..fc8285b0c6b75 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -95,4 +95,10 @@ enum EnumNotEq { #[derive(Debug, PartialEq, Clone)] struct RustFixWithOtherDerives; +#[derive(PartialEq)] +struct Generic(T); + +#[derive(PartialEq, Eq)] +struct GenericPhantom(core::marker::PhantomData); + fn main() {} diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs new file mode 100644 index 0000000000000..ab52fb1a4a69e --- /dev/null +++ b/tests/ui/doc_link_with_quotes.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_link_with_quotes)] + +fn main() { + foo() +} + +/// Calls ['bar'] +pub fn foo() { + bar() +} + +pub fn bar() {} diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr new file mode 100644 index 0000000000000..bf6d57d8afe8c --- /dev/null +++ b/tests/ui/doc_link_with_quotes.stderr @@ -0,0 +1,10 @@ +error: possible intra-doc link using quotes instead of backticks + --> $DIR/doc_link_with_quotes.rs:7:1 + | +LL | /// Calls ['bar'] + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index 3e92bca986ab5..d15c84d7438a0 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -4,6 +4,9 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] +#[macro_use] +extern crate clap; + #[macro_use] extern crate proc_macro_attr; @@ -110,4 +113,11 @@ pub trait Bazz { } } +#[derive(clap::Parser)] +#[clap(after_help = "This ia a help message. + +You're welcome. +")] +pub struct Args; + fn main() {} diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index 594fca44a3210..acc3edef9b928 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:11:1 + --> $DIR/empty_line_after_outer_attribute.rs:14:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:23:1 + --> $DIR/empty_line_after_outer_attribute.rs:26:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:28:1 + --> $DIR/empty_line_after_outer_attribute.rs:31:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:35:1 + --> $DIR/empty_line_after_outer_attribute.rs:38:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:43:1 + --> $DIR/empty_line_after_outer_attribute.rs:46:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:51:1 + --> $DIR/empty_line_after_outer_attribute.rs:54:1 | LL | / #[crate_type = "lib"] LL | | diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed index 3de2a51ffa5f3..92f27e68549a3 100644 --- a/tests/ui/explicit_deref_methods.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs index a08d75964220a..d118607f992b9 100644 --- a/tests/ui/explicit_deref_methods.rs +++ b/tests/ui/explicit_deref_methods.rs @@ -1,6 +1,11 @@ // run-rustfix -#![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)] +#![allow( + unused_variables, + clippy::clone_double_ref, + clippy::needless_borrow, + clippy::borrow_deref_ref +)] #![warn(clippy::explicit_deref_methods)] use std::ops::{Deref, DerefMut}; diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr index 8035d77d18d5c..8e8b358972be7 100644 --- a/tests/ui/explicit_deref_methods.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,5 +1,5 @@ error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:30:19 + --> $DIR/explicit_deref_methods.rs:35:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try this: `&*a` @@ -7,67 +7,67 @@ LL | let b: &str = a.deref(); = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` error: explicit `deref_mut` method call - --> $DIR/explicit_deref_methods.rs:32:23 + --> $DIR/explicit_deref_methods.rs:37:23 | LL | let b: &mut str = a.deref_mut(); | ^^^^^^^^^^^^^ help: try this: `&mut **a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:39 + --> $DIR/explicit_deref_methods.rs:40:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:35:50 + --> $DIR/explicit_deref_methods.rs:40:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:37:20 + --> $DIR/explicit_deref_methods.rs:42:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:40:11 + --> $DIR/explicit_deref_methods.rs:45:11 | LL | match a.deref() { | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:44:28 + --> $DIR/explicit_deref_methods.rs:49:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try this: `&*a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:46:13 + --> $DIR/explicit_deref_methods.rs:51:13 | LL | let b = just_return(a).deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:48:28 + --> $DIR/explicit_deref_methods.rs:53:28 | LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:50:19 + --> $DIR/explicit_deref_methods.rs:55:19 | LL | let b: &str = a.deref().deref(); | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:53:13 + --> $DIR/explicit_deref_methods.rs:58:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()` error: explicit `deref` method call - --> $DIR/explicit_deref_methods.rs:79:31 + --> $DIR/explicit_deref_methods.rs:84:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try this: `&*a` diff --git a/tests/ui/forget_ref.rs b/tests/ui/forget_ref.rs index 6c8c4c9c0edec..031b415f56ff6 100644 --- a/tests/ui/forget_ref.rs +++ b/tests/ui/forget_ref.rs @@ -1,6 +1,7 @@ #![warn(clippy::forget_ref)] #![allow(clippy::toplevel_ref_arg)] #![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)] +#![allow(clippy::borrow_deref_ref)] use std::mem::forget; diff --git a/tests/ui/forget_ref.stderr b/tests/ui/forget_ref.stderr index 73409388ed16a..df5cd8cacdb8d 100644 --- a/tests/ui/forget_ref.stderr +++ b/tests/ui/forget_ref.stderr @@ -1,108 +1,108 @@ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:10:5 + --> $DIR/forget_ref.rs:11:5 | LL | forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::forget-ref` implied by `-D warnings` note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:10:12 + --> $DIR/forget_ref.rs:11:12 | LL | forget(&SomeStruct); | ^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:13:5 + --> $DIR/forget_ref.rs:14:5 | LL | forget(&owned); | ^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:13:12 + --> $DIR/forget_ref.rs:14:12 | LL | forget(&owned); | ^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:14:5 + --> $DIR/forget_ref.rs:15:5 | LL | forget(&&owned); | ^^^^^^^^^^^^^^^ | note: argument has type `&&SomeStruct` - --> $DIR/forget_ref.rs:14:12 + --> $DIR/forget_ref.rs:15:12 | LL | forget(&&owned); | ^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:15:5 + --> $DIR/forget_ref.rs:16:5 | LL | forget(&mut owned); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:15:12 + --> $DIR/forget_ref.rs:16:12 | LL | forget(&mut owned); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:19:5 + --> $DIR/forget_ref.rs:20:5 | LL | forget(&*reference1); | ^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:19:12 + --> $DIR/forget_ref.rs:20:12 | LL | forget(&*reference1); | ^^^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:22:5 + --> $DIR/forget_ref.rs:23:5 | LL | forget(reference2); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&mut SomeStruct` - --> $DIR/forget_ref.rs:22:12 + --> $DIR/forget_ref.rs:23:12 | LL | forget(reference2); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:25:5 + --> $DIR/forget_ref.rs:26:5 | LL | forget(reference3); | ^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:25:12 + --> $DIR/forget_ref.rs:26:12 | LL | forget(reference3); | ^^^^^^^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:30:5 + --> $DIR/forget_ref.rs:31:5 | LL | forget(&val); | ^^^^^^^^^^^^ | note: argument has type `&T` - --> $DIR/forget_ref.rs:30:12 + --> $DIR/forget_ref.rs:31:12 | LL | forget(&val); | ^^^^ error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing - --> $DIR/forget_ref.rs:38:5 + --> $DIR/forget_ref.rs:39:5 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: argument has type `&SomeStruct` - --> $DIR/forget_ref.rs:38:22 + --> $DIR/forget_ref.rs:39:22 | LL | std::mem::forget(&SomeStruct); | ^^^^^^^^^^^ diff --git a/tests/ui/get_first.fixed b/tests/ui/get_first.fixed new file mode 100644 index 0000000000000..def58afa4fbf2 --- /dev/null +++ b/tests/ui/get_first.fixed @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.first(); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.first(); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.first(); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.rs b/tests/ui/get_first.rs new file mode 100644 index 0000000000000..85a381854cd3b --- /dev/null +++ b/tests/ui/get_first.rs @@ -0,0 +1,42 @@ +// run-rustfix +#![warn(clippy::get_first)] +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; + +struct Bar { + arr: [u32; 3], +} + +impl Bar { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } +} + +fn main() { + let x = vec![2, 3, 5]; + let _ = x.get(0); // Use x.first() + let _ = x.get(1); + let _ = x[0]; + + let y = [2, 3, 5]; + let _ = y.get(0); // Use y.first() + let _ = y.get(1); + let _ = y[0]; + + let z = &[2, 3, 5]; + let _ = z.get(0); // Use z.first() + let _ = z.get(1); + let _ = z[0]; + + let vecdeque: VecDeque<_> = x.iter().cloned().collect(); + let hashmap: HashMap = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let btreemap: BTreeMap = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]); + let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice. + let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice. + let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice. + + let bar = Bar { arr: [0, 1, 2] }; + let _ = bar.get(0); // Do not lint, because Bar is struct. +} diff --git a/tests/ui/get_first.stderr b/tests/ui/get_first.stderr new file mode 100644 index 0000000000000..466beff9c92df --- /dev/null +++ b/tests/ui/get_first.stderr @@ -0,0 +1,22 @@ +error: accessing first element with `x.get(0)` + --> $DIR/get_first.rs:19:13 + | +LL | let _ = x.get(0); // Use x.first() + | ^^^^^^^^ help: try: `x.first()` + | + = note: `-D clippy::get-first` implied by `-D warnings` + +error: accessing first element with `y.get(0)` + --> $DIR/get_first.rs:24:13 + | +LL | let _ = y.get(0); // Use y.first() + | ^^^^^^^^ help: try: `y.first()` + +error: accessing first element with `z.get(0)` + --> $DIR/get_first.rs:29:13 + | +LL | let _ = z.get(0); // Use z.first() + | ^^^^^^^^ help: try: `z.first()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/get_last_with_len.fixed b/tests/ui/get_last_with_len.fixed index c8b363f9c38e6..1e90b37687a15 100644 --- a/tests/ui/get_last_with_len.fixed +++ b/tests/ui/get_last_with_len.fixed @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.last(); // ~ERROR Use x.last() + let _ = x.last(); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.last(); +} + fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); + let slice = &[1, 2, 3]; + let _ = slice.last(); + + let array = [4, 5, 6]; + let _ = array.last(); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.back(); + + let nested = [[1]]; + let _ = nested[0].last(); } diff --git a/tests/ui/get_last_with_len.rs b/tests/ui/get_last_with_len.rs index bf9cb2d7e0ccd..d63a731bd5246 100644 --- a/tests/ui/get_last_with_len.rs +++ b/tests/ui/get_last_with_len.rs @@ -1,10 +1,13 @@ // run-rustfix #![warn(clippy::get_last_with_len)] +#![allow(unused)] + +use std::collections::VecDeque; fn dont_use_last() { let x = vec![2, 3, 5]; - let _ = x.get(x.len() - 1); // ~ERROR Use x.last() + let _ = x.get(x.len() - 1); } fn indexing_two_from_end() { @@ -23,9 +26,24 @@ fn use_last_with_different_vec_length() { let _ = x.get(y.len() - 1); } +struct S { + field: Vec, +} + +fn in_field(s: &S) { + let _ = s.field.get(s.field.len() - 1); +} + fn main() { - dont_use_last(); - indexing_two_from_end(); - index_into_last(); - use_last_with_different_vec_length(); + let slice = &[1, 2, 3]; + let _ = slice.get(slice.len() - 1); + + let array = [4, 5, 6]; + let _ = array.get(array.len() - 1); + + let deq = VecDeque::from([7, 8, 9]); + let _ = deq.get(deq.len() - 1); + + let nested = [[1]]; + let _ = nested[0].get(nested[0].len() - 1); } diff --git a/tests/ui/get_last_with_len.stderr b/tests/ui/get_last_with_len.stderr index 55baf87384a24..ac8dd6c2e41a8 100644 --- a/tests/ui/get_last_with_len.stderr +++ b/tests/ui/get_last_with_len.stderr @@ -1,10 +1,40 @@ error: accessing last element with `x.get(x.len() - 1)` - --> $DIR/get_last_with_len.rs:7:13 + --> $DIR/get_last_with_len.rs:10:13 | -LL | let _ = x.get(x.len() - 1); // ~ERROR Use x.last() +LL | let _ = x.get(x.len() - 1); | ^^^^^^^^^^^^^^^^^^ help: try: `x.last()` | = note: `-D clippy::get-last-with-len` implied by `-D warnings` -error: aborting due to previous error +error: accessing last element with `s.field.get(s.field.len() - 1)` + --> $DIR/get_last_with_len.rs:34:13 + | +LL | let _ = s.field.get(s.field.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.field.last()` + +error: accessing last element with `slice.get(slice.len() - 1)` + --> $DIR/get_last_with_len.rs:39:13 + | +LL | let _ = slice.get(slice.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.last()` + +error: accessing last element with `array.get(array.len() - 1)` + --> $DIR/get_last_with_len.rs:42:13 + | +LL | let _ = array.get(array.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array.last()` + +error: accessing last element with `deq.get(deq.len() - 1)` + --> $DIR/get_last_with_len.rs:45:13 + | +LL | let _ = deq.get(deq.len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `deq.back()` + +error: accessing last element with `nested[0].get(nested[0].len() - 1)` + --> $DIR/get_last_with_len.rs:48:13 + | +LL | let _ = nested[0].get(nested[0].len() - 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `nested[0].last()` + +error: aborting due to 6 previous errors diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index 8f165d675890c..5827fc7d76e60 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index 786749daa746e..a2a323c14fb7c 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)] #![warn(clippy::unwrap_used)] #![deny(clippy::get_unwrap)] diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed new file mode 100644 index 0000000000000..5f9cebe212abd --- /dev/null +++ b/tests/ui/identity_op.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + +use std::fmt::Write as _; + +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +struct A(String); + +impl std::ops::Shl for A { + type Output = A; + fn shl(mut self, other: i32) -> Self { + let _ = write!(self.0, "{}", other); + self + } +} + +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[rustfmt::skip] +fn main() { + let x = 0; + + x; + x; + x + 1; + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x; + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x; + x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + x; + + let u: u8 = 0; + u; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42; + 1; + 42; + &x; + x; + + let mut a = A("".into()); + let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer + + 2; + -2; + 2 + x; + -2 + x; + x + 1; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + (if b { 1 } else { 2 }); + (if b { 1 } else { 2 }) + if b { 3 } else { 4 }; + (match a { 0 => 10, _ => 20 }); + (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 }; + (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 }; + (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }); + + ({ a }) + 3; + ({ a } * 2); + (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 }; + + fn f(_: i32) { + todo!(); + } + f(a + { 8 * 5 }); + f(if b { 1 } else { 2 } + 3); + const _: i32 = { 2 * 4 } + 3; + const _: i32 = { 1 + 2 * 3 } + 3; + + a as usize; + let _ = a as usize; + ({ a } as usize); + + 2 * { a }; + (({ a } + 4)); + 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + (if a { 1 } else { 2 }) + if b { 3 } else { 5 } +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index fec54d00ccb4b..ca799c9cfac0f 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,15 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + use std::fmt::Write as _; const ONE: i64 = 1; @@ -24,14 +36,6 @@ impl core::ops::Mul for u8 { } } -#[allow( - clippy::eq_op, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::op_ref, - clippy::double_parens -)] -#[warn(clippy::identity_op)] #[rustfmt::skip] fn main() { let x = 0; @@ -82,29 +86,34 @@ fn main() { let a = 0; let b = true; 0 + if b { 1 } else { 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; 0 + match a { 0 => 10, _ => 20 }; - 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error - 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error - 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error - - 0 + if b { 0 + 1 } else { 2 }; - 0 + match a { 0 => 0 + 10, _ => 20 }; - 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - - let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - - 0 + { a } + 3; // no error - 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error - + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }) + 0; + + 0 + { a } + 3; + 0 + { a } * 2; + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + fn f(_: i32) { todo!(); } f(1 * a + { 8 * 5 }); - f(0 + if b { 1 } else { 2 } + 3); // no error + f(0 + if b { 1 } else { 2 } + 3); const _: i32 = { 2 * 4 } + 0 + 3; - const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error + const _: i32 = 0 + { 1 + 2 * 3 } + 3; + + 0 + a as usize; + let _ = 0 + a as usize; + 0 + { a } as usize; + + 2 * (0 + { a }); + 1 * ({ a } + 4); + 1 * 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index d8cb65839cbba..1a104a20b841c 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,202 +1,238 @@ -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:39:5 +error: this operation has no effect + --> $DIR/identity_op.rs:43:5 | LL | x + 0; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` | = note: `-D clippy::identity-op` implied by `-D warnings` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 +error: this operation has no effect + --> $DIR/identity_op.rs:44:5 | LL | x + (1 - 1); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 +error: this operation has no effect + --> $DIR/identity_op.rs:46:5 | LL | 0 + x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:45:5 +error: this operation has no effect + --> $DIR/identity_op.rs:49:5 | LL | x | (0); - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:48:5 +error: this operation has no effect + --> $DIR/identity_op.rs:52:5 | LL | x * 1; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:49:5 +error: this operation has no effect + --> $DIR/identity_op.rs:53:5 | LL | 1 * x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:55:5 +error: this operation has no effect + --> $DIR/identity_op.rs:59:5 | LL | -1 & x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:58:5 +error: this operation has no effect + --> $DIR/identity_op.rs:62:5 | LL | u & 255; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `u` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 +error: this operation has no effect + --> $DIR/identity_op.rs:65:5 | LL | 42 << 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:62:5 +error: this operation has no effect + --> $DIR/identity_op.rs:66:5 | LL | 1 >> 0; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:63:5 +error: this operation has no effect + --> $DIR/identity_op.rs:67:5 | LL | 42 >> 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:64:5 +error: this operation has no effect + --> $DIR/identity_op.rs:68:5 | LL | &x >> 0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `&x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:65:5 +error: this operation has no effect + --> $DIR/identity_op.rs:69:5 | LL | x >> &0; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 +error: this operation has no effect + --> $DIR/identity_op.rs:76:5 | LL | 2 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 +error: this operation has no effect + --> $DIR/identity_op.rs:77:5 | LL | -2 % 3; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:74:5 +error: this operation has no effect + --> $DIR/identity_op.rs:78:5 | LL | 2 % -3 + x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:75:5 +error: this operation has no effect + --> $DIR/identity_op.rs:79:5 | LL | -2 % -3 + x; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:76:9 +error: this operation has no effect + --> $DIR/identity_op.rs:80:9 | LL | x + 1 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` - --> $DIR/identity_op.rs:84:5 +error: this operation has no effect + --> $DIR/identity_op.rs:88:5 | LL | 0 + if b { 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` - --> $DIR/identity_op.rs:86:5 +error: this operation has no effect + --> $DIR/identity_op.rs:89:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:90:5 | LL | 0 + match a { 0 => 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` +error: this operation has no effect --> $DIR/identity_op.rs:91:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:91:16 +error: this operation has no effect + --> $DIR/identity_op.rs:92:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^ +LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` - --> $DIR/identity_op.rs:92:5 +error: this operation has no effect + --> $DIR/identity_op.rs:93:5 + | +LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:94:5 + | +LL | (if b { 1 } else { 2 }) + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:96:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + { a } + 3; + | ^^^^^^^^^ help: consider reducing it to: `({ a })` -error: the operation is ineffective. Consider reducing it to `10` - --> $DIR/identity_op.rs:92:25 +error: this operation has no effect + --> $DIR/identity_op.rs:97:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^ +LL | 0 + { a } * 2; + | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:93:16 +error: this operation has no effect + --> $DIR/identity_op.rs:98:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^ +LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })` -error: the operation is ineffective. Consider reducing it to `30` - --> $DIR/identity_op.rs:93:52 +error: this operation has no effect + --> $DIR/identity_op.rs:103:7 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^^ +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ help: consider reducing it to: `a` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:20 +error: this operation has no effect + --> $DIR/identity_op.rs:104:7 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | f(0 + if b { 1 } else { 2 } + 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:52 +error: this operation has no effect + --> $DIR/identity_op.rs:105:20 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:23 +error: this operation has no effect + --> $DIR/identity_op.rs:106:20 | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ +LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3; + | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:58 +error: this operation has no effect + --> $DIR/identity_op.rs:108:5 | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ +LL | 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` -error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` - --> $DIR/identity_op.rs:98:5 +error: this operation has no effect + --> $DIR/identity_op.rs:109:13 | -LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` -error: the operation is ineffective. Consider reducing it to `a` - --> $DIR/identity_op.rs:106:7 +error: this operation has no effect + --> $DIR/identity_op.rs:110:5 | -LL | f(1 * a + { 8 * 5 }); - | ^^^^^ +LL | 0 + { a } as usize; + | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)` -error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` - --> $DIR/identity_op.rs:108:20 +error: this operation has no effect + --> $DIR/identity_op.rs:112:9 | -LL | const _: i32 = { 2 * 4 } + 0 + 3; - | ^^^^^^^^^^^^^ +LL | 2 * (0 + { a }); + | ^^^^^^^^^^^ help: consider reducing it to: `{ a }` + +error: this operation has no effect + --> $DIR/identity_op.rs:113:5 + | +LL | 1 * ({ a } + 4); + | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))` + +error: this operation has no effect + --> $DIR/identity_op.rs:114:5 + | +LL | 1 * 1; + | ^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:118:5 + | +LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` -error: aborting due to 33 previous errors +error: aborting due to 39 previous errors diff --git a/tests/ui/implicit_clone.fixed b/tests/ui/implicit_clone.fixed new file mode 100644 index 0000000000000..33770fc2a2cf9 --- /dev/null +++ b/tests/ui/implicit_clone.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::implicit_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] +use std::borrow::Borrow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +fn return_owned_from_slice(slice: &[u32]) -> Vec { + slice.to_owned() +} + +pub fn own_same(v: T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_same_from_ref(v: &T) -> T +where + T: ToOwned, +{ + v.to_owned() +} + +pub fn own_different(v: T) -> U +where + T: ToOwned, +{ + v.to_owned() +} + +#[derive(Copy, Clone)] +struct Kitten; +impl Kitten { + // badly named method + fn to_vec(self) -> Kitten { + Kitten {} + } +} +impl Borrow for Kitten { + fn borrow(&self) -> &BorrowedKitten { + static VALUE: BorrowedKitten = BorrowedKitten {}; + &VALUE + } +} + +struct BorrowedKitten; +impl ToOwned for BorrowedKitten { + type Owned = Kitten; + fn to_owned(&self) -> Kitten { + Kitten {} + } +} + +mod weird { + #[allow(clippy::ptr_arg)] + pub fn to_vec(v: &Vec) -> Vec { + v.clone() + } +} + +fn main() { + let vec = vec![5]; + let _ = return_owned_from_slice(&vec); + let _ = vec.clone(); + let _ = vec.clone(); + + let vec_ref = &vec; + let _ = return_owned_from_slice(vec_ref); + let _ = vec_ref.clone(); + let _ = vec_ref.clone(); + + // we expect no lint for this + let _ = weird::to_vec(&vec); + + // we expect no lints for this + let slice: &[u32] = &[1, 2, 3, 4, 5]; + let _ = return_owned_from_slice(slice); + let _ = slice.to_owned(); + let _ = slice.to_vec(); + + let str = "hello world".to_string(); + let _ = str.clone(); + + // testing w/ an arbitrary type + let kitten = Kitten {}; + let _ = kitten.clone(); + let _ = own_same_from_ref(&kitten); + // this shouln't lint + let _ = kitten.to_vec(); + + // we expect no lints for this + let borrowed = BorrowedKitten {}; + let _ = borrowed.to_owned(); + + let pathbuf = PathBuf::new(); + let _ = pathbuf.clone(); + let _ = pathbuf.clone(); + + let os_string = OsString::from("foo"); + let _ = os_string.clone(); + let _ = os_string.clone(); + + // we expect no lints for this + let os_str = OsStr::new("foo"); + let _ = os_str.to_owned(); + let _ = os_str.to_os_string(); + + // issue #8227 + let pathbuf_ref = &pathbuf; + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf` + let _ = (*pathbuf_ref).clone(); + let pathbuf_ref = &pathbuf_ref; + let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf` + let _ = (**pathbuf_ref).clone(); +} diff --git a/tests/ui/implicit_clone.rs b/tests/ui/implicit_clone.rs index 2549c9f32f904..fc896525bd270 100644 --- a/tests/ui/implicit_clone.rs +++ b/tests/ui/implicit_clone.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::implicit_clone)] -#![allow(clippy::redundant_clone)] +#![allow(clippy::clone_on_copy, clippy::redundant_clone)] use std::borrow::Borrow; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; diff --git a/tests/ui/implicit_clone.stderr b/tests/ui/implicit_clone.stderr index 0f4124241907f..92c1aa58affb2 100644 --- a/tests/ui/implicit_clone.stderr +++ b/tests/ui/implicit_clone.stderr @@ -1,5 +1,5 @@ error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:65:13 + --> $DIR/implicit_clone.rs:66:13 | LL | let _ = vec.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `vec.clone()` @@ -7,67 +7,67 @@ LL | let _ = vec.to_owned(); = note: `-D clippy::implicit-clone` implied by `-D warnings` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:66:13 + --> $DIR/implicit_clone.rs:67:13 | LL | let _ = vec.to_vec(); | ^^^^^^^^^^^^ help: consider using: `vec.clone()` error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:70:13 + --> $DIR/implicit_clone.rs:71:13 | LL | let _ = vec_ref.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type - --> $DIR/implicit_clone.rs:71:13 + --> $DIR/implicit_clone.rs:72:13 | LL | let _ = vec_ref.to_vec(); | ^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()` error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:83:13 + --> $DIR/implicit_clone.rs:84:13 | LL | let _ = str.to_owned(); | ^^^^^^^^^^^^^^ help: consider using: `str.clone()` error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:87:13 + --> $DIR/implicit_clone.rs:88:13 | LL | let _ = kitten.to_owned(); | ^^^^^^^^^^^^^^^^^ help: consider using: `kitten.clone()` error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:97:13 + --> $DIR/implicit_clone.rs:98:13 | LL | let _ = pathbuf.to_owned(); | ^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:98:13 + --> $DIR/implicit_clone.rs:99:13 | LL | let _ = pathbuf.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()` error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type - --> $DIR/implicit_clone.rs:101:13 + --> $DIR/implicit_clone.rs:102:13 | LL | let _ = os_string.to_owned(); | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type - --> $DIR/implicit_clone.rs:102:13 + --> $DIR/implicit_clone.rs:103:13 | LL | let _ = os_string.to_os_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:113:13 + --> $DIR/implicit_clone.rs:114:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(*pathbuf_ref).clone()` error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type - --> $DIR/implicit_clone.rs:116:13 + --> $DIR/implicit_clone.rs:117:13 | LL | let _ = pathbuf_ref.to_path_buf(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()` diff --git a/tests/ui/issue_2356.fixed b/tests/ui/issue_2356.fixed new file mode 100644 index 0000000000000..942e99fa8787b --- /dev/null +++ b/tests/ui/issue_2356.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] + +use std::iter::Iterator; + +struct Foo; + +impl Foo { + fn foo1>(mut it: I) { + while let Some(_) = it.next() { + println!("{:?}", it.size_hint()); + } + } + + fn foo2>(mut it: I) { + for e in it { + println!("{:?}", e); + } + } +} + +fn main() { + Foo::foo1(vec![].into_iter()); + Foo::foo2(vec![].into_iter()); +} diff --git a/tests/ui/issue_2356.rs b/tests/ui/issue_2356.rs index da580a1839a17..b000234ea5966 100644 --- a/tests/ui/issue_2356.rs +++ b/tests/ui/issue_2356.rs @@ -1,4 +1,6 @@ +// run-rustfix #![deny(clippy::while_let_on_iterator)] +#![allow(unused_mut)] use std::iter::Iterator; diff --git a/tests/ui/issue_2356.stderr b/tests/ui/issue_2356.stderr index 51b872e21c085..4e3ff7522e0bb 100644 --- a/tests/ui/issue_2356.stderr +++ b/tests/ui/issue_2356.stderr @@ -1,11 +1,11 @@ error: this loop could be written as a `for` loop - --> $DIR/issue_2356.rs:15:9 + --> $DIR/issue_2356.rs:17:9 | LL | while let Some(e) = it.next() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it` | note: the lint level is defined here - --> $DIR/issue_2356.rs:1:9 + --> $DIR/issue_2356.rs:2:9 | LL | #![deny(clippy::while_let_on_iterator)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/iter_next_slice.fixed b/tests/ui/iter_next_slice.fixed index 11ffc8edb149e..f612d26aaabcc 100644 --- a/tests/ui/iter_next_slice.fixed +++ b/tests/ui/iter_next_slice.fixed @@ -6,8 +6,8 @@ fn main() { let s = [1, 2, 3]; let v = vec![1, 2, 3]; - let _ = s.get(0); - // Should be replaced by s.get(0) + let _ = s.first(); + // Should be replaced by s.first() let _ = s.get(2); // Should be replaced by s.get(2) @@ -15,8 +15,8 @@ fn main() { let _ = v.get(5); // Should be replaced by v.get(5) - let _ = v.get(0); - // Should be replaced by v.get(0) + let _ = v.first(); + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.rs b/tests/ui/iter_next_slice.rs index e0d3aabd54acd..5195f1c86675f 100644 --- a/tests/ui/iter_next_slice.rs +++ b/tests/ui/iter_next_slice.rs @@ -7,7 +7,7 @@ fn main() { let v = vec![1, 2, 3]; let _ = s.iter().next(); - // Should be replaced by s.get(0) + // Should be replaced by s.first() let _ = s[2..].iter().next(); // Should be replaced by s.get(2) @@ -16,7 +16,7 @@ fn main() { // Should be replaced by v.get(5) let _ = v.iter().next(); - // Should be replaced by v.get(0) + // Should be replaced by v.first() let o = Some(5); o.iter().next(); diff --git a/tests/ui/iter_next_slice.stderr b/tests/ui/iter_next_slice.stderr index a78d2c2d5e838..d8b89061ff895 100644 --- a/tests/ui/iter_next_slice.stderr +++ b/tests/ui/iter_next_slice.stderr @@ -2,7 +2,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:9:13 | LL | let _ = s.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `s.first()` | = note: `-D clippy::iter-next-slice` implied by `-D warnings` @@ -22,7 +22,7 @@ error: using `.iter().next()` on an array --> $DIR/iter_next_slice.rs:18:13 | LL | let _ = v.iter().next(); - | ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)` + | ^^^^^^^^^^^^^^^ help: try calling: `v.first()` error: aborting due to 4 previous errors diff --git a/tests/ui/large_enum_variant.rs b/tests/ui/large_enum_variant.rs index cee9e2372c227..23152a13322e8 100644 --- a/tests/ui/large_enum_variant.rs +++ b/tests/ui/large_enum_variant.rs @@ -98,6 +98,38 @@ struct Struct2 { a: [i32; 8000], } +#[derive(Copy, Clone)] +enum CopyableLargeEnum { + A(bool), + B([u128; 4000]), +} + +enum ManuallyCopyLargeEnum { + A(bool), + B([u128; 4000]), +} + +impl Clone for ManuallyCopyLargeEnum { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for ManuallyCopyLargeEnum {} + +enum SomeGenericPossiblyCopyEnum { + A(bool, std::marker::PhantomData), + B([u64; 4000]), +} + +impl Clone for SomeGenericPossiblyCopyEnum { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SomeGenericPossiblyCopyEnum {} + fn main() { large_enum_variant!(); } diff --git a/tests/ui/large_enum_variant.stderr b/tests/ui/large_enum_variant.stderr index cbf2ac972e2b2..0248327262da0 100644 --- a/tests/ui/large_enum_variant.stderr +++ b/tests/ui/large_enum_variant.stderr @@ -127,5 +127,71 @@ help: consider boxing the large fields to reduce the total size of the enum LL | B(Box), | ~~~~~~~~~~~~ -error: aborting due to 8 previous errors +error: large size difference between variants + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:103:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:102:6 + | +LL | enum CopyableLargeEnum { + | ^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:104:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ this variant is 64000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:108:5 + | +LL | A(bool), + | ^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:107:6 + | +LL | enum ManuallyCopyLargeEnum { + | ^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:109:5 + | +LL | B([u128; 4000]), + | ^^^^^^^^^^^^^^^ + +error: large size difference between variants + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ this variant is 32000 bytes + | +note: and the second-largest variant is 1 bytes: + --> $DIR/large_enum_variant.rs:121:5 + | +LL | A(bool, std::marker::PhantomData), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: boxing a variant would require the type no longer be `Copy` + --> $DIR/large_enum_variant.rs:120:6 + | +LL | enum SomeGenericPossiblyCopyEnum { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider boxing the large fields to reduce the total size of the enum + --> $DIR/large_enum_variant.rs:122:5 + | +LL | B([u64; 4000]), + | ^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors diff --git a/tests/ui/map_flatten_fixable.fixed b/tests/ui/map_flatten_fixable.fixed index fec3a95edd62d..928e5bd509c3f 100644 --- a/tests/ui/map_flatten_fixable.fixed +++ b/tests/ui/map_flatten_fixable.fixed @@ -28,4 +28,41 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); + + issue8734(); + issue8878(); +} + +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .and_then(|_| { +// we need some newlines +// so that the span is big enough +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }); } diff --git a/tests/ui/map_flatten_fixable.rs b/tests/ui/map_flatten_fixable.rs index aa1f76e335af0..4345c6eee7461 100644 --- a/tests/ui/map_flatten_fixable.rs +++ b/tests/ui/map_flatten_fixable.rs @@ -28,4 +28,40 @@ fn main() { // mapping to Result on Result let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); + + issue8734(); + issue8878(); +} + +fn issue8734() { + // let _ = [0u8, 1, 2, 3] + // .into_iter() + // .map(|n| match n { + // 1 => [n + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1) + // .saturating_add(1)], + // n => [n], + // }) + // .flatten(); +} + +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again +#[rustfmt::skip] // whitespace is important for this one +fn issue8878() { + std::collections::HashMap::::new() + .get(&0) + .map(|_| { +// we need some newlines +// so that the span is big enough +// for a splitted output of the diagnostic + Some("") + // whitespace beforehand is important as well + }) + .flatten(); } diff --git a/tests/ui/map_flatten_fixable.stderr b/tests/ui/map_flatten_fixable.stderr index c91c73846b69f..828e24acaad6c 100644 --- a/tests/ui/map_flatten_fixable.stderr +++ b/tests/ui/map_flatten_fixable.stderr @@ -76,5 +76,31 @@ help: try replacing `map` with `and_then`, and remove the `.flatten()` LL | let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x); | ~~~~~~~~~~~~~~~ -error: aborting due to 7 previous errors +error: called `map(..).flatten()` on `Option` + --> $DIR/map_flatten_fixable.rs:59:10 + | +LL | .map(|_| { + | __________^ +LL | | // we need some newlines +LL | | // so that the span is big enough +LL | | // for a splitted output of the diagnostic +... | +LL | | }) +LL | | .flatten(); + | |__________________^ + | +help: try replacing `map` with `and_then` + | +LL ~ .and_then(|_| { +LL + // we need some newlines +LL + // so that the span is big enough + | +help: and remove the `.flatten()` + | +LL + Some("") +LL + // whitespace beforehand is important as well +LL ~ }); + | + +error: aborting due to 8 previous errors diff --git a/tests/ui/match_ref_pats.fixed b/tests/ui/match_ref_pats.fixed new file mode 100644 index 0000000000000..1b6c2d9241218 --- /dev/null +++ b/tests/ui/match_ref_pats.fixed @@ -0,0 +1,118 @@ +// run-rustfix +#![warn(clippy::match_ref_pats)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] + +fn ref_pats() { + { + let v = &Some(0); + match *v { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + match v { + // This doesn't trigger; we have a different pattern. + &Some(v) => println!("some"), + other => println!("other"), + } + } + let tup = &(1, 2); + match tup { + &(v, 1) => println!("{}", v), + _ => println!("none"), + } + // Special case: using `&` both in expr and pats. + let w = Some(0); + match w { + Some(v) => println!("{:?}", v), + None => println!("none"), + } + // False positive: only wildcard pattern. + let w = Some(0); + #[allow(clippy::match_single_binding)] + match w { + _ => println!("none"), + } + + let a = &Some(0); + if a.is_none() { + println!("none"); + } + + let b = Some(0); + if b.is_none() { + println!("none"); + } +} + +mod ice_3719 { + macro_rules! foo_variant( + ($idx:expr) => (Foo::get($idx).unwrap()) + ); + + enum Foo { + A, + B, + } + + impl Foo { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&Foo::A), + 1 => Some(&Foo::B), + _ => None, + } + } + } + + fn ice_3719() { + // ICE #3719 + match foo_variant!(0) { + &Foo::A => println!("A"), + _ => println!("Wild"), + } + } +} + +mod issue_7740 { + macro_rules! foobar_variant( + ($idx:expr) => (FooBar::get($idx).unwrap()) + ); + + enum FooBar { + Foo, + Bar, + FooBar, + BarFoo, + } + + impl FooBar { + fn get(idx: u8) -> Option<&'static Self> { + match idx { + 0 => Some(&FooBar::Foo), + 1 => Some(&FooBar::Bar), + 2 => Some(&FooBar::FooBar), + 3 => Some(&FooBar::BarFoo), + _ => None, + } + } + } + + fn issue_7740() { + // Issue #7740 + match *foobar_variant!(0) { + FooBar::Foo => println!("Foo"), + FooBar::Bar => println!("Bar"), + FooBar::FooBar => println!("FooBar"), + _ => println!("Wild"), + } + + // This shouldn't trigger + if let &FooBar::BarFoo = foobar_variant!(3) { + println!("BarFoo"); + } else { + println!("Wild"); + } + } +} + +fn main() {} diff --git a/tests/ui/match_ref_pats.rs b/tests/ui/match_ref_pats.rs index 7e3674ab8c9f2..68dfac4e2e978 100644 --- a/tests/ui/match_ref_pats.rs +++ b/tests/ui/match_ref_pats.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::match_ref_pats)] -#![allow(clippy::equatable_if_let, clippy::enum_variant_names)] +#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)] fn ref_pats() { { diff --git a/tests/ui/match_ref_pats.stderr b/tests/ui/match_ref_pats.stderr index 901820077e20e..353f7399d9c27 100644 --- a/tests/ui/match_ref_pats.stderr +++ b/tests/ui/match_ref_pats.stderr @@ -1,5 +1,5 @@ error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:7:9 + --> $DIR/match_ref_pats.rs:8:9 | LL | / match v { LL | | &Some(v) => println!("{:?}", v), @@ -16,7 +16,7 @@ LL ~ None => println!("none"), | error: you don't need to add `&` to both the expression and the patterns - --> $DIR/match_ref_pats.rs:24:5 + --> $DIR/match_ref_pats.rs:25:5 | LL | / match &w { LL | | &Some(v) => println!("{:?}", v), @@ -32,7 +32,7 @@ LL ~ None => println!("none"), | error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:36:12 + --> $DIR/match_ref_pats.rs:37:12 | LL | if let &None = a { | -------^^^^^---- help: try this: `if a.is_none()` @@ -40,13 +40,13 @@ LL | if let &None = a { = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` error: redundant pattern matching, consider using `is_none()` - --> $DIR/match_ref_pats.rs:41:12 + --> $DIR/match_ref_pats.rs:42:12 | LL | if let &None = &b { | -------^^^^^----- help: try this: `if b.is_none()` error: you don't need to add `&` to all patterns - --> $DIR/match_ref_pats.rs:101:9 + --> $DIR/match_ref_pats.rs:102:9 | LL | / match foobar_variant!(0) { LL | | &FooBar::Foo => println!("Foo"), diff --git a/tests/ui/match_str_case_mismatch.fixed b/tests/ui/match_str_case_mismatch.fixed new file mode 100644 index 0000000000000..e436bcf495fd2 --- /dev/null +++ b/tests/ui/match_str_case_mismatch.fixed @@ -0,0 +1,186 @@ +// run-rustfix +#![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] + +// Valid + +fn as_str_match() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn unrelated_method() { + struct Item { + a: String, + } + + impl Item { + #[allow(clippy::wrong_self_convention)] + fn to_lowercase(self) -> String { + self.a + } + } + + let item = Item { a: String::from("BAR") }; + + match &*item.to_lowercase() { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +// Invalid + +fn as_str_match_mismatch() { + let var = "BAR"; + + match var.to_ascii_lowercase().as_str() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn non_alphabetic_mismatch() { + let var = "~!@#$%^&*()-_=+FOO"; + + match var.to_ascii_lowercase().as_str() { + "1234567890" => {}, + "~!@#$%^&*()-_=+foo" => {}, + "\n\r\t\x7F" => {}, + _ => {}, + } +} + +fn unicode_cased_mismatch() { + let var = "ВОДЫ"; + + match var.to_lowercase().as_str() { + "水" => {}, + "νερό" => {}, + "воды" => {}, + "물" => {}, + _ => {}, + } +} + +fn titlecase_mismatch() { + let var = "BarDz"; + + match var.to_lowercase().as_str() { + "foolj" => {}, + "bardz" => {}, + _ => {}, + } +} + +fn no_case_equivalent_mismatch() { + let var = "barʁ"; + + match var.to_uppercase().as_str() { + "FOOɕ" => {}, + "BARʁ" => {}, + _ => {}, + } +} + +fn addrof_unary_match_mismatch() { + let var = "BAR"; + + match &*var.to_ascii_lowercase() { + "foo" => {}, + "bar" => {}, + _ => {}, + } +} + +fn alternating_chain_mismatch() { + let var = "BAR"; + + match &*var + .to_ascii_lowercase() + .to_uppercase() + .to_lowercase() + .to_ascii_uppercase() + { + "FOO" => {}, + "BAR" => {}, + _ => {}, + } +} + +fn main() {} diff --git a/tests/ui/match_str_case_mismatch.rs b/tests/ui/match_str_case_mismatch.rs index ac555c87d83b2..92e2a000ade25 100644 --- a/tests/ui/match_str_case_mismatch.rs +++ b/tests/ui/match_str_case_mismatch.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::match_str_case_mismatch)] +#![allow(dead_code)] // Valid diff --git a/tests/ui/match_str_case_mismatch.stderr b/tests/ui/match_str_case_mismatch.stderr index 92baa40ef28f0..197520a3d6081 100644 --- a/tests/ui/match_str_case_mismatch.stderr +++ b/tests/ui/match_str_case_mismatch.stderr @@ -1,5 +1,5 @@ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:111:9 + --> $DIR/match_str_case_mismatch.rs:113:9 | LL | "Bar" => {}, | ^^^^^ @@ -11,7 +11,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:121:9 + --> $DIR/match_str_case_mismatch.rs:123:9 | LL | "~!@#$%^&*()-_=+Foo" => {}, | ^^^^^^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | "~!@#$%^&*()-_=+foo" => {}, | ~~~~~~~~~~~~~~~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:133:9 + --> $DIR/match_str_case_mismatch.rs:135:9 | LL | "Воды" => {}, | ^^^^^^ @@ -33,7 +33,7 @@ LL | "воды" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:144:9 + --> $DIR/match_str_case_mismatch.rs:146:9 | LL | "barDz" => {}, | ^^^^^^ @@ -44,7 +44,7 @@ LL | "bardz" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:154:9 + --> $DIR/match_str_case_mismatch.rs:156:9 | LL | "bARʁ" => {}, | ^^^^^^ @@ -55,7 +55,7 @@ LL | "BARʁ" => {}, | ~~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:164:9 + --> $DIR/match_str_case_mismatch.rs:166:9 | LL | "Bar" => {}, | ^^^^^ @@ -66,7 +66,7 @@ LL | "bar" => {}, | ~~~~~ error: this `match` arm has a differing case than its expression - --> $DIR/match_str_case_mismatch.rs:179:9 + --> $DIR/match_str_case_mismatch.rs:181:9 | LL | "bAR" => {}, | ^^^^^ diff --git a/tests/ui/mismatching_type_param_order.rs b/tests/ui/mismatching_type_param_order.rs new file mode 100644 index 0000000000000..8f286c9304ccb --- /dev/null +++ b/tests/ui/mismatching_type_param_order.rs @@ -0,0 +1,60 @@ +#![warn(clippy::mismatching_type_param_order)] +#![allow(clippy::blacklisted_name)] + +fn main() { + struct Foo { + x: A, + y: B, + } + + // lint on both params + impl Foo {} + + // lint on the 2nd param + impl Foo {} + + // should not lint + impl Foo {} + + struct FooLifetime<'l, 'm, A, B> { + x: &'l A, + y: &'m B, + } + + // should not lint on lifetimes + impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + + struct Bar { + x: i32, + } + + // should not lint + impl Bar {} + + // also works for enums + enum FooEnum { + X(A), + Y(B), + Z(C), + } + + impl FooEnum {} + + // also works for unions + union FooUnion + where + B: Copy, + { + x: A, + y: B, + } + + impl FooUnion where A: Copy {} + + impl FooUnion + where + A: Copy, + B: Copy, + { + } +} diff --git a/tests/ui/mismatching_type_param_order.stderr b/tests/ui/mismatching_type_param_order.stderr new file mode 100644 index 0000000000000..cb720256c50e5 --- /dev/null +++ b/tests/ui/mismatching_type_param_order.stderr @@ -0,0 +1,83 @@ +error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:20 + | +LL | impl Foo {} + | ^ + | + = note: `-D clippy::mismatching-type-param-order` implied by `-D warnings` + = help: try `A`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:11:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:14:23 + | +LL | impl Foo {} + | ^ + | + = help: try `B`, or a name that does not conflict with `Foo`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:44 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:25:47 + | +LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:27 + | +LL | impl FooEnum {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:30 + | +LL | impl FooEnum {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:41:33 + | +LL | impl FooEnum {} + | ^ + | + = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:31 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params + +error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order + --> $DIR/mismatching_type_param_order.rs:52:34 + | +LL | impl FooUnion where A: Copy {} + | ^ + | + = help: try `B`, or a name that does not conflict with `FooUnion`'s generic params + +error: aborting due to 10 previous errors + diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index 678a312f66e53..adff08e5d1e8b 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 03f460897fce2..04ecdef5e994e 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -38,14 +38,6 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | i32::MIN % (-1); // also caught by rustc | ^^^^^^^^^^^^^^^ -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:13:22 - | -LL | const ONE: u32 = 1 * 1; - | ^^^^^ - | - = note: `-D clippy::identity-op` implied by `-D warnings` - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:17:5 | @@ -64,5 +56,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; // also caught by rustc | ^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/needless_late_init.fixed b/tests/ui/needless_late_init.fixed new file mode 100644 index 0000000000000..fee8e3030b808 --- /dev/null +++ b/tests/ui/needless_late_init.fixed @@ -0,0 +1,273 @@ +// run-rustfix +#![feature(let_chains)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} + +fn simple() { + + let a = "zero"; + + + + let b = 1; + let c = 2; + + + let d: usize = 1; + + + let e = format!("{}", d); +} + +fn main() { + + let n = 1; + let a = match n { + 1 => "one", + _ => { + "two" + }, + }; + + + let b = if n == 3 { + "four" + } else { + "five" + }; + + + let d = if true { + let temp = 5; + temp + } else { + 15 + }; + + + let e = if true { + format!("{} {}", a, b) + } else { + format!("{}", n) + }; + + + let f = match 1 { + 1 => "three", + _ => return, + }; // has semi + + + let g: usize = if true { + 5 + } else { + panic!(); + }; + + // Drop order only matters if both are significant + + let y = SignificantDrop; + let x = 1; + + + let y = 1; + let x = SignificantDrop; + + + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + let x = SignificantDrop; +} + +async fn in_async() -> &'static str { + async fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f().await, + _ => { + "two" + }, + }; + + a +} + +const fn in_const() -> &'static str { + const fn f() -> &'static str { + "one" + } + + + let n = 1; + let a = match n { + 1 => f(), + _ => { + "two" + }, + }; + + a +} + +fn does_not_lint() { + let z; + if false { + z = 1; + } + + let x; + let y; + if true { + x = 1; + } else { + y = 1; + } + + let mut x; + if true { + x = 5; + x = 10 / x; + } else { + x = 2; + } + + let x; + let _ = match 1 { + 1 => x = 10, + _ => x = 20, + }; + + // using tuples would be possible, but not always preferable + let x; + let y; + if true { + x = 1; + y = 2; + } else { + x = 3; + y = 4; + } + + // could match with a smarter heuristic to avoid multiple assignments + let x; + if true { + let mut y = 5; + y = 6; + x = y; + } else { + x = 2; + } + + let (x, y); + if true { + x = 1; + } else { + x = 2; + } + y = 3; + + macro_rules! assign { + ($i:ident) => { + $i = 1; + }; + } + let x; + assign!(x); + + let x; + if true { + assign!(x); + } else { + x = 2; + } + + macro_rules! in_macro { + () => { + let x; + x = 1; + + let x; + if true { + x = 1; + } else { + x = 2; + } + }; + } + in_macro!(); + + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; +} + +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } + + let x; + if { x = 1; true } { + return 1; + } else { + return 2; + } + + 3 +} diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 54e66b391b8d8..402d9f9ef7f81 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,5 +1,13 @@ +// run-rustfix #![feature(let_chains)] -#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] +#![allow( + unused, + clippy::assign_op_pattern, + clippy::blocks_in_if_conditions, + clippy::let_and_return, + clippy::let_unit_value, + clippy::nonminimal_bool +)] use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::rc::Rc; @@ -11,6 +19,22 @@ impl std::ops::Drop for SignificantDrop { } } +fn simple() { + let a; + a = "zero"; + + let b; + let c; + b = 1; + c = 2; + + let d: usize; + d = 1; + + let e; + e = format!("{}", d); +} + fn main() { let a; let n = 1; @@ -229,3 +253,21 @@ fn does_not_lint() { let y = SignificantDrop; x = SignificantDrop; } + +#[rustfmt::skip] +fn issue8911() -> u32 { + let x; + match 1 { + _ if { x = 1; false } => return 1, + _ => return 2, + } + + let x; + if { x = 1; true } { + return 1; + } else { + return 2; + } + + 3 +} diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index d33a117b288cb..f320b5b9cbb37 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,12 +1,79 @@ error: unneeded late initialization - --> $DIR/needless_late_init.rs:15:5 + --> $DIR/needless_late_init.rs:23:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here | +LL | let a = "zero"; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:26:5 + | +LL | let b; + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here + | +help: declare `b` here + | +LL | let b = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:27:5 + | +LL | let c; + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here + | +help: declare `c` here + | +LL | let c = 2; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 + | +LL | let d: usize; + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here + | +help: declare `d` here + | +LL | let d: usize = 1; + | ~~~~~~~~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:34:5 + | +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `e` here + | +LL | let e = format!("{}", d); + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 + | +LL | let a; + | ^^^^^^ + | +help: declare `a` here + | LL | let a = match n { | +++++++ help: remove the assignments from the `match` arms @@ -21,7 +88,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:24:5 + --> $DIR/needless_late_init.rs:48:5 | LL | let b; | ^^^^^^ @@ -42,7 +109,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:31:5 + --> $DIR/needless_late_init.rs:55:5 | LL | let d; | ^^^^^^ @@ -63,7 +130,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:39:5 + --> $DIR/needless_late_init.rs:63:5 | LL | let e; | ^^^^^^ @@ -84,7 +151,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:46:5 + --> $DIR/needless_late_init.rs:70:5 | LL | let f; | ^^^^^^ @@ -100,7 +167,7 @@ LL + 1 => "three", | error: unneeded late initialization - --> $DIR/needless_late_init.rs:52:5 + --> $DIR/needless_late_init.rs:76:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -120,7 +187,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:60:5 + --> $DIR/needless_late_init.rs:84:5 | LL | let x; | ^^^^^^ created here @@ -134,7 +201,7 @@ LL | let x = 1; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:64:5 + --> $DIR/needless_late_init.rs:88:5 | LL | let x; | ^^^^^^ created here @@ -148,7 +215,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:68:5 + --> $DIR/needless_late_init.rs:92:5 | LL | let x; | ^^^^^^ created here @@ -162,7 +229,7 @@ LL | let x = SignificantDrop; | ~~~~~ error: unneeded late initialization - --> $DIR/needless_late_init.rs:87:5 + --> $DIR/needless_late_init.rs:111:5 | LL | let a; | ^^^^^^ @@ -183,7 +250,7 @@ LL | }; | + error: unneeded late initialization - --> $DIR/needless_late_init.rs:104:5 + --> $DIR/needless_late_init.rs:128:5 | LL | let a; | ^^^^^^ @@ -203,5 +270,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed deleted file mode 100644 index 724477e8691df..0000000000000 --- a/tests/ui/needless_late_init_fixable.fixed +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - - let a = "zero"; - - - - let b = 1; - let c = 2; - - - let d: usize = 1; - - - let e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs deleted file mode 100644 index 3e6bd36367275..0000000000000 --- a/tests/ui/needless_late_init_fixable.rs +++ /dev/null @@ -1,19 +0,0 @@ -// run-rustfix - -#![allow(unused, clippy::assign_op_pattern)] - -fn main() { - let a; - a = "zero"; - - let b; - let c; - b = 1; - c = 2; - - let d: usize; - d = 1; - - let e; - e = format!("{}", d); -} diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr deleted file mode 100644 index 8c664309e3e83..0000000000000 --- a/tests/ui/needless_late_init_fixable.stderr +++ /dev/null @@ -1,70 +0,0 @@ -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:6:5 - | -LL | let a; - | ^^^^^^ created here -LL | a = "zero"; - | ^^^^^^^^^^ initialised here - | - = note: `-D clippy::needless-late-init` implied by `-D warnings` -help: declare `a` here - | -LL | let a = "zero"; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:9:5 - | -LL | let b; - | ^^^^^^ created here -LL | let c; -LL | b = 1; - | ^^^^^ initialised here - | -help: declare `b` here - | -LL | let b = 1; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:10:5 - | -LL | let c; - | ^^^^^^ created here -LL | b = 1; -LL | c = 2; - | ^^^^^ initialised here - | -help: declare `c` here - | -LL | let c = 2; - | ~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:14:5 - | -LL | let d: usize; - | ^^^^^^^^^^^^^ created here -LL | d = 1; - | ^^^^^ initialised here - | -help: declare `d` here - | -LL | let d: usize = 1; - | ~~~~~~~~~~~~ - -error: unneeded late initialization - --> $DIR/needless_late_init_fixable.rs:17:5 - | -LL | let e; - | ^^^^^^ created here -LL | e = format!("{}", d); - | ^^^^^^^^^^^^^^^^^^^^ initialised here - | -help: declare `e` here - | -LL | let e = format!("{}", d); - | ~~~~~ - -error: aborting due to 5 previous errors - diff --git a/tests/ui/needless_lifetimes.rs b/tests/ui/needless_lifetimes.rs index 1456204ca8692..fc686b1dac0e1 100644 --- a/tests/ui/needless_lifetimes.rs +++ b/tests/ui/needless_lifetimes.rs @@ -4,7 +4,8 @@ clippy::boxed_local, clippy::needless_pass_by_value, clippy::unnecessary_wraps, - dyn_drop + dyn_drop, + clippy::get_first )] fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index a488bc01fffa2..3c428fd4674ce 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:10:1 + --> $DIR/needless_lifetimes.rs:11:1 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,181 +7,181 @@ LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} = note: `-D clippy::needless-lifetimes` implied by `-D warnings` error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:12:1 + --> $DIR/needless_lifetimes.rs:13:1 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:22:1 + --> $DIR/needless_lifetimes.rs:23:1 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:56:1 + --> $DIR/needless_lifetimes.rs:57:1 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:61:1 + --> $DIR/needless_lifetimes.rs:62:1 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:73:1 + --> $DIR/needless_lifetimes.rs:74:1 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:97:1 + --> $DIR/needless_lifetimes.rs:98:1 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:127:5 + --> $DIR/needless_lifetimes.rs:128:5 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:136:5 + --> $DIR/needless_lifetimes.rs:137:5 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:155:1 + --> $DIR/needless_lifetimes.rs:156:1 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:185:1 + --> $DIR/needless_lifetimes.rs:186:1 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:191:1 + --> $DIR/needless_lifetimes.rs:192:1 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:210:1 + --> $DIR/needless_lifetimes.rs:211:1 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:218:1 + --> $DIR/needless_lifetimes.rs:219:1 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:254:1 + --> $DIR/needless_lifetimes.rs:255:1 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'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:261:9 + --> $DIR/needless_lifetimes.rs:262:9 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:265:9 + --> $DIR/needless_lifetimes.rs:266:9 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:278:9 + --> $DIR/needless_lifetimes.rs:279: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:310:5 + --> $DIR/needless_lifetimes.rs:311: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:319:5 + --> $DIR/needless_lifetimes.rs:320: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:331:5 + --> $DIR/needless_lifetimes.rs:332: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:346:5 + --> $DIR/needless_lifetimes.rs:347: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:359:5 + --> $DIR/needless_lifetimes.rs:360: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:362:5 + --> $DIR/needless_lifetimes.rs:363:5 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&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:384:9 + --> $DIR/needless_lifetimes.rs:385:9 | LL | fn implicit<'a>(&'a self) -> &'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:387:9 + --> $DIR/needless_lifetimes.rs:388:9 | LL | fn implicit_mut<'a>(&'a mut self) -> &'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:398:9 + --> $DIR/needless_lifetimes.rs:399:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'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:404:9 + --> $DIR/needless_lifetimes.rs:405:9 | LL | fn implicit<'a>(&'a self) -> &'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:405:9 + --> $DIR/needless_lifetimes.rs:406:9 | LL | fn implicit_provided<'a>(&'a self) -> &'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:414:9 + --> $DIR/needless_lifetimes.rs:415:9 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'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:415:9 + --> $DIR/needless_lifetimes.rs:416:9 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index 603d438d55889..7c828430b7853 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -53,7 +53,7 @@ fn test_closure() { } fn test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } fn test_void_fun() { @@ -175,7 +175,7 @@ async fn async_test_closure() { } async fn async_test_macro_call() -> i32 { - return the_answer!(); + the_answer!() } async fn async_test_void_fun() { @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + format!("Hello {}", "world!") +} + fn main() {} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index c6c8cb9ec1520..fe82af00e6750 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -223,4 +223,10 @@ fn let_else() { let Some(1) = Some(1) else { return }; } +fn needless_return_macro() -> String { + let _ = "foo"; + let _ = "bar"; + return format!("Hello {}", "world!"); +} + fn main() {} diff --git a/tests/ui/needless_return.stderr b/tests/ui/needless_return.stderr index 5bc787c56a65b..4c8be47b025e2 100644 --- a/tests/ui/needless_return.stderr +++ b/tests/ui/needless_return.stderr @@ -48,6 +48,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:56:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:60:5 | @@ -168,6 +174,12 @@ error: unneeded `return` statement LL | let _ = || return true; | ^^^^^^^^^^^ help: remove `return`: `true` +error: unneeded `return` statement + --> $DIR/needless_return.rs:178:5 + | +LL | return the_answer!(); + | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()` + error: unneeded `return` statement --> $DIR/needless_return.rs:182:5 | @@ -204,5 +216,11 @@ error: unneeded `return` statement LL | return String::new(); | ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()` -error: aborting due to 34 previous errors +error: unneeded `return` statement + --> $DIR/needless_return.rs:229:5 + | +LL | return format!("Hello {}", "world!"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")` + +error: aborting due to 37 previous errors diff --git a/tests/ui/no_effect_replace.rs b/tests/ui/no_effect_replace.rs new file mode 100644 index 0000000000000..ad17d53f78906 --- /dev/null +++ b/tests/ui/no_effect_replace.rs @@ -0,0 +1,51 @@ +#![warn(clippy::no_effect_replace)] + +fn main() { + let _ = "12345".replace('1', "1"); + let _ = "12345".replace("12", "12"); + let _ = String::new().replace("12", "12"); + + let _ = "12345".replacen('1', "1", 1); + let _ = "12345".replacen("12", "12", 1); + let _ = String::new().replacen("12", "12", 1); + + let _ = "12345".replace("12", "22"); + let _ = "12345".replacen("12", "22", 1); + + let mut x = X::default(); + let _ = "hello".replace(&x.f(), &x.f()); + let _ = "hello".replace(&x.f(), &x.ff()); + + let _ = "hello".replace(&y(), &y()); + let _ = "hello".replace(&y(), &z()); + + let _ = Replaceme.replace("a", "a"); +} + +#[derive(Default)] +struct X {} + +impl X { + fn f(&mut self) -> String { + "he".to_string() + } + + fn ff(&mut self) -> String { + "hh".to_string() + } +} + +fn y() -> String { + "he".to_string() +} + +fn z() -> String { + "hh".to_string() +} + +struct Replaceme; +impl Replaceme { + pub fn replace(&mut self, a: &str, b: &str) -> Self { + Self + } +} diff --git a/tests/ui/no_effect_replace.stderr b/tests/ui/no_effect_replace.stderr new file mode 100644 index 0000000000000..53a28aa73b707 --- /dev/null +++ b/tests/ui/no_effect_replace.stderr @@ -0,0 +1,52 @@ +error: replacing text with itself + --> $DIR/no_effect_replace.rs:4:13 + | +LL | let _ = "12345".replace('1', "1"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::no-effect-replace` implied by `-D warnings` + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:5:13 + | +LL | let _ = "12345".replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:6:13 + | +LL | let _ = String::new().replace("12", "12"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:8:13 + | +LL | let _ = "12345".replacen('1', "1", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:9:13 + | +LL | let _ = "12345".replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:10:13 + | +LL | let _ = String::new().replacen("12", "12", 1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:16:13 + | +LL | let _ = "hello".replace(&x.f(), &x.f()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: replacing text with itself + --> $DIR/no_effect_replace.rs:19:13 + | +LL | let _ = "hello".replace(&y(), &y()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/nonminimal_bool_methods.fixed b/tests/ui/nonminimal_bool_methods.fixed new file mode 100644 index 0000000000000..aad44089de499 --- /dev/null +++ b/tests/ui/nonminimal_bool_methods.fixed @@ -0,0 +1,111 @@ +// run-rustfix +#![allow(unused, clippy::diverging_sub_expression)] +#![warn(clippy::nonminimal_bool)] + +fn methods_with_negation() { + let a: Option = unimplemented!(); + let b: Result = unimplemented!(); + let _ = a.is_some(); + let _ = a.is_none(); + let _ = a.is_none(); + let _ = a.is_some(); + let _ = b.is_err(); + let _ = b.is_ok(); + let _ = b.is_ok(); + let _ = b.is_err(); + let c = false; + let _ = a.is_none() || c; + let _ = a.is_none() && c; + let _ = !(!c ^ c) || a.is_none(); + let _ = (!c ^ c) || a.is_none(); + let _ = !c ^ c || a.is_none(); +} + +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not +// any other types like the following. +enum CustomResultOk { + Ok, + Err(E), +} +enum CustomResultErr { + Ok, + Err(E), +} +enum CustomSomeSome { + Some(T), + None, +} +enum CustomSomeNone { + Some(T), + None, +} + +impl CustomResultOk { + pub fn is_ok(&self) -> bool { + true + } +} + +impl CustomResultErr { + pub fn is_err(&self) -> bool { + true + } +} + +impl CustomSomeSome { + pub fn is_some(&self) -> bool { + true + } +} + +impl CustomSomeNone { + pub fn is_none(&self) -> bool { + true + } +} + +fn dont_warn_for_custom_methods_with_negation() { + let res = CustomResultOk::Err("Error"); + // Should not warn and suggest 'is_err()' because the type does not + // implement is_err(). + if !res.is_ok() {} + + let res = CustomResultErr::Err("Error"); + // Should not warn and suggest 'is_ok()' because the type does not + // implement is_ok(). + if !res.is_err() {} + + let res = CustomSomeSome::Some("thing"); + // Should not warn and suggest 'is_none()' because the type does not + // implement is_none(). + if !res.is_some() {} + + let res = CustomSomeNone::Some("thing"); + // Should not warn and suggest 'is_some()' because the type does not + // implement is_some(). + if !res.is_none() {} +} + +// Only Built-in Result and Some types should suggest the negated alternative +fn warn_for_built_in_methods_with_negation() { + let res: Result = Ok(1); + if res.is_err() {} + if res.is_ok() {} + + let res = Some(1); + if res.is_none() {} + if res.is_some() {} +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn dont_warn_for_negated_partial_ord_comparison() { + let a: f64 = unimplemented!(); + let b: f64 = unimplemented!(); + let _ = !(a < b); + let _ = !(a <= b); + let _ = !(a > b); + let _ = !(a >= b); +} + +fn main() {} diff --git a/tests/ui/nonminimal_bool_methods.rs b/tests/ui/nonminimal_bool_methods.rs index d0a289b7ea43e..b9074da842706 100644 --- a/tests/ui/nonminimal_bool_methods.rs +++ b/tests/ui/nonminimal_bool_methods.rs @@ -1,3 +1,4 @@ +// run-rustfix #![allow(unused, clippy::diverging_sub_expression)] #![warn(clippy::nonminimal_bool)] diff --git a/tests/ui/nonminimal_bool_methods.stderr b/tests/ui/nonminimal_bool_methods.stderr index a2df889d62302..21b84db858909 100644 --- a/tests/ui/nonminimal_bool_methods.stderr +++ b/tests/ui/nonminimal_bool_methods.stderr @@ -1,5 +1,5 @@ error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:8:13 + --> $DIR/nonminimal_bool_methods.rs:9:13 | LL | let _ = !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` @@ -7,73 +7,73 @@ LL | let _ = !a.is_some(); = note: `-D clippy::nonminimal-bool` implied by `-D warnings` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:10:13 + --> $DIR/nonminimal_bool_methods.rs:11:13 | LL | let _ = !a.is_none(); | ^^^^^^^^^^^^ help: try: `a.is_some()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:12:13 + --> $DIR/nonminimal_bool_methods.rs:13:13 | LL | let _ = !b.is_err(); | ^^^^^^^^^^^ help: try: `b.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:14:13 + --> $DIR/nonminimal_bool_methods.rs:15:13 | LL | let _ = !b.is_ok(); | ^^^^^^^^^^ help: try: `b.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:16:13 + --> $DIR/nonminimal_bool_methods.rs:17:13 | LL | let _ = !(a.is_some() && !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:17:13 + --> $DIR/nonminimal_bool_methods.rs:18:13 | LL | let _ = !(a.is_some() || !c); | ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:18:26 + --> $DIR/nonminimal_bool_methods.rs:19:26 | LL | let _ = !(!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:19:25 + --> $DIR/nonminimal_bool_methods.rs:20:25 | LL | let _ = (!c ^ c) || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:20:23 + --> $DIR/nonminimal_bool_methods.rs:21:23 | LL | let _ = !c ^ c || !a.is_some(); | ^^^^^^^^^^^^ help: try: `a.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:92:8 + --> $DIR/nonminimal_bool_methods.rs:93:8 | LL | if !res.is_ok() {} | ^^^^^^^^^^^^ help: try: `res.is_err()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:93:8 + --> $DIR/nonminimal_bool_methods.rs:94:8 | LL | if !res.is_err() {} | ^^^^^^^^^^^^^ help: try: `res.is_ok()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:96:8 + --> $DIR/nonminimal_bool_methods.rs:97:8 | LL | if !res.is_some() {} | ^^^^^^^^^^^^^^ help: try: `res.is_none()` error: this boolean expression can be simplified - --> $DIR/nonminimal_bool_methods.rs:97:8 + --> $DIR/nonminimal_bool_methods.rs:98:8 | LL | if !res.is_none() {} | ^^^^^^^^^^^^^^ help: try: `res.is_some()` diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index f497719971174..85d021b2f25e2 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -49,6 +49,13 @@ fn main() { x >= 10 && x <= -10; (-3. ..=3.).contains(&y); y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (0..=10).contains(&x) && (0..=10).contains(&z); + !(0..10).contains(&x) || !(0..10).contains(&z); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 9e2180b0c9944..9a7a75dc13254 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -49,6 +49,13 @@ fn main() { x >= 10 && x <= -10; y >= -3. && y <= 3.; y >= 3. && y <= -3.; + + // Fix #8745 + let z = 42; + (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + // Make sure operators in parens don't give a breaking suggestion + ((x % 2 == 0) || (x < 0)) || (x >= 10); } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index 1817ee1715d17..936859db5a126 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -96,5 +96,29 @@ error: manual `RangeInclusive::contains` implementation LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` -error: aborting due to 16 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:30 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:55:5 + | +LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); + | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:29 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` + +error: manual `!Range::contains` implementation + --> $DIR/range_contains.rs:56:5 + | +LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` + +error: aborting due to 20 previous errors diff --git a/tests/ui/rc_buffer.fixed b/tests/ui/rc_buffer.fixed new file mode 100644 index 0000000000000..8910c01b1fcf9 --- /dev/null +++ b/tests/ui/rc_buffer.fixed @@ -0,0 +1,28 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::cell::RefCell; +use std::ffi::OsString; +use std::path::PathBuf; +use std::rc::Rc; + +struct S { + // triggers lint + bad1: Rc, + bad2: Rc, + bad3: Rc<[u8]>, + bad4: Rc, + // does not trigger lint + good1: Rc>, +} + +// triggers lint +fn func_bad1(_: Rc) {} +fn func_bad2(_: Rc) {} +fn func_bad3(_: Rc<[u8]>) {} +fn func_bad4(_: Rc) {} +// does not trigger lint +fn func_good1(_: Rc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer.rs b/tests/ui/rc_buffer.rs index 1fa9864393687..1e63a43262ec1 100644 --- a/tests/ui/rc_buffer.rs +++ b/tests/ui/rc_buffer.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::cell::RefCell; use std::ffi::OsString; diff --git a/tests/ui/rc_buffer.stderr b/tests/ui/rc_buffer.stderr index e4cc169af07b9..9ed028e3df41b 100644 --- a/tests/ui/rc_buffer.stderr +++ b/tests/ui/rc_buffer.stderr @@ -1,5 +1,5 @@ error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:10:11 + --> $DIR/rc_buffer.rs:12:11 | LL | bad1: Rc, | ^^^^^^^^^^ help: try: `Rc` @@ -7,43 +7,43 @@ LL | bad1: Rc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:11:11 + --> $DIR/rc_buffer.rs:13:11 | LL | bad2: Rc, | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:12:11 + --> $DIR/rc_buffer.rs:14:11 | LL | bad3: Rc>, | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:13:11 + --> $DIR/rc_buffer.rs:15:11 | LL | bad4: Rc, | ^^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:19:17 + --> $DIR/rc_buffer.rs:21:17 | LL | fn func_bad1(_: Rc) {} | ^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:20:17 + --> $DIR/rc_buffer.rs:22:17 | LL | fn func_bad2(_: Rc) {} | ^^^^^^^^^^^ help: try: `Rc` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:21:17 + --> $DIR/rc_buffer.rs:23:17 | LL | fn func_bad3(_: Rc>) {} | ^^^^^^^^^^^ help: try: `Rc<[u8]>` error: usage of `Rc` when T is a buffer type - --> $DIR/rc_buffer.rs:22:17 + --> $DIR/rc_buffer.rs:24:17 | LL | fn func_bad4(_: Rc) {} | ^^^^^^^^^^^^ help: try: `Rc` diff --git a/tests/ui/rc_buffer_arc.fixed b/tests/ui/rc_buffer_arc.fixed new file mode 100644 index 0000000000000..13dd6f5fcd186 --- /dev/null +++ b/tests/ui/rc_buffer_arc.fixed @@ -0,0 +1,27 @@ +// run-rustfix +#![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] + +use std::ffi::OsString; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; + +struct S { + // triggers lint + bad1: Arc, + bad2: Arc, + bad3: Arc<[u8]>, + bad4: Arc, + // does not trigger lint + good1: Arc>, +} + +// triggers lint +fn func_bad1(_: Arc) {} +fn func_bad2(_: Arc) {} +fn func_bad3(_: Arc<[u8]>) {} +fn func_bad4(_: Arc) {} +// does not trigger lint +fn func_good1(_: Arc>) {} + +fn main() {} diff --git a/tests/ui/rc_buffer_arc.rs b/tests/ui/rc_buffer_arc.rs index 5d586584817bd..1a521bfeb7c86 100644 --- a/tests/ui/rc_buffer_arc.rs +++ b/tests/ui/rc_buffer_arc.rs @@ -1,4 +1,6 @@ +// run-rustfix #![warn(clippy::rc_buffer)] +#![allow(dead_code, unused_imports)] use std::ffi::OsString; use std::path::PathBuf; diff --git a/tests/ui/rc_buffer_arc.stderr b/tests/ui/rc_buffer_arc.stderr index 8252270d2ac74..911feea73529d 100644 --- a/tests/ui/rc_buffer_arc.stderr +++ b/tests/ui/rc_buffer_arc.stderr @@ -1,5 +1,5 @@ error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:9:11 + --> $DIR/rc_buffer_arc.rs:11:11 | LL | bad1: Arc, | ^^^^^^^^^^^ help: try: `Arc` @@ -7,43 +7,43 @@ LL | bad1: Arc, = note: `-D clippy::rc-buffer` implied by `-D warnings` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:10:11 + --> $DIR/rc_buffer_arc.rs:12:11 | LL | bad2: Arc, | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:11:11 + --> $DIR/rc_buffer_arc.rs:13:11 | LL | bad3: Arc>, | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:12:11 + --> $DIR/rc_buffer_arc.rs:14:11 | LL | bad4: Arc, | ^^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:18:17 + --> $DIR/rc_buffer_arc.rs:20:17 | LL | fn func_bad1(_: Arc) {} | ^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:19:17 + --> $DIR/rc_buffer_arc.rs:21:17 | LL | fn func_bad2(_: Arc) {} | ^^^^^^^^^^^^ help: try: `Arc` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:20:17 + --> $DIR/rc_buffer_arc.rs:22:17 | LL | fn func_bad3(_: Arc>) {} | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` error: usage of `Arc` when T is a buffer type - --> $DIR/rc_buffer_arc.rs:21:17 + --> $DIR/rc_buffer_arc.rs:23:17 | LL | fn func_bad4(_: Arc) {} | ^^^^^^^^^^^^^ help: try: `Arc` diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index ce84186c8e300..cd7d91e120650 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -1,4 +1,4 @@ -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:7:13 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:15:21 | LL | let v = vec![Arc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = Arc::new("x".to_string()); +LL + let data = Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:21:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Arc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/arc.rs:30:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index 0f5cc0cf98fea..fe861afe05491 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -1,4 +1,4 @@ -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:8:13 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -10,19 +10,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:16:21 | LL | let v = vec![Rc::new("x".to_string()); 2]; @@ -33,19 +33,19 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = Rc::new("x".to_string()); +LL + let data = Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:22:13 | LL | let v = vec![ @@ -75,7 +75,7 @@ LL + vec![data; 2] LL ~ }; | -error: calling `Rc::new` in `vec![elem; len]` +error: initializing a reference-counted pointer in `vec![elem; len]` --> $DIR/rc.rs:31:14 | LL | let v1 = vec![ diff --git a/tests/ui/rc_clone_in_vec_init/weak.rs b/tests/ui/rc_clone_in_vec_init/weak.rs new file mode 100644 index 0000000000000..693c9b553c562 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.rs @@ -0,0 +1,83 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::{Rc, Weak as UnSyncWeak}; +use std::sync::{Arc, Mutex, Weak as SyncWeak}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![SyncWeak::::new(); 2]; + let v2 = vec![UnSyncWeak::::new(); 2]; + + let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; +} + +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + } + } +} + +fn should_warn_complex_case() { + let v = vec![ + Arc::downgrade(&Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; + + let v1 = vec![ + Rc::downgrade(&Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + }))); + 2 + ]; +} + +fn should_not_warn_custom_weak() { + #[derive(Clone)] + struct Weak; + + impl Weak { + fn new() -> Self { + Weak + } + } + + let v = vec![Weak::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_weak() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(Arc::downgrade(&Arc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; + let v3 = vec![ + Box::new(Rc::downgrade(&Rc::new({ + let y = 3; + dbg!(y); + y + }))); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::downgrade(&Arc::new("x".to_string()))]; + let v = vec![Rc::downgrade(&Rc::new("x".to_string()))]; +} diff --git a/tests/ui/rc_clone_in_vec_init/weak.stderr b/tests/ui/rc_clone_in_vec_init/weak.stderr new file mode 100644 index 0000000000000..4a21946ccdfa8 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/weak.stderr @@ -0,0 +1,201 @@ +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:8:13 + | +LL | let v = vec![SyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(SyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = SyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:9:14 + | +LL | let v2 = vec![UnSyncWeak::::new(); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(UnSyncWeak::::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = UnSyncWeak::::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:11:13 + | +LL | let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:12:13 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:20:21 + | +LL | let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:21:22 + | +LL | let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v2 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v2 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:27:13 + | +LL | let v = vec![ + | _____________^ +LL | | Arc::downgrade(&Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: initializing a reference-counted pointer in `vec![elem; len]` + --> $DIR/weak.rs:36:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::downgrade(&Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Weak` instance +help: consider initializing each `Weak` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::downgrade(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Weak` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::downgrade(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 8 previous errors + diff --git a/tests/ui/recursive_format_impl.rs b/tests/ui/recursive_format_impl.rs index f72fc77ab9977..cb6ba36b14c80 100644 --- a/tests/ui/recursive_format_impl.rs +++ b/tests/ui/recursive_format_impl.rs @@ -2,7 +2,8 @@ #![allow( clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args, - clippy::deref_addrof + clippy::deref_addrof, + clippy::borrow_deref_ref )] use std::fmt; diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 1a717ac92d8b7..84ce69df56696 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -1,5 +1,5 @@ error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion - --> $DIR/recursive_format_impl.rs:29:25 + --> $DIR/recursive_format_impl.rs:30:25 | LL | write!(f, "{}", self.to_string()) | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | write!(f, "{}", self.to_string()) = note: `-D clippy::recursive-format-impl` implied by `-D warnings` error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:73:9 + --> $DIR/recursive_format_impl.rs:74:9 | LL | write!(f, "{}", self) | ^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | write!(f, "{}", self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:82:9 + --> $DIR/recursive_format_impl.rs:83:9 | LL | write!(f, "{}", &self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | write!(f, "{}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:88:9 + --> $DIR/recursive_format_impl.rs:89:9 | LL | write!(f, "{:?}", &self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | write!(f, "{:?}", &self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:97:9 + --> $DIR/recursive_format_impl.rs:98:9 | LL | write!(f, "{}", &&&self) | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | write!(f, "{}", &&&self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:171:9 + --> $DIR/recursive_format_impl.rs:172:9 | LL | write!(f, "{}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | write!(f, "{}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Debug` in `impl Debug` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:177:9 + --> $DIR/recursive_format_impl.rs:178:9 | LL | write!(f, "{:?}", &*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | write!(f, "{:?}", &*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:193:9 + --> $DIR/recursive_format_impl.rs:194:9 | LL | write!(f, "{}", *self) | ^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | write!(f, "{}", *self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:209:9 + --> $DIR/recursive_format_impl.rs:210:9 | LL | write!(f, "{}", **&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | write!(f, "{}", **&&*self) = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) error: using `self` as `Display` in `impl Display` will cause infinite recursion - --> $DIR/recursive_format_impl.rs:225:9 + --> $DIR/recursive_format_impl.rs:226:9 | LL | write!(f, "{}", &&**&&*self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index a394ef8f25c67..1fa9fc749a96a 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -88,4 +88,11 @@ pub async fn foo2(_a: i32, _b: i64) { let _b = _a; } +fn ice_8748() { + let _ = [0; { + let x = 1; + if let Some(x) = Some(1) { x } else { 1 } + }]; +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs index f83a6dd0eb288..4347610f393f3 100644 --- a/tests/ui/significant_drop_in_scrutinee.rs +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -7,8 +7,10 @@ #![allow(unused_assignments)] #![allow(dead_code)] +use std::num::ParseIntError; use std::ops::Deref; use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::RwLock; use std::sync::{Mutex, MutexGuard}; struct State {} @@ -552,4 +554,41 @@ fn should_not_cause_stack_overflow() { } } +fn should_not_produce_lint_for_try_desugar() -> Result { + // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope + // for this lint + let rwlock = RwLock::new("1".to_string()); + let result = rwlock.read().unwrap().parse::()?; + println!("{}", result); + rwlock.write().unwrap().push('2'); + Ok(result) +} + +struct ResultReturner { + s: String, +} + +impl ResultReturner { + fn to_number(&self) -> Result { + self.s.parse::() + } +} + +fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() { + let rwlock = RwLock::::new(ResultReturner { s: "1".to_string() }); + match rwlock.read().unwrap().to_number() { + Ok(n) => println!("Converted to number: {}", n), + Err(e) => println!("Could not convert {} to number", e), + }; +} + +fn should_trigger_lint_for_read_write_lock_for_loop() { + // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is + // designed to look for. + let rwlock = RwLock::>::new(vec!["1".to_string()]); + for s in rwlock.read().unwrap().iter() { + println!("{}", s); + } +} + fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr index af16056498505..303f3c1df033c 100644 --- a/tests/ui/significant_drop_in_scrutinee.stderr +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -1,5 +1,5 @@ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:57:11 + --> $DIR/significant_drop_in_scrutinee.rs:59:11 | LL | match mutex.lock().unwrap().foo() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:130:11 + --> $DIR/significant_drop_in_scrutinee.rs:132:11 | LL | match s.lock_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:151:11 + --> $DIR/significant_drop_in_scrutinee.rs:153:11 | LL | match s.lock_m_m().get_the_value() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:199:11 + --> $DIR/significant_drop_in_scrutinee.rs:201:11 | LL | match counter.temp_increment().len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,7 +48,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:222:16 + --> $DIR/significant_drop_in_scrutinee.rs:224:16 | LL | match (mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL ~ match (value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:231:22 + --> $DIR/significant_drop_in_scrutinee.rs:233:22 | LL | match (true, mutex1.lock().unwrap().s.len(), true) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -72,7 +72,7 @@ LL ~ match (true, value, true) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:16 + --> $DIR/significant_drop_in_scrutinee.rs:243:16 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:241:54 + --> $DIR/significant_drop_in_scrutinee.rs:243:54 | LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -96,19 +96,19 @@ LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:252:15 + --> $DIR/significant_drop_in_scrutinee.rs:254:15 | LL | match mutex3.lock().unwrap().s.as_str() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:262:22 + --> $DIR/significant_drop_in_scrutinee.rs:264:22 | LL | match (true, mutex3.lock().unwrap().s.as_str()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:281:11 + --> $DIR/significant_drop_in_scrutinee.rs:283:11 | LL | match mutex.lock().unwrap().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:288:11 + --> $DIR/significant_drop_in_scrutinee.rs:290:11 | LL | match 1 < mutex.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:306:11 + --> $DIR/significant_drop_in_scrutinee.rs:308:11 | LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:317:11 + --> $DIR/significant_drop_in_scrutinee.rs:319:11 | LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -156,7 +156,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:352:11 + --> $DIR/significant_drop_in_scrutinee.rs:354:11 | LL | match get_mutex_guard().s.len() > 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -168,7 +168,7 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:369:11 + --> $DIR/significant_drop_in_scrutinee.rs:371:11 | LL | match match i { | ___________^ @@ -191,7 +191,7 @@ LL + .len() ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:395:11 + --> $DIR/significant_drop_in_scrutinee.rs:397:11 | LL | match if i > 1 { | ___________^ @@ -214,7 +214,7 @@ LL + .s ... error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:449:11 + --> $DIR/significant_drop_in_scrutinee.rs:451:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,13 +226,13 @@ LL ~ match value { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:477:11 + --> $DIR/significant_drop_in_scrutinee.rs:479:11 | LL | match s.lock().deref().deref() { | ^^^^^^^^^^^^^^^^^^^^^^^^ error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:496:11 + --> $DIR/significant_drop_in_scrutinee.rs:498:11 | LL | match mutex.lock().unwrap().i = i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -244,7 +244,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:502:11 + --> $DIR/significant_drop_in_scrutinee.rs:504:11 | LL | match i = mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -256,7 +256,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:508:11 + --> $DIR/significant_drop_in_scrutinee.rs:510:11 | LL | match mutex.lock().unwrap().i += 1 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -268,7 +268,7 @@ LL ~ match () { | error: temporary with significant drop in match scrutinee - --> $DIR/significant_drop_in_scrutinee.rs:514:11 + --> $DIR/significant_drop_in_scrutinee.rs:516:11 | LL | match i += mutex.lock().unwrap().i { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -279,5 +279,17 @@ LL ~ i += mutex.lock().unwrap().i; LL ~ match () { | -error: aborting due to 23 previous errors +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:579:11 + | +LL | match rwlock.read().unwrap().to_number() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in for loop + --> $DIR/significant_drop_in_scrutinee.rs:589:14 + | +LL | for s in rwlock.read().unwrap().iter() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 25 previous errors diff --git a/tests/ui/suspicious_operation_groupings.fixed b/tests/ui/suspicious_operation_groupings.fixed new file mode 100644 index 0000000000000..ede8a39fed718 --- /dev/null +++ b/tests/ui/suspicious_operation_groupings.fixed @@ -0,0 +1,209 @@ +// run-rustfix +#![warn(clippy::suspicious_operation_groupings)] +#![allow(dead_code, unused_parens, clippy::eq_op)] + +struct Vec3 { + x: f64, + y: f64, + z: f64, +} + +impl Eq for Vec3 {} + +impl PartialEq for Vec3 { + fn eq(&self, other: &Self) -> bool { + // This should trigger the lint because `self.x` is compared to `other.y` + self.x == other.x && self.y == other.y && self.z == other.z + } +} + +struct S { + a: i32, + b: i32, + c: i32, + d: i32, +} + +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool { + // There's no `s1.b` + s1.a < s2.a && s1.b < s2.b +} + +struct SaOnly { + a: i32, +} + +impl S { + fn a(&self) -> i32 { + 0 + } +} + +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool { + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1.a() < s1.b +} + +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool { + macro_rules! s1 { + () => { + S { + a: 1, + b: 1, + c: 1, + d: 1, + } + }; + } + + // This is superficially similar to `buggy_ab_cmp`, but we should not suggest + // `s2.b` since that is invalid. + s1.a < s2.a && s1!().a < s1.b +} + +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool { + // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid + s1.a < s2.a && s1.b < s1.b +} + +fn permissable(s1: &S, s2: &S) -> bool { + // Something like this seems like it might actually be what is desired. + s1.a == s2.b +} + +fn non_boolean_operators(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d +} + +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 { + // There's no `s2.a` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + s1.a * s2.a + s1.b * s2.b + s1.c * s2.c +} + +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) { + ( + s1.b * s2.c - s1.c * s2.b, + s1.c * s2.a - s1.a * s2.c, + s1.a * s2.b - s1.b * s2.a, + ) +} + +fn outer_parens_simple(s1: &S, s2: &S) -> i32 { + // There's no `s2.b` + (s1.a * s2.a + s1.b * s2.b) +} + +fn outer_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d) +} + +fn inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d) +} + +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)) +} + +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.c) + (s1.d * s2.d))) +} + +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c)) + (s1.d * s2.d)) +} + +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d))) +} + +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + (s1.a * s2.a + s1.b * s2.b) / 2 +} + +fn inside_function_call(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + i32::swap_bytes(s1.a * s2.a + s1.b * s2.b) +} + +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.b > 0 && s1.c == s2.c && s1.d == s2.d +} + +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool { + // There's no `s1.c` + s1.a > 0 && s1.c == s2.c && s1.b > 0 && s1.d == s2.d +} + +struct Nested { + inner: ((i32,), (i32,), (i32,)), +} + +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.2.0` + (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +// `eq_op` should catch this one. +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool { + // There's no `n2.inner.0.0` + (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0 +} + +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool { + if strict { + s1.a < s2.a && s1.b < s2.b + } else { + // There's no `s1.b` in this subexpression + s1.a <= s2.a && s1.b <= s2.b + } +} + +fn inside_an_if_statement(s1: &mut S, s2: &S) { + // There's no `s1.b` + if s1.a < s2.a && s1.b < s2.b { + s1.c = s2.c; + } +} + +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 { + // There's no `s2.c` + -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.c) + -(-s1.d * -s2.d))) +} + +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 { + // There's no `s1.b` + -(if -s1.a < -s2.a && -s1.b < -s2.b { s1.c } else { s2.a }) +} + +fn main() {} diff --git a/tests/ui/suspicious_operation_groupings.rs b/tests/ui/suspicious_operation_groupings.rs index 3201d5de0f356..26ce97bb37f84 100644 --- a/tests/ui/suspicious_operation_groupings.rs +++ b/tests/ui/suspicious_operation_groupings.rs @@ -1,5 +1,6 @@ +// run-rustfix #![warn(clippy::suspicious_operation_groupings)] -#![allow(clippy::eq_op)] +#![allow(dead_code, unused_parens, clippy::eq_op)] struct Vec3 { x: f64, diff --git a/tests/ui/suspicious_operation_groupings.stderr b/tests/ui/suspicious_operation_groupings.stderr index baf9bc74b000e..29f229245fed3 100644 --- a/tests/ui/suspicious_operation_groupings.stderr +++ b/tests/ui/suspicious_operation_groupings.stderr @@ -1,5 +1,5 @@ error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:15:9 + --> $DIR/suspicious_operation_groupings.rs:16:9 | LL | self.x == other.y && self.y == other.y && self.z == other.z | ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x` @@ -7,151 +7,151 @@ LL | self.x == other.y && self.y == other.y && self.z == other.z = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:28:20 + --> $DIR/suspicious_operation_groupings.rs:29:20 | LL | s1.a < s2.a && s1.a < s2.b | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:76:33 + --> $DIR/suspicious_operation_groupings.rs:77:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:81:19 + --> $DIR/suspicious_operation_groupings.rs:82:19 | LL | s1.a * s2.a + s1.b * s2.c + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:86:19 + --> $DIR/suspicious_operation_groupings.rs:87:19 | LL | s1.a * s2.a + s2.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:91:19 + --> $DIR/suspicious_operation_groupings.rs:92:19 | LL | s1.a * s2.a + s1.b * s1.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:96:5 + --> $DIR/suspicious_operation_groupings.rs:97:5 | LL | s1.a * s1.a + s1.b * s2.b + s1.c * s2.c | ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:101:33 + --> $DIR/suspicious_operation_groupings.rs:102:33 | LL | s1.a * s2.a + s1.b * s2.b + s1.c * s1.c | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:114:20 + --> $DIR/suspicious_operation_groupings.rs:115:20 | LL | (s1.a * s2.a + s1.b * s1.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:119:34 + --> $DIR/suspicious_operation_groupings.rs:120:34 | LL | (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:124:38 + --> $DIR/suspicious_operation_groupings.rs:125:38 | LL | (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:129:39 + --> $DIR/suspicious_operation_groupings.rs:130:39 | LL | ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:134:42 + --> $DIR/suspicious_operation_groupings.rs:135:42 | LL | (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:139:40 + --> $DIR/suspicious_operation_groupings.rs:140:40 | LL | (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d)) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:144:40 + --> $DIR/suspicious_operation_groupings.rs:145:40 | LL | ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))) | ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:149:20 + --> $DIR/suspicious_operation_groupings.rs:150:20 | LL | (s1.a * s2.a + s2.b * s2.b) / 2 | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:154:35 + --> $DIR/suspicious_operation_groupings.rs:155:35 | LL | i32::swap_bytes(s1.a * s2.a + s2.b * s2.b) | ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:159:29 + --> $DIR/suspicious_operation_groupings.rs:160:29 | LL | s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:164:17 + --> $DIR/suspicious_operation_groupings.rs:165:17 | LL | s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d | ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:173:77 + --> $DIR/suspicious_operation_groupings.rs:174:77 | LL | (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:187:25 + --> $DIR/suspicious_operation_groupings.rs:188:25 | LL | s1.a <= s2.a && s1.a <= s2.b | ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:193:23 + --> $DIR/suspicious_operation_groupings.rs:194:23 | LL | if s1.a < s2.a && s1.a < s2.b { | ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:200:48 + --> $DIR/suspicious_operation_groupings.rs:201:48 | LL | -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d))) | ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c` error: this sequence of operators looks suspiciously like a bug - --> $DIR/suspicious_operation_groupings.rs:205:27 + --> $DIR/suspicious_operation_groupings.rs:206:27 | LL | -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a }) | ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b` diff --git a/tests/ui/swap_ptr_to_ref.fixed b/tests/ui/swap_ptr_to_ref.fixed new file mode 100644 index 0000000000000..596b6ee919bb4 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.fixed @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::ptr::swap(y, z); + core::ptr::swap(y, &mut x); + core::ptr::swap(&mut x, y); + core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.rs b/tests/ui/swap_ptr_to_ref.rs new file mode 100644 index 0000000000000..282f571211d95 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.rs @@ -0,0 +1,24 @@ +// run-rustfix + +#![warn(clippy::swap_ptr_to_ref)] + +use core::ptr::addr_of_mut; + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + let z: *mut _ = &mut x; + + unsafe { + core::mem::swap(&mut *y, &mut *z); + core::mem::swap(&mut *y, &mut x); + core::mem::swap(&mut x, &mut *y); + core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + } + + let y = &mut x; + let mut z = 0u32; + let z = &mut z; + + core::mem::swap(y, z); +} diff --git a/tests/ui/swap_ptr_to_ref.stderr b/tests/ui/swap_ptr_to_ref.stderr new file mode 100644 index 0000000000000..401ce070869a2 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref.stderr @@ -0,0 +1,28 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:13:9 + | +LL | core::mem::swap(&mut *y, &mut *z); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, z)` + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:14:9 + | +LL | core::mem::swap(&mut *y, &mut x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, &mut x)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:15:9 + | +LL | core::mem::swap(&mut x, &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(&mut x, y)` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref.rs:16:9 + | +LL | core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x))` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/swap_ptr_to_ref_unfixable.rs b/tests/ui/swap_ptr_to_ref_unfixable.rs new file mode 100644 index 0000000000000..66ea7c6529bd2 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.rs @@ -0,0 +1,18 @@ +#![warn(clippy::swap_ptr_to_ref)] + +macro_rules! addr_of_mut_to_ref { + ($e:expr) => { + &mut *core::ptr::addr_of_mut!($e) + }; +} + +fn main() { + let mut x = 0u32; + let y: *mut _ = &mut x; + + unsafe { + core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + } +} diff --git a/tests/ui/swap_ptr_to_ref_unfixable.stderr b/tests/ui/swap_ptr_to_ref_unfixable.stderr new file mode 100644 index 0000000000000..c261205d556e4 --- /dev/null +++ b/tests/ui/swap_ptr_to_ref_unfixable.stderr @@ -0,0 +1,22 @@ +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:14:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), &mut *y); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings` + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:15:9 + | +LL | core::mem::swap(&mut *y, addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: call to `core::mem::swap` with a parameter derived from a raw pointer + --> $DIR/swap_ptr_to_ref_unfixable.rs:16:9 + | +LL | core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 5b688ce4d644f..001c910239aff 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -16,7 +16,8 @@ fn my_vec() -> MyVec { #[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)] #[warn(clippy::useless_transmute)] unsafe fn _generic<'a, T, U: 'a>(t: &'a T) { - let _: &'a T = core::intrinsics::transmute(t); + // FIXME: should lint + // let _: &'a T = core::intrinsics::transmute(t); let _: &'a U = core::intrinsics::transmute(t); @@ -48,6 +49,22 @@ fn useless() { let _ = (1 + 1_usize) as *const usize; } + + unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 { + std::mem::transmute(x) + } + + unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator + 'a)) -> *const (dyn Iterator + 'b) { + std::mem::transmute(x) + } + + unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) { + std::mem::transmute(x) + } + + unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> { + std::mem::transmute(x) + } } struct Usize(usize); diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 72a386e8fa618..008b4a981d7e6 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,73 +1,67 @@ -error: transmute from a type (`&T`) to itself - --> $DIR/transmute.rs:19:20 - | -LL | let _: &'a T = core::intrinsics::transmute(t); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::useless-transmute` implied by `-D warnings` - error: transmute from a reference to a pointer - --> $DIR/transmute.rs:23:23 + --> $DIR/transmute.rs:24:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` + | + = note: `-D clippy::useless-transmute` implied by `-D warnings` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:25:21 + --> $DIR/transmute.rs:26:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:27:23 + --> $DIR/transmute.rs:28:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:33:27 + --> $DIR/transmute.rs:34:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:35:27 + --> $DIR/transmute.rs:36:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:37:27 + --> $DIR/transmute.rs:38:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:39:27 + --> $DIR/transmute.rs:40:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:42:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:43:31 + --> $DIR/transmute.rs:44:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:47:31 + --> $DIR/transmute.rs:48:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:62:24 + --> $DIR/transmute.rs:79:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,25 +69,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = note: `-D clippy::crosspointer-transmute` implied by `-D warnings` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:64:24 + --> $DIR/transmute.rs:81:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:66:31 + --> $DIR/transmute.rs:83:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:68:29 + --> $DIR/transmute.rs:85:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u32` to a `char` - --> $DIR/transmute.rs:74:28 + --> $DIR/transmute.rs:91:28 | LL | let _: char = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` @@ -101,13 +95,13 @@ LL | let _: char = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-char` implied by `-D warnings` error: transmute from a `i32` to a `char` - --> $DIR/transmute.rs:75:28 + --> $DIR/transmute.rs:92:28 | LL | let _: char = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:84:28 + --> $DIR/transmute.rs:101:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -115,7 +109,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:90:31 + --> $DIR/transmute.rs:107:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -123,25 +117,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = note: `-D clippy::transmute-int-to-float` implied by `-D warnings` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:91:31 + --> $DIR/transmute.rs:108:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:92:31 + --> $DIR/transmute.rs:109:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:93:31 + --> $DIR/transmute.rs:110:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:113:30 + --> $DIR/transmute.rs:130:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -149,85 +143,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:114:30 + --> $DIR/transmute.rs:131:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:115:31 + --> $DIR/transmute.rs:132:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:116:30 + --> $DIR/transmute.rs:133:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:117:30 + --> $DIR/transmute.rs:134:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:118:31 + --> $DIR/transmute.rs:135:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:119:30 + --> $DIR/transmute.rs:136:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:120:30 + --> $DIR/transmute.rs:137:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:125:30 + --> $DIR/transmute.rs:142:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:126:30 + --> $DIR/transmute.rs:143:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:127:31 + --> $DIR/transmute.rs:144:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:128:30 + --> $DIR/transmute.rs:145:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:129:30 + --> $DIR/transmute.rs:146:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:130:31 + --> $DIR/transmute.rs:147:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:140:28 + --> $DIR/transmute.rs:157:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -235,16 +229,16 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:141:32 + --> $DIR/transmute.rs:158:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:142:30 + --> $DIR/transmute.rs:159:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` -error: aborting due to 39 previous errors +error: aborting due to 38 previous errors diff --git a/tests/ui/transmute_undefined_repr.rs b/tests/ui/transmute_undefined_repr.rs index b06ed4a917376..ebcaa7a84cfb1 100644 --- a/tests/ui/transmute_undefined_repr.rs +++ b/tests/ui/transmute_undefined_repr.rs @@ -1,5 +1,5 @@ #![warn(clippy::transmute_undefined_repr)] -#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)] +#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)] use core::any::TypeId; use core::ffi::c_void; diff --git a/tests/ui/unicode.fixed b/tests/ui/unicode.fixed new file mode 100644 index 0000000000000..328cda369e11b --- /dev/null +++ b/tests/ui/unicode.fixed @@ -0,0 +1,36 @@ +// run-rustfix +#[warn(clippy::invisible_characters)] +fn zero() { + print!("Here >\u{200B}< is a ZWS, and \u{200B}another"); + print!("This\u{200B}is\u{200B}fine"); + print!("Here >\u{AD}< is a SHY, and \u{AD}another"); + print!("This\u{ad}is\u{ad}fine"); + print!("Here >\u{2060}< is a WJ, and \u{2060}another"); + print!("This\u{2060}is\u{2060}fine"); +} + +#[warn(clippy::unicode_not_nfc)] +fn canon() { + print!("̀àh?"); + print!("a\u{0300}h?"); // also ok +} + +#[warn(clippy::non_ascii_literal)] +fn uni() { + print!("\u{dc}ben!"); + print!("\u{DC}ben!"); // this is ok +} + +// issue 8013 +#[warn(clippy::non_ascii_literal)] +fn single_quote() { + const _EMPTY_BLOCK: char = '\u{25b1}'; + const _FULL_BLOCK: char = '\u{25b0}'; +} + +fn main() { + zero(); + uni(); + canon(); + single_quote(); +} diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index e0a4eadce33bd..7828d6bcbea7a 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -1,3 +1,4 @@ +// run-rustfix #[warn(clippy::invisible_characters)] fn zero() { print!("Here >​< is a ZWS, and ​another"); diff --git a/tests/ui/unicode.stderr b/tests/ui/unicode.stderr index 3f54e3880e747..01d3f3c029679 100644 --- a/tests/ui/unicode.stderr +++ b/tests/ui/unicode.stderr @@ -1,5 +1,5 @@ error: invisible character detected - --> $DIR/unicode.rs:3:12 + --> $DIR/unicode.rs:4: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"` @@ -7,19 +7,19 @@ LL | print!("Here >​< is a ZWS, and ​another"); = note: `-D clippy::invisible-characters` implied by `-D warnings` error: invisible character detected - --> $DIR/unicode.rs:5:12 + --> $DIR/unicode.rs:6: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 + --> $DIR/unicode.rs:8: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:13:12 + --> $DIR/unicode.rs:14:12 | LL | print!("̀àh?"); | ^^^^^ help: consider replacing the string with: `"̀àh?"` @@ -27,7 +27,7 @@ LL | print!("̀àh?"); = note: `-D clippy::unicode-not-nfc` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:19:12 + --> $DIR/unicode.rs:20:12 | LL | print!("Üben!"); | ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"` @@ -35,13 +35,13 @@ LL | print!("Üben!"); = note: `-D clippy::non-ascii-literal` implied by `-D warnings` error: literal non-ASCII character detected - --> $DIR/unicode.rs:26:32 + --> $DIR/unicode.rs:27:32 | LL | const _EMPTY_BLOCK: char = '▱'; | ^^^ help: consider replacing the string with: `'/u{25b1}'` error: literal non-ASCII character detected - --> $DIR/unicode.rs:27:31 + --> $DIR/unicode.rs:28:31 | LL | const _FULL_BLOCK: char = '▰'; | ^^^ help: consider replacing the string with: `'/u{25b0}'` diff --git a/tests/ui/unit_arg_empty_blocks.fixed b/tests/ui/unit_arg_empty_blocks.fixed new file mode 100644 index 0000000000000..9400e93cac839 --- /dev/null +++ b/tests/ui/unit_arg_empty_blocks.fixed @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::unit_arg)] +#![allow(clippy::no_effect, unused_must_use, unused_variables)] + +use std::fmt::Debug; + +fn foo(t: T) { + println!("{:?}", t); +} + +fn foo3(t1: T1, t2: T2, t3: T3) { + println!("{:?}, {:?}, {:?}", t1, t2, t3); +} + +fn bad() { + foo(()); + foo3((), 2, 2); + foo(0); + taking_two_units((), ()); + foo(0); + foo(1); + taking_three_units((), (), ()); +} + +fn taking_two_units(a: (), b: ()) {} +fn taking_three_units(a: (), b: (), c: ()) {} + +fn main() { + bad(); +} diff --git a/tests/ui/unit_arg_empty_blocks.rs b/tests/ui/unit_arg_empty_blocks.rs index 18a31eb3deee2..5f52b6c5315fd 100644 --- a/tests/ui/unit_arg_empty_blocks.rs +++ b/tests/ui/unit_arg_empty_blocks.rs @@ -1,3 +1,4 @@ +// run-rustfix #![warn(clippy::unit_arg)] #![allow(clippy::no_effect, unused_must_use, unused_variables)] diff --git a/tests/ui/unit_arg_empty_blocks.stderr b/tests/ui/unit_arg_empty_blocks.stderr index 39072c9a8cc0f..d35e931697d21 100644 --- a/tests/ui/unit_arg_empty_blocks.stderr +++ b/tests/ui/unit_arg_empty_blocks.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:15:5 + --> $DIR/unit_arg_empty_blocks.rs:16:5 | LL | foo({}); | ^^^^--^ @@ -9,7 +9,7 @@ LL | foo({}); = note: `-D clippy::unit-arg` implied by `-D warnings` error: passing a unit value to a function - --> $DIR/unit_arg_empty_blocks.rs:16:5 + --> $DIR/unit_arg_empty_blocks.rs:17:5 | LL | foo3({}, 2, 2); | ^^^^^--^^^^^^^ @@ -17,7 +17,7 @@ LL | foo3({}, 2, 2); | help: use a unit literal instead: `()` error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:17:5 + --> $DIR/unit_arg_empty_blocks.rs:18:5 | LL | taking_two_units({}, foo(0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL ~ taking_two_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg_empty_blocks.rs:18:5 + --> $DIR/unit_arg_empty_blocks.rs:19:5 | LL | taking_three_units({}, foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed new file mode 100644 index 0000000000000..b352b285c8626 --- /dev/null +++ b/tests/ui/unnecessary_cast.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::unnecessary_cast)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] + +#[rustfmt::skip] +fn main() { + // Test cast_unnecessary + 1_i32; + 1_f32; + false; + &1i32 as &i32; + + -1_i32; + - 1_i32; + -1_f32; + 1_i32; + 1_f32; + + // macro version + macro_rules! foo { + ($a:ident, $b:ident) => { + #[allow(unused)] + pub fn $a() -> $b { + 1 as $b + } + }; + } + foo!(a, i32); + foo!(b, f32); + foo!(c, f64); + + // do not lint cast to cfg-dependant type + 1 as std::os::raw::c_char; + + // do not lint cast to alias type + 1 as I32Alias; + &1 as &I32Alias; +} + +type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100_f32; + 100_f64; + 100_f64; + let _ = -100_f32; + let _ = -100_f64; + let _ = -100_f64; + 100_f32; + 100_f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1_u32; + 0x10_i32; + 0b10_usize; + 0o73_u16; + 1_000_000_000_u32; + + 1.0_f64; + 0.5_f32; + + 1.0 as u16; + + let _ = -1_i32; + let _ = -1.0_f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 62c3e9636866a..6c8cc3effe8fe 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -1,5 +1,12 @@ +// run-rustfix #![warn(clippy::unnecessary_cast)] -#![allow(clippy::no_effect)] +#![allow( + unused_must_use, + clippy::borrow_as_ptr, + clippy::no_effect, + clippy::nonstandard_macro_braces, + clippy::unnecessary_operation +)] #[rustfmt::skip] fn main() { @@ -37,3 +44,48 @@ fn main() { } type I32Alias = i32; + +mod fixable { + #![allow(dead_code)] + + fn main() { + // casting integer literal to float is unnecessary + 100 as f32; + 100 as f64; + 100_i32 as f64; + let _ = -100 as f32; + let _ = -100 as f64; + let _ = -100_i32 as f64; + 100. as f32; + 100. as f64; + // Should not trigger + #[rustfmt::skip] + let v = vec!(1); + &v as &[i32]; + 0x10 as f32; + 0o10 as f32; + 0b10 as f32; + 0x11 as f64; + 0o11 as f64; + 0b11 as f64; + + 1 as u32; + 0x10 as i32; + 0b10 as usize; + 0o73 as u16; + 1_000_000_000 as u32; + + 1.0 as f64; + 0.5 as f32; + + 1.0 as u16; + + let _ = -1 as i32; + let _ = -1.0 as f32; + + let _ = 1 as I32Alias; + let _ = &1 as &I32Alias; + } + + type I32Alias = i32; +} diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index a5a93c6110c6a..bad45f0025b22 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:7:5 + --> $DIR/unnecessary_cast.rs:14:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` @@ -7,46 +7,148 @@ LL | 1i32 as i32; = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:8:5 + --> $DIR/unnecessary_cast.rs:15:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:9:5 + --> $DIR/unnecessary_cast.rs:16:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:12:5 + --> $DIR/unnecessary_cast.rs:19:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:13:5 + --> $DIR/unnecessary_cast.rs:20:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:14:5 + --> $DIR/unnecessary_cast.rs:21:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:15:5 + --> $DIR/unnecessary_cast.rs:22:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:16:5 + --> $DIR/unnecessary_cast.rs:23:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` -error: aborting due to 8 previous errors +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:53:9 + | +LL | 100 as f32; + | ^^^^^^^^^^ help: try: `100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:54:9 + | +LL | 100 as f64; + | ^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:55:9 + | +LL | 100_i32 as f64; + | ^^^^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:56:17 + | +LL | let _ = -100 as f32; + | ^^^^^^^^^^^ help: try: `-100_f32` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:57:17 + | +LL | let _ = -100 as f64; + | ^^^^^^^^^^^ help: try: `-100_f64` + +error: casting integer literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:58:17 + | +LL | let _ = -100_i32 as f64; + | ^^^^^^^^^^^^^^^ help: try: `-100_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:59:9 + | +LL | 100. as f32; + | ^^^^^^^^^^^ help: try: `100_f32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:60:9 + | +LL | 100. as f64; + | ^^^^^^^^^^^ help: try: `100_f64` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:72:9 + | +LL | 1 as u32; + | ^^^^^^^^ help: try: `1_u32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:73:9 + | +LL | 0x10 as i32; + | ^^^^^^^^^^^ help: try: `0x10_i32` + +error: casting integer literal to `usize` is unnecessary + --> $DIR/unnecessary_cast.rs:74:9 + | +LL | 0b10 as usize; + | ^^^^^^^^^^^^^ help: try: `0b10_usize` + +error: casting integer literal to `u16` is unnecessary + --> $DIR/unnecessary_cast.rs:75:9 + | +LL | 0o73 as u16; + | ^^^^^^^^^^^ help: try: `0o73_u16` + +error: casting integer literal to `u32` is unnecessary + --> $DIR/unnecessary_cast.rs:76:9 + | +LL | 1_000_000_000 as u32; + | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` + +error: casting float literal to `f64` is unnecessary + --> $DIR/unnecessary_cast.rs:78:9 + | +LL | 1.0 as f64; + | ^^^^^^^^^^ help: try: `1.0_f64` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:79:9 + | +LL | 0.5 as f32; + | ^^^^^^^^^^ help: try: `0.5_f32` + +error: casting integer literal to `i32` is unnecessary + --> $DIR/unnecessary_cast.rs:83:17 + | +LL | let _ = -1 as i32; + | ^^^^^^^^^ help: try: `-1_i32` + +error: casting float literal to `f32` is unnecessary + --> $DIR/unnecessary_cast.rs:84:17 + | +LL | let _ = -1.0 as f32; + | ^^^^^^^^^^^ help: try: `-1.0_f32` + +error: aborting due to 25 previous errors diff --git a/tests/ui/unnecessary_cast_fixable.fixed b/tests/ui/unnecessary_cast_fixable.fixed deleted file mode 100644 index 36800c5340db2..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.fixed +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100_f32; - 100_f64; - 100_f64; - let _ = -100_f32; - let _ = -100_f64; - let _ = -100_f64; - 100_f32; - 100_f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1_u32; - 0x10_i32; - 0b10_usize; - 0o73_u16; - 1_000_000_000_u32; - - 1.0_f64; - 0.5_f32; - - 1.0 as u16; - - let _ = -1_i32; - let _ = -1.0_f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.rs b/tests/ui/unnecessary_cast_fixable.rs deleted file mode 100644 index d4b6bb952ab35..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.rs +++ /dev/null @@ -1,50 +0,0 @@ -// run-rustfix - -#![warn(clippy::unnecessary_cast)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::nonstandard_macro_braces, - clippy::borrow_as_ptr -)] - -fn main() { - // casting integer literal to float is unnecessary - 100 as f32; - 100 as f64; - 100_i32 as f64; - let _ = -100 as f32; - let _ = -100 as f64; - let _ = -100_i32 as f64; - 100. as f32; - 100. as f64; - // Should not trigger - #[rustfmt::skip] - let v = vec!(1); - &v as &[i32]; - 0x10 as f32; - 0o10 as f32; - 0b10 as f32; - 0x11 as f64; - 0o11 as f64; - 0b11 as f64; - - 1 as u32; - 0x10 as i32; - 0b10 as usize; - 0o73 as u16; - 1_000_000_000 as u32; - - 1.0 as f64; - 0.5 as f32; - - 1.0 as u16; - - let _ = -1 as i32; - let _ = -1.0 as f32; - - let _ = 1 as I32Alias; - let _ = &1 as &I32Alias; -} - -type I32Alias = i32; diff --git a/tests/ui/unnecessary_cast_fixable.stderr b/tests/ui/unnecessary_cast_fixable.stderr deleted file mode 100644 index a281143281b54..0000000000000 --- a/tests/ui/unnecessary_cast_fixable.stderr +++ /dev/null @@ -1,106 +0,0 @@ -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:13:5 - | -LL | 100 as f32; - | ^^^^^^^^^^ help: try: `100_f32` - | - = note: `-D clippy::unnecessary-cast` implied by `-D warnings` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:14:5 - | -LL | 100 as f64; - | ^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:15:5 - | -LL | 100_i32 as f64; - | ^^^^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:16:13 - | -LL | let _ = -100 as f32; - | ^^^^^^^^^^^ help: try: `-100_f32` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:17:13 - | -LL | let _ = -100 as f64; - | ^^^^^^^^^^^ help: try: `-100_f64` - -error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:18:13 - | -LL | let _ = -100_i32 as f64; - | ^^^^^^^^^^^^^^^ help: try: `-100_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:19:5 - | -LL | 100. as f32; - | ^^^^^^^^^^^ help: try: `100_f32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:20:5 - | -LL | 100. as f64; - | ^^^^^^^^^^^ help: try: `100_f64` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:32:5 - | -LL | 1 as u32; - | ^^^^^^^^ help: try: `1_u32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:33:5 - | -LL | 0x10 as i32; - | ^^^^^^^^^^^ help: try: `0x10_i32` - -error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:34:5 - | -LL | 0b10 as usize; - | ^^^^^^^^^^^^^ help: try: `0b10_usize` - -error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:35:5 - | -LL | 0o73 as u16; - | ^^^^^^^^^^^ help: try: `0o73_u16` - -error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:36:5 - | -LL | 1_000_000_000 as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` - -error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:38:5 - | -LL | 1.0 as f64; - | ^^^^^^^^^^ help: try: `1.0_f64` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:39:5 - | -LL | 0.5 as f32; - | ^^^^^^^^^^ help: try: `0.5_f32` - -error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:43:13 - | -LL | let _ = -1 as i32; - | ^^^^^^^^^ help: try: `-1_i32` - -error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast_fixable.rs:44:13 - | -LL | let _ = -1.0 as f32; - | ^^^^^^^^^^^ help: try: `-1.0_f32` - -error: aborting due to 17 previous errors - diff --git a/tests/ui/unused_rounding.fixed b/tests/ui/unused_rounding.fixed new file mode 100644 index 0000000000000..54f85806ac3b3 --- /dev/null +++ b/tests/ui/unused_rounding.fixed @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32; + let _ = 1.0f64; + let _ = 1.00f32; + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.rs b/tests/ui/unused_rounding.rs new file mode 100644 index 0000000000000..8d007bc4a1dc8 --- /dev/null +++ b/tests/ui/unused_rounding.rs @@ -0,0 +1,9 @@ +// run-rustfix +#![warn(clippy::unused_rounding)] + +fn main() { + let _ = 1f32.ceil(); + let _ = 1.0f64.floor(); + let _ = 1.00f32.round(); + let _ = 2e-54f64.floor(); +} diff --git a/tests/ui/unused_rounding.stderr b/tests/ui/unused_rounding.stderr new file mode 100644 index 0000000000000..6cfb02e040283 --- /dev/null +++ b/tests/ui/unused_rounding.stderr @@ -0,0 +1,22 @@ +error: used the `ceil` method with a whole number float + --> $DIR/unused_rounding.rs:5:13 + | +LL | let _ = 1f32.ceil(); + | ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32` + | + = note: `-D clippy::unused-rounding` implied by `-D warnings` + +error: used the `floor` method with a whole number float + --> $DIR/unused_rounding.rs:6:13 + | +LL | let _ = 1.0f64.floor(); + | ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64` + +error: used the `round` method with a whole number float + --> $DIR/unused_rounding.rs:7:13 + | +LL | let _ = 1.00f32.round(); + | ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 3e62ffe74fedd..4f80aaecc902d 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + Self::Num(n) => *n, + Self::TupleNums(n, _m) => *n, + Self::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Self(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Self { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let Self { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index da2faddee12a7..52da72db53ce3 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -542,3 +542,69 @@ mod use_self_in_pat { } } } + +mod issue8845 { + pub enum Something { + Num(u8), + TupleNums(u8, u8), + StructNums { one: u8, two: u8 }, + } + + struct Foo(u8); + + struct Bar { + x: u8, + y: usize, + } + + impl Something { + fn get_value(&self) -> u8 { + match self { + Something::Num(n) => *n, + Something::TupleNums(n, _m) => *n, + Something::StructNums { one, two: _ } => *one, + } + } + + fn use_crate(&self) -> u8 { + match self { + crate::issue8845::Something::Num(n) => *n, + crate::issue8845::Something::TupleNums(n, _m) => *n, + crate::issue8845::Something::StructNums { one, two: _ } => *one, + } + } + + fn imported_values(&self) -> u8 { + use Something::*; + match self { + Num(n) => *n, + TupleNums(n, _m) => *n, + StructNums { one, two: _ } => *one, + } + } + } + + impl Foo { + fn get_value(&self) -> u8 { + let Foo(x) = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Foo(x) = self; + *x + } + } + + impl Bar { + fn get_value(&self) -> u8 { + let Bar { x, .. } = self; + *x + } + + fn use_crate(&self) -> u8 { + let crate::issue8845::Bar { x, .. } = self; + *x + } + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 34d98618253a6..f06bb959b3bde 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -186,5 +186,65 @@ error: unnecessary structure name repetition LL | if let Foo::Bar = self { | ^^^ help: use the applicable keyword: `Self` -error: aborting due to 31 previous errors +error: unnecessary structure name repetition + --> $DIR/use_self.rs:563:17 + | +LL | Something::Num(n) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:564:17 + | +LL | Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:565:17 + | +LL | Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:571:17 + | +LL | crate::issue8845::Something::Num(n) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:572:17 + | +LL | crate::issue8845::Something::TupleNums(n, _m) => *n, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:573:17 + | +LL | crate::issue8845::Something::StructNums { one, two: _ } => *one, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:589:17 + | +LL | let Foo(x) = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:594:17 + | +LL | let crate::issue8845::Foo(x) = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:601:17 + | +LL | let Bar { x, .. } = self; + | ^^^ help: use the applicable keyword: `Self` + +error: unnecessary structure name repetition + --> $DIR/use_self.rs:606:17 + | +LL | let crate::issue8845::Bar { x, .. } = self; + | ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self` + +error: aborting due to 41 previous errors diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index bf4ce79b2cbc9..6909fbcae09ff 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -232,6 +232,9 @@ return true; } searchStr = searchStr.toLowerCase(); + if (searchStr.startsWith("clippy::")) { + searchStr = searchStr.slice(8); + } // Search by id if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { @@ -343,17 +346,23 @@ function setTheme(theme, store) { let enableNight = false; let enableAyu = false; - if (theme == "ayu") { - enableAyu = true; - } else if (theme == "coal" || theme == "navy") { - enableNight = true; - } else if (theme == "rust") { - enableHighlight = true; - } else { - enableHighlight = true; - // this makes sure that an unknown theme request gets set to a known one - theme = "light"; + switch(theme) { + case "ayu": + enableAyu = true; + break; + case "coal": + case "navy": + enableNight = true; + break; + case "rust": + enableHighlight = true; + break; + default: + enableHighlight = true; + theme = "light"; + break; } + document.getElementsByTagName("body")[0].className = theme; document.getElementById("styleHighlight").disabled = !enableHighlight; @@ -368,4 +377,10 @@ function setTheme(theme, store) { } // loading the theme after the initial load -setTheme(localStorage.getItem('clippy-lint-list-theme'), false); +const prefersDark = window.matchMedia("(prefers-color-scheme: dark)"); +const theme = localStorage.getItem('clippy-lint-list-theme'); +if (prefersDark.matches && !theme) { + setTheme("coal", false); +} else { + setTheme(theme, false); +} From 7713f28f543e4e36cbdc0ec560e100fe47fbc75e Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Sat, 4 Jun 2022 14:04:35 +0200 Subject: [PATCH 08/78] Remove unnecessary clap_derive dependency added in 9ee211af The fixed issue in this commit can be tested without depending on clap/clap_derive. This updates the test case to do so. --- Cargo.toml | 1 - tests/compile-test.rs | 3 --- tests/ui/auxiliary/proc_macro_attr.rs | 5 +++++ tests/ui/empty_line_after_outer_attribute.rs | 9 +++------ tests/ui/empty_line_after_outer_attribute.stderr | 12 ++++++------ 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d23d681df00c1..3c8b758d53dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,6 @@ filetime = "0.2" rustc-workspace-hack = "1.0" # UI test dependencies -clap = { version = "3.1", features = ["derive"] } clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 7d21983572324..04c2eeff08b6f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -23,7 +23,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal"); /// All crates used in UI tests are listed here static TEST_DEPENDENCIES: &[&str] = &[ - "clap", "clippy_utils", "derive_new", "futures", @@ -42,8 +41,6 @@ static TEST_DEPENDENCIES: &[&str] = &[ // Test dependencies may need an `extern crate` here to ensure that they show up // in the depinfo file (otherwise cargo thinks they are unused) #[allow(unused_extern_crates)] -extern crate clap; -#[allow(unused_extern_crates)] extern crate clippy_utils; #[allow(unused_extern_crates)] extern crate derive_new; diff --git a/tests/ui/auxiliary/proc_macro_attr.rs b/tests/ui/auxiliary/proc_macro_attr.rs index e370a98df1acf..ae2cc2492f414 100644 --- a/tests/ui/auxiliary/proc_macro_attr.rs +++ b/tests/ui/auxiliary/proc_macro_attr.rs @@ -19,6 +19,11 @@ use syn::{ parse_quote, FnArg, ImplItem, ItemImpl, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, Type, }; +#[proc_macro_attribute] +pub fn dummy(_args: TokenStream, input: TokenStream) -> TokenStream { + input +} + #[proc_macro_attribute] pub fn fake_async_trait(_args: TokenStream, input: TokenStream) -> TokenStream { let mut item = parse_macro_input!(input as ItemTrait); diff --git a/tests/ui/empty_line_after_outer_attribute.rs b/tests/ui/empty_line_after_outer_attribute.rs index d15c84d7438a0..697412c00275e 100644 --- a/tests/ui/empty_line_after_outer_attribute.rs +++ b/tests/ui/empty_line_after_outer_attribute.rs @@ -4,9 +4,6 @@ #![feature(custom_inner_attributes)] #![rustfmt::skip] -#[macro_use] -extern crate clap; - #[macro_use] extern crate proc_macro_attr; @@ -113,10 +110,10 @@ pub trait Bazz { } } -#[derive(clap::Parser)] -#[clap(after_help = "This ia a help message. +#[derive(Clone, Copy)] +#[dummy(string = "first line -You're welcome. +second line ")] pub struct Args; diff --git a/tests/ui/empty_line_after_outer_attribute.stderr b/tests/ui/empty_line_after_outer_attribute.stderr index acc3edef9b928..594fca44a3210 100644 --- a/tests/ui/empty_line_after_outer_attribute.stderr +++ b/tests/ui/empty_line_after_outer_attribute.stderr @@ -1,5 +1,5 @@ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:14:1 + --> $DIR/empty_line_after_outer_attribute.rs:11:1 | LL | / #[crate_type = "lib"] LL | | @@ -10,7 +10,7 @@ LL | | fn with_one_newline_and_comment() { assert!(true) } = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:26:1 + --> $DIR/empty_line_after_outer_attribute.rs:23:1 | LL | / #[crate_type = "lib"] LL | | @@ -18,7 +18,7 @@ LL | | fn with_one_newline() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:31:1 + --> $DIR/empty_line_after_outer_attribute.rs:28:1 | LL | / #[crate_type = "lib"] LL | | @@ -27,7 +27,7 @@ LL | | fn with_two_newlines() { assert!(true) } | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:38:1 + --> $DIR/empty_line_after_outer_attribute.rs:35:1 | LL | / #[crate_type = "lib"] LL | | @@ -35,7 +35,7 @@ LL | | enum Baz { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:46:1 + --> $DIR/empty_line_after_outer_attribute.rs:43:1 | LL | / #[crate_type = "lib"] LL | | @@ -43,7 +43,7 @@ LL | | struct Foo { | |_ error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> $DIR/empty_line_after_outer_attribute.rs:54:1 + --> $DIR/empty_line_after_outer_attribute.rs:51:1 | LL | / #[crate_type = "lib"] LL | | From 42cf98553a72f4b6cdac2c4c6378121d30f3bf5f Mon Sep 17 00:00:00 2001 From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com> Date: Sat, 4 Jun 2022 21:20:41 +0900 Subject: [PATCH 09/78] refactor: check copied and cloned --- clippy_lints/src/methods/filter_map.rs | 4 +- tests/ui/manual_filter_map.fixed | 44 ++++++++------- tests/ui/manual_filter_map.rs | 59 +++++++++++--------- tests/ui/manual_filter_map.stderr | 74 ++++++++++++++++---------- tests/ui/manual_find_map.fixed | 48 ++++++++++------- tests/ui/manual_find_map.rs | 61 +++++++++++++-------- tests/ui/manual_find_map.stderr | 74 +++++++++++++++++--------- 7 files changed, 227 insertions(+), 137 deletions(-) diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 6cabe74fdef4e..7127d8242d810 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -181,9 +181,11 @@ pub(super) fn check<'tcx>( } fn acceptable_methods(method: &PathSegment<'_>) -> bool { - let methods: [Symbol; 6] = [ + let methods: [Symbol; 8] = [ sym::clone, sym::as_ref, + sym!(copied), + sym!(cloned), sym!(as_deref), sym!(as_mut), sym!(as_deref_mut), diff --git a/tests/ui/manual_filter_map.fixed b/tests/ui/manual_filter_map.fixed index 9de7040000a03..de0d86148899d 100644 --- a/tests/ui/manual_filter_map.fixed +++ b/tests/ui/manual_filter_map.fixed @@ -36,44 +36,52 @@ fn to_res(_: T) -> Result { unimplemented!() } -struct OptionFoo { - field: Option, -} - -struct ResultFoo { - field: Result, +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, } fn issue_8920() { - let vec = vec![OptionFoo { - field: Some(String::from("str")), + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), }]; + let _ = vec .iter() - .filter_map(|f| f.field.clone()); + .filter_map(|f| f.option_field.clone()); - let mut vec = vec![ResultFoo { - field: Ok(String::from("str")), - }]; - let _ = vec.iter().filter_map(|f| f.field.clone().ok()); + let _ = vec + .iter() + .filter_map(|f| f.ref_field.cloned()); + + let _ = vec + .iter() + .filter_map(|f| f.ref_field.copied()); + + let _ = vec + .iter() + .filter_map(|f| f.result_field.clone().ok()); let _ = vec .iter() - .filter_map(|f| f.field.as_ref().ok()); + .filter_map(|f| f.result_field.as_ref().ok()); let _ = vec .iter() - .filter_map(|f| f.field.as_deref().ok()); + .filter_map(|f| f.result_field.as_deref().ok()); let _ = vec .iter_mut() - .filter_map(|f| f.field.as_mut().ok()); + .filter_map(|f| f.result_field.as_mut().ok()); let _ = vec .iter_mut() - .filter_map(|f| f.field.as_deref_mut().ok()); + .filter_map(|f| f.result_field.as_deref_mut().ok()); let _ = vec .iter() - .filter_map(|f| f.field.to_owned().ok()); + .filter_map(|f| f.result_field.to_owned().ok()); } diff --git a/tests/ui/manual_filter_map.rs b/tests/ui/manual_filter_map.rs index 6766078d694a5..bd6516f038b29 100644 --- a/tests/ui/manual_filter_map.rs +++ b/tests/ui/manual_filter_map.rs @@ -36,50 +36,61 @@ fn to_res(_: T) -> Result { unimplemented!() } -struct OptionFoo { - field: Option, -} - -struct ResultFoo { - field: Result, +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, } fn issue_8920() { - let vec = vec![OptionFoo { - field: Some(String::from("str")), + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), }]; + let _ = vec .iter() - .filter(|f| f.field.is_some()) - .map(|f| f.field.clone().unwrap()); + .filter(|f| f.option_field.is_some()) + .map(|f| f.option_field.clone().unwrap()); - let mut vec = vec![ResultFoo { - field: Ok(String::from("str")), - }]; - let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + let _ = vec + .iter() + .filter(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.cloned().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.copied().unwrap()); + + let _ = vec + .iter() + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.clone().unwrap()); let _ = vec .iter() - .filter(|f| f.field.is_ok()) - .map(|f| f.field.as_ref().unwrap()); + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_ref().unwrap()); let _ = vec .iter() - .filter(|f| f.field.is_ok()) - .map(|f| f.field.as_deref().unwrap()); + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref().unwrap()); let _ = vec .iter_mut() - .filter(|f| f.field.is_ok()) - .map(|f| f.field.as_mut().unwrap()); + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_mut().unwrap()); let _ = vec .iter_mut() - .filter(|f| f.field.is_ok()) - .map(|f| f.field.as_deref_mut().unwrap()); + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref_mut().unwrap()); let _ = vec .iter() - .filter(|f| f.field.is_ok()) - .map(|f| f.field.to_owned().unwrap()); + .filter(|f| f.result_field.is_ok()) + .map(|f| f.result_field.to_owned().unwrap()); } diff --git a/tests/ui/manual_filter_map.stderr b/tests/ui/manual_filter_map.stderr index 5e67ca348fab0..465f1b1911017 100644 --- a/tests/ui/manual_filter_map.stderr +++ b/tests/ui/manual_filter_map.stderr @@ -19,58 +19,76 @@ LL | let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_o | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:53:10 + --> $DIR/manual_filter_map.rs:54:10 | -LL | .filter(|f| f.field.is_some()) +LL | .filter(|f| f.option_field.is_some()) | __________^ -LL | | .map(|f| f.field.clone().unwrap()); - | |__________________________________________^ help: try: `filter_map(|f| f.field.clone())` +LL | | .map(|f| f.option_field.clone().unwrap()); + | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:59:24 + --> $DIR/manual_filter_map.rs:59:10 | -LL | let _ = vec.iter().filter(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|f| f.field.clone().ok())` +LL | .filter(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.cloned().unwrap()); + | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:64:10 + | +LL | .filter(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.copied().unwrap()); + | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())` + +error: `filter(..).map(..)` can be simplified as `filter_map(..)` + --> $DIR/manual_filter_map.rs:69:10 + | +LL | .filter(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.clone().unwrap()); + | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:63:10 + --> $DIR/manual_filter_map.rs:74:10 | -LL | .filter(|f| f.field.is_ok()) +LL | .filter(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_ref().unwrap()); - | |___________________________________________^ help: try: `filter_map(|f| f.field.as_ref().ok())` +LL | | .map(|f| f.result_field.as_ref().unwrap()); + | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:68:10 + --> $DIR/manual_filter_map.rs:79:10 | -LL | .filter(|f| f.field.is_ok()) +LL | .filter(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_deref().unwrap()); - | |_____________________________________________^ help: try: `filter_map(|f| f.field.as_deref().ok())` +LL | | .map(|f| f.result_field.as_deref().unwrap()); + | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:73:10 + --> $DIR/manual_filter_map.rs:84:10 | -LL | .filter(|f| f.field.is_ok()) +LL | .filter(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_mut().unwrap()); - | |___________________________________________^ help: try: `filter_map(|f| f.field.as_mut().ok())` +LL | | .map(|f| f.result_field.as_mut().unwrap()); + | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:78:10 + --> $DIR/manual_filter_map.rs:89:10 | -LL | .filter(|f| f.field.is_ok()) +LL | .filter(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_deref_mut().unwrap()); - | |_________________________________________________^ help: try: `filter_map(|f| f.field.as_deref_mut().ok())` +LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); + | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())` error: `filter(..).map(..)` can be simplified as `filter_map(..)` - --> $DIR/manual_filter_map.rs:83:10 + --> $DIR/manual_filter_map.rs:94:10 | -LL | .filter(|f| f.field.is_ok()) +LL | .filter(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.to_owned().unwrap()); - | |_____________________________________________^ help: try: `filter_map(|f| f.field.to_owned().ok())` +LL | | .map(|f| f.result_field.to_owned().unwrap()); + | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/manual_find_map.fixed b/tests/ui/manual_find_map.fixed index e7b5081ea59a5..d69b6c1dcf3bb 100644 --- a/tests/ui/manual_find_map.fixed +++ b/tests/ui/manual_find_map.fixed @@ -36,40 +36,52 @@ fn to_res(_: T) -> Result { unimplemented!() } -struct OptionFoo { - field: Option, -} - -struct ResultFoo { - field: Result, +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, } fn issue_8920() { - let vec = vec![OptionFoo { - field: Some(String::from("str")), + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), }]; - let _ = vec.iter().find_map(|f| f.field.clone()); - let mut vec = vec![ResultFoo { - field: Ok(String::from("str")), - }]; - let _ = vec.iter().find_map(|f| f.field.clone().ok()); + let _ = vec + .iter() + .find_map(|f| f.option_field.clone()); + + let _ = vec + .iter() + .find_map(|f| f.ref_field.cloned()); - let _ = vec.iter().find_map(|f| f.field.as_ref().ok()); + let _ = vec + .iter() + .find_map(|f| f.ref_field.copied()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.clone().ok()); + + let _ = vec + .iter() + .find_map(|f| f.result_field.as_ref().ok()); let _ = vec .iter() - .find_map(|f| f.field.as_deref().ok()); + .find_map(|f| f.result_field.as_deref().ok()); let _ = vec .iter_mut() - .find_map(|f| f.field.as_mut().ok()); + .find_map(|f| f.result_field.as_mut().ok()); let _ = vec .iter_mut() - .find_map(|f| f.field.as_deref_mut().ok()); + .find_map(|f| f.result_field.as_deref_mut().ok()); let _ = vec .iter() - .find_map(|f| f.field.to_owned().ok()); + .find_map(|f| f.result_field.to_owned().ok()); } diff --git a/tests/ui/manual_find_map.rs b/tests/ui/manual_find_map.rs index 46badbb9a18ea..1c4e18e31c8b1 100644 --- a/tests/ui/manual_find_map.rs +++ b/tests/ui/manual_find_map.rs @@ -36,44 +36,61 @@ fn to_res(_: T) -> Result { unimplemented!() } -struct OptionFoo { - field: Option, -} - -struct ResultFoo { - field: Result, +struct Issue8920<'a> { + option_field: Option, + result_field: Result, + ref_field: Option<&'a usize>, } fn issue_8920() { - let vec = vec![OptionFoo { - field: Some(String::from("str")), + let mut vec = vec![Issue8920 { + option_field: Some(String::from("str")), + result_field: Ok(String::from("str")), + ref_field: Some(&1), }]; - let _ = vec.iter().find(|f| f.field.is_some()).map(|f| f.field.clone().unwrap()); - let mut vec = vec![ResultFoo { - field: Ok(String::from("str")), - }]; - let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); + let _ = vec + .iter() + .find(|f| f.option_field.is_some()) + .map(|f| f.option_field.clone().unwrap()); + + let _ = vec + .iter() + .find(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.cloned().unwrap()); - let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.as_ref().unwrap()); + let _ = vec + .iter() + .find(|f| f.ref_field.is_some()) + .map(|f| f.ref_field.copied().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.clone().unwrap()); + + let _ = vec + .iter() + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_ref().unwrap()); let _ = vec .iter() - .find(|f| f.field.is_ok()) - .map(|f| f.field.as_deref().unwrap()); + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref().unwrap()); let _ = vec .iter_mut() - .find(|f| f.field.is_ok()) - .map(|f| f.field.as_mut().unwrap()); + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_mut().unwrap()); let _ = vec .iter_mut() - .find(|f| f.field.is_ok()) - .map(|f| f.field.as_deref_mut().unwrap()); + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.as_deref_mut().unwrap()); let _ = vec .iter() - .find(|f| f.field.is_ok()) - .map(|f| f.field.to_owned().unwrap()); + .find(|f| f.result_field.is_ok()) + .map(|f| f.result_field.to_owned().unwrap()); } diff --git a/tests/ui/manual_find_map.stderr b/tests/ui/manual_find_map.stderr index 2b6955a17df83..9dea42b76868b 100644 --- a/tests/ui/manual_find_map.stderr +++ b/tests/ui/manual_find_map.stderr @@ -19,54 +19,76 @@ LL | let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or( | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:51:24 + --> $DIR/manual_find_map.rs:54:10 | -LL | let _ = vec.iter().find(|f| f.field.is_some()).map(|f| f.field.clone().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.clone())` +LL | .find(|f| f.option_field.is_some()) + | __________^ +LL | | .map(|f| f.option_field.clone().unwrap()); + | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:59:10 + | +LL | .find(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.cloned().unwrap()); + | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:56:24 + --> $DIR/manual_find_map.rs:64:10 | -LL | let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.clone().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.clone().ok())` +LL | .find(|f| f.ref_field.is_some()) + | __________^ +LL | | .map(|f| f.ref_field.copied().unwrap()); + | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:58:24 + --> $DIR/manual_find_map.rs:69:10 | -LL | let _ = vec.iter().find(|f| f.field.is_ok()).map(|f| f.field.as_ref().unwrap()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|f| f.field.as_ref().ok())` +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.clone().unwrap()); + | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())` + +error: `find(..).map(..)` can be simplified as `find_map(..)` + --> $DIR/manual_find_map.rs:74:10 + | +LL | .find(|f| f.result_field.is_ok()) + | __________^ +LL | | .map(|f| f.result_field.as_ref().unwrap()); + | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:62:10 + --> $DIR/manual_find_map.rs:79:10 | -LL | .find(|f| f.field.is_ok()) +LL | .find(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_deref().unwrap()); - | |_____________________________________________^ help: try: `find_map(|f| f.field.as_deref().ok())` +LL | | .map(|f| f.result_field.as_deref().unwrap()); + | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:67:10 + --> $DIR/manual_find_map.rs:84:10 | -LL | .find(|f| f.field.is_ok()) +LL | .find(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_mut().unwrap()); - | |___________________________________________^ help: try: `find_map(|f| f.field.as_mut().ok())` +LL | | .map(|f| f.result_field.as_mut().unwrap()); + | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:72:10 + --> $DIR/manual_find_map.rs:89:10 | -LL | .find(|f| f.field.is_ok()) +LL | .find(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.as_deref_mut().unwrap()); - | |_________________________________________________^ help: try: `find_map(|f| f.field.as_deref_mut().ok())` +LL | | .map(|f| f.result_field.as_deref_mut().unwrap()); + | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())` error: `find(..).map(..)` can be simplified as `find_map(..)` - --> $DIR/manual_find_map.rs:77:10 + --> $DIR/manual_find_map.rs:94:10 | -LL | .find(|f| f.field.is_ok()) +LL | .find(|f| f.result_field.is_ok()) | __________^ -LL | | .map(|f| f.field.to_owned().unwrap()); - | |_____________________________________________^ help: try: `find_map(|f| f.field.to_owned().ok())` +LL | | .map(|f| f.result_field.to_owned().unwrap()); + | |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())` -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors From c31b4a9d21c9a5af7fcd2ee7cf1fbec0589b9954 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 4 Jun 2022 14:35:44 +0200 Subject: [PATCH 10/78] List configuration values can now be extended instead of replaced --- clippy_lints/src/utils/conf.rs | 74 +++++++++++++------ .../blacklisted_names.rs | 10 +++ .../blacklisted_names.stderr | 16 ++++ .../blacklisted_names_append/clippy.toml | 1 + .../blacklisted_names.rs | 10 +++ .../blacklisted_names.stderr | 10 +++ .../blacklisted_names_replace/clippy.toml | 1 + .../doc_valid_idents_append/clippy.toml | 1 + .../doc_valid_idents_append/doc_markdown.rs | 12 +++ .../doc_markdown.stderr | 14 ++++ .../doc_valid_idents_replace/clippy.toml | 1 + .../doc_valid_idents_replace/doc_markdown.rs | 12 +++ .../doc_markdown.stderr | 36 +++++++++ 13 files changed, 174 insertions(+), 24 deletions(-) create mode 100644 tests/ui-toml/blacklisted_names_append/blacklisted_names.rs create mode 100644 tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr create mode 100644 tests/ui-toml/blacklisted_names_append/clippy.toml create mode 100644 tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs create mode 100644 tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr create mode 100644 tests/ui-toml/blacklisted_names_replace/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_append/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_append/doc_markdown.rs create mode 100644 tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr create mode 100644 tests/ui-toml/doc_valid_idents_replace/clippy.toml create mode 100644 tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs create mode 100644 tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b5c5d35135f90..38e5c5e5b7365 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -9,6 +9,29 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{cmp, env, fmt, fs, io, iter}; +#[rustfmt::skip] +const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ + "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", + "DirectX", + "ECMAScript", + "GPLv2", "GPLv3", + "GitHub", "GitLab", + "IPv4", "IPv6", + "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", + "NaN", "NaNs", + "OAuth", "GraphQL", + "OCaml", + "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", + "WebGL", + "TensorFlow", + "TrueType", + "iOS", "macOS", "FreeBSD", + "TeX", "LaTeX", "BibTeX", "BibLaTeX", + "MinGW", + "CamelCase", +]; +const DEFAULT_BLACKLISTED_NAMES: &[&str] = &["foo", "baz", "quux"]; + /// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint. #[derive(Clone, Debug, Deserialize)] pub struct Rename { @@ -178,8 +201,10 @@ define_Conf! { (msrv: Option = None), /// Lint: BLACKLISTED_NAME. /// - /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses - (blacklisted_names: Vec = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()), + /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses. The value + /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// default configuration of Clippy. By default any configuraction will replace the default value. + (blacklisted_names: Vec = super::DEFAULT_BLACKLISTED_NAMES.iter().map(ToString::to_string).collect()), /// Lint: COGNITIVE_COMPLEXITY. /// /// The maximum cognitive complexity a function can have @@ -191,27 +216,14 @@ define_Conf! { (cyclomatic_complexity_threshold: Option = None), /// Lint: DOC_MARKDOWN. /// - /// The list of words this lint should not consider as identifiers needing ticks - (doc_valid_idents: Vec = [ - "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", - "DirectX", - "ECMAScript", - "GPLv2", "GPLv3", - "GitHub", "GitLab", - "IPv4", "IPv6", - "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", - "NaN", "NaNs", - "OAuth", "GraphQL", - "OCaml", - "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", - "WebGL", - "TensorFlow", - "TrueType", - "iOS", "macOS", "FreeBSD", - "TeX", "LaTeX", "BibTeX", "BibLaTeX", - "MinGW", - "CamelCase", - ].iter().map(ToString::to_string).collect()), + /// The list of words this lint should not consider as identifiers needing ticks. The value + /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// default configuration of Clippy. By default any configuraction will replace the default value. For example: + /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. + /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. + /// + /// Default list: + (doc_valid_idents: Vec = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()), /// Lint: TOO_MANY_ARGUMENTS. /// /// The maximum number of argument a function or method can have @@ -401,7 +413,21 @@ pub fn read(path: &Path) -> TryConf { Err(e) => return TryConf::from_error(e), Ok(content) => content, }; - toml::from_str(&content).unwrap_or_else(TryConf::from_error) + match toml::from_str::(&content) { + Ok(mut conf) => { + extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS); + extend_vec_if_indicator_present(&mut conf.conf.blacklisted_names, DEFAULT_BLACKLISTED_NAMES); + + conf + }, + Err(e) => TryConf::from_error(e), + } +} + +fn extend_vec_if_indicator_present(vec: &mut Vec, default: &[&str]) { + if vec.contains(&"..".to_string()) { + vec.extend(default.iter().map(ToString::to_string)); + } } const SEPARATOR_WIDTH: usize = 4; diff --git a/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs b/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs new file mode 100644 index 0000000000000..fb2395cf90be3 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/blacklisted_names.rs @@ -0,0 +1,10 @@ +#[warn(clippy::blacklisted_name)] + +fn main() { + // `foo` is part of the default configuration + let foo = "bar"; + // `ducks` was unrightfully blacklisted + let ducks = ["quack", "quack"]; + // `fox` is okay + let fox = ["what", "does", "the", "fox", "say", "?"]; +} diff --git a/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr b/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr new file mode 100644 index 0000000000000..9169bb0e866ac --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/blacklisted_names.stderr @@ -0,0 +1,16 @@ +error: use of a blacklisted/placeholder name `foo` + --> $DIR/blacklisted_names.rs:5:9 + | +LL | let foo = "bar"; + | ^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: use of a blacklisted/placeholder name `ducks` + --> $DIR/blacklisted_names.rs:7:9 + | +LL | let ducks = ["quack", "quack"]; + | ^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/blacklisted_names_append/clippy.toml b/tests/ui-toml/blacklisted_names_append/clippy.toml new file mode 100644 index 0000000000000..0e052ef50f07b --- /dev/null +++ b/tests/ui-toml/blacklisted_names_append/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["ducks", ".."] diff --git a/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs new file mode 100644 index 0000000000000..fb2395cf90be3 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.rs @@ -0,0 +1,10 @@ +#[warn(clippy::blacklisted_name)] + +fn main() { + // `foo` is part of the default configuration + let foo = "bar"; + // `ducks` was unrightfully blacklisted + let ducks = ["quack", "quack"]; + // `fox` is okay + let fox = ["what", "does", "the", "fox", "say", "?"]; +} diff --git a/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr new file mode 100644 index 0000000000000..ec6f7f084f2a5 --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/blacklisted_names.stderr @@ -0,0 +1,10 @@ +error: use of a blacklisted/placeholder name `ducks` + --> $DIR/blacklisted_names.rs:7:9 + | +LL | let ducks = ["quack", "quack"]; + | ^^^^^ + | + = note: `-D clippy::blacklisted-name` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui-toml/blacklisted_names_replace/clippy.toml b/tests/ui-toml/blacklisted_names_replace/clippy.toml new file mode 100644 index 0000000000000..4582f1c06674c --- /dev/null +++ b/tests/ui-toml/blacklisted_names_replace/clippy.toml @@ -0,0 +1 @@ +blacklisted-names = ["ducks"] diff --git a/tests/ui-toml/doc_valid_idents_append/clippy.toml b/tests/ui-toml/doc_valid_idents_append/clippy.toml new file mode 100644 index 0000000000000..daf3276854bdb --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["ClipPy", ".."] diff --git a/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs b/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs new file mode 100644 index 0000000000000..327a592e9cadc --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/doc_markdown.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +/// This is a special interface for ClipPy which doesn't require backticks +fn allowed_name() {} + +/// OAuth and LaTeX are inside Clippy's default list. +fn default_name() {} + +/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. +fn unknown_name() {} + +fn main() {} diff --git a/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr b/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr new file mode 100644 index 0000000000000..0f767c9b8559f --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_append/doc_markdown.stderr @@ -0,0 +1,14 @@ +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:9:5 + | +LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` +help: try + | +LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted. + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui-toml/doc_valid_idents_replace/clippy.toml b/tests/ui-toml/doc_valid_idents_replace/clippy.toml new file mode 100644 index 0000000000000..70bc477b08c6a --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["ClipPy"] diff --git a/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs new file mode 100644 index 0000000000000..327a592e9cadc --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.rs @@ -0,0 +1,12 @@ +#![warn(clippy::doc_markdown)] + +/// This is a special interface for ClipPy which doesn't require backticks +fn allowed_name() {} + +/// OAuth and LaTeX are inside Clippy's default list. +fn default_name() {} + +/// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. +fn unknown_name() {} + +fn main() {} diff --git a/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr new file mode 100644 index 0000000000000..e0613eb863b39 --- /dev/null +++ b/tests/ui-toml/doc_valid_idents_replace/doc_markdown.stderr @@ -0,0 +1,36 @@ +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:6:5 + | +LL | /// OAuth and LaTeX are inside Clippy's default list. + | ^^^^^ + | + = note: `-D clippy::doc-markdown` implied by `-D warnings` +help: try + | +LL | /// `OAuth` and LaTeX are inside Clippy's default list. + | ~~~~~~~ + +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:6:15 + | +LL | /// OAuth and LaTeX are inside Clippy's default list. + | ^^^^^ + | +help: try + | +LL | /// OAuth and `LaTeX` are inside Clippy's default list. + | ~~~~~~~ + +error: item in documentation is missing backticks + --> $DIR/doc_markdown.rs:9:5 + | +LL | /// TestItemThingyOfCoolness might sound cool but is not on the list and should be linted. + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | /// `TestItemThingyOfCoolness` might sound cool but is not on the list and should be linted. + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 3 previous errors + From a0821fbd756147a2018aed5ed2deb16ba481cd59 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 4 Jun 2022 17:28:23 -0400 Subject: [PATCH 11/78] Don't lint `derive_partial_eq_without_eq` on private types --- clippy_lints/src/derive.rs | 5 +- tests/ui/derive_partial_eq_without_eq.fixed | 56 ++++++++++++++------ tests/ui/derive_partial_eq_without_eq.rs | 56 ++++++++++++++------ tests/ui/derive_partial_eq_without_eq.stderr | 14 ++++- 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 99347ebadc602..a98060fe9a655 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -11,7 +11,9 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::subst::GenericArg; -use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty}; +use rustc_middle::ty::{ + self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty, Visibility, +}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::sym; @@ -459,6 +461,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, substs) = ty.kind(); + if cx.tcx.visibility(adt.did()) == Visibility::Public; if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 012780258fc3a..25e7050d08e0f 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -4,28 +4,28 @@ #![warn(clippy::derive_partial_eq_without_eq)] // Don't warn on structs that aren't PartialEq -struct NotPartialEq { +pub struct NotPartialEq { foo: u32, bar: String, } // Eq can be derived but is missing #[derive(Debug, PartialEq, Eq)] -struct MissingEq { +pub struct MissingEq { foo: u32, bar: String, } // Eq is derived #[derive(PartialEq, Eq)] -struct NotMissingEq { +pub struct NotMissingEq { foo: u32, bar: String, } // Eq is manually implemented #[derive(PartialEq)] -struct ManualEqImpl { +pub struct ManualEqImpl { foo: u32, bar: String, } @@ -34,13 +34,13 @@ impl Eq for ManualEqImpl {} // Cannot be Eq because f32 isn't Eq #[derive(PartialEq)] -struct CannotBeEq { +pub struct CannotBeEq { foo: u32, bar: f32, } // Don't warn if PartialEq is manually implemented -struct ManualPartialEqImpl { +pub struct ManualPartialEqImpl { foo: u32, bar: String, } @@ -53,52 +53,74 @@ impl PartialEq for ManualPartialEqImpl { // Generic fields should be properly checked for Eq-ness #[derive(PartialEq)] -struct GenericNotEq { +pub struct GenericNotEq { foo: T, bar: U, } #[derive(PartialEq, Eq)] -struct GenericEq { +pub struct GenericEq { foo: T, bar: U, } #[derive(PartialEq, Eq)] -struct TupleStruct(u32); +pub struct TupleStruct(u32); #[derive(PartialEq, Eq)] -struct GenericTupleStruct(T); +pub struct GenericTupleStruct(T); #[derive(PartialEq)] -struct TupleStructNotEq(f32); +pub struct TupleStructNotEq(f32); #[derive(PartialEq, Eq)] -enum Enum { +pub enum Enum { Foo(u32), Bar { a: String, b: () }, } #[derive(PartialEq, Eq)] -enum GenericEnum { +pub enum GenericEnum { Foo(T), Bar { a: U, b: V }, } #[derive(PartialEq)] -enum EnumNotEq { +pub enum EnumNotEq { Foo(u32), Bar { a: String, b: f32 }, } // Ensure that rustfix works properly when `PartialEq` has other derives on either side #[derive(Debug, PartialEq, Eq, Clone)] -struct RustFixWithOtherDerives; +pub struct RustFixWithOtherDerives; #[derive(PartialEq)] -struct Generic(T); +pub struct Generic(T); #[derive(PartialEq, Eq)] -struct GenericPhantom(core::marker::PhantomData); +pub struct GenericPhantom(core::marker::PhantomData); + +mod _hidden { + #[derive(PartialEq, Eq)] + pub struct Reexported; + + #[derive(PartialEq, Eq)] + pub struct InPubFn; + + #[derive(PartialEq)] + pub(crate) struct PubCrate; + + #[derive(PartialEq)] + pub(super) struct PubSuper; +} + +pub use _hidden::Reexported; +pub fn _from_mod() -> _hidden::InPubFn { + _hidden::InPubFn +} + +#[derive(PartialEq)] +struct InternalTy; fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs index fc8285b0c6b75..88d6fbd1af7e8 100644 --- a/tests/ui/derive_partial_eq_without_eq.rs +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -4,28 +4,28 @@ #![warn(clippy::derive_partial_eq_without_eq)] // Don't warn on structs that aren't PartialEq -struct NotPartialEq { +pub struct NotPartialEq { foo: u32, bar: String, } // Eq can be derived but is missing #[derive(Debug, PartialEq)] -struct MissingEq { +pub struct MissingEq { foo: u32, bar: String, } // Eq is derived #[derive(PartialEq, Eq)] -struct NotMissingEq { +pub struct NotMissingEq { foo: u32, bar: String, } // Eq is manually implemented #[derive(PartialEq)] -struct ManualEqImpl { +pub struct ManualEqImpl { foo: u32, bar: String, } @@ -34,13 +34,13 @@ impl Eq for ManualEqImpl {} // Cannot be Eq because f32 isn't Eq #[derive(PartialEq)] -struct CannotBeEq { +pub struct CannotBeEq { foo: u32, bar: f32, } // Don't warn if PartialEq is manually implemented -struct ManualPartialEqImpl { +pub struct ManualPartialEqImpl { foo: u32, bar: String, } @@ -53,52 +53,74 @@ impl PartialEq for ManualPartialEqImpl { // Generic fields should be properly checked for Eq-ness #[derive(PartialEq)] -struct GenericNotEq { +pub struct GenericNotEq { foo: T, bar: U, } #[derive(PartialEq)] -struct GenericEq { +pub struct GenericEq { foo: T, bar: U, } #[derive(PartialEq)] -struct TupleStruct(u32); +pub struct TupleStruct(u32); #[derive(PartialEq)] -struct GenericTupleStruct(T); +pub struct GenericTupleStruct(T); #[derive(PartialEq)] -struct TupleStructNotEq(f32); +pub struct TupleStructNotEq(f32); #[derive(PartialEq)] -enum Enum { +pub enum Enum { Foo(u32), Bar { a: String, b: () }, } #[derive(PartialEq)] -enum GenericEnum { +pub enum GenericEnum { Foo(T), Bar { a: U, b: V }, } #[derive(PartialEq)] -enum EnumNotEq { +pub enum EnumNotEq { Foo(u32), Bar { a: String, b: f32 }, } // Ensure that rustfix works properly when `PartialEq` has other derives on either side #[derive(Debug, PartialEq, Clone)] -struct RustFixWithOtherDerives; +pub struct RustFixWithOtherDerives; #[derive(PartialEq)] -struct Generic(T); +pub struct Generic(T); #[derive(PartialEq, Eq)] -struct GenericPhantom(core::marker::PhantomData); +pub struct GenericPhantom(core::marker::PhantomData); + +mod _hidden { + #[derive(PartialEq)] + pub struct Reexported; + + #[derive(PartialEq)] + pub struct InPubFn; + + #[derive(PartialEq)] + pub(crate) struct PubCrate; + + #[derive(PartialEq)] + pub(super) struct PubSuper; +} + +pub use _hidden::Reexported; +pub fn _from_mod() -> _hidden::InPubFn { + _hidden::InPubFn +} + +#[derive(PartialEq)] +struct InternalTy; fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr index bf55165890a5a..b762efc474426 100644 --- a/tests/ui/derive_partial_eq_without_eq.stderr +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -42,5 +42,17 @@ error: you are deriving `PartialEq` and can implement `Eq` LL | #[derive(Debug, PartialEq, Clone)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` -error: aborting due to 7 previous errors +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:105:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:108:14 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 9 previous errors From 648dbebfb116e028ff77cb539634bfc1fcc7b256 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Sat, 4 Jun 2022 20:21:56 -0400 Subject: [PATCH 12/78] Actually get the correct `ParamEnv` in `derive_partial_eq_without_eq` --- clippy_lints/src/derive.rs | 94 +++++++++++--------- tests/ui/derive_partial_eq_without_eq.fixed | 4 +- tests/ui/derive_partial_eq_without_eq.stderr | 14 ++- 3 files changed, 66 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index a98060fe9a655..28f218a8e344f 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -4,15 +4,18 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, + Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArg; +use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty, Visibility, + self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, + Ty, TyCtxt, Visibility, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; @@ -463,49 +466,16 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r if let ty::Adt(adt, substs) = ty.kind(); if cx.tcx.visibility(adt.did()) == Visibility::Public; if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); - if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); - // New `ParamEnv` replacing `T: PartialEq` with `T: Eq` - let param_env = ParamEnv::new( - cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| { - let kind = p.kind(); - match kind.skip_binder() { - PredicateKind::Trait(p) - if p.trait_ref.def_id == peq_trait_def_id - && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1) - && matches!(p.trait_ref.self_ty().kind(), ty::Param(_)) - && p.constness == BoundConstness::NotConst - && p.polarity == ImplPolarity::Positive => - { - cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate { - trait_ref: TraitRef::new( - eq_trait_def_id, - cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()), - ), - constness: BoundConstness::NotConst, - polarity: ImplPolarity::Positive, - }))) - }, - _ => p, - } - })), - cx.param_env.reveal(), - cx.param_env.constness(), - ); - if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs); + let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); + if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]); + // If all of our fields implement `Eq`, we can implement `Eq` too + if adt + .all_fields() + .map(|f| f.ty(cx.tcx, substs)) + .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); then { - // If all of our fields implement `Eq`, we can implement `Eq` too - for variant in adt.variants() { - for field in &variant.fields { - let ty = field.ty(cx.tcx, substs); - - if !implements_trait(cx, ty, eq_trait_def_id, substs) { - return; - } - } - } - span_lint_and_sugg( cx, DERIVE_PARTIAL_EQ_WITHOUT_EQ, @@ -518,3 +488,41 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r } } } + +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl. +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let PredicateKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + && p.constness == BoundConstness::NotConst + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + ParamEnv::new( + tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { + trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())), + constness: BoundConstness::NotConst, + polarity: ImplPolarity::Positive, + }))) + }), + )), + Reveal::UserFacing, + Constness::NotConst, + ) +} diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed index 25e7050d08e0f..bbbe467590f91 100644 --- a/tests/ui/derive_partial_eq_without_eq.fixed +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -52,7 +52,7 @@ impl PartialEq for ManualPartialEqImpl { } // Generic fields should be properly checked for Eq-ness -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub struct GenericNotEq { foo: T, bar: U, @@ -95,7 +95,7 @@ pub enum EnumNotEq { #[derive(Debug, PartialEq, Eq, Clone)] pub struct RustFixWithOtherDerives; -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] pub struct Generic(T); #[derive(PartialEq, Eq)] diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr index b762efc474426..794c5dab8445b 100644 --- a/tests/ui/derive_partial_eq_without_eq.stderr +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -6,6 +6,12 @@ LL | #[derive(Debug, PartialEq)] | = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings` +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:55:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + error: you are deriving `PartialEq` and can implement `Eq` --> $DIR/derive_partial_eq_without_eq.rs:61:10 | @@ -42,6 +48,12 @@ error: you are deriving `PartialEq` and can implement `Eq` LL | #[derive(Debug, PartialEq, Clone)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:98:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + error: you are deriving `PartialEq` and can implement `Eq` --> $DIR/derive_partial_eq_without_eq.rs:105:14 | @@ -54,5 +66,5 @@ error: you are deriving `PartialEq` and can implement `Eq` LL | #[derive(PartialEq)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors From 94e321a6ff9a224f690b355fd4298caf6342e883 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 5 Jun 2022 07:14:31 +0200 Subject: [PATCH 13/78] needless_late_init refactoring Remove duplication in creating suggestions by first mapping assignments to spans and then suggestions. --- clippy_lints/src/needless_late_init.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 26c694a71fedd..9c4e2ef671219 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -185,13 +185,13 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() - .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new()))) - .chain(assignments.iter().map(|assignment| { - Some(( - assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), - String::new(), - )) - })) + .map(|assignment| assignment.span.until(assignment.rhs_span)) + .chain( + assignments + .iter() + .map(|assignment| assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())), + ) + .map(|span| Some((span, String::new()))) .collect::>>()?; match suggestions.len() { From 2a1a80d80cdc75ed0bb9f35744591089b8a89f31 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 5 Jun 2022 07:22:45 +0200 Subject: [PATCH 14/78] needless_late_init refactoring Simplify the creation of suggestions by using `flat_map` instead of `chain`. Note that the order of the suggestions is not important. --- clippy_lints/src/needless_late_init.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9c4e2ef671219..5e97c606e21fe 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -185,12 +185,12 @@ fn assignment_suggestions<'tcx>( let suggestions = assignments .iter() - .map(|assignment| assignment.span.until(assignment.rhs_span)) - .chain( - assignments - .iter() - .map(|assignment| assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())), - ) + .flat_map(|assignment| { + [ + assignment.span.until(assignment.rhs_span), + assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), + ] + }) .map(|span| Some((span, String::new()))) .collect::>>()?; From a2de34720d984669499e981b640cde050c2a4dfa Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Sun, 5 Jun 2022 07:28:28 +0200 Subject: [PATCH 15/78] needless_late_init refactoring Remove the unneeded wrapping and unwrapping in suggestion creation. Collecting to Option> only returns None if one of the elements is None and that is never the case here. --- clippy_lints/src/needless_late_init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 5e97c606e21fe..4154c71b42868 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -191,8 +191,8 @@ fn assignment_suggestions<'tcx>( assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()), ] }) - .map(|span| Some((span, String::new()))) - .collect::>>()?; + .map(|span| (span, String::new())) + .collect::>(); match suggestions.len() { // All of `exprs` are never types From 3737abe802dc4c0f776634499bc666d6446a64c7 Mon Sep 17 00:00:00 2001 From: DevAccentor Date: Sun, 5 Jun 2022 10:26:29 +0200 Subject: [PATCH 16/78] change based on review --- tests/ui/for_loops_over_fallibles.rs | 4 ++-- tests/ui/for_loops_over_fallibles.stderr | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/for_loops_over_fallibles.rs b/tests/ui/for_loops_over_fallibles.rs index bde78a9fd4ae9..3390111d0a8fe 100644 --- a/tests/ui/for_loops_over_fallibles.rs +++ b/tests/ui/for_loops_over_fallibles.rs @@ -21,12 +21,12 @@ fn for_loops_over_fallibles() { } // check over a `Result` - for x in result.into_iter() { + for x in result.iter_mut() { println!("{}", x); } // check over a `Result` - for x in result.iter_mut() { + for x in result.into_iter() { println!("{}", x); } diff --git a/tests/ui/for_loops_over_fallibles.stderr b/tests/ui/for_loops_over_fallibles.stderr index 635e08182d5b5..8c8c022243aeb 100644 --- a/tests/ui/for_loops_over_fallibles.stderr +++ b/tests/ui/for_loops_over_fallibles.stderr @@ -26,18 +26,18 @@ LL | for x in result { error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:24:14 | -LL | for x in result.into_iter() { +LL | for x in result.iter_mut() { | ^^^^^^ | - = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` + = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:29:14 | -LL | for x in result.iter_mut() { +LL | for x in result.into_iter() { | ^^^^^^ | - = help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result` + = help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result` error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement --> $DIR/for_loops_over_fallibles.rs:33:14 From 9aeed6b9bfea42e4a580ad6e7289b4fc72b4365c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 5 Jun 2022 15:24:41 -0400 Subject: [PATCH 17/78] Improve lint doc consistency --- clippy_lints/src/assertions_on_constants.rs | 3 - clippy_lints/src/async_yields_async.rs | 1 + clippy_lints/src/await_holding_invalid.rs | 2 - clippy_lints/src/bool_assert_comparison.rs | 5 +- clippy_lints/src/borrow_deref_ref.rs | 13 +- clippy_lints/src/casts/mod.rs | 46 ++-- clippy_lints/src/comparison_chain.rs | 1 - clippy_lints/src/dbg_macro.rs | 5 +- clippy_lints/src/default.rs | 11 +- clippy_lints/src/dereference.rs | 16 +- clippy_lints/src/derivable_impls.rs | 4 +- clippy_lints/src/enum_variants.rs | 3 +- clippy_lints/src/eq_op.rs | 10 +- clippy_lints/src/eta_reduction.rs | 8 +- clippy_lints/src/excessive_bools.rs | 3 +- clippy_lints/src/float_literal.rs | 5 +- clippy_lints/src/functions/mod.rs | 5 +- clippy_lints/src/get_first.rs | 3 +- clippy_lints/src/implicit_saturating_sub.rs | 14 +- clippy_lints/src/indexing_slicing.rs | 45 ++-- clippy_lints/src/infinite_iter.rs | 1 + clippy_lints/src/inherent_to_string.rs | 12 +- clippy_lints/src/int_plus_one.rs | 3 +- clippy_lints/src/integer_division.rs | 5 +- clippy_lints/src/items_after_statements.rs | 3 +- clippy_lints/src/large_const_arrays.rs | 5 +- clippy_lints/src/large_enum_variant.rs | 4 +- clippy_lints/src/let_underscore.rs | 24 +- clippy_lints/src/lib.rs | 5 +- clippy_lints/src/lifetimes.rs | 12 +- clippy_lints/src/literal_representation.rs | 28 ++- clippy_lints/src/loops/mod.rs | 20 +- clippy_lints/src/matches/mod.rs | 68 ++++-- clippy_lints/src/methods/mod.rs | 207 +++++++++++------- clippy_lints/src/misc.rs | 30 +-- clippy_lints/src/misc_early/mod.rs | 71 ++++-- .../src/mixed_read_write_in_expression.rs | 6 +- clippy_lints/src/mut_reference.rs | 15 +- clippy_lints/src/mutex_atomic.rs | 11 +- clippy_lints/src/needless_bool.rs | 12 +- clippy_lints/src/needless_borrowed_ref.rs | 9 +- clippy_lints/src/needless_update.rs | 7 +- clippy_lints/src/neg_cmp_op_on_partial_ord.rs | 14 +- clippy_lints/src/neg_multiply.rs | 9 +- clippy_lints/src/non_copy_const.rs | 14 +- clippy_lints/src/octal_escapes.rs | 5 +- clippy_lints/src/pass_by_ref_or_value.rs | 12 +- clippy_lints/src/ptr.rs | 22 +- clippy_lints/src/ranges.rs | 43 +++- clippy_lints/src/redundant_closure_call.rs | 11 +- clippy_lints/src/reference.rs | 5 +- clippy_lints/src/return_self_not_must_use.rs | 14 +- clippy_lints/src/shadow.rs | 15 +- .../src/slow_vector_initialization.rs | 7 +- clippy_lints/src/strings.rs | 18 +- clippy_lints/src/trait_bounds.rs | 3 +- clippy_lints/src/unicode.rs | 3 +- clippy_lints/src/unused_async.rs | 5 +- clippy_lints/src/useless_conversion.rs | 5 +- clippy_lints/src/utils/internal_lints.rs | 22 +- clippy_lints/src/vec.rs | 8 +- clippy_lints/src/wildcard_imports.rs | 14 +- clippy_lints/src/write.rs | 33 ++- clippy_lints/src/zero_div_zero.rs | 5 +- 64 files changed, 621 insertions(+), 427 deletions(-) diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index c82837746bd5d..2705ffffdcbff 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -14,9 +14,6 @@ declare_clippy_lint! { /// Will be optimized out by the compiler or should probably be replaced by a /// `panic!()` or `unreachable!()` /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust,ignore /// assert!(false) diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 0619490e73c43..aef21bfc36c9e 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -24,6 +24,7 @@ declare_clippy_lint! { /// }; /// } /// ``` + /// /// Use instead: /// ```rust /// async fn foo() {} diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 5b7c4591504e1..eee5f90d17885 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -140,8 +140,6 @@ declare_clippy_lint! { /// from a memory access perspective but will cause bugs at runtime if they /// are held in such a way. /// - /// ### Known problems - /// /// ### Example /// /// ```toml diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index c50e214be288d..95abe8aa59fbe 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -17,11 +17,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// assert_eq!("a".is_empty(), false); /// assert_ne!("a".is_empty(), true); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// assert!(!"a".is_empty()); /// ``` #[clippy::version = "1.53.0"] diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index ec2f31cf67374..1582ec9ee5ce6 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// Dereferencing and then borrowing a reference value has no effect in most cases. /// /// ### Known problems - /// false negative on such code: + /// False negative on such code: /// ``` /// let x = &12; /// let addr_x = &x as *const _ as usize; @@ -29,17 +29,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust + /// fn foo(_x: &str) {} + /// /// let s = &String::new(); /// - /// // Bad /// let a: &String = &* s; /// foo(&*s); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(_x: &str) {} + /// # let s = &String::new(); /// let a: &String = s; /// foo(&**s); - /// - /// fn foo(_: &str){ } /// ``` #[clippy::version = "1.59.0"] pub BORROW_DEREF_REF, diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index daf3b7b4ce4fe..fe8e372b6f50c 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -219,13 +219,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn fun() -> i32 { 1 } - /// let a = fun as i64; + /// # let _ = + /// fun as i64; + /// ``` /// - /// // Good - /// fn fun2() -> i32 { 1 } - /// let a = fun2 as usize; + /// Use instead: + /// ```rust + /// # fn fun() -> i32 { 1 } + /// # let _ = + /// fun as usize; /// ``` #[clippy::version = "pre 1.29.0"] pub FN_TO_NUMERIC_CAST, @@ -245,17 +248,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn fn1() -> i16 { /// 1 /// }; - /// let _ = fn1 as i32; + /// # let _ = + /// fn1 as i32; + /// ``` /// - /// // Better: Cast to usize first, then comment with the reason for the truncation - /// fn fn2() -> i16 { + /// Use instead: + /// ```rust + /// // Cast to usize first, then comment with the reason for the truncation + /// fn fn1() -> i16 { /// 1 /// }; - /// let fn_ptr = fn2 as usize; + /// let fn_ptr = fn1 as usize; /// let fn_ptr_truncated = fn_ptr as i32; /// ``` #[clippy::version = "pre 1.29.0"] @@ -277,23 +283,31 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: fn1 is cast as `usize` + /// // fn1 is cast as `usize` /// fn fn1() -> u16 { /// 1 /// }; - /// let _ = fn1 as usize; + /// # let _ = + /// fn1 as usize; + /// ``` /// - /// // Good: maybe you intended to call the function? + /// Use instead: + /// ```rust + /// // maybe you intended to call the function? /// fn fn2() -> u16 { /// 1 /// }; - /// let _ = fn2() as usize; + /// # let _ = + /// fn2() as usize; + /// + /// // or /// - /// // Good: maybe you intended to cast it to a function type? + /// // maybe you intended to cast it to a function type? /// fn fn3() -> u16 { /// 1 /// } - /// let _ = fn3 as fn() -> u16; + /// # let _ = + /// fn3 as fn() -> u16; /// ``` #[clippy::version = "1.58.0"] pub FN_TO_NUMERIC_CAST_ANY, diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs index 913e081af3bda..a05b41eb3ab52 100644 --- a/clippy_lints/src/comparison_chain.rs +++ b/clippy_lints/src/comparison_chain.rs @@ -35,7 +35,6 @@ declare_clippy_lint! { /// ``` /// /// Use instead: - /// /// ```rust,ignore /// use std::cmp::Ordering; /// # fn a() {} diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 17deccf8c3930..fe9f4f9ae3cb9 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -18,10 +18,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// dbg!(true) + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// true /// ``` #[clippy::version = "1.34.0"] diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 243dfd3a46183..d99a1aa296946 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -18,15 +18,16 @@ declare_clippy_lint! { /// Checks for literal calls to `Default::default()`. /// /// ### Why is this bad? - /// It's more clear to the reader to use the name of the type whose default is - /// being gotten than the generic `Default`. + /// It's easier for the reader if the name of the type is used, rather than the + /// generic `Default`. /// /// ### Example /// ```rust - /// // Bad /// let s: String = Default::default(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let s = String::default(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -47,13 +48,13 @@ declare_clippy_lint! { /// Assignments to patterns that are of tuple type are not linted. /// /// ### Example - /// Bad: /// ``` /// # #[derive(Default)] /// # struct A { i: i32 } /// let mut a: A = Default::default(); /// a.i = 42; /// ``` + /// /// Use instead: /// ``` /// # #[derive(Default)] diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8288f7a8b9b62..dee83e60d7e54 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -30,13 +30,14 @@ declare_clippy_lint! { /// let a: &mut String = &mut String::from("foo"); /// let b: &str = a.deref(); /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let a: &mut String = &mut String::from("foo"); /// let b = &*a; /// ``` /// - /// This lint excludes + /// This lint excludes: /// ```rust,ignore /// let _ = d.unwrap().deref(); /// ``` @@ -59,11 +60,13 @@ declare_clippy_lint! { /// ```rust /// fn fun(_a: &i32) {} /// - /// // Bad /// let x: &i32 = &&&&&&5; /// fun(&x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn fun(_a: &i32) {} /// let x: &i32 = &5; /// fun(x); /// ``` @@ -82,13 +85,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = Some(""); /// if let Some(ref x) = x { /// // use `x` here /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = Some(""); /// if let Some(x) = x { /// // use `&x` here diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index e98691fd5bb0a..4d7f4076d7b51 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -30,8 +30,7 @@ declare_clippy_lint! { /// } /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// #[derive(Default)] /// struct Foo { @@ -45,7 +44,6 @@ declare_clippy_lint! { /// specialized than what derive will produce. This lint can't detect the manual `impl` /// has exactly equal bounds, and therefore this lint is disabled for types with /// generic parameters. - /// #[clippy::version = "1.57.0"] pub DERIVABLE_IMPLS, complexity, diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 263a5b573c9cf..23b7510457091 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -60,7 +60,8 @@ declare_clippy_lint! { /// struct BlackForestCake; /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// mod cake { /// struct BlackForest; diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index c3176d987c637..2f4c90d07cf66 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -52,15 +52,13 @@ declare_clippy_lint! { /// ### Why is this bad? /// It is more idiomatic to dereference the other argument. /// - /// ### Known problems - /// None - /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore /// &x == y + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// x == *y /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 530d6d4de35f1..aa537aa90eca9 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -34,14 +34,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// xs.map(|x| foo(x)) + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore + /// // where `foo(_)` is a plain function that takes the exact argument type of `x`. /// xs.map(foo) /// ``` - /// where `foo(_)` is a plain function that takes the exact argument type of - /// `x`. #[clippy::version = "pre 1.29.0"] pub REDUNDANT_CLOSURE, style, diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index a2af10e2ba5ea..f7a92bc079567 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -54,12 +54,11 @@ declare_clippy_lint! { /// API easier to use. /// /// ### Example - /// Bad: /// ```rust,ignore /// fn f(is_round: bool, is_hot: bool) { ... } /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// enum Shape { /// Round, diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index f850ea31f4d6e..f2e0798096378 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -45,10 +45,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let _: f32 = 16_777_217.0; // 16_777_216.0 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let _: f32 = 16_777_216.0; /// let _: f64 = 16_777_217.0; /// ``` diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ad031cbc09d4d..73261fb8a44c7 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -76,12 +76,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// pub fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// pub unsafe fn foo(x: *const u8) { /// println!("{}", unsafe { *x }); /// } diff --git a/clippy_lints/src/get_first.rs b/clippy_lints/src/get_first.rs index 0748ab45252ad..529f7babaa5ea 100644 --- a/clippy_lints/src/get_first.rs +++ b/clippy_lints/src/get_first.rs @@ -20,13 +20,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = vec![2, 3, 5]; /// let first_element = x.get(0); /// ``` + /// /// Use instead: /// ```rust - /// // Good /// let x = vec![2, 3, 5]; /// let first_element = x.first(); /// ``` diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index ae4158662d464..9d858e0c2120a 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -16,17 +16,15 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let end: u32 = 10; - /// let start: u32 = 5; - /// - /// let mut i: u32 = end - start; - /// - /// // Bad + /// # let mut i: u32 = 0; /// if i != 0 { /// i -= 1; /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let mut i: u32 = 0; /// i = i.saturating_sub(1); /// ``` #[clippy::version = "1.44.0"] @@ -48,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; - // Ensure that the binary operator is >, != and < + // Ensure that the binary operator is >, !=, or < if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; // Check if assign operation is done diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index 4ba7477add82a..301686612a1b1 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -17,19 +17,20 @@ declare_clippy_lint! { /// ### Why is this bad? /// This will always panic at runtime. /// - /// ### Known problems - /// Hopefully none. - /// /// ### Example - /// ```no_run + /// ```rust,no_run /// # #![allow(const_err)] /// let x = [1, 2, 3, 4]; /// - /// // Bad /// x[9]; /// &x[2..9]; + /// ``` + /// + /// Use instead: + /// ```rust + /// # let x = [1, 2, 3, 4]; + /// // Index within bounds /// - /// // Good /// x[0]; /// x[3]; /// ``` @@ -49,42 +50,34 @@ declare_clippy_lint! { /// Indexing and slicing can panic at runtime and there are /// safe alternatives. /// - /// ### Known problems - /// Hopefully none. - /// /// ### Example /// ```rust,no_run /// // Vector /// let x = vec![0; 5]; /// - /// // Bad /// x[2]; /// &x[2..100]; - /// &x[2..]; - /// &x[..100]; - /// - /// // Good - /// x.get(2); - /// x.get(2..100); - /// x.get(2..); - /// x.get(..100); /// /// // Array /// let y = [0, 1, 2, 3]; /// - /// // Bad /// &y[10..100]; /// &y[10..]; - /// &y[..100]; + /// ``` + /// + /// Use instead: + /// ```rust + /// # let x = vec![0; 5]; + /// # let y = [0, 1, 2, 3]; + /// # let _ = + /// x.get(2); + /// # let _ = + /// x.get(2..100); /// - /// // Good - /// &y[2..]; - /// &y[..2]; - /// &y[0..3]; + /// # let _ = /// y.get(10); + /// # let _ = /// y.get(10..100); - /// y.get(10..); - /// y.get(..100); /// ``` #[clippy::version = "pre 1.29.0"] pub INDEXING_SLICING, diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index b2b9889f5dc74..fc8e473dd9e85 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -41,6 +41,7 @@ declare_clippy_lint! { /// ### Example /// ```rust /// let infinite_iter = 0..; + /// # let _ = /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5)); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/inherent_to_string.rs b/clippy_lints/src/inherent_to_string.rs index 55c04a1186fc3..39f68a8a1b480 100644 --- a/clippy_lints/src/inherent_to_string.rs +++ b/clippy_lints/src/inherent_to_string.rs @@ -14,12 +14,8 @@ declare_clippy_lint! { /// ### Why is this bad? /// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred. /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust - /// // Bad /// pub struct A; /// /// impl A { @@ -29,8 +25,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// use std::fmt; /// /// pub struct A; @@ -54,12 +50,8 @@ declare_clippy_lint! { /// ### Why is this bad? /// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`. /// - /// ### Known problems - /// None - /// /// ### Example /// ```rust - /// // Bad /// use std::fmt; /// /// pub struct A; @@ -77,8 +69,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// use std::fmt; /// /// pub struct A; diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 8db7b307ddb75..9a944def3eb22 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -21,8 +21,7 @@ declare_clippy_lint! { /// if x >= y + 1 {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// # let x = 1; /// # let y = 1; diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs index fa78620567880..3effba5682607 100644 --- a/clippy_lints/src/integer_division.rs +++ b/clippy_lints/src/integer_division.rs @@ -15,11 +15,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = 3 / 2; /// println!("{}", x); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = 3f32 / 2f32; /// println!("{}", x); /// ``` diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs index cdefe627efdaa..46d439b4497e1 100644 --- a/clippy_lints/src/items_after_statements.rs +++ b/clippy_lints/src/items_after_statements.rs @@ -17,7 +17,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn foo() { /// println!("cake"); /// } @@ -31,8 +30,8 @@ declare_clippy_lint! { /// } /// ``` /// + /// Use instead: /// ```rust - /// // Good /// fn foo() { /// println!("cake"); /// } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index 27db638813613..0b972bc391601 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -21,10 +21,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// pub const a = [0u32; 1_000_000]; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust.ignore /// pub static a = [0u32; 1_000_000]; /// ``` #[clippy::version = "1.44.0"] diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 63ac092dfaf12..9be057bcf901f 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -38,12 +38,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// enum Test { /// A(i32), /// B([i32; 8000]), /// } + /// ``` /// + /// Use instead: + /// ```rust /// // Possibly better /// enum Test2 { /// A(i32), diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index cb1ef01f5ba9d..26c540e2223b1 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -45,13 +45,11 @@ declare_clippy_lint! { /// `std::mem::drop` conveys your intention better and is less error-prone. /// /// ### Example - /// - /// Bad: /// ```rust,ignore /// let _ = mutex.lock(); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// let _lock = mutex.lock(); /// ``` @@ -75,24 +73,20 @@ declare_clippy_lint! { /// better and is less error-prone. /// /// ### Example - /// - /// Bad: - /// ```rust,ignore - /// struct Droppable; - /// impl Drop for Droppable { - /// fn drop(&mut self) {} - /// } + /// ```rust + /// # struct DroppableItem; /// { - /// let _ = Droppable; - /// // ^ dropped here + /// let _ = DroppableItem; + /// // ^ dropped here /// /* more code */ /// } /// ``` /// - /// Good: - /// ```rust,ignore + /// Use instead: + /// ```rust + /// # struct DroppableItem; /// { - /// let _droppable = Droppable; + /// let _droppable = DroppableItem; /// /* more code */ /// // dropped at end of scope /// } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ee0416fc0ff5e..85f7ff149d119 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -88,10 +88,11 @@ use rustc_session::Session; /// /// /// /// ### Example /// /// ```rust -/// /// // Bad /// /// Insert a short example of code that triggers the lint +/// /// ``` /// /// -/// /// // Good +/// /// Use instead: +/// /// ```rust /// /// Insert a short example of improved code that doesn't trigger the lint /// /// ``` /// pub LINT_NAME, diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 070c7e591420d..93f5663312f2e 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -36,12 +36,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetime annotations + /// // Unnecessary lifetime annotations /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 { /// x /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn elided(x: &u8, y: u8) -> &u8 { /// x /// } @@ -65,12 +67,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad: unnecessary lifetimes + /// // unnecessary lifetimes /// fn unused_lifetime<'a>(x: u8) { /// // .. /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn no_lifetime(x: u8) { /// // ... /// } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 9998712b8527d..cbd084c41687c 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -22,11 +22,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let x: u64 = 61864918973511; + /// # let _: u64 = + /// 61864918973511 + /// # ; + /// ``` /// - /// // Good - /// let x: u64 = 61_864_918_973_511; + /// Use instead: + /// ```rust + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub UNREADABLE_LITERAL, @@ -66,11 +71,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let x: u64 = 618_64_9189_73_511; + /// # let _: u64 = + /// 618_64_9189_73_511 + /// # ; + /// ``` /// - /// // Good - /// let x: u64 = 61_864_918_973_511; + /// Use instead: + /// ```rust + /// # let _: u64 = + /// 61_864_918_973_511 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub INCONSISTENT_DIGIT_GROUPING, @@ -125,9 +135,11 @@ declare_clippy_lint! { /// readable than a decimal representation. /// /// ### Example + /// ```text /// `255` => `0xFF` /// `65_535` => `0xFFFF` /// `4_042_322_160` => `0xF0F0_F0F0` + /// ``` #[clippy::version = "pre 1.29.0"] pub DECIMAL_LITERAL_REPRESENTATION, restriction, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index e32b228e104c2..acf974c586a2d 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -42,7 +42,8 @@ declare_clippy_lint! { /// dst[i + 64] = src[i]; /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// # let src = vec![1]; /// # let mut dst = vec![0; 65]; @@ -70,7 +71,8 @@ declare_clippy_lint! { /// println!("{}", vec[i]); /// } /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let vec = vec!['a', 'b', 'c']; /// for i in vec { @@ -103,7 +105,8 @@ declare_clippy_lint! { /// // .. /// } /// ``` - /// can be rewritten to + /// + /// Use instead: /// ```rust /// # let y = vec![1]; /// for x in &y { @@ -286,7 +289,8 @@ declare_clippy_lint! { /// i += 1; /// } /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let v = vec![1]; /// # fn bar(bar: usize, baz: usize) {} @@ -473,7 +477,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// This kind of operation can be expressed more succinctly with - /// `vec![item;SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also + /// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also /// have better performance. /// /// ### Example @@ -488,7 +492,8 @@ declare_clippy_lint! { /// vec.push(item2); /// } /// ``` - /// could be written as + /// + /// Use instead: /// ```rust /// let item1 = 2; /// let item2 = 3; @@ -516,7 +521,8 @@ declare_clippy_lint! { /// println!("{}", item); /// } /// ``` - /// could be written as + /// + /// Use instead: /// ```rust /// let item1 = 2; /// let item = &item1; diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index d1e42f39e470d..3e765173fb9f3 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -43,13 +43,16 @@ declare_clippy_lint! { /// ```rust /// # fn bar(stool: &str) {} /// # let x = Some("abc"); - /// // Bad /// match x { /// Some(ref foo) => bar(foo), /// _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn bar(stool: &str) {} + /// # let x = Some("abc"); /// if let Some(ref foo) = x { /// bar(foo); /// } @@ -114,14 +117,15 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// match x { /// &A(ref y) => foo(y), /// &B => bar(), /// _ => frob(&x), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// match *x { /// A(ref y) => foo(y), /// B => bar(), @@ -227,13 +231,16 @@ declare_clippy_lint! { /// ```rust /// let x: Option<()> = None; /// - /// // Bad /// let r: Option<&()> = match x { /// None => None, /// Some(ref v) => Some(v), /// }; + /// ``` + /// + /// Use instead: + /// ```rust + /// let x: Option<()> = None; /// - /// // Good /// let r: Option<&()> = x.as_ref(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -257,13 +264,16 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A(usize), B(usize) } /// # let x = Foo::B(1); - /// // Bad /// match x { /// Foo::A(_) => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # enum Foo { A(usize), B(usize) } + /// # let x = Foo::B(1); /// match x { /// Foo::A(_) => {}, /// Foo::B(_) => {}, @@ -290,14 +300,17 @@ declare_clippy_lint! { /// ```rust /// # enum Foo { A, B, C } /// # let x = Foo::B; - /// // Bad /// match x { /// Foo::A => {}, /// Foo::B => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # enum Foo { A, B, C } + /// # let x = Foo::B; /// match x { /// Foo::A => {}, /// Foo::B => {}, @@ -320,14 +333,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// match "foo" { + /// # let s = "foo"; + /// match s { /// "a" => {}, /// "bar" | _ => {}, /// } + /// ``` /// - /// // Good - /// match "foo" { + /// Use instead: + /// ```rust + /// # let s = "foo"; + /// match s { /// "a" => {}, /// _ => {}, /// } @@ -389,15 +405,17 @@ declare_clippy_lint! { /// ```rust /// # let a = 1; /// # let b = 2; - /// - /// // Bad /// match (a, b) { /// (c, d) => { /// // useless match /// } /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let a = 1; + /// # let b = 2; /// let (c, d) = (a, b); /// ``` #[clippy::version = "1.43.0"] @@ -419,13 +437,16 @@ declare_clippy_lint! { /// # struct A { a: i32 } /// let a = A { a: 5 }; /// - /// // Bad /// match a { /// A { a: 5, .. } => {}, /// _ => {}, /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # struct A { a: i32 } + /// # let a = A { a: 5 }; /// match a { /// A { a: 5 } => {}, /// _ => {}, @@ -509,7 +530,6 @@ declare_clippy_lint! { /// ```rust /// let x = Some(5); /// - /// // Bad /// let a = match x { /// Some(0) => true, /// _ => false, @@ -520,8 +540,11 @@ declare_clippy_lint! { /// } else { /// false /// }; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// let x = Some(5); /// let a = matches!(x, Some(0)); /// ``` #[clippy::version = "1.47.0"] @@ -695,19 +718,18 @@ declare_clippy_lint! { /// let arr = vec![0, 1, 2, 3]; /// let idx = 1; /// - /// // Bad /// match arr[idx] { /// 0 => println!("{}", 0), /// 1 => println!("{}", 3), /// _ => {}, /// } /// ``` + /// /// Use instead: /// ```rust, no_run /// let arr = vec![0, 1, 2, 3]; /// let idx = 1; /// - /// // Good /// match arr.get(idx) { /// Some(0) => println!("{}", 0), /// Some(1) => println!("{}", 3), diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7308e74c323e3..9d78f0a9fc14a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -124,27 +124,27 @@ declare_clippy_lint! { /// It's often inefficient to clone all elements of an iterator, when eventually, only some /// of them will be consumed. /// + /// ### Known Problems + /// This `lint` removes the side of effect of cloning items in the iterator. + /// A code that relies on that side-effect could fail. + /// /// ### Examples /// ```rust /// # let vec = vec!["string".to_string()]; - /// - /// // Bad + /// # let _ = /// vec.iter().cloned().take(10); - /// - /// // Good - /// vec.iter().take(10).cloned(); - /// - /// // Bad + /// # let _ = /// vec.iter().cloned().last(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let vec = vec!["string".to_string()]; + /// # let _ = + /// vec.iter().take(10).cloned(); + /// # let _ = /// vec.iter().last().cloned(); - /// /// ``` - /// ### Known Problems - /// This `lint` removes the side of effect of cloning items in the iterator. - /// A code that relies on that side-effect could fail. - /// #[clippy::version = "1.59.0"] pub ITER_OVEREAGER_CLONED, perf, @@ -342,11 +342,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = Ok::<_, ()>(()); - /// - /// // Bad /// x.ok().expect("why did I do this again?"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = Ok::<_, ()>(()); /// x.expect("why did I do this again?"); /// ``` #[clippy::version = "pre 1.29.0"] @@ -390,12 +391,13 @@ declare_clippy_lint! { /// ### Examples /// ```rust /// # let x = Some(1); - /// - /// // Bad /// x.unwrap_or_else(Default::default); /// x.unwrap_or_else(u32::default); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = Some(1); /// x.unwrap_or_default(); /// ``` #[clippy::version = "1.56.0"] @@ -453,11 +455,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let opt = Some(1); - /// - /// // Bad /// opt.map_or(None, |a| Some(a + 1)); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let opt = Some(1); /// opt.and_then(|a| Some(a + 1)); /// ``` #[clippy::version = "pre 1.29.0"] @@ -475,13 +478,12 @@ declare_clippy_lint! { /// `_.ok()`. /// /// ### Example - /// Bad: /// ```rust /// # let r: Result = Ok(1); /// assert_eq!(Some(1), r.map_or(None, Some)); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// # let r: Result = Ok(1); /// assert_eq!(Some(1), r.ok()); @@ -538,7 +540,8 @@ declare_clippy_lint! { /// # let vec = vec![1]; /// vec.iter().filter(|x| **x == 0).next(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let vec = vec![1]; /// vec.iter().find(|x| **x == 0); @@ -562,7 +565,8 @@ declare_clippy_lint! { /// # let vec = vec![1]; /// vec.iter().skip_while(|x| **x == 0).next(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let vec = vec![1]; /// vec.iter().find(|x| **x != 0); @@ -586,11 +590,16 @@ declare_clippy_lint! { /// let vec = vec![vec![1]]; /// let opt = Some(5); /// - /// // Bad + /// # let _ = /// vec.iter().map(|x| x.iter()).flatten(); /// opt.map(|x| Some(x * 2)).flatten(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let vec = vec![vec![1]]; + /// # let opt = Some(5); + /// # let _ = /// vec.iter().flat_map(|x| x.iter()); /// opt.and_then(|x| Some(x * 2)); /// ``` @@ -610,15 +619,16 @@ declare_clippy_lint! { /// less performant. /// /// ### Example - /// Bad: /// ```rust + /// # let _ = /// (0_i32..10) /// .filter(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); /// ``` /// - /// Good: + /// Use instead: /// ```rust + /// # let _ = /// (0_i32..10).filter_map(|n| n.checked_add(1)); /// ``` #[clippy::version = "1.51.0"] @@ -637,14 +647,13 @@ declare_clippy_lint! { /// less performant. /// /// ### Example - /// Bad: /// ```rust /// (0_i32..10) /// .find(|n| n.checked_add(1).is_some()) /// .map(|n| n.checked_add(1).unwrap()); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// (0_i32..10).find_map(|n| n.checked_add(1)); /// ``` @@ -713,16 +722,20 @@ declare_clippy_lint! { /// ### Example /// ```rust /// let vec = vec![1]; + /// # let _ = /// vec.iter().find(|x| **x == 0).is_some(); /// - /// let _ = "hello world".find("world").is_none(); + /// # let _ = + /// "hello world".find("world").is_none(); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// let vec = vec![1]; /// vec.iter().any(|x| *x == 0); /// - /// let _ = !"hello world".contains("world"); + /// # let _ = + /// !"hello world".contains("world"); /// ``` #[clippy::version = "pre 1.29.0"] pub SEARCH_IS_SOME, @@ -744,7 +757,8 @@ declare_clippy_lint! { /// let name = "foo"; /// if name.chars().next() == Some('_') {}; /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// let name = "foo"; /// if name.starts_with('_') {}; @@ -899,10 +913,13 @@ declare_clippy_lint! { /// # use std::rc::Rc; /// let x = Rc::new(1); /// - /// // Bad /// x.clone(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::rc::Rc; + /// # let x = Rc::new(1); /// Rc::clone(&x); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1034,11 +1051,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// _.split("x"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// _.split('x'); + /// ``` #[clippy::version = "pre 1.29.0"] pub SINGLE_CHAR_PATTERN, perf, @@ -1099,12 +1118,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # use std::collections::HashSet; - /// // Bad /// # let mut s = HashSet::new(); /// # s.insert(1); /// let x = s.iter().nth(0); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::collections::HashSet; /// # let mut s = HashSet::new(); /// # s.insert(1); /// let x = s.iter().next(); @@ -1210,11 +1231,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x = vec![2, 3, 5]; /// let last_element = x.get(x.len() - 1); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x = vec![2, 3, 5]; /// let last_element = x.last(); /// ``` @@ -1273,10 +1295,14 @@ declare_clippy_lint! { /// let mut a = vec![1, 2, 3]; /// let mut b = vec![4, 5, 6]; /// - /// // Bad /// a.extend(b.drain(..)); + /// ``` + /// + /// Use instead: + /// ```rust + /// let mut a = vec![1, 2, 3]; + /// let mut b = vec![4, 5, 6]; /// - /// // Good /// a.append(&mut b); /// ``` #[clippy::version = "1.55.0"] @@ -1351,11 +1377,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let name = "_"; - /// - /// // Bad /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-'); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let name = "_"; /// name.ends_with('_') || name.ends_with('-'); /// ``` #[clippy::version = "pre 1.29.0"] @@ -1401,11 +1428,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let _ = (0..3).fold(false, |acc, x| acc || x > 2); + /// # let _ = + /// (0..3).fold(false, |acc, x| acc || x > 2); /// ``` - /// This could be written as: + /// + /// Use instead: /// ```rust - /// let _ = (0..3).any(|x| x > 2); + /// # let _ = + /// (0..3).any(|x| x > 2); /// ``` #[clippy::version = "pre 1.29.0"] pub UNNECESSARY_FOLD, @@ -1485,11 +1515,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let _ = (&vec![3, 4, 5]).into_iter(); + /// # let _ = + /// (&vec![3, 4, 5]).into_iter(); + /// ``` /// - /// // Good - /// let _ = (&vec![3, 4, 5]).iter(); + /// Use instead: + /// ```rust + /// # let _ = + /// (&vec![3, 4, 5]).iter(); /// ``` #[clippy::version = "1.32.0"] pub INTO_ITER_ON_REF, @@ -1704,13 +1737,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let mut string = String::new(); + /// # let mut string = String::new(); /// string.insert_str(0, "R"); /// string.push_str("R"); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust - /// let mut string = String::new(); + /// # let mut string = String::new(); /// string.insert(0, 'R'); /// string.push('R'); /// ``` @@ -1897,11 +1931,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let _ = "Hello".bytes().nth(3); + /// # let _ = + /// "Hello".bytes().nth(3); + /// ``` /// - /// // Good - /// let _ = "Hello".as_bytes().get(3); + /// Use instead: + /// ```rust + /// # let _ = + /// "Hello".as_bytes().get(3); /// ``` #[clippy::version = "1.52.0"] pub BYTES_NTH, @@ -1945,15 +1982,20 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let some_vec = vec![0, 1, 2, 3]; - /// let _ = some_vec.iter().count(); - /// let _ = &some_vec[..].iter().count(); + /// # let _ = + /// some_vec.iter().count(); + /// # let _ = + /// &some_vec[..].iter().count(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let some_vec = vec![0, 1, 2, 3]; - /// let _ = some_vec.len(); - /// let _ = &some_vec[..].len(); + /// # let _ = + /// some_vec.len(); + /// # let _ = + /// &some_vec[..].len(); /// ``` #[clippy::version = "1.52.0"] pub ITER_COUNT, @@ -1973,16 +2015,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let s = ""; + /// # let s = ""; /// for x in s.splitn(1, ":") { - /// // use x + /// // .. /// } + /// ``` /// - /// // Good - /// let s = ""; + /// Use instead: + /// ```rust + /// # let s = ""; /// for x in s.splitn(2, ":") { - /// // use x + /// // .. /// } /// ``` #[clippy::version = "1.54.0"] @@ -2000,10 +2043,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let x: String = std::iter::repeat('x').take(10).collect(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let x: String = "x".repeat(10); /// ``` #[clippy::version = "1.54.0"] @@ -2021,7 +2065,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// let s = "key=value=add"; /// let (key, value) = s.splitn(2, '=').next_tuple()?; /// let value = s.splitn(2, '=').nth(1)?; @@ -2030,9 +2073,9 @@ declare_clippy_lint! { /// let key = parts.next()?; /// let value = parts.next()?; /// ``` + /// /// Use instead: /// ```rust,ignore - /// // Good /// let s = "key=value=add"; /// let (key, value) = s.split_once('=')?; /// let value = s.split_once('=')?.1; @@ -2057,13 +2100,12 @@ declare_clippy_lint! { /// that both functions return a lazy iterator. /// ### Example /// ```rust - /// // Bad /// let str = "key=value=add"; /// let _ = str.splitn(3, '=').next().unwrap(); /// ``` + /// /// Use instead: /// ```rust - /// // Good /// let str = "key=value=add"; /// let _ = str.split('=').next().unwrap(); /// ``` @@ -2149,7 +2191,8 @@ declare_clippy_lint! { /// let a = Some(&1); /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let a = Some(&1); /// let b = a; diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 5566569945322..01bf871198a5c 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -45,16 +45,13 @@ declare_clippy_lint! { /// dereferences, e.g., changing `*x` to `x` within the function. /// /// ### Example - /// ```rust,ignore - /// // Bad - /// fn foo(ref x: u8) -> bool { - /// true - /// } + /// ```rust + /// fn foo(ref _x: u8) {} + /// ``` /// - /// // Good - /// fn foo(x: &u8) -> bool { - /// true - /// } + /// Use instead: + /// ```rust + /// fn foo(_x: &u8) {} /// ``` #[clippy::version = "pre 1.29.0"] pub TOPLEVEL_REF_ARG, @@ -73,11 +70,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = 1.0; - /// - /// // Bad /// if x == f32::NAN { } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1.0f32; /// if x.is_nan() { } /// ``` #[clippy::version = "pre 1.29.0"] @@ -139,7 +137,8 @@ declare_clippy_lint! { /// # let y = String::from("foo"); /// if x.to_owned() == y {} /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let x = "foo"; /// # let y = String::from("foo"); @@ -232,10 +231,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let a = 0 as *const u32; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let a = std::ptr::null::(); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index 6860b60acbdb4..704918c0b979b 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -34,13 +34,21 @@ declare_clippy_lint! { /// # } /// let f = Foo { a: 0, b: 0, c: 0 }; /// - /// // Bad /// match f { /// Foo { a: _, b: 0, .. } => {}, /// Foo { a: _, b: _, c: _ } => {}, /// } + /// ``` + /// + /// Use instead: + /// ```rust + /// # struct Foo { + /// # a: i32, + /// # b: i32, + /// # c: i32, + /// # } + /// let f = Foo { a: 0, b: 0, c: 0 }; /// - /// // Good /// match f { /// Foo { b: 0, .. } => {}, /// Foo { .. } => {}, @@ -62,10 +70,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// fn foo(a: i32, _a: i32) {} + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn bar(a: i32, _b: i32) {} /// ``` #[clippy::version = "pre 1.29.0"] @@ -103,11 +112,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 0x1a9BAcD; + /// # let _ = + /// 0x1a9BAcD + /// # ; + /// ``` /// - /// // Good - /// let y = 0x1A9BACD; + /// Use instead: + /// ```rust + /// # let _ = + /// 0x1A9BACD + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub MIXED_CASE_HEX_LITERALS, @@ -127,11 +141,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 123832i32; + /// # let _ = + /// 123832i32 + /// # ; + /// ``` /// - /// // Good - /// let y = 123832_i32; + /// Use instead: + /// ```rust + /// # let _ = + /// 123832_i32 + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub UNSEPARATED_LITERAL_SUFFIX, @@ -150,11 +169,16 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let y = 123832_i32; + /// # let _ = + /// 123832_i32 + /// # ; + /// ``` /// - /// // Good - /// let y = 123832i32; + /// Use instead: + /// ```rust + /// # let _ = + /// 123832i32 + /// # ; /// ``` #[clippy::version = "1.58.0"] pub SEPARATED_LITERAL_SUFFIX, @@ -234,14 +258,15 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let v = Some("abc"); - /// - /// // Bad /// match v { /// Some(x) => (), /// y @ _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let v = Some("abc"); /// match v { /// Some(x) => (), /// y => (), @@ -262,6 +287,7 @@ declare_clippy_lint! { /// means there are 0 or more elements left. This can make a difference /// when refactoring, but shouldn't result in errors in the refactored code, /// since the wildcard pattern isn't used anyway. + /// /// ### Why is this bad? /// The wildcard pattern is unneeded as the rest pattern /// can match that element as well. @@ -270,13 +296,16 @@ declare_clippy_lint! { /// ```rust /// # struct TupleStruct(u32, u32, u32); /// # let t = TupleStruct(1, 2, 3); - /// // Bad /// match t { /// TupleStruct(0, .., _) => (), /// _ => (), /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # struct TupleStruct(u32, u32, u32); + /// # let t = TupleStruct(1, 2, 3); /// match t { /// TupleStruct(0, ..) => (), /// _ => (), diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 024bd0760715e..1ad07125b5473 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -25,14 +25,16 @@ declare_clippy_lint! { /// ```rust /// let mut x = 0; /// - /// // Bad /// let a = { /// x = 1; /// 1 /// } + x; /// // Unclear whether a is 1 or 2. + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let mut x = 0; /// let tmp = { /// x = 1; /// 1 diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 9d8f8999ce409..f434a655f8aff 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -16,12 +16,17 @@ declare_clippy_lint! { /// the value. Also the code misleads about the intent of the call site. /// /// ### Example - /// ```ignore - /// // Bad - /// my_vec.push(&mut value) + /// ```rust + /// # let mut vec = Vec::new(); + /// # let mut value = 5; + /// vec.push(&mut value); + /// ``` /// - /// // Good - /// my_vec.push(&value) + /// Use instead: + /// ```rust + /// # let mut vec = Vec::new(); + /// # let value = 5; + /// vec.push(&value); /// ``` #[clippy::version = "pre 1.29.0"] pub UNNECESSARY_MUT_PASSED, diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 73823779e493d..a98577093ed5e 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -27,12 +27,13 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let y = true; - /// - /// // Bad /// # use std::sync::Mutex; /// let x = Mutex::new(&y); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let y = true; /// # use std::sync::atomic::AtomicBool; /// let x = AtomicBool::new(y); /// ``` @@ -60,8 +61,10 @@ declare_clippy_lint! { /// ```rust /// # use std::sync::Mutex; /// let x = Mutex::new(0usize); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// # use std::sync::atomic::AtomicUsize; /// let x = AtomicUsize::new(0usize); /// ``` diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 778d49cb4b6ed..33638fe37346e 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -30,16 +30,22 @@ declare_clippy_lint! { /// shorter code. /// /// ### Example - /// ```rust,ignore + /// ```rust + /// # let x = true; /// if x { /// false + /// # ; /// } else { /// true + /// # ; /// } /// ``` - /// Could be written as - /// ```rust,ignore + /// + /// Use instead: + /// ```rust + /// # let x = true; /// !x + /// # ; /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BOOL, diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 0fcc419e72227..55da6e0f0a3cd 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -27,16 +27,17 @@ declare_clippy_lint! { /// ``` /// /// ### Example - /// Bad: /// ```rust /// let mut v = Vec::::new(); - /// let _ = v.iter_mut().filter(|&ref a| a.is_empty()); + /// # let _ = + /// v.iter_mut().filter(|&ref a| a.is_empty()); /// ``` /// - /// Good: + /// Use instead: /// ```rust /// let mut v = Vec::::new(); - /// let _ = v.iter_mut().filter(|a| a.is_empty()); + /// # let _ = + /// v.iter_mut().filter(|a| a.is_empty()); /// ``` #[clippy::version = "pre 1.29.0"] pub NEEDLESS_BORROWED_REFERENCE, diff --git a/clippy_lints/src/needless_update.rs b/clippy_lints/src/needless_update.rs index c87c174ef732c..0bd29d1776b28 100644 --- a/clippy_lints/src/needless_update.rs +++ b/clippy_lints/src/needless_update.rs @@ -24,16 +24,17 @@ declare_clippy_lint! { /// # z: i32, /// # } /// # let zero_point = Point { x: 0, y: 0, z: 0 }; - /// - /// // Bad /// Point { /// x: 1, /// y: 1, /// z: 1, /// ..zero_point /// }; + /// ``` /// - /// // Ok + /// Use instead: + /// ```rust,ignore + /// // Missing field `z` /// Point { /// x: 1, /// y: 1, diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index efe31a1544187..a7e0e35787cff 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -19,17 +19,17 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// use std::cmp::Ordering; - /// - /// // Bad /// let a = 1.0; /// let b = f64::NAN; /// - /// let _not_less_or_equal = !(a <= b); + /// let not_less_or_equal = !(a <= b); + /// ``` /// - /// // Good - /// let a = 1.0; - /// let b = f64::NAN; + /// Use instead: + /// ```rust + /// use std::cmp::Ordering; + /// # let a = 1.0; + /// # let b = f64::NAN; /// /// let _not_less_or_equal = match a.partial_cmp(&b) { /// None | Some(Ordering::Greater) => true, diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 707f3b2181ac9..ce6bb38b7c0e9 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -19,12 +19,13 @@ declare_clippy_lint! { /// This only catches integers (for now). /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore /// let a = x * -1; + /// ``` /// - /// // Good - /// let b = -x; + /// Use instead: + /// ```rust,ignore + /// let a = -x; /// ``` #[clippy::version = "pre 1.29.0"] pub NEG_MULTIPLY, diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 8db41ba6ee296..b727105f670fc 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -58,12 +58,14 @@ declare_clippy_lint! { /// ```rust /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// - /// // Bad. /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// ``` /// - /// // Good. + /// Use instead: + /// ```rust + /// # use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15); /// STATIC_ATOM.store(9, SeqCst); /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance @@ -104,11 +106,15 @@ declare_clippy_lint! { /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// - /// // Bad. /// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged /// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct + /// ``` + /// + /// Use instead: + /// ```rust + /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + /// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12); /// - /// // Good. /// static STATIC_ATOM: AtomicUsize = CONST_ATOM; /// STATIC_ATOM.store(9, SeqCst); /// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index e8532db4f711d..c8854539677c7 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -33,11 +33,12 @@ declare_clippy_lint! { /// /// # Example /// ```rust - /// // Bad /// let one = "\033[1m Bold? \033[0m"; // \033 intended as escape /// let two = "\033\0"; // \033 intended as null-3-3 + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let one = "\x1b[1mWill this be bold?\x1b[0m"; /// let two = "\x0033\x00"; /// ``` diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 5a93431f25a98..05ab62786f409 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -57,12 +57,11 @@ declare_clippy_lint! { /// ### Example /// /// ```rust - /// // Bad /// fn foo(v: &u32) {} /// ``` /// + /// Use instead: /// ```rust - /// // Better /// fn foo(v: u32) {} /// ``` #[clippy::version = "pre 1.29.0"] @@ -89,14 +88,13 @@ declare_clippy_lint! { /// #[derive(Clone, Copy)] /// struct TooLarge([u8; 2048]); /// - /// // Bad /// fn foo(v: TooLarge) {} /// ``` - /// ```rust - /// #[derive(Clone, Copy)] - /// struct TooLarge([u8; 2048]); /// - /// // Good + /// Use instead: + /// ```rust + /// # #[derive(Clone, Copy)] + /// # struct TooLarge([u8; 2048]); /// fn foo(v: &TooLarge) {} /// ``` #[clippy::version = "1.49.0"] diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 0b96f6ff68358..b06eba13d2fdb 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -48,10 +48,11 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad /// fn foo(&Vec) { .. } + /// ``` /// - /// // Good + /// Use instead: + /// ```ignore /// fn foo(&[u32]) { .. } /// ``` #[clippy::version = "pre 1.29.0"] @@ -70,15 +71,18 @@ declare_clippy_lint! { /// method instead /// /// ### Example - /// ```ignore - /// // Bad + /// ```rust,ignore + /// use std::ptr; + /// /// if x == ptr::null { - /// .. + /// // .. /// } + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// if x.is_null() { - /// .. + /// // .. /// } /// ``` #[clippy::version = "pre 1.29.0"] @@ -129,12 +133,12 @@ declare_clippy_lint! { /// /// ### Example /// ```ignore - /// // Bad. Undefined behavior + /// // Undefined behavior /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); } /// ``` /// + /// Use instead: /// ```ignore - /// // Good /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); } /// ``` #[clippy::version = "1.53.0"] diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 584b561dcf0ba..be7eae0268771 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -27,11 +27,14 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = vec![1]; + /// # let _ = /// x.iter().zip(0..x.len()); /// ``` - /// Could be written as + /// + /// Use instead: /// ```rust /// # let x = vec![1]; + /// # let _ = /// x.iter().enumerate(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -65,12 +68,21 @@ declare_clippy_lint! { /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// ### Example - /// ```rust,ignore - /// for x..(y+1) { .. } + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..(y+1) { + /// // .. + /// } /// ``` - /// Could be written as - /// ```rust,ignore - /// for x..=y { .. } + /// + /// Use instead: + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..=y { + /// // .. + /// } /// ``` #[clippy::version = "pre 1.29.0"] pub RANGE_PLUS_ONE, @@ -94,12 +106,21 @@ declare_clippy_lint! { /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)). /// /// ### Example - /// ```rust,ignore - /// for x..=(y-1) { .. } + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..=(y-1) { + /// // .. + /// } /// ``` - /// Could be written as - /// ```rust,ignore - /// for x..y { .. } + /// + /// Use instead: + /// ```rust + /// # let x = 0; + /// # let y = 1; + /// for i in x..y { + /// // .. + /// } /// ``` #[clippy::version = "pre 1.29.0"] pub RANGE_MINUS_ONE, diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 5a25008e95e5b..3aa18557d91c9 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -23,12 +23,13 @@ declare_clippy_lint! { /// complexity. /// /// ### Example - /// ```rust,ignore - /// // Bad - /// let a = (|| 42)() + /// ```rust + /// let a = (|| 42)(); + /// ``` /// - /// // Good - /// let a = 42 + /// Use instead: + /// ```rust + /// let a = 42; /// ``` #[clippy::version = "pre 1.29.0"] pub REDUNDANT_CLOSURE_CALL, diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index f789cec6d6acf..a642e2da3ba12 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -21,11 +21,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// let a = f(*&mut b); /// let c = *&d; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust,ignore /// let a = f(b); /// let c = d; /// ``` diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index 91e5e1e8b2892..60be6bd335f68 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -26,19 +26,20 @@ declare_clippy_lint! { /// if it was added on constructors for example. /// /// ### Example - /// Missing attribute /// ```rust /// pub struct Bar; /// impl Bar { - /// // Bad + /// // Missing attribute /// pub fn bar(&self) -> Self { /// Self /// } /// } /// ``` /// - /// It's better to have the `#[must_use]` attribute on the method like this: + /// Use instead: /// ```rust + /// # { + /// // It's better to have the `#[must_use]` attribute on the method like this: /// pub struct Bar; /// impl Bar { /// #[must_use] @@ -46,10 +47,10 @@ declare_clippy_lint! { /// Self /// } /// } - /// ``` + /// # } /// - /// Or on the type definition like this: - /// ```rust + /// # { + /// // Or on the type definition like this: /// #[must_use] /// pub struct Bar; /// impl Bar { @@ -57,6 +58,7 @@ declare_clippy_lint! { /// Self /// } /// } + /// # } /// ``` #[clippy::version = "1.59.0"] pub RETURN_SELF_NOT_MUST_USE, diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs index 4f74c1e44c26d..bf318c055dad1 100644 --- a/clippy_lints/src/shadow.rs +++ b/clippy_lints/src/shadow.rs @@ -23,10 +23,12 @@ declare_clippy_lint! { /// ### Example /// ```rust /// # let x = 1; - /// // Bad /// let x = &x; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let x = 1; /// let y = &x; // use different variable name /// ``` #[clippy::version = "pre 1.29.0"] @@ -79,11 +81,14 @@ declare_clippy_lint! { /// # let y = 1; /// # let z = 2; /// let x = y; - /// - /// // Bad /// let x = z; // shadows the earlier binding + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let y = 1; + /// # let z = 2; + /// let x = y; /// let w = z; // use different variable name /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index b4ad5dcbe3e9a..975a0a06e3885 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -23,15 +23,16 @@ declare_clippy_lint! { /// ```rust /// # use core::iter::repeat; /// # let len = 4; - /// - /// // Bad /// let mut vec1 = Vec::with_capacity(len); /// vec1.resize(len, 0); /// /// let mut vec2 = Vec::with_capacity(len); /// vec2.extend(repeat(0).take(len)); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # let len = 4; /// let mut vec1 = vec![0; len]; /// let mut vec2 = vec![0; len]; /// ``` diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 7c196ccaa8ccd..9ea2de001e2bf 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -99,11 +99,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad - /// let bs = "a byte string".as_bytes(); + /// let bstr = "a byte string".as_bytes(); + /// ``` /// - /// // Good - /// let bs = b"a byte string"; + /// Use instead: + /// ```rust + /// let bstr = b"a byte string"; /// ``` #[clippy::version = "pre 1.29.0"] pub STRING_LIT_AS_BYTES, @@ -223,11 +224,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); + /// # let _ = + /// std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap(); /// ``` - /// could be written as + /// + /// Use instead: /// ```rust - /// let _ = &"Hello World!"[6..11]; + /// # let _ = + /// &"Hello World!"[6..11]; /// ``` #[clippy::version = "1.50.0"] pub STRING_FROM_UTF8_AS_BYTES, diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index b91be0eb4bed8..71957572f2e64 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -29,8 +29,7 @@ declare_clippy_lint! { /// pub fn foo(t: T) where T: Copy, T: Clone {} /// ``` /// - /// Could be written as: - /// + /// Use instead: /// ```rust /// pub fn foo(t: T) where T: Copy + Clone {} /// ``` diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs index afd7be89a4e28..cc64d17be0552 100644 --- a/clippy_lints/src/unicode.rs +++ b/clippy_lints/src/unicode.rs @@ -41,7 +41,8 @@ declare_clippy_lint! { /// ```rust /// let x = String::from("€"); /// ``` - /// Could be written as: + /// + /// Use instead: /// ```rust /// let x = String::from("\u{20ac}"); /// ``` diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 41333bb2addf7..c8ec4442ab1a4 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -17,13 +17,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// async fn get_random_number() -> i64 { /// 4 // Chosen by fair dice roll. Guaranteed to be random. /// } /// let number_future = get_random_number(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// fn get_random_number_improved() -> i64 { /// 4 // Chosen by fair dice roll. Guaranteed to be random. /// } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4a3b5383c892b..fe29bf29d0caf 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -21,11 +21,12 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// // format!() returns a `String` /// let s: String = format!("hello").into(); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let s: String = format!("hello"); /// ``` #[clippy::version = "1.45.0"] diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 60f9887699498..17323e4d0efac 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -89,12 +89,11 @@ declare_clippy_lint! { /// warning/error messages. /// /// ### Example - /// Bad: /// ```rust,ignore /// cx.span_lint(LINT_NAME, "message"); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// utils::span_lint(cx, LINT_NAME, "message"); /// ``` @@ -112,12 +111,11 @@ declare_clippy_lint! { /// `cx.outer_expn_data()` is faster and more concise. /// /// ### Example - /// Bad: /// ```rust,ignore /// expr.span.ctxt().outer().expn_data() /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// expr.span.ctxt().outer_expn_data() /// ``` @@ -135,7 +133,6 @@ declare_clippy_lint! { /// ICE in large quantities can damage your teeth /// /// ### Example - /// Bad: /// ```rust,ignore /// 🍦🍦🍦🍦🍦 /// ``` @@ -153,12 +150,11 @@ declare_clippy_lint! { /// Indicates that the lint is not finished. /// /// ### Example - /// Bad: /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "default lint description" } /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" } /// ``` @@ -183,7 +179,6 @@ declare_clippy_lint! { /// convenient, readable and less error prone. /// /// ### Example - /// Bad: /// ```rust,ignore /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| { /// diag.span_suggestion( @@ -207,7 +202,7 @@ declare_clippy_lint! { /// }); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// span_lint_and_sugg( /// cx, @@ -237,12 +232,11 @@ declare_clippy_lint! { /// `utils::is_type_diagnostic_item()` does not require hardcoded paths. /// /// ### Example - /// Bad: /// ```rust,ignore /// utils::match_type(cx, ty, &paths::VEC) /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// utils::is_type_diagnostic_item(cx, ty, sym::Vec) /// ``` @@ -273,12 +267,11 @@ declare_clippy_lint! { /// It's faster and easier to use the symbol constant. /// /// ### Example - /// Bad: /// ```rust,ignore /// let _ = sym!(f32); /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// let _ = sym::f32; /// ``` @@ -295,12 +288,11 @@ declare_clippy_lint! { /// It's faster use symbols directly instead of strings. /// /// ### Example - /// Bad: /// ```rust,ignore /// symbol.as_str() == "clippy"; /// ``` /// - /// Good: + /// Use instead: /// ```rust,ignore /// symbol == sym::clippy; /// ``` diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index ba1ff65479d60..297a80e5767a1 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -28,12 +28,14 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// # fn foo(my_vec: &[u8]) {} + /// fn foo(_x: &[u8]) {} /// - /// // Bad /// foo(&vec![1, 2]); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # fn foo(_x: &[u8]) {} /// foo(&[1, 2]); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index 2f74eaf3cf5c3..5418eca382da0 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -26,13 +26,18 @@ declare_clippy_lint! { /// still around. /// /// ### Example - /// ```rust,ignore - /// // Bad + /// ```rust /// use std::cmp::Ordering::*; + /// + /// # fn foo(_: std::cmp::Ordering) {} /// foo(Less); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// use std::cmp::Ordering; + /// + /// # fn foo(_: Ordering) {} /// foo(Ordering::Less) /// ``` #[clippy::version = "pre 1.29.0"] @@ -76,14 +81,13 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// // Bad /// use crate1::*; /// /// foo(); /// ``` /// + /// Use instead: /// ```rust,ignore - /// // Good /// use crate1::foo; /// /// foo(); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index d2493c055a519..bb74f2a7f0f28 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -25,10 +25,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// println!(""); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// println!(); /// ``` #[clippy::version = "pre 1.29.0"] @@ -177,10 +178,15 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// // Bad + /// # let _ = /// writeln!(buf, ""); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let _ = /// writeln!(buf); /// ``` #[clippy::version = "pre 1.29.0"] @@ -204,10 +210,16 @@ declare_clippy_lint! { /// # use std::fmt::Write; /// # let mut buf = String::new(); /// # let name = "World"; - /// // Bad + /// # let _ = /// write!(buf, "Hello {}!\n", name); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let name = "World"; + /// # let _ = /// writeln!(buf, "Hello {}!", name); /// ``` #[clippy::version = "pre 1.29.0"] @@ -233,10 +245,15 @@ declare_clippy_lint! { /// ```rust /// # use std::fmt::Write; /// # let mut buf = String::new(); - /// // Bad + /// # let _ = /// writeln!(buf, "{}", "foo"); + /// ``` /// - /// // Good + /// Use instead: + /// ```rust + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let _ = /// writeln!(buf, "foo"); /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index 641681185a2f6..50d3c079fe675 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -14,10 +14,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// // Bad /// let nan = 0.0f32 / 0.0; + /// ``` /// - /// // Good + /// Use instead: + /// ```rust /// let nan = f32::NAN; /// ``` #[clippy::version = "pre 1.29.0"] From 0902f3ca8192a352ab4db24bf4e04bc083792a3c Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 5 Jun 2022 16:06:47 -0400 Subject: [PATCH 18/78] Make [`create_dir`] example ignored --- clippy_lints/src/create_dir.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs index 6bc4054a5abca..18d34370a7b87 100644 --- a/clippy_lints/src/create_dir.rs +++ b/clippy_lints/src/create_dir.rs @@ -15,12 +15,12 @@ declare_clippy_lint! { /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`. /// /// ### Example - /// - /// ```rust + /// ```rust,ignore /// std::fs::create_dir("foo"); /// ``` + /// /// Use instead: - /// ```rust + /// ```rust,ignore /// std::fs::create_dir_all("foo"); /// ``` #[clippy::version = "1.48.0"] From ba43f0aee9c32bad8a3d9fabfd0f442122403aaf Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Fri, 3 Jun 2022 16:12:26 +0200 Subject: [PATCH 19/78] Add new lint [`needless_braces_on_range_literal`] --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/needless_braces_on_range_literal.rs | 67 +++++++++++++++++++ tests/ui/almost_complete_letter_range.fixed | 1 + tests/ui/almost_complete_letter_range.rs | 1 + tests/ui/almost_complete_letter_range.stderr | 24 +++---- .../ui/needless_braces_on_range_literal.fixed | 9 +++ tests/ui/needless_braces_on_range_literal.rs | 9 +++ .../needless_braces_on_range_literal.stderr | 16 +++++ 12 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 clippy_lints/src/needless_braces_on_range_literal.rs create mode 100644 tests/ui/needless_braces_on_range_literal.fixed create mode 100644 tests/ui/needless_braces_on_range_literal.rs create mode 100644 tests/ui/needless_braces_on_range_literal.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ef338b819d4d..ce5fa64c0d109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3596,6 +3596,7 @@ Released 2018-09-13 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference +[`needless_braces_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_braces_on_range_literal [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index d4ec046d0bb08..68c6a33403bf2 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -241,6 +241,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index b927ba3b17c0e..01d7d404cf674 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -405,6 +405,7 @@ store.register_lints(&[ needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, + needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 35575351784a2..f5d89e67fb60e 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -90,6 +90,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), + LintId::of(needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index ee0416fc0ff5e..d43dbc06e8e97 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -312,6 +312,7 @@ mod needless_arbitrary_self_type; mod needless_bitwise_bool; mod needless_bool; mod needless_borrowed_ref; +mod needless_braces_on_range_literal; mod needless_continue; mod needless_for_each; mod needless_late_init; @@ -746,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); + store.register_early_pass(|| Box::new(needless_braces_on_range_literal::NeedlessBracesOnRangeLiteral)); store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|| Box::new(create_dir::CreateDir)); diff --git a/clippy_lints/src/needless_braces_on_range_literal.rs b/clippy_lints/src/needless_braces_on_range_literal.rs new file mode 100644 index 0000000000000..031008c509201 --- /dev/null +++ b/clippy_lints/src/needless_braces_on_range_literal.rs @@ -0,0 +1,67 @@ +//! Checks for braces on literals in range statements +//! +//! For example, the lint would catch +//! +//! ```rust +//! for i in (0)..10 { +//! println!("{i}"); +//! } +//! ``` +//! +//! And suggest this: +//! +//! ```rust +//! for i in 0..10 { +//! println!("{i}"); +//! } +//! ``` +//! +//! This lint is **warn** by default. + +use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; +use rustc_ast::ast::{Expr, ExprKind}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// The lint checks for braces on literals in range statements that are + /// superflous. + /// + /// ### Why is this bad? + /// Having superflous braces makes the code less legible as the impose an + /// overhead when reading. + + #[clippy::version = "1.63.0"] + pub NEEDLESS_BRACES_ON_RANGE_LITERAL, + style, + "needless braces on range literal can be removed" +} + +declare_lint_pass!(NeedlessBracesOnRangeLiteral => [NEEDLESS_BRACES_ON_RANGE_LITERAL]); + +fn check_for_braces(cx: &EarlyContext<'_>, e: &Expr) { + if_chain! { + if let ExprKind::Paren(ref start_statement) = &e.kind; + if let ExprKind::Lit(ref literal) = start_statement.kind; + then { + span_lint_and_then(cx, NEEDLESS_BRACES_ON_RANGE_LITERAL, e.span, + "needless braces on range literal can be removed", + |diag| { + if let Some(suggestion) = snippet_opt(cx, literal.span) { + diag.span_suggestion(e.span, "try", suggestion, Applicability::MachineApplicable); + } + }); + } + } +} + +impl EarlyLintPass for NeedlessBracesOnRangeLiteral { + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { + if let ExprKind::Range(Some(start), Some(end), _) = &e.kind { + check_for_braces(cx, start); + check_for_braces(cx, end); + } + } +} diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index 39f8f0c29495b..d2d1bdb969d52 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -6,6 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] +#![allow(clippy::needless_braces_on_range_literal)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index 3dc0219925760..28965df0805cb 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -6,6 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] +#![allow(clippy::needless_braces_on_range_literal)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.stderr b/tests/ui/almost_complete_letter_range.stderr index 74980ec1a923f..5b5dc40ee54d0 100644 --- a/tests/ui/almost_complete_letter_range.stderr +++ b/tests/ui/almost_complete_letter_range.stderr @@ -1,5 +1,5 @@ error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:19:17 + --> $DIR/almost_complete_letter_range.rs:20:17 | LL | let _ = ('a') ..'z'; | ^^^^^^--^^^ @@ -9,7 +9,7 @@ LL | let _ = ('a') ..'z'; = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:20:17 + --> $DIR/almost_complete_letter_range.rs:21:17 | LL | let _ = 'A' .. ('Z'); | ^^^^--^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = 'A' .. ('Z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:26:13 + --> $DIR/almost_complete_letter_range.rs:27:13 | LL | let _ = (b'a')..(b'z'); | ^^^^^^--^^^^^^ @@ -25,7 +25,7 @@ LL | let _ = (b'a')..(b'z'); | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:27:13 + --> $DIR/almost_complete_letter_range.rs:28:13 | LL | let _ = b'A'..b'Z'; | ^^^^--^^^^ @@ -33,7 +33,7 @@ LL | let _ = b'A'..b'Z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:32:13 + --> $DIR/almost_complete_letter_range.rs:33:13 | LL | let _ = a!()..'z'; | ^^^^--^^^ @@ -41,7 +41,7 @@ LL | let _ = a!()..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:35:9 + --> $DIR/almost_complete_letter_range.rs:36:9 | LL | b'a'..b'z' if true => 1, | ^^^^--^^^^ @@ -49,7 +49,7 @@ LL | b'a'..b'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:36:9 + --> $DIR/almost_complete_letter_range.rs:37:9 | LL | b'A'..b'Z' if true => 2, | ^^^^--^^^^ @@ -57,7 +57,7 @@ LL | b'A'..b'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:43:9 + --> $DIR/almost_complete_letter_range.rs:44:9 | LL | 'a'..'z' if true => 1, | ^^^--^^^ @@ -65,7 +65,7 @@ LL | 'a'..'z' if true => 1, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:44:9 + --> $DIR/almost_complete_letter_range.rs:45:9 | LL | 'A'..'Z' if true => 2, | ^^^--^^^ @@ -73,7 +73,7 @@ LL | 'A'..'Z' if true => 2, | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:54:9 + --> $DIR/almost_complete_letter_range.rs:55:9 | LL | 'a'..'z' => 1, | ^^^--^^^ @@ -81,7 +81,7 @@ LL | 'a'..'z' => 1, | help: use an inclusive range: `...` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:61:13 + --> $DIR/almost_complete_letter_range.rs:62:13 | LL | let _ = 'a'..'z'; | ^^^--^^^ @@ -89,7 +89,7 @@ LL | let _ = 'a'..'z'; | help: use an inclusive range: `..=` error: almost complete ascii letter range - --> $DIR/almost_complete_letter_range.rs:63:9 + --> $DIR/almost_complete_letter_range.rs:64:9 | LL | 'a'..'z' => 1, | ^^^--^^^ diff --git a/tests/ui/needless_braces_on_range_literal.fixed b/tests/ui/needless_braces_on_range_literal.fixed new file mode 100644 index 0000000000000..a6cf610005c38 --- /dev/null +++ b/tests/ui/needless_braces_on_range_literal.fixed @@ -0,0 +1,9 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::needless_braces_on_range_literal)] +#![allow(clippy::almost_complete_letter_range)] + +fn main() { + let _ = 'a'..='z'; +} diff --git a/tests/ui/needless_braces_on_range_literal.rs b/tests/ui/needless_braces_on_range_literal.rs new file mode 100644 index 0000000000000..7c24a4eb49417 --- /dev/null +++ b/tests/ui/needless_braces_on_range_literal.rs @@ -0,0 +1,9 @@ +// run-rustfix +// edition:2018 + +#![warn(clippy::needless_braces_on_range_literal)] +#![allow(clippy::almost_complete_letter_range)] + +fn main() { + let _ = ('a')..=('z'); +} diff --git a/tests/ui/needless_braces_on_range_literal.stderr b/tests/ui/needless_braces_on_range_literal.stderr new file mode 100644 index 0000000000000..581488d19faa4 --- /dev/null +++ b/tests/ui/needless_braces_on_range_literal.stderr @@ -0,0 +1,16 @@ +error: needless braces on range literal can be removed + --> $DIR/needless_braces_on_range_literal.rs:8:13 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'a'` + | + = note: `-D clippy::needless-braces-on-range-literal` implied by `-D warnings` + +error: needless braces on range literal can be removed + --> $DIR/needless_braces_on_range_literal.rs:8:21 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'z'` + +error: aborting due to 2 previous errors + From 3058cb941cd8d81c01b74e4b28b4f4ca606fb20b Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 19:12:32 +0200 Subject: [PATCH 20/78] Update clippy_lints/src/needless_braces_on_range_literal.rs Co-authored-by: Alex <69764315+Serial-ATA@users.noreply.github.com> --- clippy_lints/src/needless_braces_on_range_literal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/needless_braces_on_range_literal.rs b/clippy_lints/src/needless_braces_on_range_literal.rs index 031008c509201..b351dfe20050b 100644 --- a/clippy_lints/src/needless_braces_on_range_literal.rs +++ b/clippy_lints/src/needless_braces_on_range_literal.rs @@ -8,7 +8,7 @@ //! } //! ``` //! -//! And suggest this: +//! Use instead: //! //! ```rust //! for i in 0..10 { From 8bc1be17d633ea5c41d0c8abe24a4a3c6a150740 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 21:45:19 +0200 Subject: [PATCH 21/78] Update clippy_lints/src/needless_braces_on_range_literal.rs Co-authored-by: Alex <69764315+Serial-ATA@users.noreply.github.com> --- clippy_lints/src/needless_braces_on_range_literal.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_lints/src/needless_braces_on_range_literal.rs b/clippy_lints/src/needless_braces_on_range_literal.rs index b351dfe20050b..c96f50122f69d 100644 --- a/clippy_lints/src/needless_braces_on_range_literal.rs +++ b/clippy_lints/src/needless_braces_on_range_literal.rs @@ -16,7 +16,6 @@ //! } //! ``` //! -//! This lint is **warn** by default. use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; From 5948959c7e050a403618b134ec831f6ef1265ddd Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 19:37:28 +0200 Subject: [PATCH 22/78] Fix misnomer braces -> parenthesis --- CHANGELOG.md | 2 +- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 4 ++-- ...rs => needless_parens_on_range_literal.rs} | 24 +++++++++---------- tests/ui/almost_complete_letter_range.fixed | 2 +- tests/ui/almost_complete_letter_range.rs | 2 +- .../needless_braces_on_range_literal.stderr | 16 ------------- ...=> needless_parens_on_range_literal.fixed} | 2 +- ...rs => needless_parens_on_range_literal.rs} | 2 +- .../needless_parens_on_range_literal.stderr | 16 +++++++++++++ 12 files changed, 38 insertions(+), 38 deletions(-) rename clippy_lints/src/{needless_braces_on_range_literal.rs => needless_parens_on_range_literal.rs} (62%) delete mode 100644 tests/ui/needless_braces_on_range_literal.stderr rename tests/ui/{needless_braces_on_range_literal.fixed => needless_parens_on_range_literal.fixed} (69%) rename tests/ui/{needless_braces_on_range_literal.rs => needless_parens_on_range_literal.rs} (70%) create mode 100644 tests/ui/needless_parens_on_range_literal.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ce5fa64c0d109..d3db4be03f101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3596,7 +3596,7 @@ Released 2018-09-13 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference -[`needless_braces_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_braces_on_range_literal +[`needless_parens_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literal [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 68c6a33403bf2..69f6496818c6f 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -241,7 +241,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL), + LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 01d7d404cf674..1dc65dabf84cf 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -405,7 +405,7 @@ store.register_lints(&[ needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL, + needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index f5d89e67fb60e..4518384bd8733 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -90,7 +90,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_braces_on_range_literal::NEEDLESS_BRACES_ON_RANGE_LITERAL), + LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d43dbc06e8e97..f9743056129d1 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -312,10 +312,10 @@ mod needless_arbitrary_self_type; mod needless_bitwise_bool; mod needless_bool; mod needless_borrowed_ref; -mod needless_braces_on_range_literal; mod needless_continue; mod needless_for_each; mod needless_late_init; +mod needless_parens_on_range_literal; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -747,7 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); - store.register_early_pass(|| Box::new(needless_braces_on_range_literal::NeedlessBracesOnRangeLiteral)); + store.register_early_pass(|| Box::new(needless_parens_on_range_literal::NeedlessParensOnRangeLiteral)); store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|| Box::new(create_dir::CreateDir)); diff --git a/clippy_lints/src/needless_braces_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs similarity index 62% rename from clippy_lints/src/needless_braces_on_range_literal.rs rename to clippy_lints/src/needless_parens_on_range_literal.rs index c96f50122f69d..50a393311cdc3 100644 --- a/clippy_lints/src/needless_braces_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -1,4 +1,4 @@ -//! Checks for braces on literals in range statements +//! Checks for parantheses on literals in range statements //! //! For example, the lint would catch //! @@ -25,28 +25,28 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// The lint checks for braces on literals in range statements that are + /// The lint checks for parenthesis on literals in range statements that are /// superflous. /// /// ### Why is this bad? - /// Having superflous braces makes the code less legible as the impose an + /// Having superflous parenthesis makes the code less legible as the impose an /// overhead when reading. #[clippy::version = "1.63.0"] - pub NEEDLESS_BRACES_ON_RANGE_LITERAL, + pub NEEDLESS_PARENS_ON_RANGE_LITERAL, style, - "needless braces on range literal can be removed" + "needless parenthesis on range literal can be removed" } -declare_lint_pass!(NeedlessBracesOnRangeLiteral => [NEEDLESS_BRACES_ON_RANGE_LITERAL]); +declare_lint_pass!(NeedlessParensOnRangeLiteral => [NEEDLESS_PARENS_ON_RANGE_LITERAL]); -fn check_for_braces(cx: &EarlyContext<'_>, e: &Expr) { +fn check_for_parens(cx: &EarlyContext<'_>, e: &Expr) { if_chain! { if let ExprKind::Paren(ref start_statement) = &e.kind; if let ExprKind::Lit(ref literal) = start_statement.kind; then { - span_lint_and_then(cx, NEEDLESS_BRACES_ON_RANGE_LITERAL, e.span, - "needless braces on range literal can be removed", + span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERAL, e.span, + "needless parenthesis on range literal can be removed", |diag| { if let Some(suggestion) = snippet_opt(cx, literal.span) { diag.span_suggestion(e.span, "try", suggestion, Applicability::MachineApplicable); @@ -56,11 +56,11 @@ fn check_for_braces(cx: &EarlyContext<'_>, e: &Expr) { } } -impl EarlyLintPass for NeedlessBracesOnRangeLiteral { +impl EarlyLintPass for NeedlessParensOnRangeLiteral { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { if let ExprKind::Range(Some(start), Some(end), _) = &e.kind { - check_for_braces(cx, start); - check_for_braces(cx, end); + check_for_parens(cx, start); + check_for_parens(cx, end); } } } diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index d2d1bdb969d52..83a6bf14c3f3b 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -6,7 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] -#![allow(clippy::needless_braces_on_range_literal)] +#![allow(clippy::needless_parens_on_range_literal)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index 28965df0805cb..a8da9266ad768 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -6,7 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] -#![allow(clippy::needless_braces_on_range_literal)] +#![allow(clippy::needless_parens_on_range_literal)] macro_rules! a { () => { diff --git a/tests/ui/needless_braces_on_range_literal.stderr b/tests/ui/needless_braces_on_range_literal.stderr deleted file mode 100644 index 581488d19faa4..0000000000000 --- a/tests/ui/needless_braces_on_range_literal.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: needless braces on range literal can be removed - --> $DIR/needless_braces_on_range_literal.rs:8:13 - | -LL | let _ = ('a')..=('z'); - | ^^^^^ help: try: `'a'` - | - = note: `-D clippy::needless-braces-on-range-literal` implied by `-D warnings` - -error: needless braces on range literal can be removed - --> $DIR/needless_braces_on_range_literal.rs:8:21 - | -LL | let _ = ('a')..=('z'); - | ^^^^^ help: try: `'z'` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/needless_braces_on_range_literal.fixed b/tests/ui/needless_parens_on_range_literal.fixed similarity index 69% rename from tests/ui/needless_braces_on_range_literal.fixed rename to tests/ui/needless_parens_on_range_literal.fixed index a6cf610005c38..ba903fd688dd3 100644 --- a/tests/ui/needless_braces_on_range_literal.fixed +++ b/tests/ui/needless_parens_on_range_literal.fixed @@ -1,7 +1,7 @@ // run-rustfix // edition:2018 -#![warn(clippy::needless_braces_on_range_literal)] +#![warn(clippy::needless_parens_on_range_literal)] #![allow(clippy::almost_complete_letter_range)] fn main() { diff --git a/tests/ui/needless_braces_on_range_literal.rs b/tests/ui/needless_parens_on_range_literal.rs similarity index 70% rename from tests/ui/needless_braces_on_range_literal.rs rename to tests/ui/needless_parens_on_range_literal.rs index 7c24a4eb49417..7fddd6bf782a0 100644 --- a/tests/ui/needless_braces_on_range_literal.rs +++ b/tests/ui/needless_parens_on_range_literal.rs @@ -1,7 +1,7 @@ // run-rustfix // edition:2018 -#![warn(clippy::needless_braces_on_range_literal)] +#![warn(clippy::needless_parens_on_range_literal)] #![allow(clippy::almost_complete_letter_range)] fn main() { diff --git a/tests/ui/needless_parens_on_range_literal.stderr b/tests/ui/needless_parens_on_range_literal.stderr new file mode 100644 index 0000000000000..f9655e8948dcd --- /dev/null +++ b/tests/ui/needless_parens_on_range_literal.stderr @@ -0,0 +1,16 @@ +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:8:13 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'a'` + | + = note: `-D clippy::needless-parens-on-range-literal` implied by `-D warnings` + +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:8:21 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'z'` + +error: aborting due to 2 previous errors + From 90c8463d7e213b8d15274db1fa9c413938b5ae02 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 21:44:37 +0200 Subject: [PATCH 23/78] Rewrite check to account for floating point literals --- clippy_lints/src/lib.rs | 2 +- .../src/needless_parens_on_range_literal.rs | 67 +++++++++++++------ .../ui/needless_parens_on_range_literal.fixed | 3 + tests/ui/needless_parens_on_range_literal.rs | 3 + .../needless_parens_on_range_literal.stderr | 8 ++- 5 files changed, 61 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f9743056129d1..19e1247d642c7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -747,7 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); - store.register_early_pass(|| Box::new(needless_parens_on_range_literal::NeedlessParensOnRangeLiteral)); + store.register_late_pass(|| Box::new(needless_parens_on_range_literal::NeedlessParensOnRangeLiteral)); store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|| Box::new(create_dir::CreateDir)); diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs index 50a393311cdc3..81c7f7127703c 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -17,10 +17,17 @@ //! ``` //! -use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt}; -use rustc_ast::ast::{Expr, ExprKind}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + higher, + source::{snippet, snippet_opt}, +}; + +use rustc_ast::ast; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{Expr, ExprKind}; + +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -40,27 +47,47 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessParensOnRangeLiteral => [NEEDLESS_PARENS_ON_RANGE_LITERAL]); -fn check_for_parens(cx: &EarlyContext<'_>, e: &Expr) { - if_chain! { - if let ExprKind::Paren(ref start_statement) = &e.kind; - if let ExprKind::Lit(ref literal) = start_statement.kind; - then { - span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERAL, e.span, - "needless parenthesis on range literal can be removed", - |diag| { - if let Some(suggestion) = snippet_opt(cx, literal.span) { - diag.span_suggestion(e.span, "try", suggestion, Applicability::MachineApplicable); - } - }); +fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool { + snippet.starts_with('(') && snippet.ends_with(')') +} + +fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { + if is_start && + let ExprKind::Lit(ref literal) = e.kind && + let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node + { + // don't check floating point literals on the start expression of a range + return; } + if_chain! { + if let ExprKind::Lit(ref literal) = e.kind; + // the indicator that paranthese surround the literal is that span of the expression and the literal differ + if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo); + // inspect the source code of the expression for parenthesis + if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); + then { + span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERAL, e.span, + "needless parenthesis on range literal can be removed", + |diag| { + if let Some(suggestion) = snippet_opt(cx, literal.span) { + diag.span_suggestion(e.span, "try", suggestion, Applicability::MachineApplicable); + } + }); + } } } -impl EarlyLintPass for NeedlessParensOnRangeLiteral { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { - if let ExprKind::Range(Some(start), Some(end), _) = &e.kind { - check_for_parens(cx, start); - check_for_parens(cx, end); +impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiteral { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + // if let higher::Range { start, end, limits } = &e.kind { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::Range::hir(expr) + { + check_for_parens(cx, start, true); + check_for_parens(cx, end, false); } } } diff --git a/tests/ui/needless_parens_on_range_literal.fixed b/tests/ui/needless_parens_on_range_literal.fixed index ba903fd688dd3..7b1d09909fcc7 100644 --- a/tests/ui/needless_parens_on_range_literal.fixed +++ b/tests/ui/needless_parens_on_range_literal.fixed @@ -6,4 +6,7 @@ fn main() { let _ = 'a'..='z'; + let _ = 'a'..='z'; + let _ = (1.)..2.; + let _ = (1.)..2.; } diff --git a/tests/ui/needless_parens_on_range_literal.rs b/tests/ui/needless_parens_on_range_literal.rs index 7fddd6bf782a0..306498c0faf96 100644 --- a/tests/ui/needless_parens_on_range_literal.rs +++ b/tests/ui/needless_parens_on_range_literal.rs @@ -6,4 +6,7 @@ fn main() { let _ = ('a')..=('z'); + let _ = 'a'..='z'; + let _ = (1.)..2.; + let _ = (1.)..(2.); } diff --git a/tests/ui/needless_parens_on_range_literal.stderr b/tests/ui/needless_parens_on_range_literal.stderr index f9655e8948dcd..3a646c6b22c68 100644 --- a/tests/ui/needless_parens_on_range_literal.stderr +++ b/tests/ui/needless_parens_on_range_literal.stderr @@ -12,5 +12,11 @@ error: needless parenthesis on range literal can be removed LL | let _ = ('a')..=('z'); | ^^^^^ help: try: `'z'` -error: aborting due to 2 previous errors +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:11:19 + | +LL | let _ = (1.)..(2.); + | ^^^^ help: try: `2.` + +error: aborting due to 3 previous errors From 2e6903ccf9310ffdbfdcb00baa2bda63bf9c6a73 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 21:48:44 +0200 Subject: [PATCH 24/78] Move description into lint macro --- .../src/needless_parens_on_range_literal.rs | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs index 81c7f7127703c..f02323bd109e8 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -1,22 +1,3 @@ -//! Checks for parantheses on literals in range statements -//! -//! For example, the lint would catch -//! -//! ```rust -//! for i in (0)..10 { -//! println!("{i}"); -//! } -//! ``` -//! -//! Use instead: -//! -//! ```rust -//! for i in 0..10 { -//! println!("{i}"); -//! } -//! ``` -//! - use clippy_utils::{ diagnostics::span_lint_and_then, higher, @@ -38,7 +19,22 @@ declare_clippy_lint! { /// ### Why is this bad? /// Having superflous parenthesis makes the code less legible as the impose an /// overhead when reading. - + /// + /// ### Example + /// + /// ```rust + /// for i in (0)..10 { + /// println!("{i}"); + /// } + /// ``` + /// + /// Use instead: + /// + /// ```rust + /// for i in 0..10 { + /// println!("{i}"); + /// } + /// ``` #[clippy::version = "1.63.0"] pub NEEDLESS_PARENS_ON_RANGE_LITERAL, style, From 0409306bb02a73d9299410b42c9cd774a216090b Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 22:01:52 +0200 Subject: [PATCH 25/78] Implement support for implicit start and end --- .../src/needless_parens_on_range_literal.rs | 16 +++++++-------- .../ui/needless_parens_on_range_literal.fixed | 4 +++- tests/ui/needless_parens_on_range_literal.rs | 4 +++- .../needless_parens_on_range_literal.stderr | 20 ++++++++++++++++++- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs index f02323bd109e8..1979810f7c4d6 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -75,15 +75,13 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // if let higher::Range { start, end, limits } = &e.kind { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::Range::hir(expr) - { - check_for_parens(cx, start, true); - check_for_parens(cx, end, false); + if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) { + if let Some(start) = start { + check_for_parens(cx, start, true); + } + if let Some(end) = end { + check_for_parens(cx, end, false); + } } } } diff --git a/tests/ui/needless_parens_on_range_literal.fixed b/tests/ui/needless_parens_on_range_literal.fixed index 7b1d09909fcc7..e76189bd2a3f0 100644 --- a/tests/ui/needless_parens_on_range_literal.fixed +++ b/tests/ui/needless_parens_on_range_literal.fixed @@ -6,7 +6,9 @@ fn main() { let _ = 'a'..='z'; - let _ = 'a'..='z'; + let _ = 'a'..'z'; let _ = (1.)..2.; let _ = (1.)..2.; + let _ = 'a'..; + let _ = ..'z'; } diff --git a/tests/ui/needless_parens_on_range_literal.rs b/tests/ui/needless_parens_on_range_literal.rs index 306498c0faf96..aa4bcd40065e5 100644 --- a/tests/ui/needless_parens_on_range_literal.rs +++ b/tests/ui/needless_parens_on_range_literal.rs @@ -6,7 +6,9 @@ fn main() { let _ = ('a')..=('z'); - let _ = 'a'..='z'; + let _ = 'a'..('z'); let _ = (1.)..2.; let _ = (1.)..(2.); + let _ = ('a')..; + let _ = ..('z'); } diff --git a/tests/ui/needless_parens_on_range_literal.stderr b/tests/ui/needless_parens_on_range_literal.stderr index 3a646c6b22c68..e971f9cd629e5 100644 --- a/tests/ui/needless_parens_on_range_literal.stderr +++ b/tests/ui/needless_parens_on_range_literal.stderr @@ -12,11 +12,29 @@ error: needless parenthesis on range literal can be removed LL | let _ = ('a')..=('z'); | ^^^^^ help: try: `'z'` +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:9:18 + | +LL | let _ = 'a'..('z'); + | ^^^^^ help: try: `'z'` + error: needless parenthesis on range literal can be removed --> $DIR/needless_parens_on_range_literal.rs:11:19 | LL | let _ = (1.)..(2.); | ^^^^ help: try: `2.` -error: aborting due to 3 previous errors +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:12:13 + | +LL | let _ = ('a')..; + | ^^^^^ help: try: `'a'` + +error: needless parenthesis on range literal can be removed + --> $DIR/needless_parens_on_range_literal.rs:13:15 + | +LL | let _ = ..('z'); + | ^^^^^ help: try: `'z'` + +error: aborting due to 6 previous errors From c0aa1f78b488e0c99441bd19cb31001d1daab8cf Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 22:08:33 +0200 Subject: [PATCH 26/78] Implement suggestion generation with snippet_with_applicability(.) --- clippy_lints/src/needless_parens_on_range_literal.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs index 1979810f7c4d6..89cb015b7d476 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -1,7 +1,7 @@ use clippy_utils::{ diagnostics::span_lint_and_then, higher, - source::{snippet, snippet_opt}, + source::{snippet, snippet_with_applicability}, }; use rustc_ast::ast; @@ -62,12 +62,12 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { // inspect the source code of the expression for parenthesis if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); then { + let mut applicability = Applicability::MachineApplicable; span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERAL, e.span, "needless parenthesis on range literal can be removed", |diag| { - if let Some(suggestion) = snippet_opt(cx, literal.span) { - diag.span_suggestion(e.span, "try", suggestion, Applicability::MachineApplicable); - } + let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); + diag.span_suggestion(e.span, "try", suggestion, applicability); }); } } From 58a605f45301717219242c9bbd38764072092d01 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sat, 4 Jun 2022 22:12:35 +0200 Subject: [PATCH 27/78] Rerun cargo dev update_lints --- CHANGELOG.md | 2 +- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3db4be03f101..5d2b630096961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3596,7 +3596,6 @@ Released 2018-09-13 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool [`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow [`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference -[`needless_parens_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literal [`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect [`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main @@ -3606,6 +3605,7 @@ Released 2018-09-13 [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take +[`needless_parens_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literal [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 69f6496818c6f..0c6d01083c51b 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -241,8 +241,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), + LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1dc65dabf84cf..eb46a719bbf23 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -405,10 +405,10 @@ store.register_lints(&[ needless_bool::BOOL_COMPARISON, needless_bool::NEEDLESS_BOOL, needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, + needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 4518384bd8733..37c55ae4916c3 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -90,8 +90,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), + LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), From bf7a7864815f0e42ab020f768aaf8c671895d727 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 5 Jun 2022 21:41:00 +0200 Subject: [PATCH 28/78] Apply suggestions from code review Co-authored-by: dswij --- clippy_lints/src/needless_parens_on_range_literal.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literal.rs index 89cb015b7d476..b9f618d5f564c 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literal.rs @@ -14,10 +14,10 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does /// The lint checks for parenthesis on literals in range statements that are - /// superflous. + /// superfluous. /// /// ### Why is this bad? - /// Having superflous parenthesis makes the code less legible as the impose an + /// Having superfluous parenthesis makes the code less readable /// overhead when reading. /// /// ### Example @@ -57,7 +57,7 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { } if_chain! { if let ExprKind::Lit(ref literal) = e.kind; - // the indicator that paranthese surround the literal is that span of the expression and the literal differ + // the indicator that parenthesis surround the literal is that the span of the expression and the literal differ if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo); // inspect the source code of the expression for parenthesis if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); From 8ba377a78329824da2dac54cd35a86ab6c13f686 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 5 Jun 2022 22:12:30 +0200 Subject: [PATCH 29/78] Fix names to use plural --- clippy_lints/src/lib.register_all.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_style.rs | 2 +- clippy_lints/src/lib.rs | 4 +- ...s => needless_parens_on_range_literals.rs} | 12 +++--- tests/ui/almost_complete_letter_range.fixed | 2 +- tests/ui/almost_complete_letter_range.rs | 2 +- .../needless_parens_on_range_literal.stderr | 40 ------------------- ...> needless_parens_on_range_literals.fixed} | 2 +- ...s => needless_parens_on_range_literals.rs} | 2 +- .../needless_parens_on_range_literals.stderr | 40 +++++++++++++++++++ 11 files changed, 55 insertions(+), 55 deletions(-) rename clippy_lints/src/{needless_parens_on_range_literal.rs => needless_parens_on_range_literals.rs} (88%) delete mode 100644 tests/ui/needless_parens_on_range_literal.stderr rename tests/ui/{needless_parens_on_range_literal.fixed => needless_parens_on_range_literals.fixed} (81%) rename tests/ui/{needless_parens_on_range_literal.rs => needless_parens_on_range_literals.rs} (81%) create mode 100644 tests/ui/needless_parens_on_range_literals.stderr diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 0c6d01083c51b..4691f957cb4ed 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -242,7 +242,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), + LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index eb46a719bbf23..efb073af6b6e9 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -408,7 +408,7 @@ store.register_lints(&[ needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, needless_late_init::NEEDLESS_LATE_INIT, - needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL, + needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 37c55ae4916c3..b6992ae0ad25f 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -91,7 +91,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literal::NEEDLESS_PARENS_ON_RANGE_LITERAL), + LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), LintId::of(neg_multiply::NEG_MULTIPLY), LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19e1247d642c7..a0a6bab407c39 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -315,7 +315,7 @@ mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; mod needless_late_init; -mod needless_parens_on_range_literal; +mod needless_parens_on_range_literals; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -747,7 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf)); store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements)); store.register_early_pass(|| Box::new(precedence::Precedence)); - store.register_late_pass(|| Box::new(needless_parens_on_range_literal::NeedlessParensOnRangeLiteral)); + store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|| Box::new(create_dir::CreateDir)); diff --git a/clippy_lints/src/needless_parens_on_range_literal.rs b/clippy_lints/src/needless_parens_on_range_literals.rs similarity index 88% rename from clippy_lints/src/needless_parens_on_range_literal.rs rename to clippy_lints/src/needless_parens_on_range_literals.rs index b9f618d5f564c..6e54b243c0371 100644 --- a/clippy_lints/src/needless_parens_on_range_literal.rs +++ b/clippy_lints/src/needless_parens_on_range_literals.rs @@ -36,12 +36,12 @@ declare_clippy_lint! { /// } /// ``` #[clippy::version = "1.63.0"] - pub NEEDLESS_PARENS_ON_RANGE_LITERAL, + pub NEEDLESS_PARENS_ON_RANGE_LITERALS, style, - "needless parenthesis on range literal can be removed" + "needless parenthesis on range literals can be removed" } -declare_lint_pass!(NeedlessParensOnRangeLiteral => [NEEDLESS_PARENS_ON_RANGE_LITERAL]); +declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]); fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool { snippet.starts_with('(') && snippet.ends_with(')') @@ -63,8 +63,8 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, "")); then { let mut applicability = Applicability::MachineApplicable; - span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERAL, e.span, - "needless parenthesis on range literal can be removed", + span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span, + "needless parenthesis on range literals can be removed", |diag| { let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability); diag.span_suggestion(e.span, "try", suggestion, applicability); @@ -73,7 +73,7 @@ fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) { } } -impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiteral { +impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) { if let Some(start) = start { diff --git a/tests/ui/almost_complete_letter_range.fixed b/tests/ui/almost_complete_letter_range.fixed index 83a6bf14c3f3b..e69b40f35f4c6 100644 --- a/tests/ui/almost_complete_letter_range.fixed +++ b/tests/ui/almost_complete_letter_range.fixed @@ -6,7 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] -#![allow(clippy::needless_parens_on_range_literal)] +#![allow(clippy::needless_parens_on_range_literals)] macro_rules! a { () => { diff --git a/tests/ui/almost_complete_letter_range.rs b/tests/ui/almost_complete_letter_range.rs index a8da9266ad768..f2240981d45fa 100644 --- a/tests/ui/almost_complete_letter_range.rs +++ b/tests/ui/almost_complete_letter_range.rs @@ -6,7 +6,7 @@ #![feature(stmt_expr_attributes)] #![warn(clippy::almost_complete_letter_range)] #![allow(ellipsis_inclusive_range_patterns)] -#![allow(clippy::needless_parens_on_range_literal)] +#![allow(clippy::needless_parens_on_range_literals)] macro_rules! a { () => { diff --git a/tests/ui/needless_parens_on_range_literal.stderr b/tests/ui/needless_parens_on_range_literal.stderr deleted file mode 100644 index e971f9cd629e5..0000000000000 --- a/tests/ui/needless_parens_on_range_literal.stderr +++ /dev/null @@ -1,40 +0,0 @@ -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:8:13 - | -LL | let _ = ('a')..=('z'); - | ^^^^^ help: try: `'a'` - | - = note: `-D clippy::needless-parens-on-range-literal` implied by `-D warnings` - -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:8:21 - | -LL | let _ = ('a')..=('z'); - | ^^^^^ help: try: `'z'` - -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:9:18 - | -LL | let _ = 'a'..('z'); - | ^^^^^ help: try: `'z'` - -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:11:19 - | -LL | let _ = (1.)..(2.); - | ^^^^ help: try: `2.` - -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:12:13 - | -LL | let _ = ('a')..; - | ^^^^^ help: try: `'a'` - -error: needless parenthesis on range literal can be removed - --> $DIR/needless_parens_on_range_literal.rs:13:15 - | -LL | let _ = ..('z'); - | ^^^^^ help: try: `'z'` - -error: aborting due to 6 previous errors - diff --git a/tests/ui/needless_parens_on_range_literal.fixed b/tests/ui/needless_parens_on_range_literals.fixed similarity index 81% rename from tests/ui/needless_parens_on_range_literal.fixed rename to tests/ui/needless_parens_on_range_literals.fixed index e76189bd2a3f0..1bd75c806bc94 100644 --- a/tests/ui/needless_parens_on_range_literal.fixed +++ b/tests/ui/needless_parens_on_range_literals.fixed @@ -1,7 +1,7 @@ // run-rustfix // edition:2018 -#![warn(clippy::needless_parens_on_range_literal)] +#![warn(clippy::needless_parens_on_range_literals)] #![allow(clippy::almost_complete_letter_range)] fn main() { diff --git a/tests/ui/needless_parens_on_range_literal.rs b/tests/ui/needless_parens_on_range_literals.rs similarity index 81% rename from tests/ui/needless_parens_on_range_literal.rs rename to tests/ui/needless_parens_on_range_literals.rs index aa4bcd40065e5..7abb8a1adc1bc 100644 --- a/tests/ui/needless_parens_on_range_literal.rs +++ b/tests/ui/needless_parens_on_range_literals.rs @@ -1,7 +1,7 @@ // run-rustfix // edition:2018 -#![warn(clippy::needless_parens_on_range_literal)] +#![warn(clippy::needless_parens_on_range_literals)] #![allow(clippy::almost_complete_letter_range)] fn main() { diff --git a/tests/ui/needless_parens_on_range_literals.stderr b/tests/ui/needless_parens_on_range_literals.stderr new file mode 100644 index 0000000000000..505f7ac916dda --- /dev/null +++ b/tests/ui/needless_parens_on_range_literals.stderr @@ -0,0 +1,40 @@ +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:8:13 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'a'` + | + = note: `-D clippy::needless-parens-on-range-literals` implied by `-D warnings` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:8:21 + | +LL | let _ = ('a')..=('z'); + | ^^^^^ help: try: `'z'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:9:18 + | +LL | let _ = 'a'..('z'); + | ^^^^^ help: try: `'z'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:11:19 + | +LL | let _ = (1.)..(2.); + | ^^^^ help: try: `2.` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:12:13 + | +LL | let _ = ('a')..; + | ^^^^^ help: try: `'a'` + +error: needless parenthesis on range literals can be removed + --> $DIR/needless_parens_on_range_literals.rs:13:15 + | +LL | let _ = ..('z'); + | ^^^^^ help: try: `'z'` + +error: aborting due to 6 previous errors + From 61d7dd2af08c995283eda6580cbbcad15897fa57 Mon Sep 17 00:00:00 2001 From: Dennis Luxen Date: Sun, 5 Jun 2022 22:13:08 +0200 Subject: [PATCH 30/78] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d2b630096961..462e56892fd99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3605,7 +3605,7 @@ Released 2018-09-13 [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take -[`needless_parens_on_range_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literal +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop From a613460e8ade73dc387c19b0a45588681de46fef Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 6 Jun 2022 11:51:36 +0200 Subject: [PATCH 31/78] Fix `#[expect]` for `needless_borrow`, `ref_binding_to_ref` --- clippy_lints/src/dereference.rs | 45 ++++++++++++++---------- tests/ui/needless_borrow.fixed | 8 +++++ tests/ui/needless_borrow.rs | 8 +++++ tests/ui/needless_borrow.stderr | 32 ++++++++--------- tests/ui/ref_binding_to_reference.rs | 10 ++++++ tests/ui/ref_binding_to_reference.stderr | 14 ++++---- 6 files changed, 75 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 8288f7a8b9b62..6d32cc8253f10 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::peel_mid_ty_refs; @@ -131,6 +131,7 @@ pub struct Dereferencing { struct StateData { /// Span of the top level expression span: Span, + hir_id: HirId, } enum State { @@ -165,6 +166,8 @@ struct RefPat { app: Applicability, /// All the replacements which need to be made. replacements: Vec<(Span, String)>, + /// The [`HirId`] that the lint should be emitted at. + hir_id: HirId, } impl<'tcx> LateLintPass<'tcx> for Dereferencing { @@ -218,7 +221,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), target_mut, }, - StateData { span: expr.span }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + }, )); }, RefOp::AddrOf => { @@ -290,7 +296,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { required_precedence, msg, }, - StateData { span: expr.span }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + }, )); } }, @@ -383,6 +392,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { spans: vec![pat.span], app, replacements: vec![(pat.span, snip.into())], + hir_id: pat.hir_id }), ); } @@ -395,13 +405,15 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) { let replacements = pat.replacements; let app = pat.app; - span_lint_and_then( + let lint = if pat.always_deref { + NEEDLESS_BORROW + } else { + REF_BINDING_TO_REFERENCE + }; + span_lint_hir_and_then( cx, - if pat.always_deref { - NEEDLESS_BORROW - } else { - REF_BINDING_TO_REFERENCE - }, + lint, + pat.hir_id, pat.spans, "this pattern creates a reference to a reference", |diag| { @@ -638,19 +650,14 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S } => { let mut app = Applicability::MachineApplicable; let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; - span_lint_and_sugg( - cx, - NEEDLESS_BORROW, - data.span, - msg, - "change this to", - if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) { + span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| { + let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) { format!("({})", snip) } else { snip.into() - }, - app, - ); + }; + diag.span_suggestion(data.span, "change this to", sugg, app); + }); }, } } diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index efeb5cf5b2b25..6b1576d67329d 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -1,5 +1,7 @@ // run-rustfix +#![feature(lint_reasons)] + #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] fn main() { @@ -96,3 +98,9 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} + +fn check_expect_suppression() { + let a = 5; + #[expect(clippy::needless_borrow)] + let _ = x(&&a); +} diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index 3e416a0eb84aa..ebe76361db404 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -1,5 +1,7 @@ // run-rustfix +#![feature(lint_reasons)] + #[warn(clippy::all, clippy::needless_borrow)] #[allow(unused_variables, clippy::unnecessary_mut_passed)] fn main() { @@ -96,3 +98,9 @@ trait Trait {} impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} + +fn check_expect_suppression() { + let a = 5; + #[expect(clippy::needless_borrow)] + let _ = x(&&a); +} diff --git a/tests/ui/needless_borrow.stderr b/tests/ui/needless_borrow.stderr index 05591ce4117b2..be59d8f546d23 100644 --- a/tests/ui/needless_borrow.stderr +++ b/tests/ui/needless_borrow.stderr @@ -1,5 +1,5 @@ error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:9:15 + --> $DIR/needless_borrow.rs:11:15 | LL | let _ = x(&&a); // warn | ^^^ help: change this to: `&a` @@ -7,91 +7,91 @@ LL | let _ = x(&&a); // warn = note: `-D clippy::needless-borrow` implied by `-D warnings` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:13:13 + --> $DIR/needless_borrow.rs:15:13 | LL | mut_ref(&mut &mut b); // warn | ^^^^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:25:13 + --> $DIR/needless_borrow.rs:27:13 | LL | &&a | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:27:15 + --> $DIR/needless_borrow.rs:29:15 | LL | 46 => &&a, | ^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:33:27 + --> $DIR/needless_borrow.rs:35:27 | LL | break &ref_a; | ^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:40:15 + --> $DIR/needless_borrow.rs:42:15 | LL | let _ = x(&&&a); | ^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:41:15 + --> $DIR/needless_borrow.rs:43:15 | LL | let _ = x(&mut &&a); | ^^^^^^^^ help: change this to: `&a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:42:15 + --> $DIR/needless_borrow.rs:44:15 | LL | let _ = x(&&&mut b); | ^^^^^^^^ help: change this to: `&mut b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:43:15 + --> $DIR/needless_borrow.rs:45:15 | LL | let _ = x(&&ref_a); | ^^^^^^^ help: change this to: `ref_a` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:46:11 + --> $DIR/needless_borrow.rs:48:11 | LL | x(&b); | ^^ help: change this to: `b` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:53:13 + --> $DIR/needless_borrow.rs:55:13 | LL | mut_ref(&mut x); | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:54:13 + --> $DIR/needless_borrow.rs:56:13 | LL | mut_ref(&mut &mut x); | ^^^^^^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:55:23 + --> $DIR/needless_borrow.rs:57:23 | LL | let y: &mut i32 = &mut x; | ^^^^^^ help: change this to: `x` error: this expression creates a reference which is immediately dereferenced by the compiler - --> $DIR/needless_borrow.rs:56:23 + --> $DIR/needless_borrow.rs:58:23 | LL | let y: &mut i32 = &mut &mut x; | ^^^^^^^^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:72:13 + --> $DIR/needless_borrow.rs:74:13 | LL | let _ = (&x).0; | ^^^^ help: change this to: `x` error: this expression borrows a value the compiler would automatically borrow - --> $DIR/needless_borrow.rs:74:22 + --> $DIR/needless_borrow.rs:76:22 | LL | let _ = unsafe { (&*x).0 }; | ^^^^^ help: change this to: `(*x)` diff --git a/tests/ui/ref_binding_to_reference.rs b/tests/ui/ref_binding_to_reference.rs index fe742a4c2f4c5..570ef406e4a99 100644 --- a/tests/ui/ref_binding_to_reference.rs +++ b/tests/ui/ref_binding_to_reference.rs @@ -1,5 +1,6 @@ // FIXME: run-rustfix waiting on multi-span suggestions +#![feature(lint_reasons)] #![warn(clippy::ref_binding_to_reference)] #![allow(clippy::needless_borrowed_reference)] @@ -73,3 +74,12 @@ impl T1 for S { let _: &&String = x; } } + +fn check_expect_suppression() { + let x = String::new(); + #[expect(clippy::ref_binding_to_reference)] + let _: &&String = match Some(&x) { + Some(ref x) => x, + None => return, + }; +} diff --git a/tests/ui/ref_binding_to_reference.stderr b/tests/ui/ref_binding_to_reference.stderr index c5856e15fa987..eb36cd516a246 100644 --- a/tests/ui/ref_binding_to_reference.stderr +++ b/tests/ui/ref_binding_to_reference.stderr @@ -1,5 +1,5 @@ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:30:14 + --> $DIR/ref_binding_to_reference.rs:31:14 | LL | Some(ref x) => x, | ^^^^^ @@ -11,7 +11,7 @@ LL | Some(x) => &x, | ~ ~~ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:36:14 + --> $DIR/ref_binding_to_reference.rs:37:14 | LL | Some(ref x) => { | ^^^^^ @@ -25,7 +25,7 @@ LL ~ &x | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:46:14 + --> $DIR/ref_binding_to_reference.rs:47:14 | LL | Some(ref x) => m2!(x), | ^^^^^ @@ -36,7 +36,7 @@ LL | Some(x) => m2!(&x), | ~ ~~ error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:51:15 + --> $DIR/ref_binding_to_reference.rs:52:15 | LL | let _ = |&ref x: &&String| { | ^^^^^ @@ -48,7 +48,7 @@ LL ~ let _: &&String = &x; | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:57:12 + --> $DIR/ref_binding_to_reference.rs:58:12 | LL | fn f2<'a>(&ref x: &&'a String) -> &'a String { | ^^^^^ @@ -61,7 +61,7 @@ LL ~ x | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:64:11 + --> $DIR/ref_binding_to_reference.rs:65:11 | LL | fn f(&ref x: &&String) { | ^^^^^ @@ -73,7 +73,7 @@ LL ~ let _: &&String = &x; | error: this pattern creates a reference to a reference - --> $DIR/ref_binding_to_reference.rs:72:11 + --> $DIR/ref_binding_to_reference.rs:73:11 | LL | fn f(&ref x: &&String) { | ^^^^^ From 7e1730e16c779f688392e0edcd553036b2afe1f1 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 6 Jun 2022 12:36:57 +0200 Subject: [PATCH 32/78] Fix `#[expect]` for `same_name_method` --- clippy_lints/src/same_name_method.rs | 17 ++++++++++------- tests/ui/same_name_method.rs | 16 ++++++++++++++++ tests/ui/same_name_method.stderr | 20 ++++++++++---------- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 9158cbcc04e1c..04d9ee7d912f3 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -1,7 +1,7 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; +use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::AssocKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -42,7 +42,7 @@ declare_clippy_lint! { declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]); struct ExistingName { - impl_methods: BTreeMap, + impl_methods: BTreeMap, trait_methods: BTreeMap>, } @@ -97,10 +97,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }; let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { - if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { - span_lint_and_then( + if let Some((impl_span, hir_id)) = existing_name.impl_methods.get(&method_name) { + span_lint_hir_and_then( cx, SAME_NAME_METHOD, + *hir_id, *impl_span, "method's name is the same as an existing method in a trait", |diag| { @@ -136,10 +137,12 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }) { let method_name = impl_item_ref.ident.name; let impl_span = impl_item_ref.span; + let hir_id = impl_item_ref.id.hir_id(); if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { - span_lint_and_then( + span_lint_hir_and_then( cx, SAME_NAME_METHOD, + hir_id, impl_span, "method's name is the same as an existing method in a trait", |diag| { @@ -152,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { }, ); } - existing_name.impl_methods.insert(method_name, impl_span); + existing_name.impl_methods.insert(method_name, (impl_span, hir_id)); } }, } diff --git a/tests/ui/same_name_method.rs b/tests/ui/same_name_method.rs index 12e10ba6c493b..9562b47f0c4ff 100644 --- a/tests/ui/same_name_method.rs +++ b/tests/ui/same_name_method.rs @@ -1,3 +1,4 @@ +#![feature(lint_reasons)] #![warn(clippy::same_name_method)] #![allow(dead_code, non_camel_case_types)] @@ -108,4 +109,19 @@ mod should_not_lint { } } +mod check_expect_suppression { + use crate::T1; + + struct S; + + impl S { + #[expect(clippy::same_name_method)] + fn foo() {} + } + + impl T1 for S { + fn foo() {} + } +} + fn main() {} diff --git a/tests/ui/same_name_method.stderr b/tests/ui/same_name_method.stderr index cf06eb32e0c7f..f55ec9f3cc66b 100644 --- a/tests/ui/same_name_method.stderr +++ b/tests/ui/same_name_method.stderr @@ -1,61 +1,61 @@ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:20:13 + --> $DIR/same_name_method.rs:21:13 | LL | fn foo() {} | ^^^^^^^^^^^ | = note: `-D clippy::same-name-method` implied by `-D warnings` note: existing `foo` defined here - --> $DIR/same_name_method.rs:24:13 + --> $DIR/same_name_method.rs:25:13 | LL | fn foo() {} | ^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:34:13 + --> $DIR/same_name_method.rs:35:13 | LL | fn clone() {} | ^^^^^^^^^^^^^ | note: existing `clone` defined here - --> $DIR/same_name_method.rs:30:18 + --> $DIR/same_name_method.rs:31:18 | LL | #[derive(Clone)] | ^^^^^ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:44:13 + --> $DIR/same_name_method.rs:45:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:48:13 + --> $DIR/same_name_method.rs:49:13 | LL | fn foo() {} | ^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:58:13 + --> $DIR/same_name_method.rs:59:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:61:9 + --> $DIR/same_name_method.rs:62:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ error: method's name is the same as an existing method in a trait - --> $DIR/same_name_method.rs:70:13 + --> $DIR/same_name_method.rs:71:13 | LL | fn foo() {} | ^^^^^^^^^^^ | note: existing `foo` defined here - --> $DIR/same_name_method.rs:73:9 + --> $DIR/same_name_method.rs:74:9 | LL | impl T1 for S {} | ^^^^^^^^^^^^^^^^ From 8db734990be40e2acd59c2b20bf6e02343830fd6 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Mon, 6 Jun 2022 14:03:11 +0200 Subject: [PATCH 33/78] Fix `#[expect]` for `async_yields_async` --- clippy_lints/src/async_yields_async.rs | 5 +++-- clippy_lints/src/same_name_method.rs | 1 + tests/ui/async_yields_async.fixed | 13 ++++++++++++- tests/ui/async_yields_async.rs | 13 ++++++++++++- tests/ui/needless_borrow.fixed | 1 + tests/ui/needless_borrow.rs | 1 + 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 0619490e73c43..d32724d040cdf 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; @@ -63,9 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { _ => None, }; if let Some(return_expr_span) = return_expr_span { - span_lint_and_then( + span_lint_hir_and_then( cx, ASYNC_YIELDS_ASYNC, + body.value.hir_id, return_expr_span, "an async construct yields a type which is itself awaitable", |db| { diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index 04d9ee7d912f3..73f8e083b29a0 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -47,6 +47,7 @@ struct ExistingName { } impl<'tcx> LateLintPass<'tcx> for SameNameMethod { + #[expect(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { let mut map = FxHashMap::::default(); diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index e20b58269b93e..3cf380d2b954b 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -1,5 +1,5 @@ // run-rustfix - +#![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] @@ -65,3 +65,14 @@ fn main() { let _n = async || custom_future_type_ctor(); let _o = async || f(); } + +#[rustfmt::skip] +#[allow(dead_code)] +fn check_expect_suppression() { + #[expect(clippy::async_yields_async)] + let _j = async || { + async { + 3 + } + }; +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index c1dfa39845025..dd4131b60ab3a 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -1,5 +1,5 @@ // run-rustfix - +#![feature(lint_reasons)] #![feature(async_closure)] #![warn(clippy::async_yields_async)] @@ -65,3 +65,14 @@ fn main() { let _n = async || custom_future_type_ctor(); let _o = async || f(); } + +#[rustfmt::skip] +#[allow(dead_code)] +fn check_expect_suppression() { + #[expect(clippy::async_yields_async)] + let _j = async || { + async { + 3 + } + }; +} diff --git a/tests/ui/needless_borrow.fixed b/tests/ui/needless_borrow.fixed index 6b1576d67329d..e7a483c058295 100644 --- a/tests/ui/needless_borrow.fixed +++ b/tests/ui/needless_borrow.fixed @@ -99,6 +99,7 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} +#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] diff --git a/tests/ui/needless_borrow.rs b/tests/ui/needless_borrow.rs index ebe76361db404..1d6bf46405a2f 100644 --- a/tests/ui/needless_borrow.rs +++ b/tests/ui/needless_borrow.rs @@ -99,6 +99,7 @@ impl<'a> Trait for &'a str {} fn h(_: &dyn Trait) {} +#[allow(dead_code)] fn check_expect_suppression() { let a = 5; #[expect(clippy::needless_borrow)] From 62d43d2f821b7072e8072d321439193fa0c8f9c4 Mon Sep 17 00:00:00 2001 From: Ikko Ashimine Date: Mon, 6 Jun 2022 21:16:31 +0900 Subject: [PATCH 34/78] Fix typo in redundant_pattern_match.rs alway -> always --- clippy_lints/src/matches/redundant_pattern_match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 1a8b9d15f370f..b1728b0ae18ca 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -68,7 +68,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< } } }, - // the base type is alway taken by reference. + // the base type is always taken by reference. // e.g. In `(vec![0])[0]` the vector is a temporary value. ExprKind::Index(base, index) => { if !matches!(base.kind, ExprKind::Path(_)) { From 4e6b55e9b8d64af5d14ed5465fdcf54c62ef3b8b Mon Sep 17 00:00:00 2001 From: josh rotenberg Date: Tue, 15 Jun 2021 22:05:44 -0700 Subject: [PATCH 35/78] Initial commit for the Clippy Book --- .gitignore | 3 + book/README.md | 4 + book/book.toml | 28 + book/src/README.md | 34 + book/src/SUMMARY.md | 26 + book/src/configuration.md | 92 +++ book/src/development/README.md | 1 + book/src/development/adding_lints.md | 670 ++++++++++++++++++ book/src/development/basics.md | 172 +++++ .../development/common_tools_writing_lints.md | 203 ++++++ book/src/infrastructure/README.md | 1 + book/src/infrastructure/backport.md | 71 ++ book/src/infrastructure/book.md | 38 + book/src/infrastructure/changelog_update.md | 97 +++ book/src/infrastructure/release.md | 124 ++++ book/src/installation_and_usage.md | 108 +++ book/src/lints/README.md | 1 + book/src/lints/cargo.md | 0 book/src/lints/complexity.md | 0 book/src/lints/correctness.md | 0 book/src/lints/deprecated.md | 0 book/src/lints/nursery.md | 0 book/src/lints/pedantic.md | 0 book/src/lints/perf.md | 0 book/src/lints/restriction.md | 0 book/src/lints/style.md | 0 book/src/roadmap/2021.md | 235 ++++++ book/src/roadmap/README.md | 0 28 files changed, 1908 insertions(+) create mode 100644 book/README.md create mode 100644 book/book.toml create mode 100644 book/src/README.md create mode 100644 book/src/SUMMARY.md create mode 100644 book/src/configuration.md create mode 100644 book/src/development/README.md create mode 100644 book/src/development/adding_lints.md create mode 100644 book/src/development/basics.md create mode 100644 book/src/development/common_tools_writing_lints.md create mode 100644 book/src/infrastructure/README.md create mode 100644 book/src/infrastructure/backport.md create mode 100644 book/src/infrastructure/book.md create mode 100644 book/src/infrastructure/changelog_update.md create mode 100644 book/src/infrastructure/release.md create mode 100644 book/src/installation_and_usage.md create mode 100644 book/src/lints/README.md create mode 100644 book/src/lints/cargo.md create mode 100644 book/src/lints/complexity.md create mode 100644 book/src/lints/correctness.md create mode 100644 book/src/lints/deprecated.md create mode 100644 book/src/lints/nursery.md create mode 100644 book/src/lints/pedantic.md create mode 100644 book/src/lints/perf.md create mode 100644 book/src/lints/restriction.md create mode 100644 book/src/lints/style.md create mode 100644 book/src/roadmap/2021.md create mode 100644 book/src/roadmap/README.md diff --git a/.gitignore b/.gitignore index 3e50c45a9b63e..503ae3c509039 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ helper.txt *.iml .vscode .idea + +# mdbook generated output +/book/book diff --git a/book/README.md b/book/README.md new file mode 100644 index 0000000000000..b652194d0d13b --- /dev/null +++ b/book/README.md @@ -0,0 +1,4 @@ +# Clippy Book + +This is the source for the Clippy Book. See the +[book](src/infrastructure/book.md) for more information. diff --git a/book/book.toml b/book/book.toml new file mode 100644 index 0000000000000..93b6641f7e1e7 --- /dev/null +++ b/book/book.toml @@ -0,0 +1,28 @@ +[book] +authors = ["The Rust Clippy Developers"] +language = "en" +multilingual = false +src = "src" +title = "Clippy Documentation" + +[rust] +edition = "2018" + +[output.html] +edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}" +git-repository-url = "https://github.com/rust-lang/rust-clippy/tree/master/book" +mathjax-support = true +site-url = "/rust-clippy/" + +[output.html.playground] +editable = true +line-numbers = true + +[output.html.search] +boost-hierarchy = 2 +boost-paragraph = 1 +boost-title = 2 +expand = true +heading-split-level = 2 +limit-results = 20 +use-boolean-and = true diff --git a/book/src/README.md b/book/src/README.md new file mode 100644 index 0000000000000..de1f70d7e9640 --- /dev/null +++ b/book/src/README.md @@ -0,0 +1,34 @@ +# Clippy + +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto) +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license) + +A collection of lints to catch common mistakes and improve your +[Rust](https://github.com/rust-lang/rust) code. + +[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) + +Lints are divided into categories, each with a default [lint +level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how +much Clippy is supposed to ~~annoy~~ help you by changing the lint level by +category. + +| Category | Description | Default level | +| --------------------- | ----------------------------------------------------------------------------------- | ------------- | +| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** | +| `clippy::correctness` | code that is outright wrong or useless | **deny** | +| `clippy::suspicious` | code that is most likely wrong or useless | **warn** | +| `clippy::complexity` | code that does something simple but in a complex way | **warn** | +| `clippy::perf` | code that can be written to run faster | **warn** | +| `clippy::style` | code that should be written in a more idiomatic way | **warn** | +| `clippy::pedantic` | lints which are rather strict or might have false positives | allow | +| `clippy::nursery` | new lints that are still under development | allow | +| `clippy::cargo` | lints for the cargo manifest | allow | | allow | + +More to come, please [file an +issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas! + +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also +contains "restriction lints", which are for things which are usually not +considered "bad", but may be useful to turn on in specific cases. These should +be used very selectively, if at all. diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md new file mode 100644 index 0000000000000..470760b6d1688 --- /dev/null +++ b/book/src/SUMMARY.md @@ -0,0 +1,26 @@ +# Summary + +[Introduction](README.md) + +- [Installation and Usage](installation_and_usage.md) +- [Configuration](configuration.md) +- [Clippy's Lints](lints/README.md) + - [Correctness]() + - [Suspicious]() + - [Style]() + - [Complexity]() + - [Perf]() + - [Pendantic]() + - [Nursery]() + - [Cargo]() +- [Development](development/README.md) + - [Basics](development/basics.md) + - [Adding Lints](development/adding_lints.md) + - [Common Tools](development/common_tools_writing_lints.md) +- [Infrastructure](infrastructure/README.md) + - [Backporting Changes](infrastructure/backport.md) + - [Updating the Changelog](infrastructure/changelog_update.md) + - [Release a New Version](infrastructure/release.md) + - [The Clippy Book](infrastructure/book.md) +- [Roadmap](roadmap/README.md) + - [2021](roadmap/2021.md) diff --git a/book/src/configuration.md b/book/src/configuration.md new file mode 100644 index 0000000000000..6e295ac3181dd --- /dev/null +++ b/book/src/configuration.md @@ -0,0 +1,92 @@ +# Configuring Clippy + +> **Note:** The configuration file is unstable and may be deprecated in the future. + +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a +basic `variable = value` mapping eg. + +```toml +avoid-breaking-exported-api = false +blacklisted-names = ["toto", "tata", "titi"] +cognitive-complexity-threshold = 30 +``` + +See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which +lints can be configured and the meaning of the variables. + +To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS` +environment variable. + +### Allowing/denying lints + +You can add options to your code to `allow`/`warn`/`deny` Clippy lints: + +* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`) + +* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false + positives. + +* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) + +* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc. + +Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny` +the lint will emit an error, when triggering for your code. An error causes clippy to exit with an error code, so is +useful in scripts like CI/CD. + +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 allow `lint_name`, run + +```terminal +cargo clippy -- -A clippy::lint_name +``` + +And to warn on `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 warn on the lint(s) you are +interested in: + +```terminal +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::... +``` + +### Specifying the minimum supported Rust version + +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the +minimum supported Rust version (MSRV) in the clippy configuration file. + +```toml +msrv = "1.30.0" +``` + +The MSRV can also be specified as an inner attribute, like below. + +```rust +#![feature(custom_inner_attributes)] +#![clippy::msrv = "1.30.0"] + +fn main() { + ... +} +``` + +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30` +is equivalent to `msrv = 1.30.0`. + +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly. + +Lints that recognize this configuration option can be +found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv) diff --git a/book/src/development/README.md b/book/src/development/README.md new file mode 100644 index 0000000000000..09d6aad2c538e --- /dev/null +++ b/book/src/development/README.md @@ -0,0 +1 @@ +# Clippy Development diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md new file mode 100644 index 0000000000000..5a06afedbf4c2 --- /dev/null +++ b/book/src/development/adding_lints.md @@ -0,0 +1,670 @@ +# Adding a new lint + +You are probably here because you want to add a new lint to Clippy. If this is +the first time you're contributing to Clippy, this document guides you through +creating an example lint from scratch. + +To get started, we will create a lint that detects functions called `foo`, +because that's clearly a non-descriptive name. + +- [Adding a new lint](#adding-a-new-lint) + - [Setup](#setup) + - [Getting Started](#getting-started) + - [Testing](#testing) + - [Rustfix tests](#rustfix-tests) + - [Edition 2018 tests](#edition-2018-tests) + - [Testing manually](#testing-manually) + - [Lint declaration](#lint-declaration) + - [Lint passes](#lint-passes) + - [Emitting a lint](#emitting-a-lint) + - [Adding the lint logic](#adding-the-lint-logic) + - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) + - [Author lint](#author-lint) + - [Documentation](#documentation) + - [Running rustfmt](#running-rustfmt) + - [Debugging](#debugging) + - [PR Checklist](#pr-checklist) + - [Adding configuration to a lint](#adding-configuration-to-a-lint) + - [Cheatsheet](#cheatsheet) + +## Setup + +See the [Basics](basics.md#get-the-code) documentation. + +## Getting Started + +There is a bit of boilerplate code that needs to be set up when creating a new +lint. Fortunately, you can use the clippy dev tools to handle this for you. We +are naming our new lint `foo_functions` (lints are generally written in snake +case), and we don't need type information so it will have an early pass type +(more on this later on). If you're not sure if the name you chose fits the lint, +take a look at our [lint naming guidelines][lint_naming]. To get started on this +lint you can run `cargo dev new_lint --name=foo_functions --pass=early +--category=pedantic` (category will default to nursery if not provided). This +command will create two files: `tests/ui/foo_functions.rs` and +`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to +register the new lint. For cargo lints, two project hierarchies (fail/pass) will +be created by default under `tests/ui-cargo`. + +Next, we'll open up these files and add our lint! + +## Testing + +Let's write some tests first that we can execute while we iterate on our lint. + +Clippy uses UI tests for testing. UI tests check that the output of Clippy is +exactly as expected. Each test is just a plain Rust file that contains the code +we want to check. The output of Clippy is compared against a `.stderr` file. +Note that you don't have to create this file yourself, we'll get to +generating the `.stderr` files further down. + +We start by opening the test file created at `tests/ui/foo_functions.rs`. + +Update the file with some examples to get started: + +```rust +#![warn(clippy::foo_functions)] + +// Impl methods +struct A; +impl A { + pub fn fo(&self) {} + pub fn foo(&self) {} + pub fn food(&self) {} +} + +// Default trait methods +trait B { + fn fo(&self) {} + fn foo(&self) {} + fn food(&self) {} +} + +// Plain functions +fn fo() {} +fn foo() {} +fn food() {} + +fn main() { + // We also don't want to lint method calls + foo(); + let a = A; + a.foo(); +} +``` + +Now we can run the test with `TESTNAME=foo_functions cargo uitest`, +currently this test is meaningless though. + +While we are working on implementing our lint, we can keep running the UI +test. That allows us to check if the output is turning into what we want. + +Once we are satisfied with the output, we need to run +`cargo dev bless` to update the `.stderr` file for our lint. +Please note that, we should run `TESTNAME=foo_functions cargo uitest` +every time before running `cargo dev bless`. +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 `cargo dev bless` for the +specific lint you are creating/editing. Note that if the generated files are +empty, they should be removed. + +Note that you can run multiple test files by specifying a comma separated list: +`TESTNAME=foo_functions,test2,test3`. + +### Cargo lints + +For cargo lints, the process of testing differs in that we are interested in +the `Cargo.toml` manifest file. We also need a minimal crate associated +with that manifest. + +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` +we will find by default two new crates, each with its manifest file: + +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. + +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. + +The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` +variable to `cargo uitest` works too. + +## Rustfix tests + +If the lint you are working on is making use of structured suggestions, the +test file should include a `// run-rustfix` comment at the top. This will +additionally run [rustfix] for that test. Rustfix will apply the suggestions +from the lint to the code of the test file and compare that to the contents of +a `.fixed` file. + +Use `cargo dev bless` to automatically generate the +`.fixed` file after running the tests. + +[rustfix]: https://github.com/rust-lang/rustfix + +## Edition 2018 tests + +Some features require the 2018 edition to work (e.g. `async_await`), but +compile-test tests run on the 2015 edition by default. To change this behavior +add `// edition:2018` at the top of the test file (note that it's space-sensitive). + +## Testing manually + +Manually testing against an example file can be useful if you have added some +`println!`s and the test suite output becomes unreadable. To try Clippy with +your local modifications, run + +``` +env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs +``` + +from the working copy root. With tests in place, let's have a look at +implementing our lint now. + +## Lint declaration + +Let's start by opening the new file created in the `clippy_lints` crate +at `clippy_lints/src/foo_functions.rs`. That's the crate where all the +lint code is. This file has already imported some initial things we will need: + +```rust +use rustc_lint::{EarlyLintPass, EarlyContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_ast::ast::*; +``` + +The next step is to update the lint declaration. Lints are declared using the +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update +the auto-generated lint declaration to have a real description, something like this: + +```rust +declare_clippy_lint! { + /// **What it does:** + /// + /// **Why is this bad?** + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// // example code + /// ``` + pub FOO_FUNCTIONS, + pedantic, + "function named `foo`, which is not a descriptive name" +} +``` + +* 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]. 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 + read well when used with `allow`/`warn`/`deny`. +* `pedantic` sets the lint level to `Allow`. + The exact mapping can be found [here][category_level_mapping] +* The last part should be a text that explains what exactly is wrong with the + code + +The rest of this file contains an empty implementation for our lint pass, +which in this case is `EarlyLintPass` and should look like this: + +```rust +// clippy_lints/src/foo_functions.rs + +// .. imports and lint declaration .. + +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]); + +impl EarlyLintPass for FooFunctions {} +``` + +Normally after declaring the lint, we have to run `cargo dev update_lints`, +which updates some files, so Clippy knows about the new lint. Since we used +`cargo dev new_lint ...` to generate the lint declaration, this was done +automatically. While `update_lints` automates most of the things, it doesn't +automate everything. We will have to register our lint pass manually in the +`register_plugins` function in `clippy_lints/src/lib.rs`: + +```rust +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 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 + +## Lint passes + +Writing a lint that only checks for the name of a function means that we only +have to deal with the AST and don't have to deal with the type system at all. +This is good, because it makes writing this particular lint less complicated. + +We have to make this decision with every new Clippy lint. It boils down to using +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. + +In short, the `LateLintPass` has access to type information while the +`EarlyLintPass` doesn't. If you don't need access to type information, use the +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed +hasn't really been a concern with Clippy so far. + +Since we don't need type information for checking the function name, we used +`--pass=early` when running the new lint automation and all the imports were +added accordingly. + +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html + +## Emitting a lint + +With UI tests and the lint declaration in place, we can start working on the +implementation of the lint logic. + +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + // TODO: Emit lint here + } +} +``` + +We implement the [`check_fn`][check_fn] method from the +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various +information about the function that is currently being checked. More on that in +the next section. Let's worry about the details later and emit our lint for +*every* function definition first. + +Depending on how complex we want our lint message to be, we can choose from a +variety of lint emission functions. They can all be found in +[`clippy_utils/src/diagnostics.rs`][diagnostics]. + +`span_lint_and_help` seems most appropriate in this case. It allows us to +provide an extra help message and we can't really suggest a better name +automatically. This is how it looks: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + span_lint_and_help( + cx, + FOO_FUNCTIONS, + span, + "function named `foo`", + None, + "consider using a more meaningful name" + ); + } +} +``` + +Running our UI test should now produce output that contains the lint message. + +According to [the rustc-dev-guide], the text should be matter of fact and avoid +capitalization and periods, unless multiple sentences are needed. +When code or an identifier must appear in a message or label, it should be +surrounded with single grave accents \`. + +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html + +## Adding the lint logic + +Writing the logic for your lint will most likely be different from our example, +so this section is kept rather short. + +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind] +that has the [`FnKind::Fn`] variant. It provides access to the name of the +function/method via an [`Ident`][ident]. + +With that we can expand our `check_fn` method to: + +```rust +impl EarlyLintPass for FooFunctions { + fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { + if is_foo_fn(fn_kind) { + span_lint_and_help( + cx, + FOO_FUNCTIONS, + span, + "function named `foo`", + None, + "consider using a more meaningful name" + ); + } + } +} +``` + +We separate the lint conditional from the lint emissions because it makes the +code a bit easier to read. In some cases this separation would also allow to +write some unit tests (as opposed to only UI tests) for the separate function. + +In our example, `is_foo_fn` looks like: + +```rust +// use statements, impl EarlyLintPass, check_fn, .. + +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { + match fn_kind { + FnKind::Fn(_, ident, ..) => { + // check if `fn` name is `foo` + ident.name.as_str() == "foo" + } + // ignore closures + FnKind::Closure(..) => false + } +} +``` + +Now we should also run the full test suite with `cargo test`. At this point +running `cargo test` should produce the expected output. Remember to run +`cargo dev bless` to update the `.stderr` file. + +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint +implementation is not violating any Clippy lints itself. + +That should be it for the lint implementation. Running `cargo test` should now +pass. + +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html + +## Specifying the lint's minimum supported Rust version (MSRV) + +Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests +using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to +ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are +required, just use the one with a lower MSRV. + +First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be +accessed later as `msrvs::STR_STRIP_PREFIX`, for example. + +```rust +msrv_aliases! { + .. + 1,45,0 { STR_STRIP_PREFIX } +} +``` + +In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a +constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`. + +```rust +pub struct ManualStrip { + msrv: Option, +} + +impl ManualStrip { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} +``` + +The project's MSRV can then be matched against the feature MSRV in the LintPass +using the `meets_msrv` utility function. + +``` rust +if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { + return; +} +``` + +The project's MSRV can also be specified as an inner attribute, which overrides +the value from `clippy.toml`. This can be accounted for using the +`extract_msrv_attr!(LintContext)` macro and passing +`LateContext`/`EarlyContext`. + +```rust +impl<'tcx> LateLintPass<'tcx> for ManualStrip { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + ... + } + extract_msrv_attr!(LateContext); +} +``` + +Once the `msrv` is added to the lint, a relevant test case should be added to +`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted +if the project's MSRV is lower. + +As a last step, the lint should be added to the lint documentation. This is done +in `clippy_lints/src/utils/conf.rs`: + +```rust +define_Conf! { + /// Lint: LIST, OF, LINTS, . The minimum rust version that the project supports + (msrv: Option = None), + ... +} +``` + +## Author lint + +If you have trouble implementing your lint, there is also the internal `author` +lint to generate Clippy code that detects the offending pattern. It does not +work for all of the Rust syntax, but can give a good starting point. + +The quickest way to use it, is the +[Rust playground: play.rust-lang.org][author_example]. +Put the code you want to lint into the editor and add the `#[clippy::author]` +attribute above the item. Then run Clippy via `Tools -> Clippy` and you should +see the generated code in the output below. + +[Here][author_example] is an example on the playground. + +If the command was executed successfully, you can copy the code over to where +you are implementing your lint. + +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 + +## Documentation + +The final thing before submitting our PR is to add some documentation to our +lint declaration. + +Please document your lint with a doc comment akin to the following: + +```rust +declare_clippy_lint! { + /// **What it does:** Checks for ... (describe what the lint matches). + /// + /// **Why is this bad?** Supply the reason for linting the code. + /// + /// **Known problems:** None. (Or describe where it could go wrong.) + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// Insert a short example of code that triggers the lint + /// + /// // Good + /// Insert a short example of improved code that doesn't trigger the lint + /// ``` + pub FOO_FUNCTIONS, + pedantic, + "function named `foo`, which is not a descriptive name" +} +``` + +Once your lint is merged, this documentation will show up in the [lint +list][lint_list]. + +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html + +## Running rustfmt + +[Rustfmt] is a tool for formatting Rust code according to style guidelines. +Your code has to be formatted by `rustfmt` before a PR can be merged. +Clippy uses nightly `rustfmt` in the CI. + +It can be installed via `rustup`: + +```bash +rustup component add rustfmt --toolchain=nightly +``` + +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is +installed for the nightly toolchain. + +[Rustfmt]: https://github.com/rust-lang/rustfmt + +## Debugging + +If you want to debug parts of your lint implementation, you can use the [`dbg!`] +macro anywhere in your code. Running the tests should then include the debug +output in the `stdout` part. + +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html + +## PR Checklist + +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` + +## Adding configuration to a lint + +Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace +directory. Adding a configuration to a lint can be useful for thresholds or to constrain some +behavior that can be seen as a false positive for some users. Adding a configuration is done +in the following steps: + +1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs) + like this: + ```rust + /// Lint: LINT_NAME. + (configuration_ident: Type = DefaultValue), + ``` + The configuration value and identifier should usually be the same. The doc comment will be + automatically added to the lint documentation. +2. Adding the configuration value to the lint impl struct: + 1. This first requires the definition of a lint impl struct. Lint impl structs are usually + generated with the `declare_lint_pass!` macro. This struct needs to be defined manually + to add some kind of metadata to it: + ```rust + // Generated struct definition + declare_lint_pass!(StructName => [ + LINT_NAME + ]); + + // New manual definition struct + #[derive(Copy, Clone)] + pub struct StructName {} + + impl_lint_pass!(StructName => [ + LINT_NAME + ]); + ``` + + 2. Next add the configuration value and a corresponding creation method like this: + ```rust + #[derive(Copy, Clone)] + pub struct StructName { + configuration_ident: Type, + } + + // ... + + impl StructName { + pub fn new(configuration_ident: Type) -> Self { + Self { + configuration_ident, + } + } + } + ``` +3. Passing the configuration value to the lint impl struct: + + First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). + The configuration value is now cloned or copied into a local value that is then passed to the + impl struct like this: + ```rust + // Default generated registration: + store.register_*_pass(|| box module::StructName); + + // New registration with configuration value + let configuration_ident = conf.configuration_ident.clone(); + store.register_*_pass(move || box module::StructName::new(configuration_ident)); + ``` + + Congratulations the work is almost done. The configuration value can now be accessed + in the linting code via `self.configuration_ident`. + +4. Adding tests: + 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui). + 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). + Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file + with the configuration value and a rust file that should be linted by Clippy. The test can + otherwise be written as usual. + +## Cheatsheet + +Here are some pointers to things you are likely going to need for every lint: + +* [Clippy utils][utils] - Various helper functions. Maybe the function you need + is already in here (`implements_trait`, `match_def_path`, `snippet`, etc) +* [Clippy diagnostics][diagnostics] +* [The `if_chain` macro][if_chain] +* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] +* [`Span`][span] +* [`Applicability`][applicability] +* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts +* [The nightly rustc docs][nightly_docs] which has been linked to throughout + this guide + +For `EarlyLintPass` lints: + +* [`EarlyLintPass`][early_lint_pass] +* [`rustc_ast::ast`][ast] + +For `LateLintPass` lints: + +* [`LateLintPass`][late_lint_pass] +* [`Ty::TyKind`][ty] + +While most of Clippy's lint utils are documented, most of rustc's internals lack +documentation currently. This is unfortunate, but in most cases you can probably +get away with copying things from existing similar lints. If you are stuck, +don't hesitate to ask on [Zulip] or in the issue/PR. + +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs +[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy diff --git a/book/src/development/basics.md b/book/src/development/basics.md new file mode 100644 index 0000000000000..aaf31158f58a8 --- /dev/null +++ b/book/src/development/basics.md @@ -0,0 +1,172 @@ +# Basics for hacking on Clippy + +This document explains the basics for hacking on Clippy. Besides others, this +includes how to build and test Clippy. For a more in depth description on +the codebase take a look at [Adding Lints] or [Common Tools]. + +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md + +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) + - [Get the Code](#get-the-code) + - [Building and Testing](#building-and-testing) + - [`cargo dev`](#cargo-dev) + - [lintcheck](#lintcheck) + - [PR](#pr) + - [Common Abbreviations](#common-abbreviations) + - [Install from source](#install-from-source) + +## Get the Code + +First, make sure you have checked out the latest version of Clippy. If this is +your first time working on Clippy, create a fork of the repository and clone it +afterwards with the following command: + +```bash +git clone git@github.com:/rust-clippy +``` + +If you've already cloned Clippy in the past, update it to the latest version: + +```bash +# If the upstream remote has not been added yet +git remote add upstream https://github.com/rust-lang/rust-clippy +# upstream has to be the remote of the rust-lang/rust-clippy repo +git fetch upstream +# make sure that you are on the master branch +git checkout master +# rebase your master branch on the upstream master +git rebase upstream/master +# push to the master branch of your fork +git push +``` + +## Building and Testing + +You can build and test Clippy like every other Rust project: + +```bash +cargo build # builds Clippy +cargo test # tests Clippy +``` + +Since Clippy's test suite is pretty big, there are some commands that only run a +subset of Clippy's tests: + +```bash +# only run UI tests +cargo uitest +# only run UI tests starting with `test_` +TESTNAME="test_" cargo uitest +# only run dogfood tests +cargo test --test dogfood +``` + +If the output of a [UI test] differs from the expected output, you can update the +reference file with: + +```bash +cargo dev bless +``` + +For example, this is necessary, if you fix a typo in an error message of a lint +or if you modify a test file to add a test case. + +_Note:_ This command may update more files than you intended. In that case only +commit the files you wanted to update. + +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests + +## `cargo dev` + +Clippy has some dev tools to make working on Clippy more convenient. These tools +can be accessed through the `cargo dev` command. Available tools are listed +below. To get more information about these commands, just call them with +`--help`. + +```bash +# formats the whole Clippy codebase and all tests +cargo dev fmt +# register or update lint names/groups/... +cargo dev update_lints +# create a new lint and register it +cargo dev new_lint +# (experimental) Setup Clippy to work with IntelliJ-Rust +cargo dev ide_setup +``` + +## lintcheck +`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. +You can `git diff` the updated log against its previous version and +see what impact your lint made on a small set of crates. +If you add a new lint, please audit the resulting warnings and make sure +there are no false positives and that the suggestions are valid. + +Refer to the tools [README] for more details. + +[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md +## PR + +We follow a rustc no merge-commit policy. +See . + +## Common Abbreviations + +| Abbreviation | Meaning | +| ------------ | -------------------------------------- | +| UB | Undefined Behavior | +| FP | False Positive | +| FN | False Negative | +| ICE | Internal Compiler Error | +| AST | Abstract Syntax Tree | +| MIR | Mid-Level Intermediate Representation | +| HIR | High-Level Intermediate Representation | +| TCX | Type context | + +This is a concise list of abbreviations that can come up during Clippy development. An extensive +general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if +an abbreviation or meaning is unclear to you. + +## Install from source + +If you are hacking on Clippy and want to install it from source, do the following: + +First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`. +We will use this override to install Clippy into the right toolchain. + +> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`. + +From the Clippy project root, run the following command to build the Clippy binaries and copy them into the +toolchain directory. This will override the currently installed Clippy component. + +```terminal +cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" +``` + +Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy. + +```terminal +cd my-project +cargo +nightly-2021-07-01 clippy +``` + +...or `clippy-driver` + +```terminal +clippy-driver +nightly-2021-07-01 +``` + +If you need to restore the default Clippy installation, run the following (from the Clippy project root). + +```terminal +rustup component remove clippy +rustup component add clippy +``` + +> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and +> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running +> `rustup update`. + + +[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md new file mode 100644 index 0000000000000..0a85f65001101 --- /dev/null +++ b/book/src/development/common_tools_writing_lints.md @@ -0,0 +1,203 @@ +# Common tools for writing lints + +You may need following tooltips to catch up with common operations. + +- [Common tools for writing lints](#common-tools-for-writing-lints) + - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) + - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) + - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait) + - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method) + - [Dealing with macros](#dealing-with-macros) + +Useful Rustc dev guide links: +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) + +# Retrieving the type of an expression + +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: + +- which type does this expression correspond to (using its [`TyKind`][TyKind])? +- is it a sized type? +- is it a primitive type? +- does it implement a trait? + +This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct, +that gives you access to the underlying structure [`TyS`][TyS]. + +Example of use: +```rust +impl LateLintPass<'_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // Get type of `expr` + let ty = cx.typeck_results().expr_ty(expr); + // Match its kind to enter its type + match ty.kind { + ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), + _ => () + } + } +} +``` + +Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method +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 + 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 + +Starting with an `expr`, you can check whether it is calling a specific method `some_method`: + +```rust +impl LateLintPass<'_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind; + // Check the name of this method is `some_method` + if path.ident.name == sym!(some_method); + then { + // ... + } + } + } +} +``` + +# Checking if a type implements a specific trait + +There are two ways to do this, depending if the target trait is part of lang items. + +```rust +use clippy_utils::{implements_trait, match_trait_method, paths}; + +impl LateLintPass<'_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // 1. Using expression and Clippy's convenient method + // we use `match_trait_method` function from Clippy's toolbox + if match_trait_method(cx, expr, &paths::INTO) { + // `expr` implements `Into` trait + } + + // 2. Using type context `TyCtxt` + let ty = cx.typeck_results().expr_ty(expr); + if cx.tcx.lang_items() + // we are looking for the `DefId` of `Drop` trait in lang items + .drop_trait() + // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils + .map_or(false, |id| implements_trait(cx, ty, id, &[])) { + // `expr` implements `Drop` trait + } + } +} +``` + +> Prefer using lang items, if the target trait is available there. + +A list of defined paths for Clippy can be found in [paths.rs][paths] + +We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. + +# Checking if a type defines a specific method + +To check if our type defines a method called `some_method`: + +```rust +use clippy_utils::{is_type_diagnostic_item, return_ty}; + +impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { + if_chain! { + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check the method is named `some_method` + if impl_item.ident.name == sym!(some_method); + // We can also check it has a parameter `self` + if signature.decl.implicit_self.has_implicit_self(); + // We can go further and even check if its return type is `String` + if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); + then { + // ... + } + } + } +} +``` + +# Dealing with macros + +There are several helpers in [`clippy_utils`][utils] to deal with macros: + +- `in_macro()`: detect if the given span is expanded by a macro + +You may want to use this for example to not start linting in any macro. + +```rust +macro_rules! foo { + ($param:expr) => { + match $param { + "bar" => println!("whatever"), + _ => () + } + }; +} + +foo!("bar"); + +// if we lint the `match` of `foo` call and test its span +assert_eq!(in_macro(match_span), true); +``` + +- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate + +You may want to use it for example to not start linting in macros from other crates + +```rust +#[macro_use] +extern crate a_crate_with_macros; + +// `foo` is defined in `a_crate_with_macros` +foo!("bar"); + +// if we lint the `match` of `foo` call and test its span +assert_eq!(in_external_macro(cx.sess(), match_span), true); +``` + +- `differing_macro_contexts()`: returns true if the two given spans are not from the same context + +```rust +macro_rules! m { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + } +} + +let x: Option = Some(42); +m!(x, x.unwrap()); + +// These spans are not from the same context +// x.is_some() is from inside the macro +// x.unwrap() is from outside the macro +assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true); +``` + +[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[paths]: ../clippy_utils/src/paths.rs +[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs diff --git a/book/src/infrastructure/README.md b/book/src/infrastructure/README.md new file mode 100644 index 0000000000000..2b4e5f6a6efe0 --- /dev/null +++ b/book/src/infrastructure/README.md @@ -0,0 +1 @@ +# Infrastructure diff --git a/book/src/infrastructure/backport.md b/book/src/infrastructure/backport.md new file mode 100644 index 0000000000000..15f3d1f080604 --- /dev/null +++ b/book/src/infrastructure/backport.md @@ -0,0 +1,71 @@ +# Backport Changes + +Sometimes it is necessary to backport changes to the beta release of Clippy. +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` 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. + + +## Backport the changes + +Backports are done on the beta branch of the Clippy repository. + +```bash +# 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(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 +``` + +After this, you can open a PR to the `beta` branch of the Clippy repository. + + +## Update Clippy in the Rust Repository + +This step must be done, **after** the PR of the previous step was merged. + +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 +$ git subtree pull -p src/tools/clippy https://github.com/rust-lang/rust-clippy beta +$ git push origin clippy_backport +``` + +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. diff --git a/book/src/infrastructure/book.md b/book/src/infrastructure/book.md new file mode 100644 index 0000000000000..056d54b679243 --- /dev/null +++ b/book/src/infrastructure/book.md @@ -0,0 +1,38 @@ +# The Clippy Book + +This document explains how to make additions and changes to the Clippy book, the guide to Clippy that you're reading +right now. The Clippy book is formatted with [Markdown](https://www.markdownguide.org) and generated +by [mdbook](https://github.com/rust-lang/mdBook). + +- [Get mdbook](#get-mdbook) +- [Make changes](#make-changes) + +## Get mdbook + +While not strictly necessary since the book source is simply Markdown text files, having mdbook locally will allow you +to build, test and serve the book locally to view changes before you commit them to the repository. You likely already +have +`cargo` installed, so the easiest option is to simply: + +```shell +cargo install mdbook +``` + +See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) instructions for other options. + +## Make changes + +The book's [src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) directory contains all of the +markdown files used to generate the book. If you want to see your changes in real time, you can use the mdbook `serve` +command to run a web server locally that will automatically update changes as they are made. From the top level of +your `rust-clippy` +directory: + +```shell +mdbook serve book --open +``` + +Then navigate to `http://localhost:3000` to see the generated book. While the server is running, changes you make will +automatically be updated. + +For more information, see the mdbook [guide](https://rust-lang.github.io/mdBook/). diff --git a/book/src/infrastructure/changelog_update.md b/book/src/infrastructure/changelog_update.md new file mode 100644 index 0000000000000..115848c48044c --- /dev/null +++ b/book/src/infrastructure/changelog_update.md @@ -0,0 +1,97 @@ +# Changelog Update + +If you want to help with updating the [changelog][changelog], you're in the right place. + +## When to update + +Typos and other small fixes/additions are _always_ welcome. + +Special care needs to be taken when it comes to updating the changelog for a new +Rust release. For that purpose, the changelog is ideally updated during the week +before an upcoming stable release. You can find the release dates on the [Rust +Forge][forge]. + +Most of the time we only need to update the changelog for minor Rust releases. It's +been very rare that Clippy changes were included in a patch release. + +## Changelog update walkthrough + +### 1. Finding the relevant Clippy commits + +Each Rust release ships with its own version of Clippy. The Clippy subtree can +be found in the `tools` directory of the Rust repository. + +Depending on the current time and what exactly you want to update, the following +bullet points might be helpful: + +* When writing the release notes for the **upcoming stable release** you need to check + out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] +* When writing the release notes for the **upcoming beta release**, you need to check + out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] +* When writing the (forgotten) release notes for a **past stable release**, you + need to check out the Rust release tag of the stable release. + [Link][rust_stable_tools] + +Usually you want to wirte the changelog of the **upcoming stable release**. Make +sure though, that `beta` was already branched in the Rust repository. + +To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: +``` +git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" +``` + +### 2. Fetching the PRs between those commits + +Once you've got the correct commit range, run + + util/fetch_prs_between.sh commit1 commit2 > changes.txt + +and open that file in your editor of choice. + +When updating the changelog it's also a good idea to make sure that `commit1` is +already correct in the current changelog. + +### 3. Authoring the final changelog + +The above script should have dumped all the relevant PRs to the file you +specified. It should have filtered out most of the irrelevant PRs +already, but it's a good idea to do a manual cleanup pass where you look for +more irrelevant PRs. If you're not sure about some PRs, just leave them in for +the review and ask for feedback. + +With the PRs filtered, you can start to take each PR and move the +`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but +try to keep it somewhat coherent. + +The order should roughly be: + +1. New lints +2. Moves or deprecations of lints +3. Changes that expand what code existing lints cover +4. False positive fixes +5. Suggestion fixes/improvements +6. ICE fixes +7. Documentation improvements +8. Others + +As section headers, we use: + +``` +### New Lints +### Moves and Deprecations +### Enhancements +### False Positive Fixes +### Suggestion Fixes/Improvements +### ICE Fixes +### Documentation Improvements +### Others +``` + +Please also be sure to update the Beta/Unreleased sections at the top with the +relevant commit ranges. + +[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md +[forge]: https://forge.rust-lang.org/ +[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy +[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy +[rust_stable_tools]: https://github.com/rust-lang/rust/releases diff --git a/book/src/infrastructure/release.md b/book/src/infrastructure/release.md new file mode 100644 index 0000000000000..afe3033c288cf --- /dev/null +++ b/book/src/infrastructure/release.md @@ -0,0 +1,124 @@ +# Release a new Clippy Version + +_NOTE: This document is probably only relevant to you, if you're a member of the +Clippy team._ + +Clippy is released together with stable Rust releases. The dates for these +releases can be found at the [Rust Forge]. This document explains the necessary +steps to create a Clippy release. + +1. [Remerge the `beta` branch](#remerge-the-beta-branch) +2. [Update the `beta` branch](#update-the-beta-branch) +3. [Find the Clippy commit](#find-the-clippy-commit) +4. [Tag the stable commit](#tag-the-stable-commit) +5. [Update `CHANGELOG.md`](#update-changelogmd) + +_NOTE: This document is for stable Rust releases, not for point releases. For +point releases, step 1. and 2. should be enough._ + +[Rust Forge]: https://forge.rust-lang.org/ + + +## Remerge the `beta` branch + +This step is only necessary, if since the last release something was backported +to the beta Rust release. The remerge is then necessary, to make sure that the +Clippy commit, that was used by the now stable Rust release, persists in the +tree of the Clippy repository. + +To find out if this step is necessary run + +```bash +# Assumes that the local master branch is up-to-date +$ git fetch upstream +$ git branch master --contains upstream/beta +``` + +If this command outputs `master`, this step is **not** necessary. + +```bash +# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy +$ git checkout -b backport_remerge +$ git merge upstream/beta +$ git diff # This diff has to be empty, otherwise something with the remerge failed +$ git push origin backport_remerge # This can be pushed to your fork +``` + +After this, open a PR to the master branch. In this PR, the commit hash of the +`HEAD` of the `beta` branch must exists. In addition to that, no files should +be changed by this PR. + + +## Update the `beta` branch + +This step must be done **after** the PR of the previous step was merged. + +First, the Clippy commit of the `beta` branch of the Rust repository has to be +determined. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git checkout beta +$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") +``` + +After finding the Clippy commit, the `beta` branch in the Clippy repository can +be updated. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout beta +$ git reset --hard $BETA_SHA +$ git push upstream beta +``` + + +## Find the Clippy commit + +The first step is to tag the Clippy commit, that is included in the stable Rust +release. This commit can be found in the Rust repository. + +```bash +# Assuming the current directory corresponds to the Rust repository +$ git fetch upstream # `upstream` is the `rust-lang/rust` remote +$ git checkout 1.XX.0 # XX should be exchanged with the corresponding version +$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") +``` + + +## Tag the stable commit + +After finding the Clippy commit, it can be tagged with the release number. + +```bash +# Assuming the current directory corresponds to the Clippy repository +$ git checkout $SHA +$ git tag rust-1.XX.0 # XX should be exchanged with the corresponding version +$ git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote +``` + +After this, the release should be available on the Clippy [release page]. + +[release page]: https://github.com/rust-lang/rust-clippy/releases + +## Update the `stable` branch + +At this step you should have already checked out the commit of the `rust-1.XX.0` +tag. Updating the stable branch from here is as easy as: + +```bash +# Assuming the current directory corresponds to the Clippy repository and the +# commit of the just created rust-1.XX.0 tag is checked out. +$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote +``` + +_NOTE: Usually there are no stable backports for Clippy, so this update should +be possible without force pushing or anything like this. If there should have +happened a stable backport, make sure to re-merge those changes just as with the +`beta` branch._ + +## Update `CHANGELOG.md` + +For this see the document on [how to update the changelog]. + +[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md diff --git a/book/src/installation_and_usage.md b/book/src/installation_and_usage.md new file mode 100644 index 0000000000000..190c8ed5342ab --- /dev/null +++ b/book/src/installation_and_usage.md @@ -0,0 +1,108 @@ +# Installation and Usage + +Below are instructions on how to use Clippy as a subcommand, compiled from source +or in Travis CI. Note that Clippy is installed as a +[component](https://rust-lang.github.io/rustup/concepts/components.html?highlight=clippy#components) as part of the +[rustup](https://rust-lang.github.io/rustup/installation/index.html) installation. + +### As a cargo subcommand (`cargo clippy`) + +One way to use Clippy is by installing Clippy through rustup as a cargo +subcommand. + +#### Step 1: Install rustup + +You can install [rustup](https://rustup.rs/) on supported platforms. This will help +us install Clippy and its dependencies. + +If you already have rustup installed, update to ensure you have the latest +rustup and compiler: + +```terminal +rustup update +``` + +#### Step 2: Install Clippy + +Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command: + +```terminal +rustup component add clippy +``` +If it says that it can't find the `clippy` component, please run `rustup self update`. + +#### Step 3: Run Clippy + +Now you can run Clippy by invoking the following command: + +```terminal +cargo clippy +``` + +#### Automatically applying Clippy suggestions + +Clippy can automatically apply some lint suggestions. +Note that this is still experimental and only supported on the nightly channel: + +```terminal +cargo clippy --fix +``` + +#### Workspaces + +All the usual workspace options should work with Clippy. For example the following command +will run Clippy on the `example` crate: + +```terminal +cargo clippy -p example +``` + +As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies. +If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this: + +```terminal +cargo clippy -p example --no-deps +``` + +### As a rustc replacement (`clippy-driver`) + +Clippy can also be used in projects that do not use cargo. To do so, you will need to replace +your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: + +```terminal +rustc --edition 2018 -Cpanic=abort foo.rs +``` + +Then, to enable Clippy, you will need to call: + +```terminal +clippy-driver --edition 2018 -Cpanic=abort foo.rs +``` + +Note that `rustc` will still run, i.e. it will still emit the output files it normally does. + +### Travis CI + +You can add Clippy to Travis CI in the same way you use it locally: + +```yml +language: rust +rust: + - stable + - beta +before_script: + - rustup component add clippy +script: + - cargo clippy + # if you want the build job to fail when encountering warnings, use + - cargo clippy -- -D warnings + # in order to also check tests and non-default crate features, use + - cargo clippy --all-targets --all-features -- -D warnings + - cargo test + # etc. +``` + +Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. +That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command +line. (You can swap `clippy::all` with the specific lint category you are targeting.) diff --git a/book/src/lints/README.md b/book/src/lints/README.md new file mode 100644 index 0000000000000..a2777e8f4d583 --- /dev/null +++ b/book/src/lints/README.md @@ -0,0 +1 @@ +# Clippy's Lints diff --git a/book/src/lints/cargo.md b/book/src/lints/cargo.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/complexity.md b/book/src/lints/complexity.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/correctness.md b/book/src/lints/correctness.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/deprecated.md b/book/src/lints/deprecated.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/nursery.md b/book/src/lints/nursery.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/pedantic.md b/book/src/lints/pedantic.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/perf.md b/book/src/lints/perf.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/restriction.md b/book/src/lints/restriction.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/lints/style.md b/book/src/lints/style.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/book/src/roadmap/2021.md b/book/src/roadmap/2021.md new file mode 100644 index 0000000000000..fe8b080f56f2b --- /dev/null +++ b/book/src/roadmap/2021.md @@ -0,0 +1,235 @@ +# Roadmap 2021 + +# Summary + +This Roadmap lays out the plans for Clippy in 2021: + +- Improving usability and reliability +- Improving experience of contributors and maintainers +- Develop and specify processes + +Members of the Clippy team will be assigned tasks from one or more of these +topics. The team member is then responsible to complete the assigned tasks. This +can either be done by implementing them or by providing mentorship to interested +contributors. + +# Motivation + +With the ongoing growth of the Rust language and with that of the whole +ecosystem, also Clippy gets more and more users and contributors. This is good +for the project, but also brings challenges along. Some of these challenges are: + +- More issues about reliability or usability are popping up +- Traffic is hard to handle for a small team +- Bigger projects don't get completed due to the lack of processes and/or time + of the team members + +Additionally, according to the [Rust Roadmap 2021], clear processes should be +defined by every team and unified across teams. This Roadmap is the first step +towards this. + +[Rust Roadmap 2021]: https://github.com/rust-lang/rfcs/pull/3037 + +# Explanation + +This section will explain the things that should be done in 2021. It is +important to note, that this document focuses on the "What?", not the "How?". +The later will be addressed in follow-up tracking issue, with an assigned team +member. + +The following is split up in two major sections. The first section covers the +user facing plans, the second section the internal plans. + +## User Facing + +Clippy should be as pleasant to use and configure as possible. This section +covers plans that should be implemented to improve the situation of Clippy in +this regard. + +### Usability + +In the following, plans to improve the usability are covered. + +#### No Output After `cargo check` + +Currently when `cargo clippy` is run after `cargo check`, it does not produce +any output. This is especially problematic since `rust-analyzer` is on the rise +and it uses `cargo check` for checking code. A fix is already implemented, but +it still has to be pushed over the finish line. This also includes the +stabilization of the `cargo clippy --fix` command or the support of multi-span +suggestions in `rustfix`. + +- [#4612](https://github.com/rust-lang/rust-clippy/issues/4612) + +#### `lints.toml` Configuration + +This is something that comes up every now and then: a reusable configuration +file, where lint levels can be defined. Discussions about this often lead to +nothing specific or to "we need an RFC for this". And this is exactly what needs +to be done. Get together with the cargo team and write an RFC and implement such +a configuration file somehow and somewhere. + +- [#3164](https://github.com/rust-lang/rust-clippy/issues/3164) +- [cargo#5034](https://github.com/rust-lang/cargo/issues/5034) +- [IRLO](https://internals.rust-lang.org/t/proposal-cargo-lint-configuration/9135/8) + +#### Lint Groups + +There are more and more issues about managing lints in Clippy popping up. Lints +are hard to implement with a guarantee of no/few false positives (FPs). One way +to address this might be to introduce more lint groups to give users the ability +to better manage lints, or improve the process of classifying lints, so that +disabling lints due to FPs becomes rare. It is important to note, that Clippy +lints are less conservative than `rustc` lints, which won't change in the +future. + +- [#5537](https://github.com/rust-lang/rust-clippy/issues/5537) +- [#6366](https://github.com/rust-lang/rust-clippy/issues/6366) + +### Reliability + +In the following, plans to improve the reliability are covered. + +#### False Positive Rate + +In the worst case, new lints are only available in nightly for 2 weeks, before +hitting beta and ultimately stable. This and the fact that fewer people use +nightly Rust nowadays makes it more probable that a lint with many FPs hits +stable. This leads to annoyed users, that will disable these new lints in the +best case and to more annoyed users, that will stop using Clippy in the worst. +A process should be developed and implemented to prevent this from happening. + +- [#6429](https://github.com/rust-lang/rust-clippy/issues/6429) + +## Internal + +(The end of) 2020 has shown, that Clippy has to think about the available +resources, especially regarding management and maintenance of the project. This +section address issues affecting team members and contributors. + +### Management + +In 2020 Clippy achieved over 1000 open issues with regularly between 25-35 open +PRs. This is simultaneously a win and a loss. More issues and PRs means more +people are interested in Clippy and in contributing to it. On the other hand, it +means for team members more work and for contributors longer wait times for +reviews. The following will describe plans how to improve the situation for both +team members and contributors. + +#### Clear Expectations for Team Members + +According to the [Rust Roadmap 2021], a document specifying what it means to be +a member of the team should be produced. This should not put more pressure on +the team members, but rather help them and interested folks to know what the +expectations are. With this it should also be easier to recruit new team members +and may encourage people to get in touch, if they're interested to join. + +#### Scaling up the Team + +More people means less work for each individual. Together with the document +about expectations for team members, a document defining the process of how to +join the team should be produced. This can also increase the stability of the +team, in case of current members dropping out (temporarily). There can also be +different roles in the team, like people triaging vs. people reviewing. + +#### Regular Meetings + +Other teams have regular meetings. Clippy is big enough that it might be worth +to also do them. Especially if more people join the team, this can be important +for sync-ups. Besides the asynchronous communication, that works well for +working on separate lints, a meeting adds a synchronous alternative at a known +time. This is especially helpful if there are bigger things that need to be +discussed (like the projects in this roadmap). For starters bi-weekly meetings +before Rust syncs might make sense. + +#### Triaging + +To get a handle on the influx of open issues, a process for triaging issues and +PRs should be developed. Officially, Clippy follows the Rust triage process, but +currently no one enforces it. This can be improved by sharing triage teams +across projects or by implementing dashboards / tools which simplify triaging. + +### Development + +Improving the developer and contributor experience is something the Clippy team +works on regularly. Though, some things might need special attention and +planing. These topics are listed in the following. + +#### Process for New and Existing Lints + +As already mentioned above, classifying new lints gets quite hard, because the +probability of a buggy lint getting into stable is quite high. A process should +be implemented on how to classify lints. In addition, a test system should be +developed to find out which lints are currently problematic in real world code +to fix or disable them. + +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741056379) +- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741153345) + +#### Processes + +Related to the point before, a process for suggesting and discussing major +changes should be implemented. It's also not clearly defined when a lint should +be enabled or disabled by default. This can also be improved by the test system +mentioned above. + +#### Dev-Tools + +There's already `cargo dev` which makes Clippy development easier and more +pleasant. This can still be expanded, so that it covers more areas of the +development process. + +- [#5394](https://github.com/rust-lang/rust-clippy/issues/5394) + +#### Contributor Guide + +Similar to a Clippy Book, which describes how to use Clippy, a book about how to +contribute to Clippy might be helpful for new and existing contributors. There's +already the `doc` directory in the Clippy repo, this can be turned into a +`mdbook`. + +#### `rustc` integration + +Recently Clippy was integrated with `git subtree` into the `rust-lang/rust` +repository. This made syncing between the two repositories easier. A +`#[non_exhaustive]` list of things that still can be improved is: + +1. Use the same `rustfmt` version and configuration as `rustc`. +2. Make `cargo dev` work in the Rust repo, just as it works in the Clippy repo. + E.g. `cargo dev bless` or `cargo dev update_lints`. And even add more things + to it that might be useful for the Rust repo, e.g. `cargo dev deprecate`. +3. Easier sync process. The `subtree` situation is not ideal. + +## Prioritization + +The most pressing issues for users of Clippy are of course the user facing +issues. So there should be a priority on those issues, but without losing track +of the internal issues listed in this document. + +Getting the FP rate of warn/deny-by-default lints under control should have the +highest priority. Other user facing issues should also get a high priority, but +shouldn't be in the way of addressing internal issues. + +To better manage the upcoming projects, the basic internal processes, like +meetings, tracking issues and documentation, should be established as soon as +possible. They might even be necessary to properly manage the projects, +regarding the user facing issues. + +# Prior Art + +## Rust Roadmap + +Rust's roadmap process was established by [RFC 1728] in 2016. Since then every +year a roadmap was published, that defined the bigger plans for the coming +years. This years roadmap can be found [here][Rust Roadmap 2021]. + +[RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html + +# Drawbacks + +## Big Roadmap + +This roadmap is pretty big and not all items listed in this document might be +addressed during 2021. Because this is the first roadmap for Clippy, having open +tasks at the end of 2021 is fine, but they should be revisited in the 2022 +roadmap. diff --git a/book/src/roadmap/README.md b/book/src/roadmap/README.md new file mode 100644 index 0000000000000..e69de29bb2d1d From 853d7eeed688eb8eab9cf88d8189006e712af6ca Mon Sep 17 00:00:00 2001 From: josh rotenberg Date: Tue, 3 Aug 2021 13:05:20 -0700 Subject: [PATCH 36/78] Book: add a ci chapter --- book/src/SUMMARY.md | 4 +++ book/src/continuous_integration/README.md | 5 ++++ .../continuous_integration/github_actions.md | 18 ++++++++++++ book/src/continuous_integration/gitlab.md | 3 ++ book/src/continuous_integration/travis.md | 25 +++++++++++++++++ book/src/installation_and_usage.md | 28 +++---------------- 6 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 book/src/continuous_integration/README.md create mode 100644 book/src/continuous_integration/github_actions.md create mode 100644 book/src/continuous_integration/gitlab.md create mode 100644 book/src/continuous_integration/travis.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 470760b6d1688..5d10101d6377a 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,6 +13,10 @@ - [Pendantic]() - [Nursery]() - [Cargo]() +- [Continuous Integration](continuous_integration/README.md) + - [Travis CI](continuous_integration/travis.md) + - [GitHub Actions](continuous_integration/github_actions.md) + - [Gitlab](continuous_integration/gitlab.md) - [Development](development/README.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) diff --git a/book/src/continuous_integration/README.md b/book/src/continuous_integration/README.md new file mode 100644 index 0000000000000..a28959de5c565 --- /dev/null +++ b/book/src/continuous_integration/README.md @@ -0,0 +1,5 @@ +# Continuous Integration + +- [Travis CI](travis.md) +- [Github Actions](github_actions.md) +- [Gitlab](gitlab.md) diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md new file mode 100644 index 0000000000000..070ef5b41c2fd --- /dev/null +++ b/book/src/continuous_integration/github_actions.md @@ -0,0 +1,18 @@ +# GitHub Actions + +## actions-rs + +An easy way to get started with adding Clippy to GitHub Actions is +the [actions-rs](https://github.com/actions-rs) [clippy-check](https://github.com/actions-rs/clippy-check): + +```yml +on: push +name: Clippy check +jobs: + clippy_check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Run Clippy + run: cargo clippy +``` diff --git a/book/src/continuous_integration/gitlab.md b/book/src/continuous_integration/gitlab.md new file mode 100644 index 0000000000000..bb5b755d5ddfe --- /dev/null +++ b/book/src/continuous_integration/gitlab.md @@ -0,0 +1,3 @@ +# Gitlab + +***placeholder*** diff --git a/book/src/continuous_integration/travis.md b/book/src/continuous_integration/travis.md new file mode 100644 index 0000000000000..36e467f9858c4 --- /dev/null +++ b/book/src/continuous_integration/travis.md @@ -0,0 +1,25 @@ +# Travis CI + +You can add Clippy to Travis CI in the same way you use it locally: + +```yml +language: rust +rust: + - stable + - beta +before_script: + - rustup component add clippy +script: + - cargo clippy + # if you want the build job to fail when encountering warnings, use + - cargo clippy -- -D warnings + # in order to also check tests and non-default crate features, use + - cargo clippy --all-targets --all-features -- -D warnings + - cargo test + # etc. +``` + +Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. +That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command +line. (You can swap `clippy::all` with the specific lint category you are targeting.) diff --git a/book/src/installation_and_usage.md b/book/src/installation_and_usage.md index 190c8ed5342ab..8982e0f10378c 100644 --- a/book/src/installation_and_usage.md +++ b/book/src/installation_and_usage.md @@ -81,28 +81,8 @@ clippy-driver --edition 2018 -Cpanic=abort foo.rs Note that `rustc` will still run, i.e. it will still emit the output files it normally does. -### Travis CI - -You can add Clippy to Travis CI in the same way you use it locally: - -```yml -language: rust -rust: - - stable - - beta -before_script: - - rustup component add clippy -script: - - cargo clippy - # if you want the build job to fail when encountering warnings, use - - cargo clippy -- -D warnings - # in order to also check tests and non-default crate features, use - - cargo clippy --all-targets --all-features -- -D warnings - - cargo test - # etc. -``` +### Continuous Integration + +Adding Clippy to your continuous integration pipeline is a great way to automate the linting process. See the +[Continuous Integration](continuous_integration) chapter for more information. -Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. -That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause -an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command -line. (You can swap `clippy::all` with the specific lint category you are targeting.) From af385799e0d5483170973a79110bdb1ce2949579 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 16:55:26 +0100 Subject: [PATCH 37/78] Move internal documentation to book --- book/src/development/adding_lints.md | 125 ++-- book/src/development/basics.md | 12 +- .../development/common_tools_writing_lints.md | 253 ++++--- book/src/infrastructure/changelog_update.md | 2 +- book/src/infrastructure/release.md | 21 + doc/adding_lints.md | 697 ------------------ doc/backport.md | 71 -- doc/basics.md | 174 ----- doc/changelog_update.md | 97 --- doc/common_tools_writing_lints.md | 266 ------- doc/release.md | 145 ---- doc/roadmap-2021.md | 235 ------ 12 files changed, 263 insertions(+), 1835 deletions(-) delete mode 100644 doc/adding_lints.md delete mode 100644 doc/backport.md delete mode 100644 doc/basics.md delete mode 100644 doc/changelog_update.md delete mode 100644 doc/common_tools_writing_lints.md delete mode 100644 doc/release.md delete mode 100644 doc/roadmap-2021.md diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 5a06afedbf4c2..3e0b1c5c4f782 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -11,21 +11,24 @@ because that's clearly a non-descriptive name. - [Setup](#setup) - [Getting Started](#getting-started) - [Testing](#testing) + - [Cargo lints](#cargo-lints) - [Rustfix tests](#rustfix-tests) - [Edition 2018 tests](#edition-2018-tests) - [Testing manually](#testing-manually) - [Lint declaration](#lint-declaration) + - [Lint registration](#lint-registration) - [Lint passes](#lint-passes) - [Emitting a lint](#emitting-a-lint) - [Adding the lint logic](#adding-the-lint-logic) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) + - [Print HIR lint](#print-hir-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) - [PR Checklist](#pr-checklist) - [Adding configuration to a lint](#adding-configuration-to-a-lint) - - [Cheatsheet](#cheatsheet) + - [Cheat Sheet](#cheat-sheet) ## Setup @@ -42,9 +45,9 @@ take a look at our [lint naming guidelines][lint_naming]. To get started on this lint you can run `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and -`clippy_lints/src/foo_functions.rs`, as well as run `cargo dev update_lints` to -register the new lint. For cargo lints, two project hierarchies (fail/pass) will -be created by default under `tests/ui-cargo`. +`clippy_lints/src/foo_functions.rs`, as well as +[registering the lint](#lint-registration). For cargo lints, two project +hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -155,7 +158,7 @@ Manually testing against an example file can be useful if you have added some your local modifications, run ``` -env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs +cargo dev lint input.rs ``` from the working copy root. With tests in place, let's have a look at @@ -179,17 +182,15 @@ the auto-generated lint declaration to have a real description, something like t ```rust declare_clippy_lint! { - /// **What it does:** + /// ### What it does /// - /// **Why is this bad?** - /// - /// **Known problems:** None. - /// - /// **Example:** + /// ### Why is this bad? /// + /// ### Example /// ```rust /// // example code /// ``` + #[clippy::version = "1.29.0"] pub FOO_FUNCTIONS, pedantic, "function named `foo`, which is not a descriptive name" @@ -200,6 +201,10 @@ declare_clippy_lint! { section. This is the default documentation style and will be displayed [like this][example_lint_page]. To render and open this documentation locally in a browser, run `cargo dev serve`. +* The `#[clippy::version]` attribute will be rendered as part of the lint documentation. + The value should be set to the current Rust version that the lint is developed in, + it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version + is listed under *release*. (Use the version without the `-nightly`) suffix. * `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 @@ -222,32 +227,34 @@ declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]); impl EarlyLintPass for FooFunctions {} ``` -Normally after declaring the lint, we have to run `cargo dev update_lints`, -which updates some files, so Clippy knows about the new lint. Since we used -`cargo dev new_lint ...` to generate the lint declaration, this was done -automatically. While `update_lints` automates most of the things, it doesn't -automate everything. We will have to register our lint pass manually in the -`register_plugins` function in `clippy_lints/src/lib.rs`: +[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 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 + +## Lint registration + +When using `cargo dev new_lint`, the lint is automatically registered and +nothing more has to be done. + +When declaring a new lint by hand and `cargo dev update_lints` is used, the lint +pass may have to be registered manually in the `register_plugins` function in +`clippy_lints/src/lib.rs`: ```rust -store.register_early_pass(|| box foo_functions::FooFunctions); +store.register_early_pass(|| Box::new(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 -[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 +One reason that `cargo dev update_lints` 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. ## Lint passes @@ -425,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass using the `meets_msrv` utility function. ``` rust -if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } ``` @@ -478,6 +485,19 @@ you are implementing your lint. [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +## Print HIR lint + +To implement a lint, it's helpful to first understand the internal representation +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or +expression that the attribute is attached to. To attach the attribute to expressions +you often need to enable `#![feature(stmt_expr_attributes)]`. + +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. + +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb + ## Documentation The final thing before submitting our PR is to add some documentation to our @@ -487,21 +507,23 @@ Please document your lint with a doc comment akin to the following: ```rust declare_clippy_lint! { - /// **What it does:** Checks for ... (describe what the lint matches). + /// ### What it does + /// Checks for ... (describe what the lint matches). /// - /// **Why is this bad?** Supply the reason for linting the code. + /// ### Why is this bad? + /// Supply the reason for linting the code. /// - /// **Known problems:** None. (Or describe where it could go wrong.) - /// - /// **Example:** + /// ### Example /// /// ```rust,ignore - /// // Bad - /// Insert a short example of code that triggers the lint - /// - /// // Good - /// Insert a short example of improved code that doesn't trigger the lint + /// // A short example of code that triggers the lint /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// // A short example of improved code that doesn't trigger the lint + /// ``` + #[clippy::version = "1.29.0"] pub FOO_FUNCTIONS, pedantic, "function named `foo`, which is not a descriptive name" @@ -558,14 +580,16 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: -1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs) +1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) like this: ```rust - /// Lint: LINT_NAME. + /// Lint: LINT_NAME. + /// + /// (configuration_ident: Type = DefaultValue), ``` - The configuration value and identifier should usually be the same. The doc comment will be - automatically added to the lint documentation. + The doc comment is automatically added to the documentation of the listed lints. The default + value will be formatted using the `Debug` implementation of the type. 2. Adding the configuration value to the lint impl struct: 1. This first requires the definition of a lint impl struct. Lint impl structs are usually generated with the `declare_lint_pass!` macro. This struct needs to be defined manually @@ -626,14 +650,14 @@ in the following steps: with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. -## Cheatsheet +## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here (`implements_trait`, `match_def_path`, `snippet`, etc) + is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] -* [The `if_chain` macro][if_chain] +* [Let chains][let-chains] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] @@ -657,8 +681,11 @@ documentation currently. This is unfortunate, but in most cases you can probably get away with copying things from existing similar lints. If you are stuck, don't hesitate to ask on [Zulip] or in the issue/PR. -[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs -[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html +[let-chains]: https://github.com/rust-lang/rust/pull/94927 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html diff --git a/book/src/development/basics.md b/book/src/development/basics.md index aaf31158f58a8..57a90a924ec3c 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -91,15 +91,18 @@ cargo dev fmt cargo dev update_lints # create a new lint and register it cargo dev new_lint +# automatically formatting all code before each commit +cargo dev setup git-hook # (experimental) Setup Clippy to work with IntelliJ-Rust -cargo dev ide_setup +cargo dev setup intellij ``` +More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) ## lintcheck `cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. -You can `git diff` the updated log against its previous version and +You can `git diff` the updated log against its previous version and see what impact your lint made on a small set of crates. -If you add a new lint, please audit the resulting warnings and make sure +If you add a new lint, please audit the resulting warnings and make sure there are no false positives and that the suggestions are valid. Refer to the tools [README] for more details. @@ -167,6 +170,5 @@ rustup component add clippy > [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and > `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running > `rustup update`. - - + [glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 0a85f65001101..1d1aee0da2cc7 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -4,17 +4,19 @@ You may need following tooltips to catch up with common operations. - [Common tools for writing lints](#common-tools-for-writing-lints) - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) - - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) + - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) + - [Checking for a specific type](#checking-for-a-specific-type) - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait) - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method) - - [Dealing with macros](#dealing-with-macros) + - [Dealing with macros](#dealing-with-macros-and-expansions) Useful Rustc dev guide links: - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) +- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) -# Retrieving the type of an expression +## Retrieving the type of an expression Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: @@ -24,7 +26,7 @@ Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for ex - does it implement a trait? This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct, -that gives you access to the underlying structure [`TyS`][TyS]. +that gives you access to the underlying structure [`Ty`][Ty]. Example of use: ```rust @@ -53,42 +55,81 @@ Two noticeable items here: 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 +## Checking if an expr is calling a specific method Starting with an `expr`, you can check whether it is calling a specific method `some_method`: ```rust -impl LateLintPass<'_> for MyStructLint { +impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind; + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind // Check the name of this method is `some_method` - if path.ident.name == sym!(some_method); - then { + && path.ident.name == sym!(some_method) + // Optionally, check the type of the self argument. + // - See "Checking for a specific type" + { // ... - } } } } ``` -# Checking if a type implements a specific trait +## Checking for a specific type -There are two ways to do this, depending if the target trait is part of lang items. +There are three ways to check if an expression type is a specific type we want to check for. +All of these methods only check for the base type, generic arguments have to be checked separately. ```rust -use clippy_utils::{implements_trait, match_trait_method, paths}; +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::{paths, match_def_path}; +use rustc_span::symbol::sym; +use rustc_hir::LangItem; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using expression and Clippy's convenient method - // we use `match_trait_method` function from Clippy's toolbox - if match_trait_method(cx, expr, &paths::INTO) { - // `expr` implements `Into` trait + // Getting the expression type + let ty = cx.typeck_results().expr_ty(expr); + + // 1. Using diagnostic items + // The last argument is the diagnostic item to check for + if is_type_diagnostic_item(cx, ty, sym::Option) { + // The type is an `Option` + } + + // 2. Using lang items + if is_type_lang_item(cx, ty, LangItem::RangeFull) { + // The type is a full range like `.drain(..)` + } + + // 3. Using the type path + // This method should be avoided if possible + if match_def_path(cx, def_id, &paths::RESULT) { + // The type is a `core::result::Result` + } + } +} +``` + +Prefer using diagnostic items and lang items where possible. + +## Checking if a type implements a specific trait + +There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. + +```rust +use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; +use rustc_span::symbol::sym; + +impl LateLintPass<'_> for MyStructLint { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + // 1. Using diagnostic items with the expression + // we use `is_trait_method` function from Clippy's utils + if is_trait_method(cx, expr, sym::Iterator) { + // method call in `expr` belongs to `Iterator` trait } - // 2. Using type context `TyCtxt` + // 2. Using lang items with the expression type let ty = cx.typeck_results().expr_ty(expr); if cx.tcx.lang_items() // we are looking for the `DefId` of `Drop` trait in lang items @@ -97,102 +138,125 @@ impl LateLintPass<'_> for MyStructLint { .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // `expr` implements `Drop` trait } + + // 3. Using the type path with the expression + // we use `match_trait_method` function from Clippy's utils + // (This method should be avoided if possible) + if match_trait_method(cx, expr, &paths::INTO) { + // `expr` implements `Into` trait + } } } ``` -> Prefer using lang items, if the target trait is available there. - -A list of defined paths for Clippy can be found in [paths.rs][paths] +> Prefer using diagnostic and lang items, if the target trait has one. We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. +A list of defined paths for Clippy can be found in [paths.rs][paths] -# Checking if a type defines a specific method +## Checking if a type defines a specific method To check if our type defines a method called `some_method`: ```rust -use clippy_utils::{is_type_diagnostic_item, return_ty}; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::return_ty; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if_chain! { - // Check if item is a method/function - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - if impl_item.ident.name == sym!(some_method); + && impl_item.ident.name == sym!(some_method) // We can also check it has a parameter `self` - if signature.decl.implicit_self.has_implicit_self(); + && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); - then { - // ... - } + && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + { + // ... } } } ``` -# Dealing with macros - -There are several helpers in [`clippy_utils`][utils] to deal with macros: - -- `in_macro()`: detect if the given span is expanded by a macro - -You may want to use this for example to not start linting in any macro. - -```rust -macro_rules! foo { - ($param:expr) => { - match $param { - "bar" => println!("whatever"), - _ => () - } - }; -} - -foo!("bar"); - -// if we lint the `match` of `foo` call and test its span -assert_eq!(in_macro(match_span), true); -``` - -- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate - -You may want to use it for example to not start linting in macros from other crates - -```rust -#[macro_use] -extern crate a_crate_with_macros; - -// `foo` is defined in `a_crate_with_macros` -foo!("bar"); - -// if we lint the `match` of `foo` call and test its span -assert_eq!(in_external_macro(cx.sess(), match_span), true); -``` - -- `differing_macro_contexts()`: returns true if the two given spans are not from the same context - -```rust -macro_rules! m { - ($a:expr, $b:expr) => { - if $a.is_some() { - $b; - } - } -} - -let x: Option = Some(42); -m!(x, x.unwrap()); - -// These spans are not from the same context -// x.is_some() is from inside the macro -// x.unwrap() is from outside the macro -assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true); -``` - -[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html +## Dealing with macros and expansions + +Keep in mind that macros are already expanded and desugaring is already applied +to the code representation that you are working with in Clippy. This unfortunately causes a lot of +false positives because macro expansions are "invisible" unless you actively check for them. +Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be +dynamic in ways that are difficult or impossible to see. +Use the following functions to deal with macros: + +- `span.from_expansion()`: detects if a span is from macro expansion or desugaring. + Checking this is a common first step in a lint. + + ```rust + if expr.span.from_expansion() { + // just forget it + return; + } + ``` + +- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it. + It is sometimes useful to check if the context of two spans are equal. + + ```rust + // expands to `1 + 0`, but don't lint + 1 + mac!() + ``` + ```rust + if left.span.ctxt() != right.span.ctxt() { + // the coder most likely cannot modify this expression + return; + } + ``` + Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can + be assumed to have the same context. And so just using `span.from_expansion()` is often good enough. + + +- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate. + If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros + not defined in the current crate. It doesn't make sense to lint code that the coder can't change. + + You may want to use it for example to not start linting in macros from other crates + + ```rust + #[macro_use] + extern crate a_crate_with_macros; + + // `foo` is defined in `a_crate_with_macros` + foo!("bar"); + + // if we lint the `match` of `foo` call and test its span + assert_eq!(in_external_macro(cx.sess(), match_span), true); + ``` + +- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it + +One thing `SpanContext` is useful for is to check if two spans are in the same context. For example, +in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some +expression with a different context from `a`. + + ```rust + macro_rules! m { + ($a:expr, $b:expr) => { + if $a.is_some() { + $b; + } + } + } + + let x: Option = Some(42); + m!(x, x.unwrap()); + + // These spans are not from the same context + // x.is_some() is from inside the macro + // x.unwrap() is from outside the macro + assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt()); + ``` + +[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty @@ -200,4 +264,3 @@ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true); [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html [pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty [paths]: ../clippy_utils/src/paths.rs -[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs diff --git a/book/src/infrastructure/changelog_update.md b/book/src/infrastructure/changelog_update.md index 115848c48044c..0cbad2c09249c 100644 --- a/book/src/infrastructure/changelog_update.md +++ b/book/src/infrastructure/changelog_update.md @@ -32,7 +32,7 @@ bullet points might be helpful: need to check out the Rust release tag of the stable release. [Link][rust_stable_tools] -Usually you want to wirte the changelog of the **upcoming stable release**. Make +Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: diff --git a/book/src/infrastructure/release.md b/book/src/infrastructure/release.md index afe3033c288cf..c4f8f98938428 100644 --- a/book/src/infrastructure/release.md +++ b/book/src/infrastructure/release.md @@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the For this see the document on [how to update the changelog]. +If you don't have time to do a complete changelog update right away, just update +the following parts: + +- Remove the `(beta)` from the new stable version: + + ```markdown + ## Rust 1.XX (beta) -> ## Rust 1.XX + ``` + +- Update the release date line of the new stable version: + + ```markdown + Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD + ``` + +- Update the release date line of the previous stable version: + + ```markdown + Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD + ``` + [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md diff --git a/doc/adding_lints.md b/doc/adding_lints.md deleted file mode 100644 index 3e0b1c5c4f782..0000000000000 --- a/doc/adding_lints.md +++ /dev/null @@ -1,697 +0,0 @@ -# Adding a new lint - -You are probably here because you want to add a new lint to Clippy. If this is -the first time you're contributing to Clippy, this document guides you through -creating an example lint from scratch. - -To get started, we will create a lint that detects functions called `foo`, -because that's clearly a non-descriptive name. - -- [Adding a new lint](#adding-a-new-lint) - - [Setup](#setup) - - [Getting Started](#getting-started) - - [Testing](#testing) - - [Cargo lints](#cargo-lints) - - [Rustfix tests](#rustfix-tests) - - [Edition 2018 tests](#edition-2018-tests) - - [Testing manually](#testing-manually) - - [Lint declaration](#lint-declaration) - - [Lint registration](#lint-registration) - - [Lint passes](#lint-passes) - - [Emitting a lint](#emitting-a-lint) - - [Adding the lint logic](#adding-the-lint-logic) - - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - - [Author lint](#author-lint) - - [Print HIR lint](#print-hir-lint) - - [Documentation](#documentation) - - [Running rustfmt](#running-rustfmt) - - [Debugging](#debugging) - - [PR Checklist](#pr-checklist) - - [Adding configuration to a lint](#adding-configuration-to-a-lint) - - [Cheat Sheet](#cheat-sheet) - -## Setup - -See the [Basics](basics.md#get-the-code) documentation. - -## Getting Started - -There is a bit of boilerplate code that needs to be set up when creating a new -lint. Fortunately, you can use the clippy dev tools to handle this for you. We -are naming our new lint `foo_functions` (lints are generally written in snake -case), and we don't need type information so it will have an early pass type -(more on this later on). If you're not sure if the name you chose fits the lint, -take a look at our [lint naming guidelines][lint_naming]. To get started on this -lint you can run `cargo dev new_lint --name=foo_functions --pass=early ---category=pedantic` (category will default to nursery if not provided). This -command will create two files: `tests/ui/foo_functions.rs` and -`clippy_lints/src/foo_functions.rs`, as well as -[registering the lint](#lint-registration). For cargo lints, two project -hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. - -Next, we'll open up these files and add our lint! - -## Testing - -Let's write some tests first that we can execute while we iterate on our lint. - -Clippy uses UI tests for testing. UI tests check that the output of Clippy is -exactly as expected. Each test is just a plain Rust file that contains the code -we want to check. The output of Clippy is compared against a `.stderr` file. -Note that you don't have to create this file yourself, we'll get to -generating the `.stderr` files further down. - -We start by opening the test file created at `tests/ui/foo_functions.rs`. - -Update the file with some examples to get started: - -```rust -#![warn(clippy::foo_functions)] - -// Impl methods -struct A; -impl A { - pub fn fo(&self) {} - pub fn foo(&self) {} - pub fn food(&self) {} -} - -// Default trait methods -trait B { - fn fo(&self) {} - fn foo(&self) {} - fn food(&self) {} -} - -// Plain functions -fn fo() {} -fn foo() {} -fn food() {} - -fn main() { - // We also don't want to lint method calls - foo(); - let a = A; - a.foo(); -} -``` - -Now we can run the test with `TESTNAME=foo_functions cargo uitest`, -currently this test is meaningless though. - -While we are working on implementing our lint, we can keep running the UI -test. That allows us to check if the output is turning into what we want. - -Once we are satisfied with the output, we need to run -`cargo dev bless` to update the `.stderr` file for our lint. -Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `cargo dev bless`. -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 `cargo dev bless` for the -specific lint you are creating/editing. Note that if the generated files are -empty, they should be removed. - -Note that you can run multiple test files by specifying a comma separated list: -`TESTNAME=foo_functions,test2,test3`. - -### Cargo lints - -For cargo lints, the process of testing differs in that we are interested in -the `Cargo.toml` manifest file. We also need a minimal crate associated -with that manifest. - -If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` -we will find by default two new crates, each with its manifest file: - -* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. -* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. - -If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. - -The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too. - -## Rustfix tests - -If the lint you are working on is making use of structured suggestions, the -test file should include a `// run-rustfix` comment at the top. This will -additionally run [rustfix] for that test. Rustfix will apply the suggestions -from the lint to the code of the test file and compare that to the contents of -a `.fixed` file. - -Use `cargo dev bless` to automatically generate the -`.fixed` file after running the tests. - -[rustfix]: https://github.com/rust-lang/rustfix - -## Edition 2018 tests - -Some features require the 2018 edition to work (e.g. `async_await`), but -compile-test tests run on the 2015 edition by default. To change this behavior -add `// edition:2018` at the top of the test file (note that it's space-sensitive). - -## Testing manually - -Manually testing against an example file can be useful if you have added some -`println!`s and the test suite output becomes unreadable. To try Clippy with -your local modifications, run - -``` -cargo dev lint input.rs -``` - -from the working copy root. With tests in place, let's have a look at -implementing our lint now. - -## Lint declaration - -Let's start by opening the new file created in the `clippy_lints` crate -at `clippy_lints/src/foo_functions.rs`. That's the crate where all the -lint code is. This file has already imported some initial things we will need: - -```rust -use rustc_lint::{EarlyLintPass, EarlyContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_ast::ast::*; -``` - -The next step is to update the lint declaration. Lints are declared using the -[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update -the auto-generated lint declaration to have a real description, something like this: - -```rust -declare_clippy_lint! { - /// ### What it does - /// - /// ### Why is this bad? - /// - /// ### Example - /// ```rust - /// // example code - /// ``` - #[clippy::version = "1.29.0"] - pub FOO_FUNCTIONS, - pedantic, - "function named `foo`, which is not a descriptive name" -} -``` - -* 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]. To render and open this documentation locally - in a browser, run `cargo dev serve`. -* The `#[clippy::version]` attribute will be rendered as part of the lint documentation. - The value should be set to the current Rust version that the lint is developed in, - it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version - is listed under *release*. (Use the version without the `-nightly`) suffix. -* `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 - read well when used with `allow`/`warn`/`deny`. -* `pedantic` sets the lint level to `Allow`. - The exact mapping can be found [here][category_level_mapping] -* The last part should be a text that explains what exactly is wrong with the - code - -The rest of this file contains an empty implementation for our lint pass, -which in this case is `EarlyLintPass` and should look like this: - -```rust -// clippy_lints/src/foo_functions.rs - -// .. imports and lint declaration .. - -declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]); - -impl EarlyLintPass for FooFunctions {} -``` - -[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 -[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110 - -## Lint registration - -When using `cargo dev new_lint`, the lint is automatically registered and -nothing more has to be done. - -When declaring a new lint by hand and `cargo dev update_lints` is used, the lint -pass may have to be registered manually in the `register_plugins` function in -`clippy_lints/src/lib.rs`: - -```rust -store.register_early_pass(|| Box::new(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 update_lints` 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. - -## Lint passes - -Writing a lint that only checks for the name of a function means that we only -have to deal with the AST and don't have to deal with the type system at all. -This is good, because it makes writing this particular lint less complicated. - -We have to make this decision with every new Clippy lint. It boils down to using -either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. - -In short, the `LateLintPass` has access to type information while the -`EarlyLintPass` doesn't. If you don't need access to type information, use the -`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed -hasn't really been a concern with Clippy so far. - -Since we don't need type information for checking the function name, we used -`--pass=early` when running the new lint automation and all the imports were -added accordingly. - -[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html -[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html - -## Emitting a lint - -With UI tests and the lint declaration in place, we can start working on the -implementation of the lint logic. - -Let's start by implementing the `EarlyLintPass` for our `FooFunctions`: - -```rust -impl EarlyLintPass for FooFunctions { - fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { - // TODO: Emit lint here - } -} -``` - -We implement the [`check_fn`][check_fn] method from the -[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various -information about the function that is currently being checked. More on that in -the next section. Let's worry about the details later and emit our lint for -*every* function definition first. - -Depending on how complex we want our lint message to be, we can choose from a -variety of lint emission functions. They can all be found in -[`clippy_utils/src/diagnostics.rs`][diagnostics]. - -`span_lint_and_help` seems most appropriate in this case. It allows us to -provide an extra help message and we can't really suggest a better name -automatically. This is how it looks: - -```rust -impl EarlyLintPass for FooFunctions { - fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { - span_lint_and_help( - cx, - FOO_FUNCTIONS, - span, - "function named `foo`", - None, - "consider using a more meaningful name" - ); - } -} -``` - -Running our UI test should now produce output that contains the lint message. - -According to [the rustc-dev-guide], the text should be matter of fact and avoid -capitalization and periods, unless multiple sentences are needed. -When code or an identifier must appear in a message or label, it should be -surrounded with single grave accents \`. - -[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn -[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs -[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html - -## Adding the lint logic - -Writing the logic for your lint will most likely be different from our example, -so this section is kept rather short. - -Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind] -that has the [`FnKind::Fn`] variant. It provides access to the name of the -function/method via an [`Ident`][ident]. - -With that we can expand our `check_fn` method to: - -```rust -impl EarlyLintPass for FooFunctions { - fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) { - if is_foo_fn(fn_kind) { - span_lint_and_help( - cx, - FOO_FUNCTIONS, - span, - "function named `foo`", - None, - "consider using a more meaningful name" - ); - } - } -} -``` - -We separate the lint conditional from the lint emissions because it makes the -code a bit easier to read. In some cases this separation would also allow to -write some unit tests (as opposed to only UI tests) for the separate function. - -In our example, `is_foo_fn` looks like: - -```rust -// use statements, impl EarlyLintPass, check_fn, .. - -fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { - match fn_kind { - FnKind::Fn(_, ident, ..) => { - // check if `fn` name is `foo` - ident.name.as_str() == "foo" - } - // ignore closures - FnKind::Closure(..) => false - } -} -``` - -Now we should also run the full test suite with `cargo test`. At this point -running `cargo test` should produce the expected output. Remember to run -`cargo dev bless` to update the `.stderr` file. - -`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint -implementation is not violating any Clippy lints itself. - -That should be it for the lint implementation. Running `cargo test` should now -pass. - -[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html -[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn -[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html - -## Specifying the lint's minimum supported Rust version (MSRV) - -Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests -using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to -ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are -required, just use the one with a lower MSRV. - -First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be -accessed later as `msrvs::STR_STRIP_PREFIX`, for example. - -```rust -msrv_aliases! { - .. - 1,45,0 { STR_STRIP_PREFIX } -} -``` - -In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a -constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`. - -```rust -pub struct ManualStrip { - msrv: Option, -} - -impl ManualStrip { - #[must_use] - pub fn new(msrv: Option) -> Self { - Self { msrv } - } -} -``` - -The project's MSRV can then be matched against the feature MSRV in the LintPass -using the `meets_msrv` utility function. - -``` rust -if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { - return; -} -``` - -The project's MSRV can also be specified as an inner attribute, which overrides -the value from `clippy.toml`. This can be accounted for using the -`extract_msrv_attr!(LintContext)` macro and passing -`LateContext`/`EarlyContext`. - -```rust -impl<'tcx> LateLintPass<'tcx> for ManualStrip { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - ... - } - extract_msrv_attr!(LateContext); -} -``` - -Once the `msrv` is added to the lint, a relevant test case should be added to -`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted -if the project's MSRV is lower. - -As a last step, the lint should be added to the lint documentation. This is done -in `clippy_lints/src/utils/conf.rs`: - -```rust -define_Conf! { - /// Lint: LIST, OF, LINTS, . The minimum rust version that the project supports - (msrv: Option = None), - ... -} -``` - -## Author lint - -If you have trouble implementing your lint, there is also the internal `author` -lint to generate Clippy code that detects the offending pattern. It does not -work for all of the Rust syntax, but can give a good starting point. - -The quickest way to use it, is the -[Rust playground: play.rust-lang.org][author_example]. -Put the code you want to lint into the editor and add the `#[clippy::author]` -attribute above the item. Then run Clippy via `Tools -> Clippy` and you should -see the generated code in the output below. - -[Here][author_example] is an example on the playground. - -If the command was executed successfully, you can copy the code over to where -you are implementing your lint. - -[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 - -## Print HIR lint - -To implement a lint, it's helpful to first understand the internal representation -that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the -[_High-Level Intermediate Representation (HIR)_] of the item, statement, or -expression that the attribute is attached to. To attach the attribute to expressions -you often need to enable `#![feature(stmt_expr_attributes)]`. - -[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. - -[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html -[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb - -## Documentation - -The final thing before submitting our PR is to add some documentation to our -lint declaration. - -Please document your lint with a doc comment akin to the following: - -```rust -declare_clippy_lint! { - /// ### What it does - /// Checks for ... (describe what the lint matches). - /// - /// ### Why is this bad? - /// Supply the reason for linting the code. - /// - /// ### Example - /// - /// ```rust,ignore - /// // A short example of code that triggers the lint - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// // A short example of improved code that doesn't trigger the lint - /// ``` - #[clippy::version = "1.29.0"] - pub FOO_FUNCTIONS, - pedantic, - "function named `foo`, which is not a descriptive name" -} -``` - -Once your lint is merged, this documentation will show up in the [lint -list][lint_list]. - -[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html - -## Running rustfmt - -[Rustfmt] is a tool for formatting Rust code according to style guidelines. -Your code has to be formatted by `rustfmt` before a PR can be merged. -Clippy uses nightly `rustfmt` in the CI. - -It can be installed via `rustup`: - -```bash -rustup component add rustfmt --toolchain=nightly -``` - -Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is -installed for the nightly toolchain. - -[Rustfmt]: https://github.com/rust-lang/rustfmt - -## Debugging - -If you want to debug parts of your lint implementation, you can use the [`dbg!`] -macro anywhere in your code. Running the tests should then include the debug -output in the `stdout` part. - -[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html - -## PR Checklist - -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` - -## Adding configuration to a lint - -Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace -directory. Adding a configuration to a lint can be useful for thresholds or to constrain some -behavior that can be seen as a false positive for some users. Adding a configuration is done -in the following steps: - -1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) - like this: - ```rust - /// Lint: LINT_NAME. - /// - /// - (configuration_ident: Type = DefaultValue), - ``` - The doc comment is automatically added to the documentation of the listed lints. The default - value will be formatted using the `Debug` implementation of the type. -2. Adding the configuration value to the lint impl struct: - 1. This first requires the definition of a lint impl struct. Lint impl structs are usually - generated with the `declare_lint_pass!` macro. This struct needs to be defined manually - to add some kind of metadata to it: - ```rust - // Generated struct definition - declare_lint_pass!(StructName => [ - LINT_NAME - ]); - - // New manual definition struct - #[derive(Copy, Clone)] - pub struct StructName {} - - impl_lint_pass!(StructName => [ - LINT_NAME - ]); - ``` - - 2. Next add the configuration value and a corresponding creation method like this: - ```rust - #[derive(Copy, Clone)] - pub struct StructName { - configuration_ident: Type, - } - - // ... - - impl StructName { - pub fn new(configuration_ident: Type) -> Self { - Self { - configuration_ident, - } - } - } - ``` -3. Passing the configuration value to the lint impl struct: - - First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). - The configuration value is now cloned or copied into a local value that is then passed to the - impl struct like this: - ```rust - // Default generated registration: - store.register_*_pass(|| box module::StructName); - - // New registration with configuration value - let configuration_ident = conf.configuration_ident.clone(); - store.register_*_pass(move || box module::StructName::new(configuration_ident)); - ``` - - Congratulations the work is almost done. The configuration value can now be accessed - in the linting code via `self.configuration_ident`. - -4. Adding tests: - 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui). - 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). - Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file - with the configuration value and a rust file that should be linted by Clippy. The test can - otherwise be written as usual. - -## Cheat Sheet - -Here are some pointers to things you are likely going to need for every lint: - -* [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) -* [Clippy diagnostics][diagnostics] -* [Let chains][let-chains] -* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] -* [`Span`][span] -* [`Applicability`][applicability] -* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations -* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts -* [The nightly rustc docs][nightly_docs] which has been linked to throughout - this guide - -For `EarlyLintPass` lints: - -* [`EarlyLintPass`][early_lint_pass] -* [`rustc_ast::ast`][ast] - -For `LateLintPass` lints: - -* [`LateLintPass`][late_lint_pass] -* [`Ty::TyKind`][ty] - -While most of Clippy's lint utils are documented, most of rustc's internals lack -documentation currently. This is unfortunate, but in most cases you can probably -get away with copying things from existing similar lints. If you are stuck, -don't hesitate to ask on [Zulip] or in the issue/PR. - -[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html -[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html -[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html -[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html -[let-chains]: https://github.com/rust-lang/rust/pull/94927 -[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion -[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html -[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html -[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html -[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ -[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ -[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html -[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html -[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy diff --git a/doc/backport.md b/doc/backport.md deleted file mode 100644 index 15f3d1f080604..0000000000000 --- a/doc/backport.md +++ /dev/null @@ -1,71 +0,0 @@ -# Backport Changes - -Sometimes it is necessary to backport changes to the beta release of Clippy. -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` 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. - - -## Backport the changes - -Backports are done on the beta branch of the Clippy repository. - -```bash -# 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(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 -``` - -After this, you can open a PR to the `beta` branch of the Clippy repository. - - -## Update Clippy in the Rust Repository - -This step must be done, **after** the PR of the previous step was merged. - -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 -$ git subtree pull -p src/tools/clippy https://github.com/rust-lang/rust-clippy beta -$ git push origin clippy_backport -``` - -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. diff --git a/doc/basics.md b/doc/basics.md deleted file mode 100644 index 57a90a924ec3c..0000000000000 --- a/doc/basics.md +++ /dev/null @@ -1,174 +0,0 @@ -# Basics for hacking on Clippy - -This document explains the basics for hacking on Clippy. Besides others, this -includes how to build and test Clippy. For a more in depth description on -the codebase take a look at [Adding Lints] or [Common Tools]. - -[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md -[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md - -- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy) - - [Get the Code](#get-the-code) - - [Building and Testing](#building-and-testing) - - [`cargo dev`](#cargo-dev) - - [lintcheck](#lintcheck) - - [PR](#pr) - - [Common Abbreviations](#common-abbreviations) - - [Install from source](#install-from-source) - -## Get the Code - -First, make sure you have checked out the latest version of Clippy. If this is -your first time working on Clippy, create a fork of the repository and clone it -afterwards with the following command: - -```bash -git clone git@github.com:/rust-clippy -``` - -If you've already cloned Clippy in the past, update it to the latest version: - -```bash -# If the upstream remote has not been added yet -git remote add upstream https://github.com/rust-lang/rust-clippy -# upstream has to be the remote of the rust-lang/rust-clippy repo -git fetch upstream -# make sure that you are on the master branch -git checkout master -# rebase your master branch on the upstream master -git rebase upstream/master -# push to the master branch of your fork -git push -``` - -## Building and Testing - -You can build and test Clippy like every other Rust project: - -```bash -cargo build # builds Clippy -cargo test # tests Clippy -``` - -Since Clippy's test suite is pretty big, there are some commands that only run a -subset of Clippy's tests: - -```bash -# only run UI tests -cargo uitest -# only run UI tests starting with `test_` -TESTNAME="test_" cargo uitest -# only run dogfood tests -cargo test --test dogfood -``` - -If the output of a [UI test] differs from the expected output, you can update the -reference file with: - -```bash -cargo dev bless -``` - -For example, this is necessary, if you fix a typo in an error message of a lint -or if you modify a test file to add a test case. - -_Note:_ This command may update more files than you intended. In that case only -commit the files you wanted to update. - -[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests - -## `cargo dev` - -Clippy has some dev tools to make working on Clippy more convenient. These tools -can be accessed through the `cargo dev` command. Available tools are listed -below. To get more information about these commands, just call them with -`--help`. - -```bash -# formats the whole Clippy codebase and all tests -cargo dev fmt -# register or update lint names/groups/... -cargo dev update_lints -# create a new lint and register it -cargo dev new_lint -# automatically formatting all code before each commit -cargo dev setup git-hook -# (experimental) Setup Clippy to work with IntelliJ-Rust -cargo dev setup intellij -``` -More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) - -## lintcheck -`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. -You can `git diff` the updated log against its previous version and -see what impact your lint made on a small set of crates. -If you add a new lint, please audit the resulting warnings and make sure -there are no false positives and that the suggestions are valid. - -Refer to the tools [README] for more details. - -[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md -## PR - -We follow a rustc no merge-commit policy. -See . - -## Common Abbreviations - -| Abbreviation | Meaning | -| ------------ | -------------------------------------- | -| UB | Undefined Behavior | -| FP | False Positive | -| FN | False Negative | -| ICE | Internal Compiler Error | -| AST | Abstract Syntax Tree | -| MIR | Mid-Level Intermediate Representation | -| HIR | High-Level Intermediate Representation | -| TCX | Type context | - -This is a concise list of abbreviations that can come up during Clippy development. An extensive -general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if -an abbreviation or meaning is unclear to you. - -## Install from source - -If you are hacking on Clippy and want to install it from source, do the following: - -First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`. -We will use this override to install Clippy into the right toolchain. - -> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`. - -From the Clippy project root, run the following command to build the Clippy binaries and copy them into the -toolchain directory. This will override the currently installed Clippy component. - -```terminal -cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" -``` - -Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy. - -```terminal -cd my-project -cargo +nightly-2021-07-01 clippy -``` - -...or `clippy-driver` - -```terminal -clippy-driver +nightly-2021-07-01 -``` - -If you need to restore the default Clippy installation, run the following (from the Clippy project root). - -```terminal -rustup component remove clippy -rustup component add clippy -``` - -> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup -> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and -> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running -> `rustup update`. - -[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/doc/changelog_update.md b/doc/changelog_update.md deleted file mode 100644 index 0cbad2c09249c..0000000000000 --- a/doc/changelog_update.md +++ /dev/null @@ -1,97 +0,0 @@ -# Changelog Update - -If you want to help with updating the [changelog][changelog], you're in the right place. - -## When to update - -Typos and other small fixes/additions are _always_ welcome. - -Special care needs to be taken when it comes to updating the changelog for a new -Rust release. For that purpose, the changelog is ideally updated during the week -before an upcoming stable release. You can find the release dates on the [Rust -Forge][forge]. - -Most of the time we only need to update the changelog for minor Rust releases. It's -been very rare that Clippy changes were included in a patch release. - -## Changelog update walkthrough - -### 1. Finding the relevant Clippy commits - -Each Rust release ships with its own version of Clippy. The Clippy subtree can -be found in the `tools` directory of the Rust repository. - -Depending on the current time and what exactly you want to update, the following -bullet points might be helpful: - -* When writing the release notes for the **upcoming stable release** you need to check - out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] -* When writing the release notes for the **upcoming beta release**, you need to check - out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] -* When writing the (forgotten) release notes for a **past stable release**, you - need to check out the Rust release tag of the stable release. - [Link][rust_stable_tools] - -Usually you want to write the changelog of the **upcoming stable release**. Make -sure though, that `beta` was already branched in the Rust repository. - -To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: -``` -git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" -``` - -### 2. Fetching the PRs between those commits - -Once you've got the correct commit range, run - - util/fetch_prs_between.sh commit1 commit2 > changes.txt - -and open that file in your editor of choice. - -When updating the changelog it's also a good idea to make sure that `commit1` is -already correct in the current changelog. - -### 3. Authoring the final changelog - -The above script should have dumped all the relevant PRs to the file you -specified. It should have filtered out most of the irrelevant PRs -already, but it's a good idea to do a manual cleanup pass where you look for -more irrelevant PRs. If you're not sure about some PRs, just leave them in for -the review and ask for feedback. - -With the PRs filtered, you can start to take each PR and move the -`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but -try to keep it somewhat coherent. - -The order should roughly be: - -1. New lints -2. Moves or deprecations of lints -3. Changes that expand what code existing lints cover -4. False positive fixes -5. Suggestion fixes/improvements -6. ICE fixes -7. Documentation improvements -8. Others - -As section headers, we use: - -``` -### New Lints -### Moves and Deprecations -### Enhancements -### False Positive Fixes -### Suggestion Fixes/Improvements -### ICE Fixes -### Documentation Improvements -### Others -``` - -Please also be sure to update the Beta/Unreleased sections at the top with the -relevant commit ranges. - -[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md -[forge]: https://forge.rust-lang.org/ -[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy -[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy -[rust_stable_tools]: https://github.com/rust-lang/rust/releases diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md deleted file mode 100644 index 1d1aee0da2cc7..0000000000000 --- a/doc/common_tools_writing_lints.md +++ /dev/null @@ -1,266 +0,0 @@ -# Common tools for writing lints - -You may need following tooltips to catch up with common operations. - -- [Common tools for writing lints](#common-tools-for-writing-lints) - - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression) - - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method) - - [Checking for a specific type](#checking-for-a-specific-type) - - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait) - - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method) - - [Dealing with macros](#dealing-with-macros-and-expansions) - -Useful Rustc dev guide links: -- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) -- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) -- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) -- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) - -## Retrieving the type of an expression - -Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: - -- which type does this expression correspond to (using its [`TyKind`][TyKind])? -- is it a sized type? -- is it a primitive type? -- does it implement a trait? - -This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct, -that gives you access to the underlying structure [`Ty`][Ty]. - -Example of use: -```rust -impl LateLintPass<'_> for MyStructLint { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // Get type of `expr` - let ty = cx.typeck_results().expr_ty(expr); - // Match its kind to enter its type - match ty.kind { - ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"), - _ => () - } - } -} -``` - -Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method -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 - 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 - -Starting with an `expr`, you can check whether it is calling a specific method `some_method`: - -```rust -impl<'tcx> LateLintPass<'tcx> for MyStructLint { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind - // Check the name of this method is `some_method` - && path.ident.name == sym!(some_method) - // Optionally, check the type of the self argument. - // - See "Checking for a specific type" - { - // ... - } - } -} -``` - -## Checking for a specific type - -There are three ways to check if an expression type is a specific type we want to check for. -All of these methods only check for the base type, generic arguments have to be checked separately. - -```rust -use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; -use clippy_utils::{paths, match_def_path}; -use rustc_span::symbol::sym; -use rustc_hir::LangItem; - -impl LateLintPass<'_> for MyStructLint { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // Getting the expression type - let ty = cx.typeck_results().expr_ty(expr); - - // 1. Using diagnostic items - // The last argument is the diagnostic item to check for - if is_type_diagnostic_item(cx, ty, sym::Option) { - // The type is an `Option` - } - - // 2. Using lang items - if is_type_lang_item(cx, ty, LangItem::RangeFull) { - // The type is a full range like `.drain(..)` - } - - // 3. Using the type path - // This method should be avoided if possible - if match_def_path(cx, def_id, &paths::RESULT) { - // The type is a `core::result::Result` - } - } -} -``` - -Prefer using diagnostic items and lang items where possible. - -## Checking if a type implements a specific trait - -There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. - -```rust -use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; -use rustc_span::symbol::sym; - -impl LateLintPass<'_> for MyStructLint { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using diagnostic items with the expression - // we use `is_trait_method` function from Clippy's utils - if is_trait_method(cx, expr, sym::Iterator) { - // method call in `expr` belongs to `Iterator` trait - } - - // 2. Using lang items with the expression type - let ty = cx.typeck_results().expr_ty(expr); - if cx.tcx.lang_items() - // we are looking for the `DefId` of `Drop` trait in lang items - .drop_trait() - // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils - .map_or(false, |id| implements_trait(cx, ty, id, &[])) { - // `expr` implements `Drop` trait - } - - // 3. Using the type path with the expression - // we use `match_trait_method` function from Clippy's utils - // (This method should be avoided if possible) - if match_trait_method(cx, expr, &paths::INTO) { - // `expr` implements `Into` trait - } - } -} -``` - -> Prefer using diagnostic and lang items, if the target trait has one. - -We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. -A list of defined paths for Clippy can be found in [paths.rs][paths] - -## Checking if a type defines a specific method - -To check if our type defines a method called `some_method`: - -```rust -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::return_ty; - -impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - // Check if item is a method/function - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind - // Check the method is named `some_method` - && impl_item.ident.name == sym!(some_method) - // We can also check it has a parameter `self` - && signature.decl.implicit_self.has_implicit_self() - // We can go further and even check if its return type is `String` - && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) - { - // ... - } - } -} -``` - -## Dealing with macros and expansions - -Keep in mind that macros are already expanded and desugaring is already applied -to the code representation that you are working with in Clippy. This unfortunately causes a lot of -false positives because macro expansions are "invisible" unless you actively check for them. -Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be -dynamic in ways that are difficult or impossible to see. -Use the following functions to deal with macros: - -- `span.from_expansion()`: detects if a span is from macro expansion or desugaring. - Checking this is a common first step in a lint. - - ```rust - if expr.span.from_expansion() { - // just forget it - return; - } - ``` - -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it. - It is sometimes useful to check if the context of two spans are equal. - - ```rust - // expands to `1 + 0`, but don't lint - 1 + mac!() - ``` - ```rust - if left.span.ctxt() != right.span.ctxt() { - // the coder most likely cannot modify this expression - return; - } - ``` - Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can - be assumed to have the same context. And so just using `span.from_expansion()` is often good enough. - - -- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate. - If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros - not defined in the current crate. It doesn't make sense to lint code that the coder can't change. - - You may want to use it for example to not start linting in macros from other crates - - ```rust - #[macro_use] - extern crate a_crate_with_macros; - - // `foo` is defined in `a_crate_with_macros` - foo!("bar"); - - // if we lint the `match` of `foo` call and test its span - assert_eq!(in_external_macro(cx.sess(), match_span), true); - ``` - -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it - -One thing `SpanContext` is useful for is to check if two spans are in the same context. For example, -in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some -expression with a different context from `a`. - - ```rust - macro_rules! m { - ($a:expr, $b:expr) => { - if $a.is_some() { - $b; - } - } - } - - let x: Option = Some(42); - m!(x, x.unwrap()); - - // These spans are not from the same context - // x.is_some() is from inside the macro - // x.unwrap() is from outside the macro - assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt()); - ``` - -[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html -[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html -[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty -[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html -[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty -[paths]: ../clippy_utils/src/paths.rs diff --git a/doc/release.md b/doc/release.md deleted file mode 100644 index c4f8f98938428..0000000000000 --- a/doc/release.md +++ /dev/null @@ -1,145 +0,0 @@ -# Release a new Clippy Version - -_NOTE: This document is probably only relevant to you, if you're a member of the -Clippy team._ - -Clippy is released together with stable Rust releases. The dates for these -releases can be found at the [Rust Forge]. This document explains the necessary -steps to create a Clippy release. - -1. [Remerge the `beta` branch](#remerge-the-beta-branch) -2. [Update the `beta` branch](#update-the-beta-branch) -3. [Find the Clippy commit](#find-the-clippy-commit) -4. [Tag the stable commit](#tag-the-stable-commit) -5. [Update `CHANGELOG.md`](#update-changelogmd) - -_NOTE: This document is for stable Rust releases, not for point releases. For -point releases, step 1. and 2. should be enough._ - -[Rust Forge]: https://forge.rust-lang.org/ - - -## Remerge the `beta` branch - -This step is only necessary, if since the last release something was backported -to the beta Rust release. The remerge is then necessary, to make sure that the -Clippy commit, that was used by the now stable Rust release, persists in the -tree of the Clippy repository. - -To find out if this step is necessary run - -```bash -# Assumes that the local master branch is up-to-date -$ git fetch upstream -$ git branch master --contains upstream/beta -``` - -If this command outputs `master`, this step is **not** necessary. - -```bash -# Assuming `HEAD` is the current `master` branch of rust-lang/rust-clippy -$ git checkout -b backport_remerge -$ git merge upstream/beta -$ git diff # This diff has to be empty, otherwise something with the remerge failed -$ git push origin backport_remerge # This can be pushed to your fork -``` - -After this, open a PR to the master branch. In this PR, the commit hash of the -`HEAD` of the `beta` branch must exists. In addition to that, no files should -be changed by this PR. - - -## Update the `beta` branch - -This step must be done **after** the PR of the previous step was merged. - -First, the Clippy commit of the `beta` branch of the Rust repository has to be -determined. - -```bash -# Assuming the current directory corresponds to the Rust repository -$ git checkout beta -$ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") -``` - -After finding the Clippy commit, the `beta` branch in the Clippy repository can -be updated. - -```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout beta -$ git reset --hard $BETA_SHA -$ git push upstream beta -``` - - -## Find the Clippy commit - -The first step is to tag the Clippy commit, that is included in the stable Rust -release. This commit can be found in the Rust repository. - -```bash -# Assuming the current directory corresponds to the Rust repository -$ git fetch upstream # `upstream` is the `rust-lang/rust` remote -$ git checkout 1.XX.0 # XX should be exchanged with the corresponding version -$ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") -``` - - -## Tag the stable commit - -After finding the Clippy commit, it can be tagged with the release number. - -```bash -# Assuming the current directory corresponds to the Clippy repository -$ git checkout $SHA -$ git tag rust-1.XX.0 # XX should be exchanged with the corresponding version -$ git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote -``` - -After this, the release should be available on the Clippy [release page]. - -[release page]: https://github.com/rust-lang/rust-clippy/releases - -## Update the `stable` branch - -At this step you should have already checked out the commit of the `rust-1.XX.0` -tag. Updating the stable branch from here is as easy as: - -```bash -# Assuming the current directory corresponds to the Clippy repository and the -# commit of the just created rust-1.XX.0 tag is checked out. -$ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote -``` - -_NOTE: Usually there are no stable backports for Clippy, so this update should -be possible without force pushing or anything like this. If there should have -happened a stable backport, make sure to re-merge those changes just as with the -`beta` branch._ - -## Update `CHANGELOG.md` - -For this see the document on [how to update the changelog]. - -If you don't have time to do a complete changelog update right away, just update -the following parts: - -- Remove the `(beta)` from the new stable version: - - ```markdown - ## Rust 1.XX (beta) -> ## Rust 1.XX - ``` - -- Update the release date line of the new stable version: - - ```markdown - Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD - ``` - -- Update the release date line of the previous stable version: - - ```markdown - Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD - ``` - -[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md diff --git a/doc/roadmap-2021.md b/doc/roadmap-2021.md deleted file mode 100644 index fe8b080f56f2b..0000000000000 --- a/doc/roadmap-2021.md +++ /dev/null @@ -1,235 +0,0 @@ -# Roadmap 2021 - -# Summary - -This Roadmap lays out the plans for Clippy in 2021: - -- Improving usability and reliability -- Improving experience of contributors and maintainers -- Develop and specify processes - -Members of the Clippy team will be assigned tasks from one or more of these -topics. The team member is then responsible to complete the assigned tasks. This -can either be done by implementing them or by providing mentorship to interested -contributors. - -# Motivation - -With the ongoing growth of the Rust language and with that of the whole -ecosystem, also Clippy gets more and more users and contributors. This is good -for the project, but also brings challenges along. Some of these challenges are: - -- More issues about reliability or usability are popping up -- Traffic is hard to handle for a small team -- Bigger projects don't get completed due to the lack of processes and/or time - of the team members - -Additionally, according to the [Rust Roadmap 2021], clear processes should be -defined by every team and unified across teams. This Roadmap is the first step -towards this. - -[Rust Roadmap 2021]: https://github.com/rust-lang/rfcs/pull/3037 - -# Explanation - -This section will explain the things that should be done in 2021. It is -important to note, that this document focuses on the "What?", not the "How?". -The later will be addressed in follow-up tracking issue, with an assigned team -member. - -The following is split up in two major sections. The first section covers the -user facing plans, the second section the internal plans. - -## User Facing - -Clippy should be as pleasant to use and configure as possible. This section -covers plans that should be implemented to improve the situation of Clippy in -this regard. - -### Usability - -In the following, plans to improve the usability are covered. - -#### No Output After `cargo check` - -Currently when `cargo clippy` is run after `cargo check`, it does not produce -any output. This is especially problematic since `rust-analyzer` is on the rise -and it uses `cargo check` for checking code. A fix is already implemented, but -it still has to be pushed over the finish line. This also includes the -stabilization of the `cargo clippy --fix` command or the support of multi-span -suggestions in `rustfix`. - -- [#4612](https://github.com/rust-lang/rust-clippy/issues/4612) - -#### `lints.toml` Configuration - -This is something that comes up every now and then: a reusable configuration -file, where lint levels can be defined. Discussions about this often lead to -nothing specific or to "we need an RFC for this". And this is exactly what needs -to be done. Get together with the cargo team and write an RFC and implement such -a configuration file somehow and somewhere. - -- [#3164](https://github.com/rust-lang/rust-clippy/issues/3164) -- [cargo#5034](https://github.com/rust-lang/cargo/issues/5034) -- [IRLO](https://internals.rust-lang.org/t/proposal-cargo-lint-configuration/9135/8) - -#### Lint Groups - -There are more and more issues about managing lints in Clippy popping up. Lints -are hard to implement with a guarantee of no/few false positives (FPs). One way -to address this might be to introduce more lint groups to give users the ability -to better manage lints, or improve the process of classifying lints, so that -disabling lints due to FPs becomes rare. It is important to note, that Clippy -lints are less conservative than `rustc` lints, which won't change in the -future. - -- [#5537](https://github.com/rust-lang/rust-clippy/issues/5537) -- [#6366](https://github.com/rust-lang/rust-clippy/issues/6366) - -### Reliability - -In the following, plans to improve the reliability are covered. - -#### False Positive Rate - -In the worst case, new lints are only available in nightly for 2 weeks, before -hitting beta and ultimately stable. This and the fact that fewer people use -nightly Rust nowadays makes it more probable that a lint with many FPs hits -stable. This leads to annoyed users, that will disable these new lints in the -best case and to more annoyed users, that will stop using Clippy in the worst. -A process should be developed and implemented to prevent this from happening. - -- [#6429](https://github.com/rust-lang/rust-clippy/issues/6429) - -## Internal - -(The end of) 2020 has shown, that Clippy has to think about the available -resources, especially regarding management and maintenance of the project. This -section address issues affecting team members and contributors. - -### Management - -In 2020 Clippy achieved over 1000 open issues with regularly between 25-35 open -PRs. This is simultaneously a win and a loss. More issues and PRs means more -people are interested in Clippy and in contributing to it. On the other hand, it -means for team members more work and for contributors longer wait times for -reviews. The following will describe plans how to improve the situation for both -team members and contributors. - -#### Clear Expectations for Team Members - -According to the [Rust Roadmap 2021], a document specifying what it means to be -a member of the team should be produced. This should not put more pressure on -the team members, but rather help them and interested folks to know what the -expectations are. With this it should also be easier to recruit new team members -and may encourage people to get in touch, if they're interested to join. - -#### Scaling up the Team - -More people means less work for each individual. Together with the document -about expectations for team members, a document defining the process of how to -join the team should be produced. This can also increase the stability of the -team, in case of current members dropping out (temporarily). There can also be -different roles in the team, like people triaging vs. people reviewing. - -#### Regular Meetings - -Other teams have regular meetings. Clippy is big enough that it might be worth -to also do them. Especially if more people join the team, this can be important -for sync-ups. Besides the asynchronous communication, that works well for -working on separate lints, a meeting adds a synchronous alternative at a known -time. This is especially helpful if there are bigger things that need to be -discussed (like the projects in this roadmap). For starters bi-weekly meetings -before Rust syncs might make sense. - -#### Triaging - -To get a handle on the influx of open issues, a process for triaging issues and -PRs should be developed. Officially, Clippy follows the Rust triage process, but -currently no one enforces it. This can be improved by sharing triage teams -across projects or by implementing dashboards / tools which simplify triaging. - -### Development - -Improving the developer and contributor experience is something the Clippy team -works on regularly. Though, some things might need special attention and -planing. These topics are listed in the following. - -#### Process for New and Existing Lints - -As already mentioned above, classifying new lints gets quite hard, because the -probability of a buggy lint getting into stable is quite high. A process should -be implemented on how to classify lints. In addition, a test system should be -developed to find out which lints are currently problematic in real world code -to fix or disable them. - -- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741056379) -- [#6429 (comment)](https://github.com/rust-lang/rust-clippy/issues/6429#issuecomment-741153345) - -#### Processes - -Related to the point before, a process for suggesting and discussing major -changes should be implemented. It's also not clearly defined when a lint should -be enabled or disabled by default. This can also be improved by the test system -mentioned above. - -#### Dev-Tools - -There's already `cargo dev` which makes Clippy development easier and more -pleasant. This can still be expanded, so that it covers more areas of the -development process. - -- [#5394](https://github.com/rust-lang/rust-clippy/issues/5394) - -#### Contributor Guide - -Similar to a Clippy Book, which describes how to use Clippy, a book about how to -contribute to Clippy might be helpful for new and existing contributors. There's -already the `doc` directory in the Clippy repo, this can be turned into a -`mdbook`. - -#### `rustc` integration - -Recently Clippy was integrated with `git subtree` into the `rust-lang/rust` -repository. This made syncing between the two repositories easier. A -`#[non_exhaustive]` list of things that still can be improved is: - -1. Use the same `rustfmt` version and configuration as `rustc`. -2. Make `cargo dev` work in the Rust repo, just as it works in the Clippy repo. - E.g. `cargo dev bless` or `cargo dev update_lints`. And even add more things - to it that might be useful for the Rust repo, e.g. `cargo dev deprecate`. -3. Easier sync process. The `subtree` situation is not ideal. - -## Prioritization - -The most pressing issues for users of Clippy are of course the user facing -issues. So there should be a priority on those issues, but without losing track -of the internal issues listed in this document. - -Getting the FP rate of warn/deny-by-default lints under control should have the -highest priority. Other user facing issues should also get a high priority, but -shouldn't be in the way of addressing internal issues. - -To better manage the upcoming projects, the basic internal processes, like -meetings, tracking issues and documentation, should be established as soon as -possible. They might even be necessary to properly manage the projects, -regarding the user facing issues. - -# Prior Art - -## Rust Roadmap - -Rust's roadmap process was established by [RFC 1728] in 2016. Since then every -year a roadmap was published, that defined the bigger plans for the coming -years. This years roadmap can be found [here][Rust Roadmap 2021]. - -[RFC 1728]: https://rust-lang.github.io/rfcs/1728-north-star.html - -# Drawbacks - -## Big Roadmap - -This roadmap is pretty big and not all items listed in this document might be -addressed during 2021. Because this is the first roadmap for Clippy, having open -tasks at the end of 2021 is fine, but they should be revisited in the 2022 -roadmap. From 206ec6e2f694f1e8f0c3a9d37d5273ef0286e821 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 17:11:00 +0100 Subject: [PATCH 38/78] Move syncing doc to book --- CONTRIBUTING.md | 125 -------------------------------- book/src/SUMMARY.md | 1 + book/src/infrastructure/sync.md | 125 ++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 125 deletions(-) create mode 100644 book/src/infrastructure/sync.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ab2bd59137fa..85eb82a646c22 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,11 +21,6 @@ All contributors are expected to follow the [Rust Code of Conduct]. - [IntelliJ Rust](#intellij-rust) - [Rust Analyzer](#rust-analyzer) - [How Clippy works](#how-clippy-works) - - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust) - - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos) - - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy) - - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust) - - [Defining remotes](#defining-remotes) - [Issue and PR triage](#issue-and-pr-triage) - [Bors and Homu](#bors-and-homu) - [Contributions](#contributions) @@ -205,126 +200,6 @@ That's why the `else_if_without_else` example uses the `register_early_pass` fun [early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html [late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html -## Syncing changes between Clippy and [`rust-lang/rust`] - -Clippy currently gets built with a pinned nightly version. - -In the `rust-lang/rust` repository, where rustc resides, there's a copy of Clippy -that compiler hackers modify from time to time to adapt to changes in the unstable -API of the compiler. - -We need to sync these changes back to this repository periodically, and the changes -made to this repository in the meantime also need to be synced to the `rust-lang/rust` repository. - -To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is done -in a bi-weekly basis if there's no urgent changes. This is done starting on the day of -the Rust stable release and then every other week. That way we guarantee that we keep -this repo up to date with the latest compiler API, and every feature in Clippy is available -for 2 weeks in nightly, before it can get to beta. For reference, the first sync -following this cadence was performed the 2020-08-27. - -This process is described in detail in the following sections. For general information -about `subtree`s in the Rust repository see [Rust's `CONTRIBUTING.md`][subtree]. - -### Patching git-subtree to work with big repos - -Currently, there's a bug in `git-subtree` that prevents it from working properly -with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's stale. -Before continuing with the following steps, we need to manually apply that fix to -our local copy of `git-subtree`. - -You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. -Put this file under `/usr/lib/git-core` (taking a backup of the previous file) -and make sure it has the proper permissions: - -```bash -sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree -sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree -``` - -_Note:_ The first time running `git subtree push` a cache has to be built. This -involves going through the complete Clippy history once. For this you have to -increase the stack limit though, which you can do with `ulimit -s 60000`. -Make sure to run the `ulimit` command from the same session you call git subtree. - -_Note:_ If you are a Debian user, `dash` is the shell used by default for scripts instead of `sh`. -This shell has a hardcoded recursion limit set to 1000. In order to make this process work, -you need to force the script to run `bash` instead. You can do this by editing the first -line of the `git-subtree` script and changing `sh` to `bash`. - -### Performing the sync from [`rust-lang/rust`] to Clippy - -Here is a TL;DR version of the sync process (all of the following commands have -to be run inside the `rust` directory): - -1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. -2. Checkout the commit from the latest available nightly. You can get it using `rustup check`. -3. Sync the changes to the rust-copy of Clippy to your Clippy fork: - ```bash - # Make sure to change `your-github-name` to your github name in the following command. Also be - # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand - # because changes cannot be fast forwarded - git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust - ``` - - _Note:_ This will directly push to the remote repository. You can also push - to your local copy by replacing the remote address with `/path/to/rust-clippy` - directory. - - _Note:_ Most of the time you have to create a merge commit in the - `rust-clippy` repo (this has to be done in the Clippy repo, not in the - rust-copy of Clippy): - ```bash - git fetch origin && git fetch upstream - git checkout sync-from-rust - git merge upstream/master - ``` -4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to - accelerate the process ping the `@rust-lang/clippy` team in your PR and/or - ~~annoy~~ ask them in the [Zulip] stream.) - -### Performing the sync from Clippy to [`rust-lang/rust`] - -All of the following commands have to be run inside the `rust` directory. - -1. Make sure Clippy itself is up-to-date by following the steps outlined in the previous -section if necessary. - -2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: - ```bash - git checkout -b sync-from-clippy - git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master - ``` -3. Open a PR to [`rust-lang/rust`] - -### Defining remotes - -You may want to define remotes, so you don't have to type out the remote -addresses on every sync. You can do this with the following commands (these -commands still have to be run inside the `rust` directory): - -```bash -# Set clippy-upstream remote for pulls -$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy -# Make sure to not push to the upstream repo -$ git remote set-url --push clippy-upstream DISABLED -# Set clippy-origin remote to your fork for pushes -$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy -# Set a local remote -$ git remote add clippy-local /path/to/rust-clippy -``` - -You can then sync with the remote names from above, e.g.: - -```bash -$ git subtree push -P src/tools/clippy clippy-local sync-from-rust -``` - -[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 -[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree -[`rust-lang/rust`]: https://github.com/rust-lang/rust - ## Issue and PR triage Clippy is following the [Rust triage procedure][triage] for issues and pull diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 5d10101d6377a..200b0cb29ff4e 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -22,6 +22,7 @@ - [Adding Lints](development/adding_lints.md) - [Common Tools](development/common_tools_writing_lints.md) - [Infrastructure](infrastructure/README.md) + - [Syncing changes between Clippy and rust-lang/rust](infrastructure/sync.md) - [Backporting Changes](infrastructure/backport.md) - [Updating the Changelog](infrastructure/changelog_update.md) - [Release a New Version](infrastructure/release.md) diff --git a/book/src/infrastructure/sync.md b/book/src/infrastructure/sync.md new file mode 100644 index 0000000000000..981e63cfddae6 --- /dev/null +++ b/book/src/infrastructure/sync.md @@ -0,0 +1,125 @@ +# Syncing changes between Clippy and [`rust-lang/rust`] + +Clippy currently gets built with a pinned nightly version. + +In the `rust-lang/rust` repository, where rustc resides, there's a copy of +Clippy that compiler hackers modify from time to time to adapt to changes in the +unstable API of the compiler. + +We need to sync these changes back to this repository periodically, and the +changes made to this repository in the meantime also need to be synced to the +`rust-lang/rust` repository. + +To avoid flooding the `rust-lang/rust` PR queue, this two-way sync process is +done in a bi-weekly basis if there's no urgent changes. This is done starting on +the day of the Rust stable release and then every other week. That way we +guarantee that we keep this repo up to date with the latest compiler API, and +every feature in Clippy is available for 2 weeks in nightly, before it can get +to beta. For reference, the first sync following this cadence was performed the +2020-08-27. + +This process is described in detail in the following sections. For general +information about `subtree`s in the Rust repository see [Rust's +`CONTRIBUTING.md`][subtree]. + +## Patching git-subtree to work with big repos + +Currently, there's a bug in `git-subtree` that prevents it from working properly +with the [`rust-lang/rust`] repo. There's an open PR to fix that, but it's +stale. Before continuing with the following steps, we need to manually apply +that fix to our local copy of `git-subtree`. + +You can get the patched version of `git-subtree` from [here][gitgitgadget-pr]. +Put this file under `/usr/lib/git-core` (making a backup of the previous file) +and make sure it has the proper permissions: + +```bash +sudo cp --backup /path/to/patched/git-subtree.sh /usr/lib/git-core/git-subtree +sudo chmod --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subtree +``` + +> _Note:_ The first time running `git subtree push` a cache has to be built. +> This involves going through the complete Clippy history once. For this you +> have to increase the stack limit though, which you can do with `ulimit -s +> 60000`. Make sure to run the `ulimit` command from the same session you call +> git subtree. + +> _Note:_ If you are a Debian user, `dash` is the shell used by default for +> scripts instead of `sh`. This shell has a hardcoded recursion limit set to +> 1000. In order to make this process work, you need to force the script to run +> `bash` instead. You can do this by editing the first line of the `git-subtree` +> script and changing `sh` to `bash`. + +## Performing the sync from [`rust-lang/rust`] to Clippy + +Here is a TL;DR version of the sync process (all of the following commands have +to be run inside the `rust` directory): + +1. Clone the [`rust-lang/rust`] repository or make sure it is up to date. +2. Checkout the commit from the latest available nightly. You can get it using + `rustup check`. +3. Sync the changes to the rust-copy of Clippy to your Clippy fork: + ```bash + # Make sure to change `your-github-name` to your github name in the following command. Also be + # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand + # because changes cannot be fast forwarded and you have to run this command again. + git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust + ``` + + > _Note:_ This will directly push to the remote repository. You can also + > push to your local copy by replacing the remote address with + > `/path/to/rust-clippy` directory. + + > _Note:_ Most of the time you have to create a merge commit in the + > `rust-clippy` repo (this has to be done in the Clippy repo, not in the + > rust-copy of Clippy): + ```bash + git fetch upstream # assuming upstream is the rust-lang/rust remote + git checkout sync-from-rust + git merge upstream/master + ``` +4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to + accelerate the process ping the `@rust-lang/clippy` team in your PR and/or + ask them in the [Zulip] stream.) + +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy + +## Performing the sync from Clippy to [`rust-lang/rust`] + +All of the following commands have to be run inside the `rust` directory. + +1. Make sure you have checked out the latest `master` of `rust-lang/rust`. +2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: + ```bash + git checkout -b sync-from-clippy + git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master + ``` +3. Open a PR to [`rust-lang/rust`] + +## Defining remotes + +You may want to define remotes, so you don't have to type out the remote +addresses on every sync. You can do this with the following commands (these +commands still have to be run inside the `rust` directory): + +```bash +# Set clippy-upstream remote for pulls +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy +# Make sure to not push to the upstream repo +$ git remote set-url --push clippy-upstream DISABLED +# Set clippy-origin remote to your fork for pushes +$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy +# Set a local remote +$ git remote add clippy-local /path/to/rust-clippy +``` + +You can then sync with the remote names from above, e.g.: + +```bash +$ git subtree push -P src/tools/clippy clippy-local sync-from-rust +``` + +[gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 +[subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree +[`rust-lang/rust`]: https://github.com/rust-lang/rust From d6b013ff9e5bbacfb6140f194b1700c2602f0bab Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 17:27:57 +0100 Subject: [PATCH 39/78] Reformat internal docs This reformats all the internal docs, so that the md files use at most 80 characters per line. This is the usual formatting of md files. We allow 120 chars per line in CI though. --- book/src/development/adding_lints.md | 323 ++++++++++-------- book/src/development/basics.md | 72 ++-- .../development/common_tools_writing_lints.md | 131 +++---- book/src/infrastructure/book.md | 36 +- book/src/infrastructure/changelog_update.md | 39 ++- book/src/infrastructure/release.md | 26 +- 6 files changed, 340 insertions(+), 287 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 3e0b1c5c4f782..7ffada8aef156 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -45,9 +45,9 @@ take a look at our [lint naming guidelines][lint_naming]. To get started on this lint you can run `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic` (category will default to nursery if not provided). This command will create two files: `tests/ui/foo_functions.rs` and -`clippy_lints/src/foo_functions.rs`, as well as -[registering the lint](#lint-registration). For cargo lints, two project -hierarchies (fail/pass) will be created by default under `tests/ui-cargo`. +`clippy_lints/src/foo_functions.rs`, as well as [registering the +lint](#lint-registration). For cargo lints, two project hierarchies (fail/pass) +will be created by default under `tests/ui-cargo`. Next, we'll open up these files and add our lint! @@ -58,8 +58,8 @@ Let's write some tests first that we can execute while we iterate on our lint. Clippy uses UI tests for testing. UI tests check that the output of Clippy is exactly as expected. Each test is just a plain Rust file that contains the code we want to check. The output of Clippy is compared against a `.stderr` file. -Note that you don't have to create this file yourself, we'll get to -generating the `.stderr` files further down. +Note that you don't have to create this file yourself, we'll get to generating +the `.stderr` files further down. We start by opening the test file created at `tests/ui/foo_functions.rs`. @@ -96,52 +96,54 @@ fn main() { } ``` -Now we can run the test with `TESTNAME=foo_functions cargo uitest`, -currently this test is meaningless though. +Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently +this test is meaningless though. -While we are working on implementing our lint, we can keep running the UI -test. That allows us to check if the output is turning into what we want. +While we are working on implementing our lint, we can keep running the UI test. +That allows us to check if the output is turning into what we want. -Once we are satisfied with the output, we need to run -`cargo dev bless` to update the `.stderr` file for our lint. -Please note that, we should run `TESTNAME=foo_functions cargo uitest` -every time before running `cargo dev bless`. -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 `cargo dev bless` for the +Once we are satisfied with the output, we need to run `cargo dev bless` to +update the `.stderr` file for our lint. Please note that, we should run +`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev +bless`. 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 `cargo dev bless` for the specific lint you are creating/editing. Note that if the generated files are empty, they should be removed. -Note that you can run multiple test files by specifying a comma separated list: -`TESTNAME=foo_functions,test2,test3`. +> _Note:_ you can run multiple test files by specifying a comma separated list: +> `TESTNAME=foo_functions,test2,test3`. ### Cargo lints -For cargo lints, the process of testing differs in that we are interested in -the `Cargo.toml` manifest file. We also need a minimal crate associated -with that manifest. +For cargo lints, the process of testing differs in that we are interested in the +`Cargo.toml` manifest file. We also need a minimal crate associated with that +manifest. -If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint` -we will find by default two new crates, each with its manifest file: +If our new lint is named e.g. `foo_categories`, after running `cargo dev +new_lint` we will find by default two new crates, each with its manifest file: -* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error. -* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint. +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the + new lint to raise an error. +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger + the lint. -If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it. +If you need more cases, you can copy one of those crates (under +`foo_categories`) and rename it. -The process of generating the `.stderr` file is the same, and prepending the `TESTNAME` -variable to `cargo uitest` works too. +The process of generating the `.stderr` file is the same, and prepending the +`TESTNAME` variable to `cargo uitest` works too. ## Rustfix tests -If the lint you are working on is making use of structured suggestions, the -test file should include a `// run-rustfix` comment at the top. This will +If the lint you are working on is making use of structured suggestions, the test +file should include a `// run-rustfix` comment at the top. This will additionally run [rustfix] for that test. Rustfix will apply the suggestions -from the lint to the code of the test file and compare that to the contents of -a `.fixed` file. +from the lint to the code of the test file and compare that to the contents of a +`.fixed` file. -Use `cargo dev bless` to automatically generate the -`.fixed` file after running the tests. +Use `cargo dev bless` to automatically generate the `.fixed` file after running +the tests. [rustfix]: https://github.com/rust-lang/rustfix @@ -149,7 +151,8 @@ Use `cargo dev bless` to automatically generate the Some features require the 2018 edition to work (e.g. `async_await`), but compile-test tests run on the 2015 edition by default. To change this behavior -add `// edition:2018` at the top of the test file (note that it's space-sensitive). +add `// edition:2018` at the top of the test file (note that it's +space-sensitive). ## Testing manually @@ -166,9 +169,9 @@ implementing our lint now. ## Lint declaration -Let's start by opening the new file created in the `clippy_lints` crate -at `clippy_lints/src/foo_functions.rs`. That's the crate where all the -lint code is. This file has already imported some initial things we will need: +Let's start by opening the new file created in the `clippy_lints` crate at +`clippy_lints/src/foo_functions.rs`. That's the crate where all the lint code +is. This file has already imported some initial things we will need: ```rust use rustc_lint::{EarlyLintPass, EarlyContext}; @@ -178,7 +181,8 @@ use rustc_ast::ast::*; The next step is to update the lint declaration. Lints are declared using the [`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update -the auto-generated lint declaration to have a real description, something like this: +the auto-generated lint declaration to have a real description, something like +this: ```rust declare_clippy_lint! { @@ -198,24 +202,25 @@ 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]. To render and open this documentation locally - in a browser, run `cargo dev serve`. -* The `#[clippy::version]` attribute will be rendered as part of the lint documentation. - The value should be set to the current Rust version that the lint is developed in, - it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version - is listed under *release*. (Use the version without the `-nightly`) suffix. -* `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 - read well when used with `allow`/`warn`/`deny`. -* `pedantic` sets the lint level to `Allow`. - The exact mapping can be found [here][category_level_mapping] + section. This is the default documentation style and will be displayed [like + this][example_lint_page]. To render and open this documentation locally in a + browser, run `cargo dev serve`. +* The `#[clippy::version]` attribute will be rendered as part of the lint + documentation. The value should be set to the current Rust version that the + lint is developed in, it can be retrieved by running `rustc -vV` in the + rust-clippy directory. The version is listed under *release*. (Use the version + without the `-nightly`) suffix. +* `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 read well when used with + `allow`/`warn`/`deny`. +* `pedantic` sets the lint level to `Allow`. The exact mapping can be found + [here][category_level_mapping] * The last part should be a text that explains what exactly is wrong with the code -The rest of this file contains an empty implementation for our lint pass, -which in this case is `EarlyLintPass` and should look like this: +The rest of this file contains an empty implementation for our lint pass, which +in this case is `EarlyLintPass` and should look like this: ```rust // clippy_lints/src/foo_functions.rs @@ -324,9 +329,9 @@ impl EarlyLintPass for FooFunctions { Running our UI test should now produce output that contains the lint message. According to [the rustc-dev-guide], the text should be matter of fact and avoid -capitalization and periods, unless multiple sentences are needed. -When code or an identifier must appear in a message or label, it should be -surrounded with single grave accents \`. +capitalization and periods, unless multiple sentences are needed. When code or +an identifier must appear in a message or label, it should be surrounded with +single grave accents \`. [check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn [diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs @@ -382,8 +387,8 @@ fn is_foo_fn(fn_kind: FnKind<'_>) -> bool { ``` Now we should also run the full test suite with `cargo test`. At this point -running `cargo test` should produce the expected output. Remember to run -`cargo dev bless` to update the `.stderr` file. +running `cargo test` should produce the expected output. Remember to run `cargo +dev bless` to update the `.stderr` file. `cargo test` (as opposed to `cargo uitest`) will also ensure that our lint implementation is not violating any Clippy lints itself. @@ -397,13 +402,16 @@ pass. ## Specifying the lint's minimum supported Rust version (MSRV) -Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests -using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to -ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are -required, just use the one with a lower MSRV. +Sometimes a lint makes suggestions that require a certain version of Rust. For +example, the `manual_strip` lint suggests using `str::strip_prefix` and +`str::strip_suffix` which is only available after Rust 1.45. In such cases, you +need to ensure that the MSRV configured for the project is >= the MSRV of the +required Rust feature. If multiple features are required, just use the one with +a lower MSRV. -First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be -accessed later as `msrvs::STR_STRIP_PREFIX`, for example. +First, add an MSRV alias for the required feature in +[`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be accessed later +as `msrvs::STR_STRIP_PREFIX`, for example. ```rust msrv_aliases! { @@ -412,8 +420,9 @@ msrv_aliases! { } ``` -In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a -constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`. +In order to access the project-configured MSRV, you need to have an `msrv` field +in the LintPass struct, and a constructor to initialize the field. The `msrv` +value is passed to the constructor in `clippy_lints/lib.rs`. ```rust pub struct ManualStrip { @@ -472,11 +481,10 @@ If you have trouble implementing your lint, there is also the internal `author` lint to generate Clippy code that detects the offending pattern. It does not work for all of the Rust syntax, but can give a good starting point. -The quickest way to use it, is the -[Rust playground: play.rust-lang.org][author_example]. -Put the code you want to lint into the editor and add the `#[clippy::author]` -attribute above the item. Then run Clippy via `Tools -> Clippy` and you should -see the generated code in the output below. +The quickest way to use it, is the [Rust playground: +play.rust-lang.org][author_example]. Put the code you want to lint into the +editor and add the `#[clippy::author]` attribute above the item. Then run Clippy +via `Tools -> Clippy` and you should see the generated code in the output below. [Here][author_example] is an example on the playground. @@ -487,13 +495,15 @@ you are implementing your lint. ## Print HIR lint -To implement a lint, it's helpful to first understand the internal representation -that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the -[_High-Level Intermediate Representation (HIR)_] of the item, statement, or -expression that the attribute is attached to. To attach the attribute to expressions -you often need to enable `#![feature(stmt_expr_attributes)]`. +To implement a lint, it's helpful to first understand the internal +representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that +prints the [_High-Level Intermediate Representation (HIR)_] of the item, +statement, or expression that the attribute is attached to. To attach the +attribute to expressions you often need to enable +`#![feature(stmt_expr_attributes)]`. -[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. +[Here][print_hir_example] you can find an example, just select _Tools_ and run +_Clippy_. [_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html [print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb @@ -518,7 +528,7 @@ declare_clippy_lint! { /// ```rust,ignore /// // A short example of code that triggers the lint /// ``` - /// + /// /// Use instead: /// ```rust,ignore /// // A short example of improved code that doesn't trigger the lint @@ -537,9 +547,9 @@ list][lint_list]. ## Running rustfmt -[Rustfmt] is a tool for formatting Rust code according to style guidelines. -Your code has to be formatted by `rustfmt` before a PR can be merged. -Clippy uses nightly `rustfmt` in the CI. +[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your +code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses +nightly `rustfmt` in the CI. It can be installed via `rustup`: @@ -575,94 +585,105 @@ Before submitting your PR make sure you followed all of the basic requirements: ## Adding configuration to a lint -Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace -directory. Adding a configuration to a lint can be useful for thresholds or to constrain some -behavior that can be seen as a false positive for some users. Adding a configuration is done -in the following steps: +Clippy supports the configuration of lints values using a `clippy.toml` file in +the workspace directory. Adding a configuration to a lint can be useful for +thresholds or to constrain some behavior that can be seen as a false positive +for some users. Adding a configuration is done in the following steps: -1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) - like this: - ```rust - /// Lint: LINT_NAME. - /// - /// - (configuration_ident: Type = DefaultValue), - ``` - The doc comment is automatically added to the documentation of the listed lints. The default - value will be formatted using the `Debug` implementation of the type. -2. Adding the configuration value to the lint impl struct: - 1. This first requires the definition of a lint impl struct. Lint impl structs are usually - generated with the `declare_lint_pass!` macro. This struct needs to be defined manually - to add some kind of metadata to it: - ```rust - // Generated struct definition - declare_lint_pass!(StructName => [ - LINT_NAME - ]); - - // New manual definition struct - #[derive(Copy, Clone)] - pub struct StructName {} - - impl_lint_pass!(StructName => [ - LINT_NAME - ]); - ``` - - 2. Next add the configuration value and a corresponding creation method like this: - ```rust - #[derive(Copy, Clone)] - pub struct StructName { - configuration_ident: Type, - } +1. Adding a new configuration entry to + [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) like this: - // ... + ```rust + /// Lint: LINT_NAME. + /// + /// + (configuration_ident: Type = DefaultValue), + ``` - impl StructName { - pub fn new(configuration_ident: Type) -> Self { - Self { - configuration_ident, - } - } - } - ``` + The doc comment is automatically added to the documentation of the listed + lints. The default value will be formatted using the `Debug` implementation + of the type. +2. Adding the configuration value to the lint impl struct: + 1. This first requires the definition of a lint impl struct. Lint impl + structs are usually generated with the `declare_lint_pass!` macro. This + struct needs to be defined manually to add some kind of metadata to it: + ```rust + // Generated struct definition + declare_lint_pass!(StructName => [ + LINT_NAME + ]); + + // New manual definition struct + #[derive(Copy, Clone)] + pub struct StructName {} + + impl_lint_pass!(StructName => [ + LINT_NAME + ]); + ``` + + 2. Next add the configuration value and a corresponding creation method like + this: + ```rust + #[derive(Copy, Clone)] + pub struct StructName { + configuration_ident: Type, + } + + // ... + + impl StructName { + pub fn new(configuration_ident: Type) -> Self { + Self { + configuration_ident, + } + } + } + ``` 3. Passing the configuration value to the lint impl struct: - First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs). - The configuration value is now cloned or copied into a local value that is then passed to the - impl struct like this: - ```rust - // Default generated registration: - store.register_*_pass(|| box module::StructName); + First find the struct construction in the [clippy_lints lib + file](/clippy_lints/src/lib.rs). The configuration value is now cloned or + copied into a local value that is then passed to the impl struct like this: + + ```rust + // Default generated registration: + store.register_*_pass(|| box module::StructName); - // New registration with configuration value - let configuration_ident = conf.configuration_ident.clone(); - store.register_*_pass(move || box module::StructName::new(configuration_ident)); - ``` + // New registration with configuration value + let configuration_ident = conf.configuration_ident.clone(); + store.register_*_pass(move || box module::StructName::new(configuration_ident)); + ``` - Congratulations the work is almost done. The configuration value can now be accessed - in the linting code via `self.configuration_ident`. + Congratulations the work is almost done. The configuration value can now be + accessed in the linting code via `self.configuration_ident`. 4. Adding tests: - 1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui). - 2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml). - Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file - with the configuration value and a rust file that should be linted by Clippy. The test can - otherwise be written as usual. + 1. The default configured value can be tested like any normal lint in + [`tests/ui`](/tests/ui). + 2. The configuration itself will be tested separately in + [`tests/ui-toml`](/tests/ui-toml). Simply add a new subfolder with a + fitting name. This folder contains a `clippy.toml` file with the + configuration value and a rust file that should be linted by Clippy. The + test can otherwise be written as usual. ## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need - is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) + is already in here ([`is_type_diagnostic_item`], [`implements_trait`], + [`snippet`], etc) * [Clippy diagnostics][diagnostics] * [Let chains][let-chains] -* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] +* [`from_expansion`][from_expansion] and + [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] -* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations -* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts +* [Common tools for writing lints](common_tools_writing_lints.md) helps with + common operations +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler + concepts * [The nightly rustc docs][nightly_docs] which has been linked to throughout this guide diff --git a/book/src/development/basics.md b/book/src/development/basics.md index 57a90a924ec3c..78c429ea01322 100644 --- a/book/src/development/basics.md +++ b/book/src/development/basics.md @@ -1,8 +1,8 @@ # Basics for hacking on Clippy This document explains the basics for hacking on Clippy. Besides others, this -includes how to build and test Clippy. For a more in depth description on -the codebase take a look at [Adding Lints] or [Common Tools]. +includes how to build and test Clippy. For a more in depth description on the +codebase take a look at [Adding Lints] or [Common Tools]. [Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md [Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md @@ -62,8 +62,8 @@ TESTNAME="test_" cargo uitest cargo test --test dogfood ``` -If the output of a [UI test] differs from the expected output, you can update the -reference file with: +If the output of a [UI test] differs from the expected output, you can update +the reference file with: ```bash cargo dev bless @@ -72,8 +72,8 @@ cargo dev bless For example, this is necessary, if you fix a typo in an error message of a lint or if you modify a test file to add a test case. -_Note:_ This command may update more files than you intended. In that case only -commit the files you wanted to update. +> _Note:_ This command may update more files than you intended. In that case +> only commit the files you wanted to update. [UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests @@ -96,22 +96,26 @@ cargo dev setup git-hook # (experimental) Setup Clippy to work with IntelliJ-Rust cargo dev setup intellij ``` -More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust) + +More about intellij command usage and reasons +[here](../CONTRIBUTING.md#intellij-rust) ## lintcheck -`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results. -You can `git diff` the updated log against its previous version and -see what impact your lint made on a small set of crates. -If you add a new lint, please audit the resulting warnings and make sure -there are no false positives and that the suggestions are valid. + +`cargo lintcheck` will build and run clippy on a fixed set of crates and +generate a log of the results. You can `git diff` the updated log against its +previous version and see what impact your lint made on a small set of crates. +If you add a new lint, please audit the resulting warnings and make sure there +are no false positives and that the suggestions are valid. Refer to the tools [README] for more details. [README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md + ## PR -We follow a rustc no merge-commit policy. -See . +We follow a rustc no merge-commit policy. See +. ## Common Abbreviations @@ -126,27 +130,34 @@ See . | HIR | High-Level Intermediate Representation | | TCX | Type context | -This is a concise list of abbreviations that can come up during Clippy development. An extensive -general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if -an abbreviation or meaning is unclear to you. +This is a concise list of abbreviations that can come up during Clippy +development. An extensive general list can be found in the [rustc-dev-guide +glossary][glossary]. Always feel free to ask if an abbreviation or meaning is +unclear to you. ## Install from source -If you are hacking on Clippy and want to install it from source, do the following: +If you are hacking on Clippy and want to install it from source, do the +following: -First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`. -We will use this override to install Clippy into the right toolchain. +First, take note of the toolchain +[override](https://rust-lang.github.io/rustup/overrides.html) in +`/rust-toolchain`. We will use this override to install Clippy into the right +toolchain. -> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`. +> Tip: You can view the active toolchain for the current directory with `rustup +> show active-toolchain`. -From the Clippy project root, run the following command to build the Clippy binaries and copy them into the -toolchain directory. This will override the currently installed Clippy component. +From the Clippy project root, run the following command to build the Clippy +binaries and copy them into the toolchain directory. This will override the +currently installed Clippy component. ```terminal cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin" ``` -Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy. +Now you may run `cargo clippy` in any project, using the toolchain where you +just installed Clippy. ```terminal cd my-project @@ -159,16 +170,19 @@ cargo +nightly-2021-07-01 clippy clippy-driver +nightly-2021-07-01 ``` -If you need to restore the default Clippy installation, run the following (from the Clippy project root). +If you need to restore the default Clippy installation, run the following (from +the Clippy project root). ```terminal rustup component remove clippy rustup component add clippy ``` -> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup -> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and -> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running -> `rustup update`. +> **DO NOT** install using `cargo install --path . --force` since this will +> overwrite rustup +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, +> `~/.cargo/bin/cargo-clippy` and `~/.cargo/bin/clippy-driver` should be hard or +> soft links to `~/.cargo/bin/rustup`. You can repair these by running `rustup +> update`. [glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md index 1d1aee0da2cc7..e1ed89262f677 100644 --- a/book/src/development/common_tools_writing_lints.md +++ b/book/src/development/common_tools_writing_lints.md @@ -18,15 +18,17 @@ Useful Rustc dev guide links: ## Retrieving the type of an expression -Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions: +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for +example to answer following questions: - which type does this expression correspond to (using its [`TyKind`][TyKind])? - is it a sized type? - is it a primitive type? - does it implement a trait? -This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct, -that gives you access to the underlying structure [`Ty`][Ty]. +This operation is performed using the [`expr_ty()`][expr_ty] method from the +[`TypeckResults`][TypeckResults] struct, that gives you access to the underlying +structure [`Ty`][Ty]. Example of use: ```rust @@ -43,8 +45,8 @@ impl LateLintPass<'_> for MyStructLint { } ``` -Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method -to retrieve a type from a pattern. +Similarly in [`TypeckResults`][TypeckResults] methods, you have the +[`pat_ty()`][pat_ty] method to retrieve a type from a pattern. Two noticeable items here: - `cx` is the lint context [`LateContext`][LateContext]. The two most useful @@ -52,12 +54,13 @@ Two noticeable items here: `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. + 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 -Starting with an `expr`, you can check whether it is calling a specific method `some_method`: +Starting with an `expr`, you can check whether it is calling a specific method +`some_method`: ```rust impl<'tcx> LateLintPass<'tcx> for MyStructLint { @@ -77,8 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint { ## Checking for a specific type -There are three ways to check if an expression type is a specific type we want to check for. -All of these methods only check for the base type, generic arguments have to be checked separately. +There are three ways to check if an expression type is a specific type we want +to check for. All of these methods only check for the base type, generic +arguments have to be checked separately. ```rust use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -115,7 +119,8 @@ Prefer using diagnostic items and lang items where possible. ## Checking if a type implements a specific trait -There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. +There are three ways to do this, depending on if the target trait has a +diagnostic item, lang item or neither. ```rust use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; @@ -151,8 +156,9 @@ impl LateLintPass<'_> for MyStructLint { > Prefer using diagnostic and lang items, if the target trait has one. -We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. -A list of defined paths for Clippy can be found in [paths.rs][paths] +We access lang items through the type context `tcx`. `tcx` is of type +[`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. A list of defined +paths for Clippy can be found in [paths.rs][paths] ## Checking if a type defines a specific method @@ -182,14 +188,15 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { ## Dealing with macros and expansions Keep in mind that macros are already expanded and desugaring is already applied -to the code representation that you are working with in Clippy. This unfortunately causes a lot of -false positives because macro expansions are "invisible" unless you actively check for them. -Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be -dynamic in ways that are difficult or impossible to see. -Use the following functions to deal with macros: +to the code representation that you are working with in Clippy. This +unfortunately causes a lot of false positives because macro expansions are +"invisible" unless you actively check for them. Generally speaking, code with +macro expansions should just be ignored by Clippy because that code can be +dynamic in ways that are difficult or impossible to see. Use the following +functions to deal with macros: -- `span.from_expansion()`: detects if a span is from macro expansion or desugaring. - Checking this is a common first step in a lint. +- `span.from_expansion()`: detects if a span is from macro expansion or + desugaring. Checking this is a common first step in a lint. ```rust if expr.span.from_expansion() { @@ -198,45 +205,51 @@ Use the following functions to deal with macros: } ``` -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it. - It is sometimes useful to check if the context of two spans are equal. - - ```rust - // expands to `1 + 0`, but don't lint - 1 + mac!() - ``` - ```rust - if left.span.ctxt() != right.span.ctxt() { - // the coder most likely cannot modify this expression - return; - } - ``` - Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can - be assumed to have the same context. And so just using `span.from_expansion()` is often good enough. - - -- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate. - If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros - not defined in the current crate. It doesn't make sense to lint code that the coder can't change. - - You may want to use it for example to not start linting in macros from other crates - - ```rust - #[macro_use] - extern crate a_crate_with_macros; - - // `foo` is defined in `a_crate_with_macros` - foo!("bar"); - - // if we lint the `match` of `foo` call and test its span - assert_eq!(in_external_macro(cx.sess(), match_span), true); - ``` - -- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it - -One thing `SpanContext` is useful for is to check if two spans are in the same context. For example, -in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some -expression with a different context from `a`. +- `span.ctxt()`: the span's context represents whether it is from expansion, and + if so, which macro call expanded it. It is sometimes useful to check if the + context of two spans are equal. + + ```rust + // expands to `1 + 0`, but don't lint + 1 + mac!() + ``` + ```rust + if left.span.ctxt() != right.span.ctxt() { + // the coder most likely cannot modify this expression + return; + } + ``` + > Note: Code that is not from expansion is in the "root" context. So any spans + > where `from_expansion` returns `true` can be assumed to have the same + > context. And so just using `span.from_expansion()` is often good enough. + + +- `in_external_macro(span)`: detect if the given span is from a macro defined in + a foreign crate. If you want the lint to work with macro-generated code, this + is the next line of defense to avoid macros not defined in the current crate. + It doesn't make sense to lint code that the coder can't change. + + You may want to use it for example to not start linting in macros from other + crates + + ```rust + #[macro_use] + extern crate a_crate_with_macros; + + // `foo` is defined in `a_crate_with_macros` + foo!("bar"); + + // if we lint the `match` of `foo` call and test its span + assert_eq!(in_external_macro(cx.sess(), match_span), true); + ``` + +- `span.ctxt()`: the span's context represents whether it is from expansion, and + if so, what expanded it + + One thing `SpanContext` is useful for is to check if two spans are in the same + context. For example, in `a == b`, `a` and `b` have the same context. In a + `macro_rules!` with `a == $b`, `$b` is expanded to some expression with a + different context from `a`. ```rust macro_rules! m { diff --git a/book/src/infrastructure/book.md b/book/src/infrastructure/book.md index 056d54b679243..b62314c6735a2 100644 --- a/book/src/infrastructure/book.md +++ b/book/src/infrastructure/book.md @@ -1,38 +1,42 @@ # The Clippy Book -This document explains how to make additions and changes to the Clippy book, the guide to Clippy that you're reading -right now. The Clippy book is formatted with [Markdown](https://www.markdownguide.org) and generated -by [mdbook](https://github.com/rust-lang/mdBook). +This document explains how to make additions and changes to the Clippy book, the +guide to Clippy that you're reading right now. The Clippy book is formatted with +[Markdown](https://www.markdownguide.org) and generated by +[mdbook](https://github.com/rust-lang/mdBook). - [Get mdbook](#get-mdbook) - [Make changes](#make-changes) ## Get mdbook -While not strictly necessary since the book source is simply Markdown text files, having mdbook locally will allow you -to build, test and serve the book locally to view changes before you commit them to the repository. You likely already -have -`cargo` installed, so the easiest option is to simply: +While not strictly necessary since the book source is simply Markdown text +files, having mdbook locally will allow you to build, test and serve the book +locally to view changes before you commit them to the repository. You likely +already have `cargo` installed, so the easiest option is to simply: ```shell cargo install mdbook ``` -See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) instructions for other options. +See the mdbook [installation](https://github.com/rust-lang/mdBook#installation) +instructions for other options. ## Make changes -The book's [src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) directory contains all of the -markdown files used to generate the book. If you want to see your changes in real time, you can use the mdbook `serve` -command to run a web server locally that will automatically update changes as they are made. From the top level of -your `rust-clippy` -directory: +The book's +[src](https://github.com/joshrotenberg/rust-clippy/tree/clippy_guide/book/src) +directory contains all of the markdown files used to generate the book. If you +want to see your changes in real time, you can use the mdbook `serve` command to +run a web server locally that will automatically update changes as they are +made. From the top level of your `rust-clippy` directory: ```shell mdbook serve book --open ``` -Then navigate to `http://localhost:3000` to see the generated book. While the server is running, changes you make will -automatically be updated. +Then navigate to `http://localhost:3000` to see the generated book. While the +server is running, changes you make will automatically be updated. -For more information, see the mdbook [guide](https://rust-lang.github.io/mdBook/). +For more information, see the mdbook +[guide](https://rust-lang.github.io/mdBook/). diff --git a/book/src/infrastructure/changelog_update.md b/book/src/infrastructure/changelog_update.md index 0cbad2c09249c..e560f4c6a3e51 100644 --- a/book/src/infrastructure/changelog_update.md +++ b/book/src/infrastructure/changelog_update.md @@ -1,6 +1,6 @@ # Changelog Update -If you want to help with updating the [changelog][changelog], you're in the right place. +If you want to help with updating the [changelog], you're in the right place. ## When to update @@ -11,8 +11,8 @@ Rust release. For that purpose, the changelog is ideally updated during the week before an upcoming stable release. You can find the release dates on the [Rust Forge][forge]. -Most of the time we only need to update the changelog for minor Rust releases. It's -been very rare that Clippy changes were included in a patch release. +Most of the time we only need to update the changelog for minor Rust releases. +It's been very rare that Clippy changes were included in a patch release. ## Changelog update walkthrough @@ -24,10 +24,12 @@ be found in the `tools` directory of the Rust repository. Depending on the current time and what exactly you want to update, the following bullet points might be helpful: -* When writing the release notes for the **upcoming stable release** you need to check - out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] -* When writing the release notes for the **upcoming beta release**, you need to check - out the Clippy commit of the current Rust `master`. [Link][rust_master_tools] +* When writing the release notes for the **upcoming stable release** you need to + check out the Clippy commit of the current Rust `beta` branch. + [Link][rust_beta_tools] +* When writing the release notes for the **upcoming beta release**, you need to + check out the Clippy commit of the current Rust `master`. + [Link][rust_master_tools] * When writing the (forgotten) release notes for a **past stable release**, you need to check out the Rust release tag of the stable release. [Link][rust_stable_tools] @@ -35,7 +37,8 @@ bullet points might be helpful: Usually you want to write the changelog of the **upcoming stable release**. Make sure though, that `beta` was already branched in the Rust repository. -To find the commit hash, issue the following command when in a `rust-lang/rust` checkout: +To find the commit hash, issue the following command when in a `rust-lang/rust` +checkout: ``` git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g" ``` @@ -44,7 +47,9 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into Once you've got the correct commit range, run - util/fetch_prs_between.sh commit1 commit2 > changes.txt +``` +util/fetch_prs_between.sh commit1 commit2 > changes.txt +``` and open that file in your editor of choice. @@ -54,14 +59,14 @@ already correct in the current changelog. ### 3. Authoring the final changelog The above script should have dumped all the relevant PRs to the file you -specified. It should have filtered out most of the irrelevant PRs -already, but it's a good idea to do a manual cleanup pass where you look for -more irrelevant PRs. If you're not sure about some PRs, just leave them in for -the review and ask for feedback. - -With the PRs filtered, you can start to take each PR and move the -`changelog: ` content to `CHANGELOG.md`. Adapt the wording as you see fit but -try to keep it somewhat coherent. +specified. It should have filtered out most of the irrelevant PRs already, but +it's a good idea to do a manual cleanup pass where you look for more irrelevant +PRs. If you're not sure about some PRs, just leave them in for the review and +ask for feedback. + +With the PRs filtered, you can start to take each PR and move the `changelog: ` +content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it +somewhat coherent. The order should roughly be: diff --git a/book/src/infrastructure/release.md b/book/src/infrastructure/release.md index c4f8f98938428..7c2fd29b5f9df 100644 --- a/book/src/infrastructure/release.md +++ b/book/src/infrastructure/release.md @@ -1,7 +1,7 @@ # Release a new Clippy Version -_NOTE: This document is probably only relevant to you, if you're a member of the -Clippy team._ +> _NOTE:_ This document is probably only relevant to you, if you're a member of +> the Clippy team. Clippy is released together with stable Rust releases. The dates for these releases can be found at the [Rust Forge]. This document explains the necessary @@ -13,12 +13,11 @@ steps to create a Clippy release. 4. [Tag the stable commit](#tag-the-stable-commit) 5. [Update `CHANGELOG.md`](#update-changelogmd) -_NOTE: This document is for stable Rust releases, not for point releases. For -point releases, step 1. and 2. should be enough._ +> _NOTE:_ This document is for stable Rust releases, not for point releases. For +> point releases, step 1. and 2. should be enough. [Rust Forge]: https://forge.rust-lang.org/ - ## Remerge the `beta` branch This step is only necessary, if since the last release something was backported @@ -45,9 +44,8 @@ $ git push origin backport_remerge # This can be pushed to your fork ``` After this, open a PR to the master branch. In this PR, the commit hash of the -`HEAD` of the `beta` branch must exists. In addition to that, no files should -be changed by this PR. - +`HEAD` of the `beta` branch must exists. In addition to that, no files should be +changed by this PR. ## Update the `beta` branch @@ -72,7 +70,6 @@ $ git reset --hard $BETA_SHA $ git push upstream beta ``` - ## Find the Clippy commit The first step is to tag the Clippy commit, that is included in the stable Rust @@ -85,7 +82,6 @@ $ git checkout 1.XX.0 # XX should be exchanged with the corresponding version $ SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` - ## Tag the stable commit After finding the Clippy commit, it can be tagged with the release number. @@ -112,10 +108,10 @@ tag. Updating the stable branch from here is as easy as: $ git push upstream rust-1.XX.0:stable # `upstream` is the `rust-lang/rust-clippy` remote ``` -_NOTE: Usually there are no stable backports for Clippy, so this update should -be possible without force pushing or anything like this. If there should have -happened a stable backport, make sure to re-merge those changes just as with the -`beta` branch._ +> _NOTE:_ Usually there are no stable backports for Clippy, so this update +> should be possible without force pushing or anything like this. If there +> should have happened a stable backport, make sure to re-merge those changes +> just as with the `beta` branch. ## Update `CHANGELOG.md` @@ -142,4 +138,4 @@ the following parts: Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD ``` -[how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md +[how to update the changelog]: changelog_update.md From b374e0f696b2fc7d9d2e252096a463dc780b45c6 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 17:29:18 +0100 Subject: [PATCH 40/78] Remove Edition 2018 tests section from adding lints doc The UI tests now use the latest edition by default. Testing on older editions should almost never be necessary, so I don't see a need to document this. --- book/src/development/adding_lints.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index 7ffada8aef156..3da07fcb96863 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -147,13 +147,6 @@ the tests. [rustfix]: https://github.com/rust-lang/rustfix -## Edition 2018 tests - -Some features require the 2018 edition to work (e.g. `async_await`), but -compile-test tests run on the 2015 edition by default. To change this behavior -add `// edition:2018` at the top of the test file (note that it's -space-sensitive). - ## Testing manually Manually testing against an example file can be useful if you have added some From 404432b7918dc54c190a9197c88e4c6a5bec3aaa Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 17:33:01 +0100 Subject: [PATCH 41/78] Book: Update GHA doc and remove GitLab placeholder --- book/src/SUMMARY.md | 1 - book/src/continuous_integration/github_actions.md | 6 ++---- book/src/continuous_integration/gitlab.md | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 book/src/continuous_integration/gitlab.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 200b0cb29ff4e..595b613886743 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -16,7 +16,6 @@ - [Continuous Integration](continuous_integration/README.md) - [Travis CI](continuous_integration/travis.md) - [GitHub Actions](continuous_integration/github_actions.md) - - [Gitlab](continuous_integration/gitlab.md) - [Development](development/README.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md index 070ef5b41c2fd..4154c60dbd211 100644 --- a/book/src/continuous_integration/github_actions.md +++ b/book/src/continuous_integration/github_actions.md @@ -1,9 +1,7 @@ # GitHub Actions -## actions-rs - -An easy way to get started with adding Clippy to GitHub Actions is -the [actions-rs](https://github.com/actions-rs) [clippy-check](https://github.com/actions-rs/clippy-check): +On the GitHub hosted runners, Clippy from the latest stable Rust version comes +pre-installed. So all you have to do is to run `cargo clippy`. ```yml on: push diff --git a/book/src/continuous_integration/gitlab.md b/book/src/continuous_integration/gitlab.md deleted file mode 100644 index bb5b755d5ddfe..0000000000000 --- a/book/src/continuous_integration/gitlab.md +++ /dev/null @@ -1,3 +0,0 @@ -# Gitlab - -***placeholder*** From d12a5c3a5294ee87715b64986d524090f003e76d Mon Sep 17 00:00:00 2001 From: flip1995 Date: Fri, 21 Jan 2022 18:24:43 +0100 Subject: [PATCH 42/78] Book: Split up and rewrite installation and usage --- book/src/SUMMARY.md | 3 +- book/src/continuous_integration/README.md | 4 - book/src/continuous_integration/travis.md | 5 - book/src/installation.md | 24 ++++ book/src/installation_and_usage.md | 88 ------------- book/src/usage.md | 151 ++++++++++++++++++++++ 6 files changed, 177 insertions(+), 98 deletions(-) create mode 100644 book/src/installation.md delete mode 100644 book/src/installation_and_usage.md create mode 100644 book/src/usage.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 595b613886743..db937f95d224e 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -2,7 +2,8 @@ [Introduction](README.md) -- [Installation and Usage](installation_and_usage.md) +- [Installation](installation.md) +- [Usage](usage.md) - [Configuration](configuration.md) - [Clippy's Lints](lints/README.md) - [Correctness]() diff --git a/book/src/continuous_integration/README.md b/book/src/continuous_integration/README.md index a28959de5c565..257fa0f8df41a 100644 --- a/book/src/continuous_integration/README.md +++ b/book/src/continuous_integration/README.md @@ -1,5 +1 @@ # Continuous Integration - -- [Travis CI](travis.md) -- [Github Actions](github_actions.md) -- [Gitlab](gitlab.md) diff --git a/book/src/continuous_integration/travis.md b/book/src/continuous_integration/travis.md index 36e467f9858c4..85b9ed53daeaf 100644 --- a/book/src/continuous_integration/travis.md +++ b/book/src/continuous_integration/travis.md @@ -18,8 +18,3 @@ script: - cargo test # etc. ``` - -Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code. -That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause -an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command -line. (You can swap `clippy::all` with the specific lint category you are targeting.) diff --git a/book/src/installation.md b/book/src/installation.md new file mode 100644 index 0000000000000..b2a28d0be622f --- /dev/null +++ b/book/src/installation.md @@ -0,0 +1,24 @@ +# Installation + +If you're using `rustup` to install and manage you're Rust toolchains, Clippy is +usually **already installed**. In that case you can skip this chapter and go to +the [Usage] chapter. + +> Note: If you used the `minimal` profile when installing a Rust toolchain, +> Clippy is not automatically installed. + +## Using Rustup + +If Clippy was not installed for a toolchain, it can be installed with + +``` +$ rustup component add clippy [--toolchain=] +``` + +## From Source + +Take a look at the [Basics] chapter in the Clippy developer guide to find step +by step instructions on how to build and install Clippy from source. + +[Basics]: development/basics.md#install-from-source +[Usage]: usage.md diff --git a/book/src/installation_and_usage.md b/book/src/installation_and_usage.md deleted file mode 100644 index 8982e0f10378c..0000000000000 --- a/book/src/installation_and_usage.md +++ /dev/null @@ -1,88 +0,0 @@ -# Installation and Usage - -Below are instructions on how to use Clippy as a subcommand, compiled from source -or in Travis CI. Note that Clippy is installed as a -[component](https://rust-lang.github.io/rustup/concepts/components.html?highlight=clippy#components) as part of the -[rustup](https://rust-lang.github.io/rustup/installation/index.html) installation. - -### As a cargo subcommand (`cargo clippy`) - -One way to use Clippy is by installing Clippy through rustup as a cargo -subcommand. - -#### Step 1: Install rustup - -You can install [rustup](https://rustup.rs/) on supported platforms. This will help -us install Clippy and its dependencies. - -If you already have rustup installed, update to ensure you have the latest -rustup and compiler: - -```terminal -rustup update -``` - -#### Step 2: Install Clippy - -Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command: - -```terminal -rustup component add clippy -``` -If it says that it can't find the `clippy` component, please run `rustup self update`. - -#### Step 3: Run Clippy - -Now you can run Clippy by invoking the following command: - -```terminal -cargo clippy -``` - -#### Automatically applying Clippy suggestions - -Clippy can automatically apply some lint suggestions. -Note that this is still experimental and only supported on the nightly channel: - -```terminal -cargo clippy --fix -``` - -#### Workspaces - -All the usual workspace options should work with Clippy. For example the following command -will run Clippy on the `example` crate: - -```terminal -cargo clippy -p example -``` - -As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies. -If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this: - -```terminal -cargo clippy -p example --no-deps -``` - -### As a rustc replacement (`clippy-driver`) - -Clippy can also be used in projects that do not use cargo. To do so, you will need to replace -your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: - -```terminal -rustc --edition 2018 -Cpanic=abort foo.rs -``` - -Then, to enable Clippy, you will need to call: - -```terminal -clippy-driver --edition 2018 -Cpanic=abort foo.rs -``` - -Note that `rustc` will still run, i.e. it will still emit the output files it normally does. - -### Continuous Integration - -Adding Clippy to your continuous integration pipeline is a great way to automate the linting process. See the -[Continuous Integration](continuous_integration) chapter for more information. - diff --git a/book/src/usage.md b/book/src/usage.md new file mode 100644 index 0000000000000..337680aa3139b --- /dev/null +++ b/book/src/usage.md @@ -0,0 +1,151 @@ +# Usage + +This chapter describes how to use Clippy to get the most out of it. Clippy can +be used as a `cargo` subcommand or, like `rustc`, directly with the +`clippy-driver` binary. + +> _Note:_ This chapter assumes that you have Clippy installed already. If you're +> not sure, take a look at the [Installation] chapter. + +## Cargo subcommand + +The easiest and most common way to run Clippy is through `cargo`. To do that, +just run + +```bash +cargo clippy +``` + +### Lint configuration + +The above command will run the default set of lints, which are included in the +lint group `clippy::all`. You might want to use even more lints or you might not +agree with every Clippy lint, and for that there are ways to configure lint +levels. + +> _Note:_ Clippy is meant to be used with a generous sprinkling of +> `#[allow(..)]`s through your code. So if you disagree with a lint, don't feel +> bad disabling them for parts of your code or the whole project. + +#### Command line + +You can configure lint levels on the command line by adding +`-A/W/D clippy::lint_name` like this: + +```bash +cargo clippy -- -Aclippy::style -Wclippy::double_neg -Dclippy::perf +``` + +For [CI] all warnings can be elevated to errors which will inturn fail +the build and cause Clippy to exit with a code other than `0`. + +``` +cargo clippy -- -Dwarnings +``` + +> _Note:_ Adding `-D warnings` will cause your build to fail if **any** warnings +> are found in your code. That includes warnings found by rustc (e.g. +> `dead_code`, etc.). + +For more information on configuring lint levels, see the [rustc documentation]. + +[rustc documentation]: https://doc.rust-lang.org/rustc/lints/levels.html#configuring-warning-levels + +#### Even more lints + +Clippy has lint groups which are allow-by-default. This means, that you will +have to enable the lints in those groups manually. + +For a full list of all lints with their description and examples, please refere +to [Clippy's lint list]. The two most important allow-by-default groups are +described below: + +[Clippy's lint list]: https://rust-lang.github.io/rust-clippy/master/index.html + +##### `clippy::pedantic` + +The first group is the `pedantic` group. This group contains really opinionated +lints, that may have some intentional false positives in order to prevent false +negatives. So while this group is ready to be used in production, you can expect +to sprinkle multiple `#[allow(..)]`s in your code. If you find any false +positives, you're still welcome to report them to us for future improvements. + +> FYI: Clippy uses the whole group to lint itself. + +##### `clippy::restriction` + +The second group is the `restriction` group. This group contains lints that +"restrict" the language in some way. For example the `clippy::unwrap` lint from +this group won't allow you to use `.unwrap()` in your code. You may want to look +through the lints in this group and enable the ones that fit your need. + +> _Note:_ You shouldn't enable the whole lint group, but cherry-pick lints from +> this group. Some lints in this group will even contradict other Clippy lints! + +#### Too many lints + +The most opinionated warn-by-default group of Clippy is the `clippy::style` +group. Some people prefer to disable this group completely and then cherry-pick +some lints they like from this group. The same is of course possible with every +other of Clippy's lint groups. + +> _Note:_ We try to keep the warn-by-default groups free from false positives +> (FP). If you find that a lint wrongly triggers, please report it in an issue +> (if there isn't an issue for that FP already) + +#### Source Code + +You can configure lint levels in source code the same way you can configure +`rustc` lints: + +```rust +#![allow(clippy::style)] + +#[warn(clippy::double_neg)] +fn main() { + let x = 1; + let y = --x; + // ^^ warning: double negation +} +``` + +### Automatically applying Clippy suggestions + +Clippy can automatically apply some lint suggestions, just like the compiler. + +```terminal +cargo clippy --fix +``` + +### Workspaces + +All the usual workspace options should work with Clippy. For example the +following command will run Clippy on the `example` crate in your workspace: + +```terminal +cargo clippy -p example +``` + +As with `cargo check`, this includes dependencies that are members of the +workspace, like path dependencies. If you want to run Clippy **only** on the +given crate, use the `--no-deps` option like this: + +```terminal +cargo clippy -p example -- --no-deps +``` + +## Using Clippy without `cargo`: `clippy-driver` + +Clippy can also be used in projects that do not use cargo. To do so, run +`clippy-driver` with the same arguments you use for `rustc`. For example: + +```terminal +clippy-driver --edition 2018 -Cpanic=abort foo.rs +``` + +> _Note:_ `clippy-driver` is designed for running Clippy and should not be used +> as a general replacement for `rustc`. `clippy-driver` may produce artifacts +> that are not optimized as expected, for example. + +[Installation]: installation.md +[CI]: continuous_integration From 0e42282ed5830ea014454130e04cc6e5d72955c9 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 15:45:44 +0200 Subject: [PATCH 43/78] Book: Write lint group descriptions This removes the empty separate files for the different groups and adds a single file giving short explanations for each group and what to expect from those groups. --- book/src/SUMMARY.md | 10 +--- book/src/lints.md | 105 ++++++++++++++++++++++++++++++++++ book/src/lints/README.md | 1 - book/src/lints/cargo.md | 0 book/src/lints/complexity.md | 0 book/src/lints/correctness.md | 0 book/src/lints/deprecated.md | 0 book/src/lints/nursery.md | 0 book/src/lints/pedantic.md | 0 book/src/lints/perf.md | 0 book/src/lints/restriction.md | 0 book/src/lints/style.md | 0 12 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 book/src/lints.md delete mode 100644 book/src/lints/README.md delete mode 100644 book/src/lints/cargo.md delete mode 100644 book/src/lints/complexity.md delete mode 100644 book/src/lints/correctness.md delete mode 100644 book/src/lints/deprecated.md delete mode 100644 book/src/lints/nursery.md delete mode 100644 book/src/lints/pedantic.md delete mode 100644 book/src/lints/perf.md delete mode 100644 book/src/lints/restriction.md delete mode 100644 book/src/lints/style.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index db937f95d224e..95bdde7281cf0 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -5,15 +5,7 @@ - [Installation](installation.md) - [Usage](usage.md) - [Configuration](configuration.md) -- [Clippy's Lints](lints/README.md) - - [Correctness]() - - [Suspicious]() - - [Style]() - - [Complexity]() - - [Perf]() - - [Pendantic]() - - [Nursery]() - - [Cargo]() +- [Clippy's Lints](lints.md) - [Continuous Integration](continuous_integration/README.md) - [Travis CI](continuous_integration/travis.md) - [GitHub Actions](continuous_integration/github_actions.md) diff --git a/book/src/lints.md b/book/src/lints.md new file mode 100644 index 0000000000000..35e30960b56c6 --- /dev/null +++ b/book/src/lints.md @@ -0,0 +1,105 @@ +# Clippy's Lints + +Clippy offers a bunch of additional lints, to help its users write more correct +and idiomatic Rust code. A full list of all lints, that can be filtered by +category, lint level or keywords, can be found in the [Clippy lint +documentation]. + +This chapter will give an overview of the different lint categories, which kind +of lints they offer and recommended actions when you should see a lint out of +that category. For examples, see the [Clippy lint documentation] and filter by +category. + +The different lint groups were defined in the [Clippy 1.0 RFC]. + +## Correctness + +The `clippy::correctness` group is the only lint group in Clippy which lints are +deny-by-default and abort the compilation when triggered. This is for good +reason: If you see a `correctness` lint, it means that your code is outright +wrong or useless and you should try to fix it. + +Lints in this category are carefully picked and should be free of false +positives. So just `#[allow]`ing those lints is not recommended. + +## Suspicious + +The `clippy::suspicious` group is similar to the correctness lints in that it +contains lints that trigger on code that is really _sus_ and should be fixed. As +opposed to correctness lints, it might be possible that the linted code is +intentionally written like it is. + +It is still recommended to fix code that is linted by lints out of this group +instead of `#[allow]`ing the lint. In case you intentionally have written code +that offends the lint you should specifically and locally `#[allow]` the lint +and add give a reason why the code is correct as written. + +## Complexity + +The `clippy::complexity` group offers lints that give you suggestions on how to +simplify your code. It mostly focuses on code that can be written in a shorter +and more readable way, while preserving the semantics. + +If you should see a complexity lint, it usually means that you can remove or +replace some code and it is recommended to do so. However, if you need the more +complex code for some expressiveness reason, it is recommended to allow +complexity lints on a case-by-case basis. + +## Perf + +The `clippy::perf` group gives you suggestions on how you can increase the +performance of your code. Those lints are mostly about code that the compiler +can't trivially optimize, but has to be written in a slightly different way to +make the optimizer's job easier. + +Perf lints are usually easy to apply and it is recommended to do so. + +## Style + +The `clippy::style` group is mostly about writing idiomatic code. Because style +is subjective, this lint group is the most opinionated warn-by-default group in +Clippy. + +If you see a style lint, applying the suggestion usually makes your code more +readable and idiomatic. But because we know that this is opinionated, feel free +to sprinkle `#[allow]`s for style lints in your code or `#![allow]` a style lint +on your whole crate if you disagree with the suggested style completely. + +## Pedantic + +The `clippy::pedantic` group makes Clippy even more _pedantic_. You can enable +the whole group with `#![warn(clippy::pedantic)]` in the `lib.rs`/`main.rs` of +your crate. This lint group is for Clippy power users that want an in depth +check of their code. + +> _Note:_ Instead of enabling the whole group (like Clippy itself does), you may +> want to cherry-pick lints out of the pedantic group. + +If you enable this group, expect to also use `#[allow]` attributes generously +throughout your code. Lints in this group are designed to be pedantic and false +positives sometimes are intentional in order to prevent false negatives. + +## Restriction + +The `clippy::restriction` group contains lints that will _restrict_ you from +using certain parts of the Rust language. It is **not** recommended to enable +the whole group, but rather cherry-pick lints that are useful for your code base +and your use case. + +> _Note:_ Clippy will produce a warning if it finds a +> `#![warn(clippy::restriction)]` attribute in your code! + +Lints from this group will restrict you in some way. If you enable a restriction +lint for your crate it is recommended to also fix code that this lint triggers +on. However, those lints are really strict by design and you might want to +`#[allow]` them in some special cases, with a comment justifying that. + +## Cargo + +The `clippy::cargo` group gives you suggestions on how to improve your +`Cargo.toml` file. This might be especially interesting if you want to publish +your crate and are not sure if you have all useful information in your +`Cargo.toml`. + +[Clippy lint documentation]: https://rust-lang.github.io/rust-clippy/ +[Clippy 1.0 RFC]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories diff --git a/book/src/lints/README.md b/book/src/lints/README.md deleted file mode 100644 index a2777e8f4d583..0000000000000 --- a/book/src/lints/README.md +++ /dev/null @@ -1 +0,0 @@ -# Clippy's Lints diff --git a/book/src/lints/cargo.md b/book/src/lints/cargo.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/complexity.md b/book/src/lints/complexity.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/correctness.md b/book/src/lints/correctness.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/deprecated.md b/book/src/lints/deprecated.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/nursery.md b/book/src/lints/nursery.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/pedantic.md b/book/src/lints/pedantic.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/perf.md b/book/src/lints/perf.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/restriction.md b/book/src/lints/restriction.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/book/src/lints/style.md b/book/src/lints/style.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 97bceb0e31c96ca9287db5de42c33795f1693b07 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 16:02:33 +0200 Subject: [PATCH 44/78] Book: Restructure Dev chapter Group everything that has something to do with Clippy development under the "Development" chapter, so that Clippy users can't get confused. --- book/src/SUMMARY.md | 16 ++++++++-------- .../{ => development}/infrastructure/README.md | 0 .../{ => development}/infrastructure/backport.md | 0 .../src/{ => development}/infrastructure/book.md | 0 .../infrastructure/changelog_update.md | 0 .../{ => development}/infrastructure/release.md | 0 .../src/{ => development}/infrastructure/sync.md | 0 book/src/development/proposals/README.md | 1 + .../proposals/roadmap-2021.md} | 0 book/src/roadmap/README.md | 0 10 files changed, 9 insertions(+), 8 deletions(-) rename book/src/{ => development}/infrastructure/README.md (100%) rename book/src/{ => development}/infrastructure/backport.md (100%) rename book/src/{ => development}/infrastructure/book.md (100%) rename book/src/{ => development}/infrastructure/changelog_update.md (100%) rename book/src/{ => development}/infrastructure/release.md (100%) rename book/src/{ => development}/infrastructure/sync.md (100%) create mode 100644 book/src/development/proposals/README.md rename book/src/{roadmap/2021.md => development/proposals/roadmap-2021.md} (100%) delete mode 100644 book/src/roadmap/README.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 95bdde7281cf0..16e9bea1bdc15 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,11 +13,11 @@ - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Common Tools](development/common_tools_writing_lints.md) -- [Infrastructure](infrastructure/README.md) - - [Syncing changes between Clippy and rust-lang/rust](infrastructure/sync.md) - - [Backporting Changes](infrastructure/backport.md) - - [Updating the Changelog](infrastructure/changelog_update.md) - - [Release a New Version](infrastructure/release.md) - - [The Clippy Book](infrastructure/book.md) -- [Roadmap](roadmap/README.md) - - [2021](roadmap/2021.md) + - [Infrastructure](development/infrastructure/README.md) + - [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md) + - [Backporting Changes](development/infrastructure/backport.md) + - [Updating the Changelog](development/infrastructure/changelog_update.md) + - [Release a New Version](development/infrastructure/release.md) + - [The Clippy Book](development/infrastructure/book.md) + - [Proposals](development/proposals/README.md) + - [Roadmap 2021](development/proposals/roadmap-2021.md) diff --git a/book/src/infrastructure/README.md b/book/src/development/infrastructure/README.md similarity index 100% rename from book/src/infrastructure/README.md rename to book/src/development/infrastructure/README.md diff --git a/book/src/infrastructure/backport.md b/book/src/development/infrastructure/backport.md similarity index 100% rename from book/src/infrastructure/backport.md rename to book/src/development/infrastructure/backport.md diff --git a/book/src/infrastructure/book.md b/book/src/development/infrastructure/book.md similarity index 100% rename from book/src/infrastructure/book.md rename to book/src/development/infrastructure/book.md diff --git a/book/src/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md similarity index 100% rename from book/src/infrastructure/changelog_update.md rename to book/src/development/infrastructure/changelog_update.md diff --git a/book/src/infrastructure/release.md b/book/src/development/infrastructure/release.md similarity index 100% rename from book/src/infrastructure/release.md rename to book/src/development/infrastructure/release.md diff --git a/book/src/infrastructure/sync.md b/book/src/development/infrastructure/sync.md similarity index 100% rename from book/src/infrastructure/sync.md rename to book/src/development/infrastructure/sync.md diff --git a/book/src/development/proposals/README.md b/book/src/development/proposals/README.md new file mode 100644 index 0000000000000..2195a7f483259 --- /dev/null +++ b/book/src/development/proposals/README.md @@ -0,0 +1 @@ +# Proposals diff --git a/book/src/roadmap/2021.md b/book/src/development/proposals/roadmap-2021.md similarity index 100% rename from book/src/roadmap/2021.md rename to book/src/development/proposals/roadmap-2021.md diff --git a/book/src/roadmap/README.md b/book/src/roadmap/README.md deleted file mode 100644 index e69de29bb2d1d..0000000000000 From 6b21d386f66cfd574bf8e65769fd41231e592ad8 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 16:08:39 +0200 Subject: [PATCH 45/78] Book: Add Proposals description --- book/src/development/proposals/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/book/src/development/proposals/README.md b/book/src/development/proposals/README.md index 2195a7f483259..78fe34ebf8faf 100644 --- a/book/src/development/proposals/README.md +++ b/book/src/development/proposals/README.md @@ -1 +1,11 @@ # Proposals + +This chapter is about accepted proposals for changes that should be worked on in +or around Clippy in the long run. + +Besides adding more and more lints and improve the lints that Clippy already +has, Clippy is also interested in making the experience of its users, developers +and maintainers better over time. Projects that address bigger picture things +like this usually take more time and it is useful to have a proposal for those +first. This is the place where such proposals are collected, so that we can +refer to them when working on them. From cbe4de2f6c7a3acad88a0eeb5bce1a97419da0ff Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 16:19:00 +0200 Subject: [PATCH 46/78] Book: Add continuous integration description --- book/src/SUMMARY.md | 2 +- book/src/continuous_integration/README.md | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 16e9bea1bdc15..0b945faf9b78e 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -7,8 +7,8 @@ - [Configuration](configuration.md) - [Clippy's Lints](lints.md) - [Continuous Integration](continuous_integration/README.md) - - [Travis CI](continuous_integration/travis.md) - [GitHub Actions](continuous_integration/github_actions.md) + - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) diff --git a/book/src/continuous_integration/README.md b/book/src/continuous_integration/README.md index 257fa0f8df41a..9f1374193de39 100644 --- a/book/src/continuous_integration/README.md +++ b/book/src/continuous_integration/README.md @@ -1 +1,17 @@ # Continuous Integration + +It is recommended to run Clippy on CI, preferably with `-Dwarnings`, so that +Clippy lints prevent CI from passing. + +We recommend to use Clippy from the same toolchain, that you use for compiling +your crate for maximum compatibility. E.g. if your crate is compiled with the +`stable` toolchain, you should also use `stable` Clippy. + +> _Note:_ New Clippy lints are first added to the `nightly` toolchain. If you +> want to help with improving Clippy and have CI resources left, please consider +> adding a `nightly` Clippy check to your CI and report problems like false +> positives back to us. With that we can fix bugs early, before they can get to +> stable. + +This chapter will give an overview on how to use Clippy on different popular CI +providers. From b37d24f7ff281d8887aa253caf4a22b4512efd99 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 16:43:56 +0200 Subject: [PATCH 47/78] Move parts of CONTRIBUTING.md to the book --- CONTRIBUTING.md | 36 +++++++++++------------------ book/src/development/README.md | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85eb82a646c22..6f132a274a1cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,9 +13,9 @@ anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip] All contributors are expected to follow the [Rust Code of Conduct]. - [Contributing to Clippy](#contributing-to-clippy) - - [Getting started](#getting-started) - - [High level approach](#high-level-approach) - - [Finding something to fix/improve](#finding-something-to-fiximprove) + - [The Clippy book](#the-clippy-book) + - [High level approach](#high-level-approach) + - [Finding something to fix/improve](#finding-something-to-fiximprove) - [Writing code](#writing-code) - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work) - [IntelliJ Rust](#intellij-rust) @@ -28,20 +28,24 @@ All contributors are expected to follow the [Rust Code of Conduct]. [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy [Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct -## Getting started +## The Clippy book -**Note: If this is your first time contributing to Clippy, you should -first read the [Basics docs](doc/basics.md).** +If you're new to Clippy and don't know where to start the [Clippy book] includes +a developer guide and is a good place to start your journey. -### High level approach + +[Clippy book]: book/src + +## High level approach 1. Find something to fix/improve 2. Change code (likely some file in `clippy_lints/src/`) -3. Follow the instructions in the [Basics docs](doc/basics.md) to get set up +3. Follow the instructions in the [Basics docs](book/src/development/basics.md) + to get set up 4. Run `cargo test` in the root directory and wiggle code until it passes 5. Open a PR (also can be done after 2. if you run into problems) -### Finding something to fix/improve +## Finding something to fix/improve All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date. @@ -86,20 +90,6 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us [let chains]: https://github.com/rust-lang/rust/pull/94927 [nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159 -## Writing code - -Have a look at the [docs for writing lints][adding_lints] for more details. - -If you want to add a new lint or change existing ones apart from bugfixing, it's -also a good idea to give the [stability guarantees][rfc_stability] and -[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a -quick read. - -[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md -[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md -[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees -[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories - ## Getting code-completion for rustc internals to work ### IntelliJ Rust diff --git a/book/src/development/README.md b/book/src/development/README.md index 09d6aad2c538e..5cf7201cffad7 100644 --- a/book/src/development/README.md +++ b/book/src/development/README.md @@ -1 +1,43 @@ # Clippy Development + +Hello fellow Rustacean! If you made it here, you're probably interested in +making Clippy better by contributing to it. In that case, welcome to the +project! + +> _Note:_ If you're just interested in using Clippy, there's nothing to see from +> this point onward and you should return to one of the earlier chapters. + +## Getting started + +If this is your first time contributing to Clippy, you should first read the +[Basics docs](basics.md). This will explain the basics on how to get the source +code and how to compile and test the code. + +## Writing code + +If you have done the basic setup, it's time to start hacking. + +The [Adding lints](adding_lints.md) chapter is a walk through on how to add a +new lint to Clippy. This is also interesting if you just want to fix a lint, +because it also covers how to test lints and gives an overview of the bigger +picture. + +If you want to add a new lint or change existing ones apart from bugfixing, it's +also a good idea to give the [stability guarantees][rfc_stability] and +[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a +quick read. The lint categories are also described [earlier in this +book](../lints.md). + +> _Note:_ Some higher level things about contributing to Clippy are still +> covered in the [`CONTRIBUTING.md`] document. Some of those will be moved to +> the book over time, like: +> - Finding something to fix +> - IDE setup +> - High level overview on how Clippy works +> - Triage procedure +> - Bors and Homu + +[clippy_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md +[rfc_stability]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#stability-guarantees +[rfc_lint_cats]: https://github.com/rust-lang/rfcs/blob/master/text/2476-clippy-uno.md#lint-audit-and-categories +[`CONTRIBUTING.md`]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md From c9cbead65672bd1606e630cddbbcf158d164d3e7 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 20:40:09 +0200 Subject: [PATCH 48/78] Book: Add infrastructure description --- book/src/development/infrastructure/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/book/src/development/infrastructure/README.md b/book/src/development/infrastructure/README.md index 2b4e5f6a6efe0..3b2a253999629 100644 --- a/book/src/development/infrastructure/README.md +++ b/book/src/development/infrastructure/README.md @@ -1 +1,19 @@ # Infrastructure + +In order to deploy Clippy over `rustup`, some infrastructure is necessary. This +chapter describes the different parts of the Clippy infrastructure that need to +be maintained to make this possible. + +The most important part is the sync between the `rust-lang/rust` repository and +the Clippy repository that takes place every two weeks. This process is +described in the [Syncing changes between Clippy and `rust-lang/rust`](sync.md) +section. + +A new Clippy release is done together with every Rust release, so every six +weeks. The release process is described in the [Release a new Clippy +Version](release.md) section. During a release cycle a changelog entry for the +next release has to be written. The format of that and how to do that is +documented in the [Changelog Update](changelog_update.md) section. + +> _Note:_ The Clippy CI should also be described in this chapter, but for now is +> left as a TODO. From b55192880040580bbdab9a36afa1d47bd486d6ed Mon Sep 17 00:00:00 2001 From: flip1995 Date: Sun, 17 Apr 2022 20:43:43 +0200 Subject: [PATCH 49/78] Auto update lint count in Clippy book --- clippy_dev/src/update_lints.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 5024e63bfa738..1bbd9a45b619e 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -58,6 +58,16 @@ fn generate_lint_files( }, ); + replace_region_in_file( + update_mode, + Path::new("book/src/README.md"), + "[There are over ", + " lints included in this crate!]", + |res| { + write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap(); + }, + ); + replace_region_in_file( update_mode, Path::new("CHANGELOG.md"), From 1f11cd17ac4a6d143ef36d4d82358283626afd08 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Jun 2022 13:05:08 +0200 Subject: [PATCH 50/78] Remove bit-rotty list of Clippy team members from CONTRIBUTING.md --- CONTRIBUTING.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f132a274a1cc..e81e7ceedcb50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,9 +47,10 @@ a developer guide and is a good place to start your journey. ## Finding something to fix/improve -All issues on Clippy are mentored, if you want help simply ask @Manishearth, @flip1995, @phansch -or @llogiq directly by mentioning them in the issue or over on [Zulip]. This list may be out of date. -All currently active mentors can be found [here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3) +All issues on Clippy are mentored, if you want help simply ask someone from the +Clippy team directly by mentioning them in the issue or over on [Zulip]. All +currently active team members can be found +[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3) Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy issues. You can use `@rustbot claim` to assign the issue to yourself. From 93056599220db45a67e3ba82484af4718456fefa Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Jun 2022 13:12:53 +0200 Subject: [PATCH 51/78] Book: Improve chapter on CI Recommend the -Dwarnings and --all-targets/--all-features more strongly. --- book/src/continuous_integration/README.md | 5 +++-- book/src/continuous_integration/github_actions.md | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/book/src/continuous_integration/README.md b/book/src/continuous_integration/README.md index 9f1374193de39..e5c3673bde451 100644 --- a/book/src/continuous_integration/README.md +++ b/book/src/continuous_integration/README.md @@ -1,7 +1,8 @@ # Continuous Integration -It is recommended to run Clippy on CI, preferably with `-Dwarnings`, so that -Clippy lints prevent CI from passing. +It is recommended to run Clippy on CI with `-Dwarnings`, so that Clippy lints +prevent CI from passing. To enforce errors on warnings on all `cargo` commands +not just `cargo clippy`, you can set the env var `RUSTFLAGS="-Dwarnings"`. We recommend to use Clippy from the same toolchain, that you use for compiling your crate for maximum compatibility. E.g. if your crate is compiled with the diff --git a/book/src/continuous_integration/github_actions.md b/book/src/continuous_integration/github_actions.md index 4154c60dbd211..42a43ef138016 100644 --- a/book/src/continuous_integration/github_actions.md +++ b/book/src/continuous_integration/github_actions.md @@ -6,11 +6,16 @@ pre-installed. So all you have to do is to run `cargo clippy`. ```yml on: push name: Clippy check + +# Make sure CI fails on all warnings, including Clippy lints +env: + RUSTFLAGS: "-Dwarnings" + jobs: clippy_check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Run Clippy - run: cargo clippy + run: cargo clippy --all-targets --all-features ``` From 99a731d2656923daa708513324b1a6418a1d3fb7 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Jun 2022 13:25:11 +0200 Subject: [PATCH 52/78] Book: Improve sync documentation - Move doc about defining remotes to the front and use the defined remotes in the further documentation - Don't recommend pushing to the remote repo directly - Add some clarifying comments --- book/src/development/infrastructure/sync.md | 60 ++++++++++----------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/book/src/development/infrastructure/sync.md b/book/src/development/infrastructure/sync.md index 981e63cfddae6..5a0f7409a2e4c 100644 --- a/book/src/development/infrastructure/sync.md +++ b/book/src/development/infrastructure/sync.md @@ -51,6 +51,24 @@ sudo chown --reference=/usr/lib/git-core/git-subtree~ /usr/lib/git-core/git-subt > `bash` instead. You can do this by editing the first line of the `git-subtree` > script and changing `sh` to `bash`. +## Defining remotes + +You may want to define remotes, so you don't have to type out the remote +addresses on every sync. You can do this with the following commands (these +commands still have to be run inside the `rust` directory): + +```bash +# Set clippy-upstream remote for pulls +$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy +# Make sure to not push to the upstream repo +$ git remote set-url --push clippy-upstream DISABLED +# Set a local remote +$ git remote add clippy-local /path/to/rust-clippy +``` + +> Note: The following sections assume that you have set those remotes with the +> above remote names. + ## Performing the sync from [`rust-lang/rust`] to Clippy Here is a TL;DR version of the sync process (all of the following commands have @@ -64,22 +82,25 @@ to be run inside the `rust` directory): # Make sure to change `your-github-name` to your github name in the following command. Also be # sure to either use a net-new branch, e.g. `sync-from-rust`, or delete the branch beforehand # because changes cannot be fast forwarded and you have to run this command again. - git subtree push -P src/tools/clippy git@github.com:your-github-name/rust-clippy sync-from-rust + git subtree push -P src/tools/clippy clippy-local sync-from-rust ``` - > _Note:_ This will directly push to the remote repository. You can also - > push to your local copy by replacing the remote address with - > `/path/to/rust-clippy` directory. - > _Note:_ Most of the time you have to create a merge commit in the > `rust-clippy` repo (this has to be done in the Clippy repo, not in the > rust-copy of Clippy): ```bash git fetch upstream # assuming upstream is the rust-lang/rust remote git checkout sync-from-rust - git merge upstream/master + git merge upstream/master --no-ff ``` -4. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to + > Note: This is one of the few instances where a merge commit is allowed in + > a PR. +4. Bump the nightly version in the Clippy repository by changing the date in the + rust-toolchain file to the current date and committing it with the message: + ```bash + git commit -m "Bump nightly version -> YYYY-MM-DD" + ``` +5. Open a PR to `rust-lang/rust-clippy` and wait for it to get merged (to accelerate the process ping the `@rust-lang/clippy` team in your PR and/or ask them in the [Zulip] stream.) @@ -93,33 +114,10 @@ All of the following commands have to be run inside the `rust` directory. 2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git checkout -b sync-from-clippy - git subtree pull -P src/tools/clippy https://github.com/rust-lang/rust-clippy master + git subtree pull -P src/tools/clippy clippy-upstream master ``` 3. Open a PR to [`rust-lang/rust`] -## Defining remotes - -You may want to define remotes, so you don't have to type out the remote -addresses on every sync. You can do this with the following commands (these -commands still have to be run inside the `rust` directory): - -```bash -# Set clippy-upstream remote for pulls -$ git remote add clippy-upstream https://github.com/rust-lang/rust-clippy -# Make sure to not push to the upstream repo -$ git remote set-url --push clippy-upstream DISABLED -# Set clippy-origin remote to your fork for pushes -$ git remote add clippy-origin git@github.com:your-github-name/rust-clippy -# Set a local remote -$ git remote add clippy-local /path/to/rust-clippy -``` - -You can then sync with the remote names from above, e.g.: - -```bash -$ git subtree push -P src/tools/clippy clippy-local sync-from-rust -``` - [gitgitgadget-pr]: https://github.com/gitgitgadget/git/pull/493 [subtree]: https://rustc-dev-guide.rust-lang.org/contributing.html#external-dependencies-subtree [`rust-lang/rust`]: https://github.com/rust-lang/rust From b2660de8ecf3ad341837c8884835beabb5db044b Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Mon, 6 Jun 2022 14:41:36 +0200 Subject: [PATCH 53/78] Book: Improve release documentation Make it clear for all code blocks in which repository they should be run. Also make sure that the correct beta commit is fetched. --- book/src/development/infrastructure/release.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/src/development/infrastructure/release.md b/book/src/development/infrastructure/release.md index 7c2fd29b5f9df..0572281803e71 100644 --- a/book/src/development/infrastructure/release.md +++ b/book/src/development/infrastructure/release.md @@ -28,7 +28,7 @@ tree of the Clippy repository. To find out if this step is necessary run ```bash -# Assumes that the local master branch is up-to-date +# Assumes that the local master branch of rust-lang/rust-clippy is up-to-date $ git fetch upstream $ git branch master --contains upstream/beta ``` @@ -56,7 +56,8 @@ determined. ```bash # Assuming the current directory corresponds to the Rust repository -$ git checkout beta +$ git fetch upstream +$ git checkout upstream/beta $ BETA_SHA=$(git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g") ``` From fdadebe6721a0016b87063d7a54e48c3db6bab82 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 28 May 2022 15:27:53 -0400 Subject: [PATCH 54/78] Add lint output to lint list --- Cargo.toml | 2 +- clippy_lints/Cargo.toml | 4 +- clippy_lints/src/collapsible_if.rs | 12 +- clippy_lints/src/doc.rs | 4 +- clippy_lints/src/large_include_file.rs | 3 +- clippy_lints/src/literal_representation.rs | 1 + clippy_lints/src/methods/mod.rs | 1 + .../internal_lints/metadata_collector.rs | 228 ++++++++++++++++-- tests/dogfood.rs | 4 +- util/gh-pages/index.html | 20 ++ 10 files changed, 247 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d23d681df00c1..42455998fe790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] integration = ["tempfile"] -internal = ["clippy_lints/internal"] +internal = ["clippy_lints/internal", "tempfile"] [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 0a3f04da35705..eb4582b93f2c0 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" [dependencies] cargo_metadata = "0.14" +clippy_dev = { path = "../clippy_dev", optional = true } clippy_utils = { path = "../clippy_utils" } if_chain = "1.0" itertools = "0.10.1" @@ -18,6 +19,7 @@ quine-mc_cluskey = "0.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } +tempfile = { version = "3.3.0", optional = true } toml = "0.5" unicode-normalization = "0.1" unicode-script = { version = "0.5", default-features = false } @@ -30,7 +32,7 @@ url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] # build clippy with internal lints enabled, off by default -internal = ["clippy_utils/internal", "serde_json"] +internal = ["clippy_utils/internal", "serde_json", "tempfile", "clippy_dev"] [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 3eceb848822e9..90430b71a0ef9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -32,20 +32,20 @@ declare_clippy_lint! { /// makes code look more complex than it really is. /// /// ### Example - /// ```rust,ignore + /// ```rust + /// # let (x, y) = (true, true); /// if x { /// if y { - /// … + /// // … /// } /// } - /// /// ``` /// /// Use instead: - /// - /// ```rust,ignore + /// ```rust + /// # let (x, y) = (true, true); /// if x && y { - /// … + /// // … /// } /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index aaec88f50c771..bf8c810a3e752 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -178,7 +178,7 @@ declare_clippy_lint! { /// if the `fn main()` is left implicit. /// /// ### Examples - /// ``````rust + /// ```rust /// /// An example of a doctest with a `main()` function /// /// /// /// # Examples @@ -191,7 +191,7 @@ declare_clippy_lint! { /// fn needless_main() { /// unimplemented!(); /// } - /// `````` + /// ``` #[clippy::version = "1.40.0"] pub NEEDLESS_DOCTEST_MAIN, style, diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 8bef13c682dbf..84dd61a1e4b0d 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -22,10 +22,11 @@ declare_clippy_lint! { /// let included_bytes = include_bytes!("very_large_file.txt"); /// ``` /// - /// Instead, you can load the file at runtime: + /// Use instead: /// ```rust,ignore /// use std::fs; /// + /// // You can load the file at runtime /// let string = fs::read_to_string("very_large_file.txt")?; /// let bytes = fs::read("very_large_file.txt")?; /// ``` diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 9998712b8527d..2a4e1262276d2 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -46,6 +46,7 @@ declare_clippy_lint! { /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example + /// ```ignore /// `2_32` => `2_i32` /// `250_8 => `250_u8` /// ``` diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 7308e74c323e3..904454037eade 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1039,6 +1039,7 @@ declare_clippy_lint! { /// /// // Good /// _.split('x'); + /// ``` #[clippy::version = "pre 1.29.0"] pub SINGLE_CHAR_PATTERN, perf, diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index cf2de6a42af36..069d8374b2684 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -31,6 +31,8 @@ use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::path::Path; +use std::path::PathBuf; +use std::process::Command; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; @@ -180,6 +182,7 @@ pub struct MetadataCollector { lints: BinaryHeap, applicability_info: FxHashMap, config: Vec, + clippy_project_root: PathBuf, } impl MetadataCollector { @@ -188,6 +191,7 @@ impl MetadataCollector { lints: BinaryHeap::::default(), applicability_info: FxHashMap::::default(), config: collect_configs(), + clippy_project_root: clippy_dev::clippy_project_root(), } } @@ -215,11 +219,13 @@ impl Drop for MetadataCollector { // Mapping the final data let mut lints = std::mem::take(&mut self.lints).into_sorted_vec(); - collect_renames(&mut lints); for x in &mut lints { x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()); + replace_produces(&x.id, &mut x.docs, &self.clippy_project_root); } + collect_renames(&mut lints); + // Outputting if Path::new(OUTPUT_FILE).exists() { fs::remove_file(OUTPUT_FILE).unwrap(); @@ -263,14 +269,193 @@ impl LintMetadata { } } +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) { + let mut doc_lines = docs.lines().map(ToString::to_string).collect::>(); + let mut lines = doc_lines.iter_mut(); + + 'outer: loop { + // Find the start of the example + + // ```rust + loop { + match lines.next() { + Some(line) if line.trim_start().starts_with("```rust") => { + if line.contains("ignore") || line.contains("no_run") { + // A {{produces}} marker may have been put on a ignored code block by mistake, + // just seek to the end of the code block and continue checking. + if lines.any(|line| line.trim_start().starts_with("```")) { + continue; + } + + panic!("lint `{}` has an unterminated code block", lint_name) + } + + break; + }, + Some(line) if line.trim_start() == "{{produces}}" => { + panic!( + "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block", + lint_name + ) + }, + Some(line) => { + let line = line.trim(); + // These are the two most common markers of the corrections section + if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") { + break 'outer; + } + }, + None => break 'outer, + } + } + + // Collect the example + let mut example = Vec::new(); + loop { + match lines.next() { + Some(line) if line.trim_start() == "```" => break, + Some(line) => example.push(line), + None => panic!("lint `{}` has an unterminated code block", lint_name), + } + } + + // Find the {{produces}} and attempt to generate the output + loop { + match lines.next() { + Some(line) if line.is_empty() => {}, + Some(line) if line.trim() == "{{produces}}" => { + let output = get_lint_output(lint_name, &example, clippy_project_root); + line.replace_range( + .., + &format!( + "
\ + Produces\n\ + \n\ + ```text\n\ + {}\n\ + ```\n\ +
", + output + ), + ); + + break; + }, + // No {{produces}}, we can move on to the next example + Some(_) => break, + None => break 'outer, + } + } + } + + *docs = cleanup_docs(&doc_lines); +} + +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String { + let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}")); + let file = dir.path().join("lint_example.rs"); + + let mut source = String::new(); + let unhidden = example + .iter() + .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line)); + + // Get any attributes + let mut lines = unhidden.peekable(); + while let Some(line) = lines.peek() { + if line.starts_with("#!") { + source.push_str(line); + source.push('\n'); + lines.next(); + } else { + break; + } + } + + let needs_main = !example.iter().any(|line| line.contains("fn main")); + if needs_main { + source.push_str("fn main() {\n"); + } + + for line in lines { + source.push_str(line); + source.push('\n'); + } + + if needs_main { + source.push_str("}\n"); + } + + if let Err(e) = fs::write(&file, &source) { + panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy()); + } + + let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX); + + let mut cmd = Command::new("cargo"); + + cmd.current_dir(clippy_project_root) + .env("CARGO_INCREMENTAL", "0") + .env("CLIPPY_ARGS", "") + .env("CLIPPY_DISABLE_DOCS_LINKS", "1") + // We need to disable this to enable all lints + .env("ENABLE_METADATA_COLLECTION", "0") + .args(["run", "--bin", "clippy-driver"]) + .args(["--target-dir", "./clippy_lints/target"]) + .args(["--", "--error-format=json"]) + .args(["--edition", "2021"]) + .arg("-Cdebuginfo=0") + .args(["-A", "clippy::all"]) + .args(["-W", &prefixed_name]) + .args(["-L", "./target/debug"]) + .args(["-Z", "no-codegen"]); + + let output = cmd + .arg(file.as_path()) + .output() + .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd)); + + let tmp_file_path = file.to_string_lossy(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + let msgs = stderr + .lines() + .filter(|line| line.starts_with('{')) + .map(|line| serde_json::from_str(line).unwrap()) + .collect::>(); + + let mut rendered = String::new(); + let iter = msgs + .iter() + .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name)); + + for message in iter { + let rendered_part = message["rendered"].as_str().expect("rendered field should exist"); + rendered.push_str(rendered_part); + } + + if rendered.is_empty() { + let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect(); + let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect(); + panic!( + "did not find lint `{}` in output of example, got:\n{}\n{}", + lint_name, + non_json.join("\n"), + rendered.join("\n") + ); + } + + // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :) + rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs") +} + #[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)] struct SerializableSpan { path: String, line: usize, } -impl std::fmt::Display for SerializableSpan { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for SerializableSpan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line) } } @@ -435,10 +620,10 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // metadata extraction if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item); - if let Some(mut docs) = extract_attr_docs_or_lint(cx, item); + if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item); then { if let Some(configuration_section) = self.get_lint_configs(&lint_name) { - docs.push_str(&configuration_section); + raw_docs.push_str(&configuration_section); } let version = get_lint_version(cx, item); @@ -448,7 +633,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { group, level, version, - docs, + raw_docs, )); } } @@ -459,7 +644,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase(); if !BLACK_LISTED_LINTS.contains(&lint_name.as_str()); // Metadata the little we can get from a deprecated lint - if let Some(docs) = extract_attr_docs_or_lint(cx, item); + if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item); then { let version = get_lint_version(cx, item); @@ -469,7 +654,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector { DEPRECATED_LINT_GROUP_STR.to_string(), DEPRECATED_LINT_LEVEL, version, - docs, + raw_docs, )); } } @@ -535,22 +720,28 @@ fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option, item: &Item<'_>) -> Option { + let attrs = cx.tcx.hir().attrs(item.hir_id()); + let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); + + if let Some(line) = lines.next() { + let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n"); + return Some(raw_docs); + } + + None +} + /// This function may modify the doc comment to ensure that the string can be displayed using a /// markdown viewer in Clippy's lint list. The following modifications could be applied: /// * Removal of leading space after a new line. (Important to display tables) /// * Ensures that code blocks only contain language information -fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str); - let mut docs = String::from(lines.next()?.as_str()); +fn cleanup_docs(docs_collection: &Vec) -> String { let mut in_code_block = false; let mut is_code_block_rust = false; - for line in lines { - let line = line.as_str(); + let mut docs = String::new(); + for line in docs_collection { // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :) if is_code_block_rust && line.trim_start().starts_with("# ") { continue; @@ -583,7 +774,8 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { docs.push_str(line); } } - Some(docs) + + docs } fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 9da80518ce916..fffc53603424b 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -21,7 +21,7 @@ fn dogfood_clippy() { // "" is the root package for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_package(package, &[]); + run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]); } } @@ -77,8 +77,6 @@ fn run_clippy_for_package(project: &str, args: &[&str]) { .arg("--all-features") .arg("--") .args(args) - .args(&["-D", "clippy::all"]) - .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir if cfg!(feature = "internal") { diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 2076d1299783d..4999cce75114b 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -206,6 +206,26 @@ margin: auto 5px; font-family: monospace; } + + details { + border-radius: 4px; + padding: .5em .5em 0; + } + + code { + white-space: pre !important; + } + + summary { + font-weight: bold; + margin: -.5em -.5em 0; + padding: .5em; + display: revert; + } + + details[open] { + padding: .5em; + }