Merge commit 'd7b5cbf065' into clippyup

This commit is contained in:
flip1995 2022-06-16 17:39:06 +02:00
parent bd071bf5b2
commit f8f9d01c2a
199 changed files with 4158 additions and 1931 deletions

View file

@ -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.2", 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)]

View file

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

View file

@ -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;
@ -24,6 +24,7 @@ declare_clippy_lint! {
/// };
/// }
/// ```
///
/// Use instead:
/// ```rust
/// async fn foo() {}
@ -63,9 +64,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| {

View file

@ -340,7 +340,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {
if is_word(lint, sym!(unused_imports))
if is_word(lint, sym::unused_imports)
|| is_word(lint, sym::deprecated)
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
}
},
ItemKind::ExternCrate(..) => {
if is_word(lint, sym!(unused_imports)) && skip_unused_imports {
if is_word(lint, sym::unused_imports) && skip_unused_imports {
return;
}
if is_word(lint, sym!(unused_extern_crates)) {

View file

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

View file

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

View file

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

View file

@ -219,13 +219,14 @@ 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 +246,19 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // Bad
/// fn fn1() -> i16 {
/// 1
/// };
/// 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,19 +280,24 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // Bad: fn1 is cast as `usize`
/// // fn1 is cast as `usize`
/// fn fn1() -> u16 {
/// 1
/// };
/// 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;
///
/// // Good: maybe you intended to cast it to a function type?
/// // or
///
/// // maybe you intended to cast it to a function type?
/// fn fn3() -> u16 {
/// 1
/// }
@ -406,7 +414,7 @@ declare_clippy_lint! {
/// enum E { X = 256 };
/// let _ = E::X as u8;
/// ```
#[clippy::version = "1.60.0"]
#[clippy::version = "1.61.0"]
pub CAST_ENUM_TRUNCATION,
suspicious,
"casts from an enum type to an integral type which will truncate the value"
@ -451,7 +459,7 @@ declare_clippy_lint! {
/// println!("{:?}", &*new_ptr);
/// }
/// ```
#[clippy::version = "1.60.0"]
#[clippy::version = "1.61.0"]
pub CAST_SLICE_DIFFERENT_SIZES,
correctness,
"casting using `as` between raw pointers to slices of types with different sizes"

View file

@ -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;
@ -22,18 +22,14 @@ declare_clippy_lint! {
/// ### Example
/// ```rust
/// # let foo: u32 = 5;
/// # let _ =
/// foo <= i32::MAX as u32
/// # ;
/// foo <= i32::MAX as u32;
/// ```
///
/// Could be written:
///
/// Use instead:
/// ```rust
/// # let foo = 1;
/// # let _ =
/// i32::try_from(foo).is_ok()
/// # ;
/// # #[allow(unused)]
/// i32::try_from(foo).is_ok();
/// ```
#[clippy::version = "1.37.0"]
pub CHECKED_CONVERSIONS,
@ -61,6 +57,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;

View file

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

View file

@ -35,7 +35,6 @@ declare_clippy_lint! {
/// ```
///
/// Use instead:
///
/// ```rust,ignore
/// use std::cmp::Ordering;
/// # fn a() {}

View file

@ -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>(
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 {
return;
};
// BRANCHES_SHARING_CODE prerequisites
if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
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);
// Scan start
let mut start_walker = UsedValueFinderVisitor::new(cx);
for stmt in start_stmts {
intravisit::walk_stmt(&mut start_walker, stmt);
}
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);
}
let mut block_defs = block_walker.defs;
// Scan moved stmts
let mut moved_start: Option<usize> = 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();
}
}
end_walker.uses.clear();
}
if let Some(moved_start) = moved_start {
end_eq -= moved_start;
}
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);
});
}
emit_branches_sharing_code_lint(
cx,
start_eq,
end_eq,
end_linable,
check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
blocks,
expr,
);
/// 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,
}
}
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,
}
/// 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<BlockEqual> {
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));
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 })
};
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 {
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,
block0.span,
lhs.span,
"this `if` has identical blocks",
Some(block1.span),
Some(rhs.span),
"same as this",
);
return None;
all_eq
} else {
false
}
}
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;
}
Some(BlockEqual {
start_eq,
end_eq,
expr_eq,
})
})
}
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
fn lint_branches_sharing_code<'tcx>(
cx: &LateContext<'tcx>,
conds: &[&'tcx Expr<'_>],
blocks: &[&Block<'tcx>],
expr: &'tcx Expr<'_>,
) {
// We only lint ifs with multiple blocks
let &[first_block, ref blocks @ ..] = blocks else {
return;
};
let &[.., last_block] = blocks else {
return;
};
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);
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");
}
if let Some((span, sugg)) = start_suggestion {
diag.span_suggestion(
span,
"consider moving these statements before the if",
sugg,
Applicability::Unspecified,
);
}
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");
}
}
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");
}
});
}
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<usize>,
/// 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<Span> {
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,
}
}
fn end_span(&self, b: &Block<'_>, sm: &SourceMap) -> Option<Span> {
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,
}
}
}
/// 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
}
}
/// 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)))
}
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 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,
};
}
}
}
BlockEq {
start_end_eq,
end_begin_eq: Some(end_begin_eq),
moved_locals,
}
}
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<Symb
})
}
fn emit_branches_sharing_code_lint(
cx: &LateContext<'_>,
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<HirId>,
/// The Symbols of the defined symbols in the scanned statements
def_symbols: FxHashSet<Symbol>,
/// The `HirId`s of the used values
uses: FxHashSet<HirId>,
}
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,

View file

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

View file

@ -18,10 +18,11 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust,ignore
/// // Bad
/// dbg!(true)
/// ```
///
/// // Good
/// Use instead:
/// ```rust,ignore
/// true
/// ```
#[clippy::version = "1.34.0"]

View file

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

View file

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::numeric_literal;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
@ -76,7 +76,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
}
/// Check whether a passed literal has potential to cause fallback or not.
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>) {
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
if_chain! {
if !in_external_macro(self.cx.sess(), lit.span);
if let Some(ty_bound) = self.ty_bounds.last();
@ -101,14 +101,15 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
}
};
let sugg = numeric_literal::format(&src, Some(suffix), is_float);
span_lint_and_sugg(
span_lint_hir_and_then(
self.cx,
DEFAULT_NUMERIC_FALLBACK,
emit_hir_id,
lit.span,
"default numeric fallback might occur",
"consider adding suffix",
sugg,
Applicability::MaybeIncorrect,
|diag| {
diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
}
);
}
}
@ -179,7 +180,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
ExprKind::Lit(lit) => {
let ty = self.cx.typeck_results().expr_ty(expr);
self.check_lit(lit, ty);
self.check_lit(lit, ty, expr.hir_id);
return;
},

View file

@ -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;
@ -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
@ -131,6 +135,7 @@ pub struct Dereferencing {
struct StateData {
/// Span of the top level expression
span: Span,
hir_id: HirId,
}
enum State {
@ -165,6 +170,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 +225,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 +300,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 +396,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 +409,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 +654,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);
});
},
}
}

View file

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

View file

@ -4,14 +4,19 @@ 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::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, Ty};
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
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;
use rustc_span::sym;
@ -459,50 +464,18 @@ 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();
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,
@ -515,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::<Vec<_>>();
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,
)
}

View file

@ -163,7 +163,7 @@ declare_clippy_lint! {
/// }
/// }
/// ```
#[clippy::version = "1.52.0"]
#[clippy::version = "1.51.0"]
pub MISSING_PANICS_DOC,
pedantic,
"`pub fn` may panic without `# Panics` in doc comment"
@ -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,

View file

@ -60,7 +60,8 @@ declare_clippy_lint! {
/// struct BlackForestCake;
/// }
/// ```
/// Could be written as:
///
/// Use instead:
/// ```rust
/// mod cake {
/// struct BlackForest;

View file

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

View file

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

View file

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

View file

@ -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;
/// ```

View file

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

View file

@ -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();
/// ```

View file

@ -16,17 +16,21 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// let end: u32 = 10;
/// let start: u32 = 5;
///
/// # let end: u32 = 10;
/// # let start: u32 = 5;
/// let mut i: u32 = end - start;
///
/// // Bad
/// if i != 0 {
/// i -= 1;
/// }
/// ```
///
/// Use instead:
/// ```rust
/// # let end: u32 = 10;
/// # let start: u32 = 5;
/// let mut i: u32 = end - start;
///
/// // Good
/// i = i.saturating_sub(1);
/// ```
#[clippy::version = "1.44.0"]
@ -48,7 +52,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

View file

@ -45,7 +45,7 @@ declare_clippy_lint! {
/// println!("{}", first);
/// }
/// ```
#[clippy::version = "1.58.0"]
#[clippy::version = "1.59.0"]
pub INDEX_REFUTABLE_SLICE,
nursery,
"avoid indexing on slices which could be destructed"

View file

@ -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,32 @@ 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
/// # #![allow(unused)]
///
/// # let x = vec![0; 5];
/// # let y = [0, 1, 2, 3];
/// x.get(2);
/// x.get(2..100);
///
/// // Good
/// &y[2..];
/// &y[..2];
/// &y[0..3];
/// y.get(10);
/// y.get(10..100);
/// y.get(10..);
/// y.get(..100);
/// ```
#[clippy::version = "pre 1.29.0"]
pub INDEXING_SLICING,

View file

@ -41,6 +41,7 @@ declare_clippy_lint! {
/// ### Example
/// ```rust
/// let infinite_iter = 0..;
/// # #[allow(unused)]
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
/// ```
#[clippy::version = "pre 1.29.0"]

View file

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

View file

@ -21,8 +21,7 @@ declare_clippy_lint! {
/// if x >= y + 1 {}
/// ```
///
/// Could be written as:
///
/// Use instead:
/// ```rust
/// # let x = 1;
/// # let y = 1;

View file

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

View file

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

View file

@ -20,10 +20,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"]

View file

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

View file

@ -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")?;
/// ```

View file

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

View file

@ -242,6 +242,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(needless_bool::NEEDLESS_BOOL),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
@ -270,6 +271,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),

View file

@ -55,6 +55,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
LintId::of(ptr::MUT_FROM_REF),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
LintId::of(regex::INVALID_REGEX),
LintId::of(self_assignment::SELF_ASSIGNMENT),
LintId::of(serde_api::SERDE_API_MISUSE),

View file

@ -408,6 +408,7 @@ store.register_lints(&[
needless_continue::NEEDLESS_CONTINUE,
needless_for_each::NEEDLESS_FOR_EACH,
needless_late_init::NEEDLESS_LATE_INIT,
needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
needless_question_mark::NEEDLESS_QUESTION_MARK,
needless_update::NEEDLESS_UPDATE,
@ -458,6 +459,7 @@ store.register_lints(&[
ranges::RANGE_ZIP_WITH_LEN,
ranges::REVERSED_EMPTY_RANGES,
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
redundant_clone::REDUNDANT_CLONE,
redundant_closure_call::REDUNDANT_CLOSURE_CALL,
redundant_else::REDUNDANT_ELSE,

View file

@ -91,6 +91,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
LintId::of(neg_multiply::NEG_MULTIPLY),
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),

View file

@ -1,5 +1,3 @@
// error-pattern:cargo-clippy
#![feature(array_windows)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
@ -88,10 +86,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,
@ -315,6 +314,7 @@ mod needless_borrowed_ref;
mod needless_continue;
mod needless_for_each;
mod needless_late_init;
mod needless_parens_on_range_literals;
mod needless_pass_by_value;
mod needless_question_mark;
mod needless_update;
@ -348,6 +348,7 @@ mod pub_use;
mod question_mark;
mod ranges;
mod rc_clone_in_vec_init;
mod read_zero_byte_vec;
mod redundant_clone;
mod redundant_closure_call;
mod redundant_else;
@ -746,6 +747,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
store.register_early_pass(|| Box::new(precedence::Precedence));
store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
store.register_late_pass(|| Box::new(create_dir::CreateDir));
@ -907,6 +909,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
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));
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -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) {
/// // ...
/// }

View file

@ -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,
@ -46,6 +51,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`
/// ```
@ -66,11 +72,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 +136,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,

View file

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

View file

@ -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 {
@ -188,6 +191,10 @@ declare_clippy_lint! {
/// for x in &res {
/// // ..
/// }
///
/// for x in res.iter() {
/// // ..
/// }
/// ```
///
/// Use instead:
@ -282,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) {}
@ -469,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
@ -484,7 +492,8 @@ declare_clippy_lint! {
/// vec.push(item2);
/// }
/// ```
/// could be written as
///
/// Use instead:
/// ```rust
/// let item1 = 2;
/// let item2 = 3;
@ -512,7 +521,8 @@ declare_clippy_lint! {
/// println!("{}", item);
/// }
/// ```
/// could be written as
///
/// Use instead:
/// ```rust
/// let item1 = 2;
/// let item = &item1;
@ -586,7 +596,7 @@ declare_clippy_lint! {
/// std::hint::spin_loop()
/// }
/// ```
#[clippy::version = "1.59.0"]
#[clippy::version = "1.61.0"]
pub MISSING_SPIN_LOOP,
perf,
"An empty busy waiting loop"
@ -695,10 +705,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 +722,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);
}
}

View file

@ -117,13 +117,20 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
| ExprKind::Type(e, _)
| ExprKind::Field(e, _)
| ExprKind::AddrOf(_, _, e)
| ExprKind::Struct(_, _, Some(e))
| ExprKind::Repeat(e, _)
| ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
ExprKind::Array(es) | ExprKind::MethodCall(_, es, _) | ExprKind::Tup(es) => {
never_loop_expr_all(&mut es.iter(), main_loop_id)
},
ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
if let Some(base) = base {
combine_both(fields, never_loop_expr(base, main_loop_id))
} else {
fields
}
},
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
ExprKind::Binary(_, e1, e2)
| ExprKind::Assign(e1, e2, _)
@ -180,8 +187,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
})
.fold(NeverLoopResult::Otherwise, combine_both),
ExprKind::Struct(_, _, None)
| ExprKind::Yield(_, _)
ExprKind::Yield(_, _)
| ExprKind::Closure { .. }
| ExprKind::Path(_)
| ExprKind::ConstBlock(_)

View file

@ -110,14 +110,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
arm1.span,
"this match arm has an identical body to the `_` wildcard arm",
|diag| {
diag.span_suggestion(
arm1.span,
"try removing the arm",
"",
Applicability::MaybeIncorrect,
)
.help("or try changing either arm body")
.span_note(arm2.span, "`_` wildcard arm here");
diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
.help("or try changing either arm body")
.span_note(arm2.span, "`_` wildcard arm here");
},
);
} else {

View file

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

View file

@ -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};
@ -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(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)
};
then {
let span = filter_span.with_hi(expr.span.hi());
let (filter_name, lint) = if is_find {
@ -171,3 +179,18 @@ pub(super) fn check<'tcx>(
}
}
}
fn acceptable_methods(method: &PathSegment<'_>) -> bool {
let methods: [Symbol; 8] = [
sym::clone,
sym::as_ref,
sym!(copied),
sym!(cloned),
sym!(as_deref),
sym!(as_mut),
sym!(as_deref_mut),
sym!(to_owned),
];
methods.contains(&method.ident.name)
}

View file

@ -1,70 +1,59 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy};
use itertools::Itertools;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{get_associated_type, implements_trait, is_copy};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::sym;
use std::ops::Not;
use super::ITER_OVEREAGER_CLONED;
use crate::redundant_clone::REDUNDANT_CLONE;
/// lint overeager use of `cloned()` for `Iterator`s
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
recv: &'tcx hir::Expr<'_>,
name: &str,
map_arg: &[hir::Expr<'_>],
expr: &'tcx Expr<'_>,
cloned_call: &'tcx Expr<'_>,
cloned_recv: &'tcx Expr<'_>,
is_count: bool,
needs_into_iter: bool,
) {
// Check if it's iterator and get type associated with `Item`.
let inner_ty = if_chain! {
if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
let recv_ty = cx.typeck_results().expr_ty(recv);
if implements_trait(cx, recv_ty, iterator_trait_id, &[]);
if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv));
then {
inner_ty
} else {
let typeck = cx.typeck_results();
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
&& let Some(method_id) = typeck.type_dependent_def_id(expr.hir_id)
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
&& let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id)
&& cx.tcx.trait_of_item(method_id) == Some(iter_id)
&& let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv)
&& let Some(iter_assoc_ty) = get_associated_type(cx, cloned_recv_ty, iter_id, "Item")
&& matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty))
{
if needs_into_iter
&& let Some(into_iter_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
&& !implements_trait(cx, iter_assoc_ty, into_iter_id, &[])
{
return;
}
};
match inner_ty.kind() {
ty::Ref(_, ty, _) if !is_copy(cx, *ty) => {},
_ => return,
};
let (lint, msg, trailing_clone) = if is_count {
(REDUNDANT_CLONE, "unneeded cloning of iterator items", "")
} else {
(ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()")
};
let (lint, preserve_cloned) = match name {
"count" => (REDUNDANT_CLONE, false),
_ => (ITER_OVEREAGER_CLONED, true),
};
let wildcard_params = map_arg.is_empty().not().then(|| "...").unwrap_or_default();
let msg = format!(
"called `cloned().{}({})` on an `Iterator`. It may be more efficient to call `{}({}){}` instead",
name,
wildcard_params,
name,
wildcard_params,
preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
);
span_lint_and_sugg(
cx,
lint,
expr.span,
&msg,
"try this",
format!(
"{}.{}({}){}",
snippet(cx, recv.span, ".."),
name,
map_arg.iter().map(|a| snippet(cx, a.span, "..")).join(", "),
preserve_cloned.then(|| ".cloned()").unwrap_or_default(),
),
Applicability::MachineApplicable,
);
span_lint_and_then(
cx,
lint,
expr.span,
msg,
|diag| {
let method_span = expr.span.with_lo(cloned_call.span.hi());
if let Some(mut snip) = snippet_opt(cx, method_span) {
snip.push_str(trailing_clone);
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
diag.span_suggestion(replace_span, "try this", snip, Applicability::MachineApplicable);
}
}
);
}
}

View file

@ -124,28 +124,24 @@ declare_clippy_lint! {
/// It's often inefficient to clone all elements of an iterator, when eventually, only some
/// of them will be consumed.
///
/// ### Examples
/// ```rust
/// # let vec = vec!["string".to_string()];
///
/// // Bad
/// vec.iter().cloned().take(10);
///
/// // Good
/// vec.iter().take(10).cloned();
///
/// // Bad
/// vec.iter().cloned().last();
///
/// // Good
/// 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"]
/// ### Examples
/// ```rust
/// # let vec = vec!["string".to_string()];
/// vec.iter().cloned().take(10);
/// vec.iter().cloned().last();
/// ```
///
/// Use instead:
/// ```rust
/// # let vec = vec!["string".to_string()];
/// vec.iter().take(10).cloned();
/// vec.iter().last().cloned();
/// ```
#[clippy::version = "1.60.0"]
pub ITER_OVEREAGER_CLONED,
perf,
"using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
@ -342,11 +338,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 +387,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 +451,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 +474,12 @@ declare_clippy_lint! {
/// `_.ok()`.
///
/// ### Example
/// Bad:
/// ```rust
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.map_or(None, Some));
/// ```
///
/// Good:
/// Use instead:
/// ```rust
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.ok());
@ -538,7 +536,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 +561,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 +586,14 @@ declare_clippy_lint! {
/// let vec = vec![vec![1]];
/// let opt = Some(5);
///
/// // Bad
/// 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);
/// vec.iter().flat_map(|x| x.iter());
/// opt.and_then(|x| Some(x * 2));
/// ```
@ -610,15 +613,16 @@ declare_clippy_lint! {
/// less performant.
///
/// ### Example
/// Bad:
/// ```rust
/// # #![allow(unused)]
/// (0_i32..10)
/// .filter(|n| n.checked_add(1).is_some())
/// .map(|n| n.checked_add(1).unwrap());
/// ```
///
/// Good:
/// Use instead:
/// ```rust
/// # #[allow(unused)]
/// (0_i32..10).filter_map(|n| n.checked_add(1));
/// ```
#[clippy::version = "1.51.0"]
@ -637,14 +641,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));
/// ```
@ -712,17 +715,20 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// # #![allow(unused)]
/// let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
///
/// let _ = "hello world".find("world").is_none();
/// "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");
/// # #[allow(unused)]
/// !"hello world".contains("world");
/// ```
#[clippy::version = "pre 1.29.0"]
pub SEARCH_IS_SOME,
@ -744,7 +750,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 +906,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 +1044,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 +1111,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 +1224,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 +1288,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 +1370,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 +1421,13 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
/// # #[allow(unused)]
/// (0..3).fold(false, |acc, x| acc || x > 2);
/// ```
/// This could be written as:
///
/// Use instead:
/// ```rust
/// let _ = (0..3).any(|x| x > 2);
/// (0..3).any(|x| x > 2);
/// ```
#[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_FOLD,
@ -1485,11 +1507,14 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // Bad
/// let _ = (&vec![3, 4, 5]).into_iter();
/// # let vec = vec![3, 4, 5];
/// (&vec).into_iter();
/// ```
///
/// // Good
/// let _ = (&vec![3, 4, 5]).iter();
/// Use instead:
/// ```rust
/// # let vec = vec![3, 4, 5];
/// (&vec).iter();
/// ```
#[clippy::version = "1.32.0"]
pub INTO_ITER_ON_REF,
@ -1704,13 +1729,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');
/// ```
@ -1881,7 +1907,7 @@ declare_clippy_lint! {
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
#[clippy::version = "1.52.0"]
#[clippy::version = "1.47.0"]
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
@ -1897,11 +1923,14 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // Bad
/// let _ = "Hello".bytes().nth(3);
/// # #[allow(unused)]
/// "Hello".bytes().nth(3);
/// ```
///
/// // Good
/// let _ = "Hello".as_bytes().get(3);
/// Use instead:
/// ```rust
/// # #[allow(unused)]
/// "Hello".as_bytes().get(3);
/// ```
#[clippy::version = "1.52.0"]
pub BYTES_NTH,
@ -1945,15 +1974,19 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// // Bad
/// # #![allow(unused)]
/// let some_vec = vec![0, 1, 2, 3];
/// let _ = some_vec.iter().count();
/// let _ = &some_vec[..].iter().count();
///
/// // Good
/// some_vec.iter().count();
/// &some_vec[..].iter().count();
/// ```
///
/// Use instead:
/// ```rust
/// let some_vec = vec![0, 1, 2, 3];
/// let _ = some_vec.len();
/// let _ = &some_vec[..].len();
///
/// some_vec.len();
/// &some_vec[..].len();
/// ```
#[clippy::version = "1.52.0"]
pub ITER_COUNT,
@ -1973,16 +2006,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 +2034,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 +2056,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 +2064,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,17 +2091,16 @@ 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();
/// ```
#[clippy::version = "1.58.0"]
#[clippy::version = "1.59.0"]
pub NEEDLESS_SPLITN,
complexity,
"usages of `str::splitn` that can be replaced with `str::split`"
@ -2098,7 +2131,7 @@ declare_clippy_lint! {
/// foo(&path.to_string_lossy());
/// fn foo(s: &str) {}
/// ```
#[clippy::version = "1.58.0"]
#[clippy::version = "1.59.0"]
pub UNNECESSARY_TO_OWNED,
perf,
"unnecessary calls to `to_owned`-like functions"
@ -2149,7 +2182,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;
@ -2583,8 +2617,8 @@ impl Methods {
},
_ => {},
},
(name @ "count", args @ []) => match method_call(recv) {
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
("count", []) => match method_call(recv) {
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
iter_count::check(cx, expr, recv2, name2);
},
@ -2614,9 +2648,9 @@ impl Methods {
flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span);
},
(name @ "flatten", args @ []) => match method_call(recv) {
("flatten", []) => match method_call(recv) {
Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
_ => {},
},
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
@ -2636,10 +2670,10 @@ impl Methods {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
},
("last", args @ []) | ("skip", args @ [_]) => {
("last", []) | ("skip", [_]) => {
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv2, name, args);
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
}
},
@ -2660,10 +2694,10 @@ impl Methods {
map_identity::check(cx, expr, recv, m_arg, name, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
(name @ "next", args @ []) => {
("next", []) => {
if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
match (name2, args2) {
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
("iter", []) => iter_next_slice::check(cx, expr, recv2),
@ -2673,9 +2707,9 @@ impl Methods {
}
}
},
("nth", args @ [n_arg]) => match method_call(recv) {
("nth", [n_arg]) => match method_call(recv) {
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
@ -2698,10 +2732,10 @@ impl Methods {
}
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", args @ [_arg]) => {
("take", [_arg]) => {
if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv2, name, args);
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
}
},

View file

@ -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::<u32>();
/// ```
#[clippy::version = "pre 1.29.0"]

View file

@ -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, ..) => (),
/// _ => (),

View file

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

View file

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

View file

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

View file

@ -30,16 +30,21 @@ 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,

View file

@ -27,16 +27,17 @@ declare_clippy_lint! {
/// ```
///
/// ### Example
/// Bad:
/// ```rust
/// let mut v = Vec::<String>::new();
/// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
/// # #[allow(unused)]
/// v.iter_mut().filter(|&ref a| a.is_empty());
/// ```
///
/// Good:
/// Use instead:
/// ```rust
/// let mut v = Vec::<String>::new();
/// let _ = v.iter_mut().filter(|a| a.is_empty());
/// # #[allow(unused)]
/// v.iter_mut().filter(|a| a.is_empty());
/// ```
#[clippy::version = "pre 1.29.0"]
pub NEEDLESS_BORROWED_REFERENCE,

View file

@ -56,7 +56,7 @@ declare_clippy_lint! {
/// -1
/// };
/// ```
#[clippy::version = "1.58.0"]
#[clippy::version = "1.59.0"]
pub NEEDLESS_LATE_INIT,
style,
"late initializations that can be replaced by a `let` statement with an initializer"
@ -185,14 +185,14 @@ 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((
.flat_map(|assignment| {
[
assignment.span.until(assignment.rhs_span),
assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
String::new(),
))
}))
.collect::<Option<Vec<(Span, String)>>>()?;
]
})
.map(|span| (span, String::new()))
.collect::<Vec<(Span, String)>>();
match suggestions.len() {
// All of `exprs` are never types

View file

@ -0,0 +1,87 @@
use clippy_utils::{
diagnostics::span_lint_and_then,
higher,
source::{snippet, snippet_with_applicability},
};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// The lint checks for parenthesis on literals in range statements that are
/// superfluous.
///
/// ### Why is this bad?
/// Having superfluous parenthesis makes the code less readable
/// overhead when reading.
///
/// ### Example
///
/// ```rust
/// for i in (0)..10 {
/// println!("{i}");
/// }
/// ```
///
/// Use instead:
///
/// ```rust
/// for i in 0..10 {
/// println!("{i}");
/// }
/// ```
#[clippy::version = "1.63.0"]
pub NEEDLESS_PARENS_ON_RANGE_LITERALS,
style,
"needless parenthesis on range literals can be removed"
}
declare_lint_pass!(NeedlessParensOnRangeLiterals => [NEEDLESS_PARENS_ON_RANGE_LITERALS]);
fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
snippet.starts_with('(') && snippet.ends_with(')')
}
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
if is_start &&
let ExprKind::Lit(ref literal) = e.kind &&
let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
{
// don't check floating point literals on the start expression of a range
return;
}
if_chain! {
if let ExprKind::Lit(ref literal) = e.kind;
// the indicator that parenthesis surround the literal is that the span of the expression and the literal differ
if (literal.span.data().hi - literal.span.data().lo) != (e.span.data().hi - e.span.data().lo);
// inspect the source code of the expression for parenthesis
if snippet_enclosed_in_parenthesis(&snippet(cx, e.span, ""));
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_then(cx, NEEDLESS_PARENS_ON_RANGE_LITERALS, e.span,
"needless parenthesis on range literals can be removed",
|diag| {
let suggestion = snippet_with_applicability(cx, literal.span, "_", &mut applicability);
diag.span_suggestion(e.span, "try", suggestion, applicability);
});
}
}
}
impl<'tcx> LateLintPass<'tcx> for NeedlessParensOnRangeLiterals {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(expr) {
if let Some(start) = start {
check_for_parens(cx, start, true);
}
if let Some(end) = end {
check_for_parens(cx, end, false);
}
}
}
}

View file

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

View file

@ -19,17 +19,17 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// let a = 1.0;
/// let b = f64::NAN;
///
/// let not_less_or_equal = !(a <= b);
/// ```
///
/// Use instead:
/// ```rust
/// use std::cmp::Ordering;
///
/// // Bad
/// let a = 1.0;
/// let b = f64::NAN;
///
/// let _not_less_or_equal = !(a <= b);
///
/// // Good
/// let a = 1.0;
/// let b = f64::NAN;
/// # let a = 1.0;
/// # let b = f64::NAN;
///
/// let _not_less_or_equal = match a.partial_cmp(&b) {
/// None | Some(Ordering::Greater) => true,

View file

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

View file

@ -59,12 +59,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
@ -105,11 +107,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

View file

@ -33,15 +33,16 @@ 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";
/// ```
#[clippy::version = "1.58.0"]
#[clippy::version = "1.59.0"]
pub OCTAL_ESCAPES,
suspicious,
"string escape sequences looking like octal characters"

View file

@ -87,7 +87,7 @@ declare_clippy_lint! {
/// # print!("{}", f(1));
/// # }
/// ```
#[clippy::version = "1.60.0"]
#[clippy::version = "1.61.0"]
pub ONLY_USED_IN_RECURSION,
nursery,
"arguments that is only used in recursion can be removed"

View file

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

View file

@ -48,10 +48,11 @@ declare_clippy_lint! {
///
/// ### Example
/// ```ignore
/// // Bad
/// fn foo(&Vec<u32>) { .. }
/// ```
///
/// // 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
/// if x == ptr::null {
/// ..
/// }
/// ```rust,ignore
/// use std::ptr;
///
/// // Good
/// if x == ptr::null {
/// // ..
/// }
/// ```
///
/// 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"]

View file

@ -27,12 +27,13 @@ declare_clippy_lint! {
/// ### Example
/// ```rust
/// # let x = vec![1];
/// x.iter().zip(0..x.len());
/// let _ = x.iter().zip(0..x.len());
/// ```
/// Could be written as
///
/// Use instead:
/// ```rust
/// # let x = vec![1];
/// x.iter().enumerate();
/// let _ = x.iter().enumerate();
/// ```
#[clippy::version = "pre 1.29.0"]
pub RANGE_ZIP_WITH_LEN,
@ -65,12 +66,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 +104,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,

View file

@ -0,0 +1,142 @@
use clippy_utils::{
diagnostics::{span_lint, span_lint_and_sugg},
higher::{get_vec_init_kind, VecInitKind},
source::snippet,
visitors::expr_visitor_no_bodies,
};
use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// This lint catches reads into a zero-length `Vec`.
/// Especially in the case of a call to `with_capacity`, this lint warns that read
/// gets the number of bytes from the `Vec`'s length, not its capacity.
///
/// ### Why is this bad?
/// Reading zero bytes is almost certainly not the intended behavior.
///
/// ### Known problems
/// In theory, a very unusual read implementation could assign some semantic meaning
/// to zero-byte reads. But it seems exceptionally unlikely that code intending to do
/// a zero-byte read would allocate a `Vec` for it.
///
/// ### Example
/// ```rust
/// use std::io;
/// fn foo<F: io::Read>(mut f: F) {
/// let mut data = Vec::with_capacity(100);
/// f.read(&mut data).unwrap();
/// }
/// ```
/// Use instead:
/// ```rust
/// use std::io;
/// fn foo<F: io::Read>(mut f: F) {
/// let mut data = Vec::with_capacity(100);
/// data.resize(100, 0);
/// f.read(&mut data).unwrap();
/// }
/// ```
#[clippy::version = "1.63.0"]
pub READ_ZERO_BYTE_VEC,
correctness,
"checks for reads into a zero-length `Vec`"
}
declare_lint_pass!(ReadZeroByteVec => [READ_ZERO_BYTE_VEC]);
impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
for (idx, stmt) in block.stmts.iter().enumerate() {
if !stmt.span.from_expansion()
// matches `let v = Vec::new();`
&& let StmtKind::Local(local) = stmt.kind
&& let Local { pat, init: Some(init), .. } = local
&& let PatKind::Binding(_, _, ident, _) = pat.kind
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
{
// finds use of `_.read(&mut v)`
let mut read_found = false;
let mut visitor = expr_visitor_no_bodies(|expr| {
if let ExprKind::MethodCall(path, [_self, arg], _) = expr.kind
&& let PathSegment { ident: read_or_read_exact, .. } = *path
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
&& let ExprKind::Path(QPath::Resolved(None, inner_path)) = inner.kind
&& let [inner_seg] = inner_path.segments
&& ident.name == inner_seg.ident.name
{
read_found = true;
}
!read_found
});
let next_stmt_span;
if idx == block.stmts.len() - 1 {
// case { .. stmt; expr }
if let Some(e) = block.expr {
visitor.visit_expr(e);
next_stmt_span = e.span;
} else {
return;
}
} else {
// case { .. stmt; stmt; .. }
let next_stmt = &block.stmts[idx + 1];
visitor.visit_stmt(next_stmt);
next_stmt_span = next_stmt.span;
}
drop(visitor);
if read_found && !next_stmt_span.from_expansion() {
let applicability = Applicability::MaybeIncorrect;
match vec_init_kind {
VecInitKind::WithConstCapacity(len) => {
span_lint_and_sugg(
cx,
READ_ZERO_BYTE_VEC,
next_stmt_span,
"reading zero byte data to `Vec`",
"try",
format!("{}.resize({}, 0); {}",
ident.as_str(),
len,
snippet(cx, next_stmt_span, "..")
),
applicability,
);
}
VecInitKind::WithExprCapacity(hir_id) => {
let e = cx.tcx.hir().expect_expr(hir_id);
span_lint_and_sugg(
cx,
READ_ZERO_BYTE_VEC,
next_stmt_span,
"reading zero byte data to `Vec`",
"try",
format!("{}.resize({}, 0); {}",
ident.as_str(),
snippet(cx, e.span, ".."),
snippet(cx, next_stmt_span, "..")
),
applicability,
);
}
_ => {
span_lint(
cx,
READ_ZERO_BYTE_VEC,
next_stmt_span,
"reading zero byte data to `Vec`",
);
}
}
}
}
}
}
}

View file

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

View file

@ -60,7 +60,7 @@ declare_clippy_lint! {
/// let vec = vec![1, 2, 3];
/// let slice = &*vec;
/// ```
#[clippy::version = "1.60.0"]
#[clippy::version = "1.61.0"]
pub DEREF_BY_SLICING,
restriction,
"slicing instead of dereferencing"

View file

@ -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;
/// ```

View file

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

View file

@ -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,11 +42,12 @@ declare_clippy_lint! {
declare_lint_pass!(SameNameMethod => [SAME_NAME_METHOD]);
struct ExistingName {
impl_methods: BTreeMap<Symbol, Span>,
impl_methods: BTreeMap<Symbol, (Span, HirId)>,
trait_methods: BTreeMap<Symbol, Vec<Span>>,
}
impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
#[expect(clippy::too_many_lines)]
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
let mut map = FxHashMap::<Res, ExistingName>::default();
@ -97,10 +98,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 +138,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 +156,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));
}
},
}

View file

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

View file

@ -33,7 +33,7 @@ declare_clippy_lint! {
/// source: &'src str,
/// }
/// ```
#[clippy::version = "1.59.0"]
#[clippy::version = "1.60.0"]
pub SINGLE_CHAR_LIFETIME_NAMES,
restriction,
"warns against single-character lifetime names"

View file

@ -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];
/// ```

View file

@ -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,12 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
/// std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
/// ```
/// could be written as
///
/// Use instead:
/// ```rust
/// let _ = &"Hello World!"[6..11];
/// &"Hello World!"[6..11];
/// ```
#[clippy::version = "1.50.0"]
pub STRING_FROM_UTF8_AS_BYTES,

View file

@ -29,8 +29,7 @@ declare_clippy_lint! {
/// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
/// ```
///
/// Could be written as:
///
/// Use instead:
/// ```rust
/// pub fn foo<T>(t: T) where T: Copy + Clone {}
/// ```

View file

@ -61,12 +61,7 @@ pub(super) fn check<'tcx>(
"transmute from an integer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
diag.span_suggestion(
e.span,
"try",
arg.as_ty(&to_ty.to_string()),
Applicability::Unspecified,
);
diag.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()), Applicability::Unspecified);
}
},
);

View file

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

View file

@ -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.
/// }

View file

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

View file

@ -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<String> = 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<String> = ["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<String> = 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<u64> = None),
/// Lint: DOC_MARKDOWN.
///
/// The list of words this lint should not consider as identifiers needing ticks
(doc_valid_idents: Vec<String> = [
"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<String> = 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::<TryConf>(&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<String>, default: &[&str]) {
if vec.contains(&"..".to_string()) {
vec.extend(default.iter().map(ToString::to_string));
}
}
const SEPARATOR_WIDTH: usize = 4;

View file

@ -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;
/// ```
@ -672,8 +664,8 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
if let ExprKind::Call(func, and_then_args) = expr.kind;
if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
if and_then_args.len() == 5;
if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
let body = cx.tcx.hir().body(*body_id);
if let ExprKind::Closure { body, .. } = &and_then_args[4].kind;
let body = cx.tcx.hir().body(*body);
let only_expr = peel_blocks_with_stmt(&body.value);
if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
then {

View file

@ -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<LintMetadata>,
applicability_info: FxHashMap<String, ApplicabilityInfo>,
config: Vec<ClippyConfiguration>,
clippy_project_root: PathBuf,
}
impl MetadataCollector {
@ -188,6 +191,7 @@ impl MetadataCollector {
lints: BinaryHeap::<LintMetadata>::default(),
applicability_info: FxHashMap::<String, ApplicabilityInfo>::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::<Vec<_>>();
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!(
"<details>\
<summary>Produces</summary>\n\
\n\
```text\n\
{}\n\
```\n\
</details>",
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::<Vec<serde_json::Value>>();
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<St
/// ```
///
/// Would result in `Hello world!\n=^.^=\n`
///
/// ---
///
fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
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<String> {
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>) -> 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<String> {
docs.push_str(line);
}
}
Some(docs)
docs
}
fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
@ -762,9 +954,9 @@ fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hi
}
fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
if let ExprKind::Closure { body, .. } = closure_expr.kind {
let mut scanner = IsMultiSpanScanner::new(cx);
intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
return scanner.is_multi_part();
} else if let Some(local) = get_parent_local(cx, closure_expr) {
if let Some(local_init) = local.init {

View file

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

View file

@ -26,13 +26,18 @@ declare_clippy_lint! {
/// still around.
///
/// ### Example
/// ```rust,ignore
/// // Bad
/// ```rust
/// use std::cmp::Ordering::*;
/// foo(Less);
///
/// // Good
/// # fn foo(_: std::cmp::Ordering) {}
/// foo(Less);
/// ```
///
/// 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();

View file

@ -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,13 @@ declare_clippy_lint! {
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// // Bad
/// writeln!(buf, "");
/// ```
///
/// // Good
/// Use instead:
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf);
/// ```
#[clippy::version = "pre 1.29.0"]
@ -204,10 +208,14 @@ declare_clippy_lint! {
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// // Bad
/// write!(buf, "Hello {}!\n", name);
/// ```
///
/// // Good
/// Use instead:
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// writeln!(buf, "Hello {}!", name);
/// ```
#[clippy::version = "pre 1.29.0"]
@ -233,10 +241,13 @@ declare_clippy_lint! {
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// // Bad
/// writeln!(buf, "{}", "foo");
/// ```
///
/// // Good
/// Use instead:
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "foo");
/// ```
#[clippy::version = "pre 1.29.0"]

View file

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