Auto merge of #111255 - flip1995:clippyup, r=Manishearth
Update Clippy r? `@Manishearth`
This commit is contained in:
commit
5889ecd14f
152 changed files with 2226 additions and 440 deletions
|
|
@ -2,7 +2,8 @@ use ast::AttrStyle;
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -51,6 +52,7 @@ impl LateLintPass<'_> for AllowAttribute {
|
|||
// Separate each crate's features.
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), attr.span);
|
||||
if cx.tcx.features().lint_reasons;
|
||||
if let AttrStyle::Outer = attr.style;
|
||||
if let Some(ident) = attr.ident();
|
||||
|
|
|
|||
|
|
@ -638,7 +638,7 @@ declare_clippy_lint! {
|
|||
#[clippy::version = "1.66.0"]
|
||||
pub AS_PTR_CAST_MUT,
|
||||
nursery,
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
|
|||
|
|
@ -141,9 +141,9 @@ fn lint_unnecessary_cast(
|
|||
|
||||
fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
|
||||
match expr.kind {
|
||||
ExprKind::Lit(ref lit) => Some(lit),
|
||||
ExprKind::Lit(lit) => Some(lit),
|
||||
ExprKind::Unary(UnOp::Neg, e) => {
|
||||
if let ExprKind::Lit(ref lit) = e.kind {
|
||||
if let ExprKind::Lit(lit) = e.kind {
|
||||
Some(lit)
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &De
|
|||
conds,
|
||||
|e| hash_expr(cx, e),
|
||||
|lhs, rhs| {
|
||||
// Ignore eq_expr side effects iff one of the expressin kind is a method call
|
||||
// Ignore eq_expr side effects iff one of the expression kind is a method call
|
||||
// and the caller is not a mutable, including inner mutable type.
|
||||
if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
|
||||
if method_caller_is_mutable(cx, caller, ignored_ty_ids) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::dbg_macro::DBG_MACRO_INFO,
|
||||
crate::default::DEFAULT_TRAIT_ACCESS_INFO,
|
||||
crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO,
|
||||
crate::default_constructed_unit_structs::DEFAULT_CONSTRUCTED_UNIT_STRUCTS_INFO,
|
||||
crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO,
|
||||
crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO,
|
||||
crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO,
|
||||
|
|
@ -249,6 +250,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::loops::MANUAL_FIND_INFO,
|
||||
crate::loops::MANUAL_FLATTEN_INFO,
|
||||
crate::loops::MANUAL_MEMCPY_INFO,
|
||||
crate::loops::MANUAL_WHILE_LET_SOME_INFO,
|
||||
crate::loops::MISSING_SPIN_LOOP_INFO,
|
||||
crate::loops::MUT_RANGE_BOUND_INFO,
|
||||
crate::loops::NEEDLESS_RANGE_LOOP_INFO,
|
||||
|
|
@ -445,6 +447,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
|
||||
crate::needless_bool::BOOL_COMPARISON_INFO,
|
||||
crate::needless_bool::NEEDLESS_BOOL_INFO,
|
||||
crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
|
||||
crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
|
||||
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||
|
|
|
|||
72
clippy_lints/src/default_constructed_unit_structs.rs
Normal file
72
clippy_lints/src/default_constructed_unit_structs.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, match_def_path, paths};
|
||||
use hir::{def::Res, ExprKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for construction on unit struct using `default`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This adds code complexity and an unnecessary function call.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # use std::marker::PhantomData;
|
||||
/// #[derive(Default)]
|
||||
/// struct S<T> {
|
||||
/// _marker: PhantomData<T>
|
||||
/// }
|
||||
///
|
||||
/// let _: S<i32> = S {
|
||||
/// _marker: PhantomData::default()
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # use std::marker::PhantomData;
|
||||
/// struct S<T> {
|
||||
/// _marker: PhantomData<T>
|
||||
/// }
|
||||
///
|
||||
/// let _: S<i32> = S {
|
||||
/// _marker: PhantomData
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
complexity,
|
||||
"unit structs can be contructed without calling `default`"
|
||||
}
|
||||
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
|
||||
|
||||
impl LateLintPass<'_> for DefaultConstructedUnitStructs {
|
||||
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if_chain!(
|
||||
// make sure we have a call to `Default::default`
|
||||
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind;
|
||||
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
|
||||
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
|
||||
// make sure we have a struct with no fields (unit struct)
|
||||
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if def.is_struct();
|
||||
if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant();
|
||||
if !var.is_field_list_non_exhaustive() && !is_from_proc_macro(cx, expr);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
expr.span.with_lo(qpath.qself_span().hi()),
|
||||
"use of `default` to create a unit struct",
|
||||
"remove this call to `default`",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -92,10 +92,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
|||
if trait_item.id.owner_id.def_id == fn_def_id {
|
||||
// be sure we have `self` parameter in this function
|
||||
if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
|
||||
trait_self_ty = Some(
|
||||
TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id())
|
||||
.self_ty(),
|
||||
);
|
||||
trait_self_ty =
|
||||
Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
|
|||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if_chain! {
|
||||
if let ty::Float(fty) = *ty.kind();
|
||||
if let hir::ExprKind::Lit(ref lit) = expr.kind;
|
||||
if let hir::ExprKind::Lit(lit) = expr.kind;
|
||||
if let LitKind::Float(sym, lit_float_ty) = lit.node;
|
||||
then {
|
||||
let sym_str = sym.as_str();
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ use clippy_utils::consts::{
|
|||
constant, constant_simple, Constant,
|
||||
Constant::{Int, F32, F64},
|
||||
};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_sugg, eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate,
|
||||
numeric_literal, peel_blocks, sugg,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
|
||||
|
|
@ -677,7 +678,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
{
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
if float_type == ast::LitFloatType::Unsuffixed;
|
||||
then {
|
||||
|
|
@ -703,7 +704,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
{
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
|
||||
if let ExprKind::Lit(literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
if float_type == ast::LitFloatType::Unsuffixed;
|
||||
then {
|
||||
|
|
@ -730,7 +731,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// All of these operations are currently not const.
|
||||
// All of these operations are currently not const and are in std.
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -738,7 +739,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
|||
if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
|
||||
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
||||
|
||||
if recv_ty.is_floating_point() {
|
||||
if recv_ty.is_floating_point() && !is_no_std_crate(cx) {
|
||||
match path.ident.name.as_str() {
|
||||
"ln" => check_ln1p(cx, expr, receiver),
|
||||
"log" => check_log_base(cx, expr, receiver, args),
|
||||
|
|
@ -749,10 +750,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
check_expm1(cx, expr);
|
||||
check_mul_add(cx, expr);
|
||||
check_custom_abs(cx, expr);
|
||||
check_log_division(cx, expr);
|
||||
if !is_no_std_crate(cx) {
|
||||
check_expm1(cx, expr);
|
||||
check_mul_add(cx, expr);
|
||||
check_custom_abs(cx, expr);
|
||||
check_log_division(cx, expr);
|
||||
}
|
||||
check_radians(cx, expr);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
|||
);
|
||||
}
|
||||
|
||||
let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty());
|
||||
let message = format!("replace the `Into` implementation with `From<{}>`", middle_trait_ref.self_ty());
|
||||
if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
|
||||
diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
|
|||
};
|
||||
|
||||
// Body must be &(mut) <self_data>.name
|
||||
// self_data is not neccessarilly self, to also lint sub-getters, etc…
|
||||
// self_data is not necessarily self, to also lint sub-getters, etc…
|
||||
|
||||
let block_expr = if_chain! {
|
||||
if let ExprKind::Block(block,_) = body.value.kind;
|
||||
|
|
|
|||
|
|
@ -252,6 +252,11 @@ declare_clippy_lint! {
|
|||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldomly used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
/// `Err`-variant further up the call-stack stores the `Err`-variant in
|
||||
/// question (as library code often does), it itself needs to be at least
|
||||
/// as large, propagating the problem.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
|
|
@ -330,7 +335,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when `impl Trait` is being used in a function's paremeters.
|
||||
/// Lints when `impl Trait` is being used in a function's parameters.
|
||||
/// ### Why is this bad?
|
||||
/// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
|||
if expr1.span.ctxt() == ctxt;
|
||||
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target);
|
||||
if BinOpKind::Add == op1.node;
|
||||
if let ExprKind::Lit(ref lit) = value.kind;
|
||||
if let ExprKind::Lit(lit) = value.kind;
|
||||
if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
|
||||
if block.expr.is_none();
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
|
|||
// Get the variable name
|
||||
let var_name = ares_path.segments[0].ident.name.as_str();
|
||||
match cond_num_val.kind {
|
||||
ExprKind::Lit(ref cond_lit) => {
|
||||
ExprKind::Lit(cond_lit) => {
|
||||
// Check if the constant is zero
|
||||
if let LitKind::Int(0, _) = cond_lit.node {
|
||||
if cx.typeck_results().expr_ty(cond_left).is_signed() {
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
|
|||
return;
|
||||
}
|
||||
// Index is a constant uint.
|
||||
if let Some(..) = constant(cx, cx.typeck_results(), index) {
|
||||
if constant(cx, cx.typeck_results(), index).is_some() {
|
||||
// Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,20 +64,21 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
|
|||
span_lint_and_help(cx, ITEMS_AFTER_TEST_MODULE, test_mod_span.unwrap().with_hi(item.span.hi()), "items were found after the testing module", None, "move the items to before the testing module was defined");
|
||||
}};
|
||||
|
||||
if matches!(item.kind, ItemKind::Mod(_)) {
|
||||
for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) {
|
||||
if_chain! {
|
||||
if attr.has_name(sym::cfg);
|
||||
if let ItemKind::Mod(module) = item.kind && item.span.hi() == module.spans.inner_span.hi() {
|
||||
// Check that it works the same way, the only I way I've found for #10713
|
||||
for attr in cx.tcx.get_attrs(item.owner_id.to_def_id(), sym::cfg) {
|
||||
if_chain! {
|
||||
if attr.has_name(sym::cfg);
|
||||
if let Some(mitems) = attr.meta_item_list();
|
||||
if let [mitem] = &*mitems;
|
||||
if mitem.has_name(sym::test);
|
||||
then {
|
||||
was_test_mod_visited = true;
|
||||
was_test_mod_visited = true;
|
||||
test_mod_span = Some(item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ declare_clippy_lint! {
|
|||
/// It checks for the size of a `Future` created by `async fn` or `async {}`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
|
||||
/// Due to the current [unideal implementation](https://github.com/rust-lang/rust/issues/69826) of `Generator`,
|
||||
/// large size of a `Future` may cause stack overflows.
|
||||
///
|
||||
/// ### Example
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Ex
|
|||
}
|
||||
|
||||
fn is_empty_string(expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
if let LitKind::Str(lit, _) = lit.node {
|
||||
let lit = lit.as_str();
|
||||
return lit.is_empty();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -205,8 +206,13 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
|||
LET_UNDERSCORE_UNTYPED,
|
||||
local.span,
|
||||
"non-binding `let` without a type annotation",
|
||||
None,
|
||||
"consider adding a type annotation or removing the `let` keyword",
|
||||
Some(
|
||||
Span::new(local.pat.span.hi(),
|
||||
local.pat.span.hi() + BytePos(1),
|
||||
local.pat.span.ctxt(),
|
||||
local.pat.span.parent()
|
||||
)),
|
||||
"consider adding a type annotation",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ mod crate_in_macro_def;
|
|||
mod create_dir;
|
||||
mod dbg_macro;
|
||||
mod default;
|
||||
mod default_constructed_unit_structs;
|
||||
mod default_instead_of_iter_empty;
|
||||
mod default_numeric_fallback;
|
||||
mod default_union_representation;
|
||||
|
|
@ -933,7 +934,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
|
||||
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
|
||||
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
|
||||
store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
|
||||
let semicolon_inside_block_ignore_singleline = conf.semicolon_inside_block_ignore_singleline;
|
||||
let semicolon_outside_block_ignore_multiline = conf.semicolon_outside_block_ignore_multiline;
|
||||
store.register_late_pass(move |_| {
|
||||
Box::new(semicolon_block::SemicolonBlock::new(
|
||||
semicolon_inside_block_ignore_singleline,
|
||||
semicolon_outside_block_ignore_multiline,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
|
||||
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
|
||||
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
|
||||
|
|
@ -963,6 +971,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
|
||||
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
|
||||
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
|
||||
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use rustc_span::symbol::sym;
|
|||
use std::fmt::Display;
|
||||
use std::iter::Iterator;
|
||||
|
||||
/// Checks for for loops that sequentially copy items from one slice-like
|
||||
/// Checks for `for` loops that sequentially copy items from one slice-like
|
||||
/// object to another.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
|
|
|
|||
110
clippy_lints/src/loops/manual_while_let_some.rs
Normal file
110
clippy_lints/src/loops/manual_while_let_some.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use clippy_utils::{
|
||||
diagnostics::{multispan_sugg_with_applicability, span_lint_and_then},
|
||||
match_def_path, paths,
|
||||
source::snippet,
|
||||
SpanlessEq,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::MANUAL_WHILE_LET_SOME;
|
||||
|
||||
/// The kind of statement that the `pop()` call appeared in.
|
||||
///
|
||||
/// Depending on whether the value was assigned to a variable or not changes what pattern
|
||||
/// we use for the suggestion.
|
||||
#[derive(Copy, Clone)]
|
||||
enum PopStmt<'hir> {
|
||||
/// `x.pop().unwrap()` was and assigned to a variable.
|
||||
/// The pattern of this local variable will be used and the local statement
|
||||
/// is deleted in the suggestion.
|
||||
Local(&'hir Pat<'hir>),
|
||||
/// `x.pop().unwrap()` appeared in an arbitrary expression and was not assigned to a variable.
|
||||
/// The suggestion will use some placeholder identifier and the `x.pop().unwrap()` expression
|
||||
/// is replaced with that identifier.
|
||||
Anonymous,
|
||||
}
|
||||
|
||||
fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, loop_span: Span, receiver_span: Span) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_WHILE_LET_SOME,
|
||||
pop_span,
|
||||
"you seem to be trying to pop elements from a `Vec` in a loop",
|
||||
|diag| {
|
||||
let (pat, pop_replacement) = match pop_stmt_kind {
|
||||
PopStmt::Local(pat) => (snippet(cx, pat.span, ".."), String::new()),
|
||||
PopStmt::Anonymous => (Cow::Borrowed("element"), "element".into()),
|
||||
};
|
||||
|
||||
let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, ".."));
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"consider using a `while..let` loop",
|
||||
Applicability::MachineApplicable,
|
||||
[(loop_span, loop_replacement), (pop_span, pop_replacement)],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: &[&str]) -> bool {
|
||||
if let ExprKind::MethodCall(..) = expr.kind
|
||||
&& let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||
{
|
||||
match_def_path(cx, id, method)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool {
|
||||
if (match_method_call(cx, expr, &paths::OPTION_UNWRAP) || match_method_call(cx, expr, &paths::OPTION_EXPECT))
|
||||
&& let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind
|
||||
&& match_method_call(cx, unwrap_recv, &paths::VEC_POP)
|
||||
&& let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind
|
||||
{
|
||||
// make sure they're the same `Vec`
|
||||
SpanlessEq::new(cx).eq_expr(pop_recv, is_empty_recv)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn check_local(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& is_vec_pop_unwrap(cx, init, is_empty_recv)
|
||||
{
|
||||
report_lint(cx, stmt.span, PopStmt::Local(local.pat), loop_span, is_empty_recv.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &Expr<'_>, loop_span: Span) {
|
||||
if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind {
|
||||
if let ExprKind::MethodCall(.., args, _) | ExprKind::Call(_, args) = expr.kind {
|
||||
let offending_arg = args
|
||||
.iter()
|
||||
.find_map(|arg| is_vec_pop_unwrap(cx, arg, is_empty_recv).then_some(arg.span));
|
||||
|
||||
if let Some(offending_arg) = offending_arg {
|
||||
report_lint(cx, offending_arg, PopStmt::Anonymous, loop_span, is_empty_recv.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) {
|
||||
if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind
|
||||
&& let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind
|
||||
&& match_method_call(cx, cond, &paths::VEC_IS_EMPTY)
|
||||
&& let ExprKind::Block(body, _) = body.kind
|
||||
&& let Some(stmt) = body.stmts.first()
|
||||
{
|
||||
check_local(cx, stmt, is_empty_recv, loop_span);
|
||||
check_call_arguments(cx, stmt, is_empty_recv, loop_span);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ mod iter_next_loop;
|
|||
mod manual_find;
|
||||
mod manual_flatten;
|
||||
mod manual_memcpy;
|
||||
mod manual_while_let_some;
|
||||
mod missing_spin_loop;
|
||||
mod mut_range_bound;
|
||||
mod needless_range_loop;
|
||||
|
|
@ -575,6 +576,36 @@ declare_clippy_lint! {
|
|||
"manual implementation of `Iterator::find`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
|
||||
/// in the body as a separate operation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such loops can be written in a more idiomatic way by using a while-let loop and directly
|
||||
/// pattern matching on the return value of `Vec::pop()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while !numbers.is_empty() {
|
||||
/// let number = numbers.pop().unwrap();
|
||||
/// // use `number`
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while let Some(number) = numbers.pop() {
|
||||
/// // use `number`
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub MANUAL_WHILE_LET_SOME,
|
||||
style,
|
||||
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Loops => [
|
||||
MANUAL_MEMCPY,
|
||||
MANUAL_FLATTEN,
|
||||
|
|
@ -594,6 +625,7 @@ declare_lint_pass!(Loops => [
|
|||
SINGLE_ELEMENT_LOOP,
|
||||
MISSING_SPIN_LOOP,
|
||||
MANUAL_FIND,
|
||||
MANUAL_WHILE_LET_SOME
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
|
|
@ -640,9 +672,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
|
|||
|
||||
while_let_on_iterator::check(cx, expr);
|
||||
|
||||
if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
|
||||
if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) {
|
||||
while_immutable_condition::check(cx, condition, body);
|
||||
missing_spin_loop::check(cx, condition, body);
|
||||
manual_while_let_some::check(cx, condition, body, span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ fn is_end_eq_array_len<'tcx>(
|
|||
indexed_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref lit) = end.kind;
|
||||
if let ExprKind::Lit(lit) = end.kind;
|
||||
if let ast::LitKind::Int(end_int, _) = lit.node;
|
||||
if let ty::Array(_, arr_len_const) = indexed_ty.kind();
|
||||
if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
|||
};
|
||||
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
|
||||
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
|
||||
// we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block
|
||||
// we show to the user the suggestion without the comments, but when applying the fix, include the comments in the block
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MANUAL_ASSERT,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualLetElse {
|
|||
if source != MatchSource::Normal {
|
||||
return;
|
||||
}
|
||||
// Any other number than two arms doesn't (neccessarily)
|
||||
// Any other number than two arms doesn't (necessarily)
|
||||
// have a trivial mapping to let else.
|
||||
if arms.len() != 2 {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ declare_clippy_lint! {
|
|||
#[clippy::version = "1.64.0"]
|
||||
pub MANUAL_RETAIN,
|
||||
perf,
|
||||
"`retain()` is simpler and the same functionalitys"
|
||||
"`retain()` is simpler and the same functionalities"
|
||||
}
|
||||
|
||||
pub struct ManualRetain {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
|
|||
if arms.len() == 2 {
|
||||
// no guards
|
||||
let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
|
||||
if let ExprKind::Lit(ref lit) = arm_bool.kind {
|
||||
if let ExprKind::Lit(lit) = arm_bool.kind {
|
||||
match lit.node {
|
||||
LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
|
||||
LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
|
||||
|
|
|
|||
|
|
@ -63,8 +63,11 @@ fn find_sugg_for_if_let<'tcx>(
|
|||
// Determine which function should be used, and the type contained by the corresponding
|
||||
// variant.
|
||||
let (good_method, inner_ty) = match check_pat.kind {
|
||||
PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
|
||||
if let PatKind::Wild = sub_pat.kind {
|
||||
PatKind::TupleStruct(ref qpath, args, rest) => {
|
||||
let is_wildcard = matches!(args.first().map(|p| &p.kind), Some(PatKind::Wild));
|
||||
let is_rest = matches!((args, rest.as_opt_usize()), ([], Some(_)));
|
||||
|
||||
if is_wildcard || is_rest {
|
||||
let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
|
||||
let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
|
|
@ -334,7 +337,7 @@ fn find_good_method_for_match<'a>(
|
|||
};
|
||||
|
||||
match body_node_pair {
|
||||
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||
(LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
|
||||
(LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub(super) fn check(
|
|||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods);
|
||||
if let hir::ExprKind::Lit(ref lit) = info.other.kind;
|
||||
if let hir::ExprKind::Lit(lit) = info.other.kind;
|
||||
if let ast::LitKind::Char(c) = lit.node;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
|
|||
if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
|
||||
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
|
||||
= higher::Range::hir(index_expr);
|
||||
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
|
||||
if let hir::ExprKind::Lit(start_lit) = &start_expr.kind;
|
||||
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
|
|||
// Only proceed if this is a call on some object of type std::fs::OpenOptions
|
||||
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
|
||||
let argument_option = match arguments[0].kind {
|
||||
ExprKind::Lit(ref span) => {
|
||||
ExprKind::Lit(span) => {
|
||||
if let Spanned {
|
||||
node: LitKind::Bool(lit),
|
||||
..
|
||||
} = *span
|
||||
} = span
|
||||
{
|
||||
if *lit { Argument::True } else { Argument::False }
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).subst_identity(), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = arg.kind;
|
||||
if let ExprKind::Lit(lit) = arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>)
|
|||
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
|
||||
// check if argument of `SeekFrom::Current` is `0`
|
||||
if args.len() == 1 &&
|
||||
let ExprKind::Lit(ref lit) = args[0].kind &&
|
||||
let ExprKind::Lit(lit) = args[0].kind &&
|
||||
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
|
|||
let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
|
||||
match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
|
||||
args1.len() == 1 &&
|
||||
let ExprKind::Lit(ref lit) = args1[0].kind &&
|
||||
let ExprKind::Lit(lit) = args1[0].kind &&
|
||||
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
|
||||
{
|
||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub(super) fn check(
|
|||
}
|
||||
|
||||
// Check if the first argument to .fold is a suitable literal
|
||||
if let hir::ExprKind::Lit(ref lit) = init.kind {
|
||||
if let hir::ExprKind::Lit(lit) = init.kind {
|
||||
match lit.node {
|
||||
ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
|
||||
ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ declare_clippy_lint! {
|
|||
/// Checks if a provided method is used implicitly by a trait
|
||||
/// implementation. A usage example would be a wrapper where every method
|
||||
/// should perform some operation before delegating to the inner type's
|
||||
/// implemenation.
|
||||
/// implementation.
|
||||
///
|
||||
/// This lint should typically be enabled on a specific trait `impl` item
|
||||
/// rather than globally.
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
//! This lint is **warn** by default
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt};
|
||||
use clippy_utils::{
|
||||
get_parent_node, is_else_clause, is_expn_of, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
|
||||
};
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Node, UnOp};
|
||||
|
|
@ -77,7 +79,39 @@ declare_clippy_lint! {
|
|||
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions of the form `if c { x = true } else { x = false }`
|
||||
/// (or vice versa) and suggest assigning the variable directly from the
|
||||
/// condition.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Redundant code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// # fn must_keep(x: i32, y: i32) -> bool { x == y }
|
||||
/// # let x = 32; let y = 10;
|
||||
/// # let mut skip: bool;
|
||||
/// if must_keep(x, y) {
|
||||
/// skip = false;
|
||||
/// } else {
|
||||
/// skip = true;
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// # fn must_keep(x: i32, y: i32) -> bool { x == y }
|
||||
/// # let x = 32; let y = 10;
|
||||
/// # let mut skip: bool;
|
||||
/// skip = !must_keep(x, y);
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub NEEDLESS_BOOL_ASSIGN,
|
||||
complexity,
|
||||
"setting the same boolean variable in both branches of an if-statement"
|
||||
}
|
||||
declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL, NEEDLESS_BOOL_ASSIGN]);
|
||||
|
||||
fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
|
||||
let mut inner = e;
|
||||
|
|
@ -173,6 +207,29 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
|||
_ => (),
|
||||
}
|
||||
}
|
||||
if let Some((lhs_a, a)) = fetch_assign(then) &&
|
||||
let Some((lhs_b, b)) = fetch_assign(r#else) &&
|
||||
SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) &&
|
||||
span_extract_comment(cx.tcx.sess.source_map(), e.span).is_empty()
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
|
||||
let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability);
|
||||
let sugg = if a == b {
|
||||
format!("{cond}; {lhs} = {a:?};")
|
||||
} else {
|
||||
format!("{lhs} = {};", if a { cond } else { !cond })
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_BOOL_ASSIGN,
|
||||
e.span,
|
||||
"this if-then-else expression assigns a bool literal",
|
||||
"you can reduce it to",
|
||||
sugg,
|
||||
applicability
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -369,10 +426,18 @@ fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> {
|
|||
}
|
||||
|
||||
fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
|
||||
if let ExprKind::Lit(ref lit_ptr) = peel_blocks(expr).kind {
|
||||
if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind {
|
||||
if let LitKind::Bool(value) = lit_ptr.node {
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)> {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = peel_blocks_with_stmt(expr).kind {
|
||||
fetch_bool_expr(rhs).map(|b| (lhs, b))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,14 +49,14 @@ fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
|
|||
|
||||
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
|
||||
if is_start &&
|
||||
let ExprKind::Lit(ref literal) = e.kind &&
|
||||
let ExprKind::Lit(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;
|
||||
if let ExprKind::Lit(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
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply {
|
|||
|
||||
fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref l) = lit.kind;
|
||||
if let ExprKind::Lit(l) = lit.kind;
|
||||
if consts::lit_to_mir_constant(&l.node, cx.typeck_results().expr_ty_opt(lit)) == Constant::Int(1);
|
||||
if cx.typeck_results().expr_ty(exp).is_integral();
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ impl ArithmeticSideEffects {
|
|||
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
|
||||
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
||||
let actual = peel_hir_expr_unary(expr).0;
|
||||
if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
|
||||
if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
|
||||
return Some(n)
|
||||
}
|
||||
if let Some((Constant::Int(n), _)) = constant(cx, cx.typeck_results(), expr) {
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
|
|||
.allow_invalid_utf8(!utf8)
|
||||
.build();
|
||||
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
if let LitKind::Str(ref r, style) = lit.node {
|
||||
let r = r.as_str();
|
||||
let offset = if let StrStyle::Raw(n) = style { 2 + n } else { 1 };
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -64,7 +64,78 @@ declare_clippy_lint! {
|
|||
restriction,
|
||||
"add a semicolon outside the block"
|
||||
}
|
||||
declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
|
||||
impl_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SemicolonBlock {
|
||||
semicolon_inside_block_ignore_singleline: bool,
|
||||
semicolon_outside_block_ignore_multiline: bool,
|
||||
}
|
||||
|
||||
impl SemicolonBlock {
|
||||
pub fn new(semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool) -> Self {
|
||||
Self {
|
||||
semicolon_inside_block_ignore_singleline,
|
||||
semicolon_outside_block_ignore_multiline,
|
||||
}
|
||||
}
|
||||
|
||||
fn semicolon_inside_block(self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
|
||||
let insert_span = tail.span.source_callsite().shrink_to_hi();
|
||||
let remove_span = semi_span.with_lo(block.span.hi());
|
||||
|
||||
if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SEMICOLON_INSIDE_BLOCK,
|
||||
semi_span,
|
||||
"consider moving the `;` inside the block for consistent formatting",
|
||||
|diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"put the `;` here",
|
||||
Applicability::MachineApplicable,
|
||||
[(remove_span, String::new()), (insert_span, ";".to_owned())],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn semicolon_outside_block(
|
||||
self,
|
||||
cx: &LateContext<'_>,
|
||||
block: &Block<'_>,
|
||||
tail_stmt_expr: &Expr<'_>,
|
||||
semi_span: Span,
|
||||
) {
|
||||
let insert_span = block.span.with_lo(block.span.hi());
|
||||
// account for macro calls
|
||||
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
|
||||
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
|
||||
|
||||
if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SEMICOLON_OUTSIDE_BLOCK,
|
||||
block.span,
|
||||
"consider moving the `;` outside the block for consistent formatting",
|
||||
|diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"put the `;` here",
|
||||
Applicability::MachineApplicable,
|
||||
[(remove_span, String::new()), (insert_span, ";".to_owned())],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for SemicolonBlock {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
|
|
@ -83,55 +154,23 @@ impl LateLintPass<'_> for SemicolonBlock {
|
|||
span,
|
||||
..
|
||||
} = stmt else { return };
|
||||
semicolon_outside_block(cx, block, expr, span);
|
||||
self.semicolon_outside_block(cx, block, expr, span);
|
||||
},
|
||||
StmtKind::Semi(Expr {
|
||||
kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _),
|
||||
..
|
||||
}) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span),
|
||||
}) if !block.span.from_expansion() => {
|
||||
self.semicolon_inside_block(cx, block, tail, stmt.span);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
|
||||
let insert_span = tail.span.source_callsite().shrink_to_hi();
|
||||
let remove_span = semi_span.with_lo(block.span.hi());
|
||||
fn get_line(cx: &LateContext<'_>, span: Span) -> Option<usize> {
|
||||
if let Ok(line) = cx.sess().source_map().lookup_line(span.lo()) {
|
||||
return Some(line.line);
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SEMICOLON_INSIDE_BLOCK,
|
||||
semi_span,
|
||||
"consider moving the `;` inside the block for consistent formatting",
|
||||
|diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"put the `;` here",
|
||||
Applicability::MachineApplicable,
|
||||
[(remove_span, String::new()), (insert_span, ";".to_owned())],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) {
|
||||
let insert_span = block.span.with_lo(block.span.hi());
|
||||
// account for macro calls
|
||||
let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
|
||||
let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SEMICOLON_OUTSIDE_BLOCK,
|
||||
block.span,
|
||||
"consider moving the `;` outside the block for consistent formatting",
|
||||
|diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"put the `;` here",
|
||||
Applicability::MachineApplicable,
|
||||
[(remove_span, String::new()), (insert_span, ";".to_owned())],
|
||||
);
|
||||
},
|
||||
);
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for Shadow {
|
|||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
let PatKind::Binding(_, id, ident, _) = pat.kind else { return };
|
||||
|
||||
if pat.span.desugaring_kind().is_some() {
|
||||
if pat.span.desugaring_kind().is_some() || pat.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ enum InitializationType<'tcx> {
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
|
||||
// Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)`
|
||||
if_chain! {
|
||||
if let ExprKind::Assign(left, right, _) = expr.kind;
|
||||
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
}
|
||||
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.sess(), e.span);
|
||||
if let ExprKind::MethodCall(path, receiver, ..) = &e.kind;
|
||||
if path.ident.name == sym!(as_bytes);
|
||||
if let ExprKind::Lit(lit) = &receiver.kind;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_
|
|||
if let Some(last_field) = data.fields().last();
|
||||
if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind;
|
||||
|
||||
// Then check if that that array zero-sized
|
||||
// Then check if that array is zero-sized
|
||||
let length = Const::from_anon_const(cx.tcx, length.def_id);
|
||||
let length = length.try_eval_target_usize(cx.tcx, cx.param_env);
|
||||
if let Some(length) = length;
|
||||
|
|
|
|||
|
|
@ -90,8 +90,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ### Why is this bad?
|
||||
/// `Option<_>` represents an optional value. `Option<Option<_>>`
|
||||
/// represents an optional optional value which is logically the same thing as an optional
|
||||
/// value but has an unneeded extra level of wrapping.
|
||||
/// represents an optional value which itself wraps an optional. This is logically the
|
||||
/// same thing as an optional value but has an unneeded extra level of wrapping.
|
||||
///
|
||||
/// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
|
||||
/// consider a custom `enum` instead, with clear names for each case.
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_
|
|||
|
||||
impl LateLintPass<'_> for Unicode {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Lit(ref lit) = expr.kind {
|
||||
if let ExprKind::Lit(lit) = expr.kind {
|
||||
if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
|
||||
check_str(cx, lit.span, expr.hir_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ impl LateLintPass<'_> for UnnecessaryBoxReturns {
|
|||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::ImplItem<'_>) {
|
||||
// Ignore implementations of traits, because the lint should be on the
|
||||
// trait, not on the implmentation of it.
|
||||
// trait, not on the implementation of it.
|
||||
let Node::Item(parent) = cx.tcx.hir().get_parent(item.hir_id()) else { return };
|
||||
let ItemKind::Impl(parent) = parent.kind else { return };
|
||||
if parent.of_trait.is_some() {
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
|
||||
if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
|
||||
if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) {
|
||||
bind!(self, condition, body);
|
||||
chain!(
|
||||
self,
|
||||
|
|
@ -566,7 +566,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
ExprKind::OffsetOf(container, ref fields) => {
|
||||
bind!(self, container, fields);
|
||||
kind!("OffsetOf({container}, {fields})");
|
||||
}
|
||||
},
|
||||
ExprKind::Struct(qpath, fields, base) => {
|
||||
bind!(self, qpath, fields);
|
||||
opt_bind!(self, base);
|
||||
|
|
|
|||
|
|
@ -277,6 +277,14 @@ define_Conf! {
|
|||
/// `".."` 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 configuration will replace the default value.
|
||||
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
|
||||
/// Lint: SEMICOLON_INSIDE_BLOCK.
|
||||
///
|
||||
/// Whether to lint only if it's multiline.
|
||||
(semicolon_inside_block_ignore_singleline: bool = false),
|
||||
/// Lint: SEMICOLON_OUTSIDE_BLOCK.
|
||||
///
|
||||
/// Whether to lint only if it's singleline.
|
||||
(semicolon_outside_block_ignore_multiline: bool = false),
|
||||
/// Lint: DOC_MARKDOWN.
|
||||
///
|
||||
/// The list of words this lint should not consider as identifiers needing ticks. The value
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue