Merge commit 'd7b5cbf065' into clippyup
This commit is contained in:
parent
bd071bf5b2
commit
f8f9d01c2a
199 changed files with 4158 additions and 1931 deletions
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use std::cmp::Ordering;
|
||||
/// # fn a() {}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// dbg!(true)
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// true
|
||||
/// ```
|
||||
#[clippy::version = "1.34.0"]
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ declare_clippy_lint! {
|
|||
/// struct BlackForestCake;
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// mod cake {
|
||||
/// struct BlackForest;
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ declare_clippy_lint! {
|
|||
/// if x >= y + 1 {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// # let y = 1;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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")?;
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
/// // ...
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(_)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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, ..) => (),
|
||||
/// _ => (),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
87
clippy_lints/src/needless_parens_on_range_literals.rs
Normal file
87
clippy_lints/src/needless_parens_on_range_literals.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
142
clippy_lints/src/read_zero_byte_vec.rs
Normal file
142
clippy_lints/src/read_zero_byte_vec.rs
Normal 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`",
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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}");
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
/// }
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue