Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2025-11-15 00:37:39 +01:00
commit 03cd6b6dd8
139 changed files with 4879 additions and 3506 deletions

View file

@ -20,15 +20,15 @@ body:
label: Reproducer
description: Please provide the code and steps to reproduce the bug
value: |
I tried this code:
Code:
```rust
<code>
```
I expected to see this happen:
Current output:
Instead, this happened:
Desired output:
- type: textarea
id: version
attributes:

View file

@ -25,8 +25,6 @@ jobs:
host: i686-unknown-linux-gnu
- os: windows-latest
host: x86_64-pc-windows-msvc
- os: macos-13
host: x86_64-apple-darwin
- os: macos-latest
host: aarch64-apple-darwin

View file

@ -157,18 +157,19 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
let path = "clippy_lints/src/lib.rs";
let mut lib_rs = fs::read_to_string(path).context("reading")?;
let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" };
let lint_pass = lint.pass;
let (comment, ctor_arg) = if lint.pass == Pass::Late {
("// add late passes here", "_")
} else {
("// add early passes here", "")
};
let comment_start = lib_rs.find(comment).expect("Couldn't find comment");
let module_name = lint.name;
let camel_name = to_camel_case(lint.name);
let new_lint = if enable_msrv {
format!(
"store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ",
)
format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",)
} else {
format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",)
format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",)
};
lib_rs.insert_str(comment_start, &new_lint);

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::higher::has_let_expr;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{eq_expr_value, sym};
@ -415,19 +415,20 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio
BinOpKind::Ge => Some(" < "),
_ => None,
}
.and_then(|op| {
let lhs_snippet = lhs.span.get_source_text(cx)?;
let rhs_snippet = rhs.span.get_source_text(cx)?;
.map(|op| {
let mut app = Applicability::MachineApplicable;
let (lhs_snippet, _) = snippet_with_context(cx, lhs.span, SyntaxContext::root(), "", &mut app);
let (rhs_snippet, _) = snippet_with_context(cx, rhs.span, SyntaxContext::root(), "", &mut app);
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')'))
&& let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node)
{
// e.g. `(a as u64) < b`. Without the parens the `<` is
// interpreted as a start of generic arguments for `u64`
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
return format!("({lhs_snippet}){op}{rhs_snippet}");
}
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
format!("{lhs_snippet}{op}{rhs_snippet}")
})
},
ExprKind::MethodCall(path, receiver, args, _) => {

View file

@ -2,14 +2,13 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::{
contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym,
as_some_expr, contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context,
is_none_expr, peel_blocks, sym,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -70,11 +69,10 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
}) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
&& let Some(then_arg) = as_some_expr(cx, then_expr)
&& !expr.span.from_expansion()
&& !then_expr.span.from_expansion()
&& then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
&& is_none_expr(cx, peel_blocks(els))
&& !is_else_clause(cx.tcx, expr)
&& !is_in_const_context(cx)
&& self.msrv.meets(cx, msrvs::BOOL_THEN)

View file

@ -1,14 +1,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::Msrv;
use clippy_utils::{is_in_const_context, is_in_test};
use clippy_utils::{is_in_const_context, is_in_test, sym};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::{CrateNum, DefId};
use rustc_span::{ExpnKind, Span, sym};
use rustc_span::{ExpnKind, Span};
declare_clippy_lint! {
/// ### What it does
@ -77,11 +77,36 @@ enum Availability {
Since(RustcVersion),
}
/// All known std crates containing a stability attribute.
struct StdCrates([Option<CrateNum>; 6]);
impl StdCrates {
fn new(tcx: TyCtxt<'_>) -> Self {
let mut res = Self([None; _]);
for &krate in tcx.crates(()) {
// FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date.
match tcx.crate_name(krate) {
sym::alloc => res.0[0] = Some(krate),
sym::core => res.0[1] = Some(krate),
sym::core_arch => res.0[2] = Some(krate),
sym::proc_macro => res.0[3] = Some(krate),
sym::std => res.0[4] = Some(krate),
sym::std_detect => res.0[5] = Some(krate),
_ => {},
}
}
res
}
fn contains(&self, krate: CrateNum) -> bool {
self.0.contains(&Some(krate))
}
}
pub struct IncompatibleMsrv {
msrv: Msrv,
availability_cache: FxHashMap<(DefId, bool), Availability>,
check_in_tests: bool,
core_crate: Option<CrateNum>,
std_crates: StdCrates,
// The most recently called path. Used to skip checking the path after it's
// been checked when visiting the call expression.
@ -96,11 +121,7 @@ impl IncompatibleMsrv {
msrv: conf.msrv,
availability_cache: FxHashMap::default(),
check_in_tests: conf.check_incompatible_msrv_in_tests,
core_crate: tcx
.crates(())
.iter()
.find(|krate| tcx.crate_name(**krate) == sym::core)
.copied(),
std_crates: StdCrates::new(tcx),
called_path: None,
}
}
@ -152,21 +173,24 @@ impl IncompatibleMsrv {
node: HirId,
span: Span,
) {
if def_id.is_local() {
// We don't check local items since their MSRV is supposed to always be valid.
if !self.std_crates.contains(def_id.krate) {
// No stability attributes to lookup for these items.
return;
}
let expn_data = span.ctxt().outer_expn_data();
if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind {
// Desugared expressions get to cheat and stability is ignored.
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
return;
}
// Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
// macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
// might use recent functions or methods. Compiling with an older compiler would not use those.
if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate {
return;
// Use `from_expansion` to fast-path the common case.
if span.from_expansion() {
let expn = span.ctxt().outer_expn_data();
match expn.kind {
// FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the
// current MSRV. Note that nested expansions need to be handled as well.
ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return,
ExpnKind::Macro(..) if expn.macro_def_id.is_some_and(|did| self.std_crates.contains(did.krate)) => {
return;
},
// All other expansions share the target's MSRV.
// FIXME(@Jarcho): What should we do about version dependant macros from external crates?
_ => {},
}
}
if (self.check_in_tests || !is_in_test(cx.tcx, node))

View file

@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_config::types::InherentImplLintScope;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fulfill_or_allowed;
use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_in_cfg_test};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
use rustc_hir::{Item, ItemKind, Node};
@ -100,7 +100,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
},
InherentImplLintScope::Crate => Criterion::Crate,
};
match type_map.entry((impl_ty, criterion)) {
let is_test = is_cfg_test(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id);
match type_map.entry((impl_ty, criterion, is_test)) {
Entry::Vacant(e) => {
// Store the id for the first impl block of this type. The span is retrieved lazily.
e.insert(IdOrSpan::Id(impl_id));

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::snippet;
use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::is_local_used;
use rustc_errors::Applicability;
use rustc_hir as hir;
@ -59,80 +59,88 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
for [stmt, next] in block.stmts.array_windows::<2>() {
if let hir::StmtKind::Let(local) = stmt.kind
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
&& let hir::StmtKind::Expr(if_) = next.kind
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
&& !is_local_used(cx, cond, canonical_id)
&& let hir::ExprKind::Block(then, _) = then.kind
&& let Some(value) = check_assign(cx, canonical_id, then)
&& !is_local_used(cx, value, canonical_id)
{
let span = stmt.span.to(if_.span);
let has_interior_mutability = !cx
.typeck_results()
.node_type(canonical_id)
.is_freeze(cx.tcx, cx.typing_env());
if has_interior_mutability {
return;
}
let (default_multi_stmts, default) = if let Some(else_) = else_ {
if let hir::ExprKind::Block(else_, _) = else_.kind {
if let Some(default) = check_assign(cx, canonical_id, else_) {
(else_.stmts.len() > 1, default)
} else if let Some(default) = local.init {
(true, default)
} else {
continue;
}
} else {
continue;
}
} else if let Some(default) = local.init {
(false, default)
} else {
continue;
};
let mutability = match mode {
BindingMode(_, Mutability::Mut) => "<mut> ",
_ => "",
};
// FIXME: this should not suggest `mut` if we can detect that the variable is not
// use mutably after the `if`
let sug = format!(
"let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
name=ident.name,
cond=snippet(cx, cond.span, "_"),
then=if then.stmts.len() > 1 { " ..;" } else { "" },
else=if default_multi_stmts { " ..;" } else { "" },
value=snippet(cx, value.span, "<value>"),
default=snippet(cx, default.span, "<default>"),
);
span_lint_hir_and_then(
cx,
USELESS_LET_IF_SEQ,
local.hir_id,
span,
"`if _ { .. } else { .. }` is an expression",
|diag| {
diag.span_suggestion(
span,
"it is more idiomatic to write",
sug,
Applicability::HasPlaceholders,
);
if !mutability.is_empty() {
diag.note("you might not need `mut` at all");
}
},
);
if let hir::StmtKind::Expr(if_) = next.kind {
check_block_inner(cx, stmt, if_);
}
}
if let Some(expr) = block.expr
&& let Some(stmt) = block.stmts.last()
{
check_block_inner(cx, stmt, expr);
}
}
}
fn check_block_inner<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'tcx>, if_: &'tcx hir::Expr<'tcx>) {
if let hir::StmtKind::Let(local) = stmt.kind
&& let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
&& let hir::ExprKind::If(cond, then, else_) = if_.kind
&& !is_local_used(cx, cond, canonical_id)
&& let hir::ExprKind::Block(then, _) = then.kind
&& let Some(value) = check_assign(cx, canonical_id, then)
&& !is_local_used(cx, value, canonical_id)
{
let span = stmt.span.to(if_.span);
let has_interior_mutability = !cx
.typeck_results()
.node_type(canonical_id)
.is_freeze(cx.tcx, cx.typing_env());
if has_interior_mutability {
return;
}
let (default_multi_stmts, default) = if let Some(else_) = else_ {
if let hir::ExprKind::Block(else_, _) = else_.kind {
if let Some(default) = check_assign(cx, canonical_id, else_) {
(else_.stmts.len() > 1, default)
} else if let Some(default) = local.init {
(true, default)
} else {
return;
}
} else {
return;
}
} else if let Some(default) = local.init {
(false, default)
} else {
return;
};
let mutability = match mode {
BindingMode(_, Mutability::Mut) => "<mut> ",
_ => "",
};
// FIXME: this should not suggest `mut` if we can detect that the variable is not
// use mutably after the `if`
let mut applicability = Applicability::HasPlaceholders;
let (cond_snip, _) = snippet_with_context(cx, cond.span, if_.span.ctxt(), "_", &mut applicability);
let (value_snip, _) = snippet_with_context(cx, value.span, if_.span.ctxt(), "<value>", &mut applicability);
let (default_snip, _) =
snippet_with_context(cx, default.span, if_.span.ctxt(), "<default>", &mut applicability);
let sug = format!(
"let {mutability}{name} = if {cond_snip} {{{then} {value_snip} }} else {{{else} {default_snip} }};",
name=ident.name,
then=if then.stmts.len() > 1 { " ..;" } else { "" },
else=if default_multi_stmts { " ..;" } else { "" },
);
span_lint_hir_and_then(
cx,
USELESS_LET_IF_SEQ,
local.hir_id,
span,
"`if _ { .. } else { .. }` is an expression",
|diag| {
diag.span_suggestion(span, "it is more idiomatic to write", sug, applicability);
if !mutability.is_empty() {
diag.note("you might not need `mut` at all");
}
},
);
}
}

View file

@ -401,7 +401,9 @@ mod zombie_processes;
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::Lint;
use rustc_data_structures::sync;
use rustc_lint::{EarlyLintPass, LateLintPass, Lint};
use rustc_middle::ty::TyCtxt;
use utils::attr_collector::{AttrCollector, AttrStorage};
pub fn explain(name: &str) -> i32 {
@ -443,381 +445,410 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf)));
store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf)));
let format_args_storage = FormatArgsStorage::default();
let format_args = format_args_storage.clone();
store.register_early_pass(move || {
Box::new(utils::format_args_collector::FormatArgsCollector::new(
format_args.clone(),
))
});
let attr_storage = AttrStorage::default();
let attrs = attr_storage.clone();
store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone())));
store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(utils::author::Author));
store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf)));
store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf)));
store.register_late_pass(|_| Box::new(ptr::Ptr));
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison));
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
store.register_late_pass(|_| Box::new(misc::LintPass));
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
store.register_late_pass(|_| Box::new(mut_mut::MutMut::default()));
store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed));
store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default());
store.register_late_pass(move |_| Box::new(len_zero::LenZero::new(conf)));
store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf)));
store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions));
store.register_late_pass(|_| Box::new(unicode::Unicode));
store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
store.register_late_pass(|_| Box::new(strings::StringAdd));
store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf)));
store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
store.register_late_pass(move |_| {
Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new(
conf,
))
});
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf)));
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone())));
store.register_late_pass(move |_| Box::new(matches::Matches::new(conf)));
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf)));
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf)));
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf)));
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf)));
store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(conf)));
store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(conf)));
store.register_late_pass(move |_| Box::new(ranges::Ranges::new(conf)));
store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(conf)));
store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(conf)));
store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf)));
store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
store.register_late_pass(move |_| Box::new(casts::Casts::new(conf)));
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf)));
store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf)));
store.register_late_pass(|_| Box::<shadow::Shadow>::default());
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone())));
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
store.register_late_pass(|_| Box::new(entry::HashMapPass));
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
store.register_late_pass(|_| Box::new(mutex_atomic::Mutex));
store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef));
store.register_late_pass(|_| Box::<no_effect::NoEffect>::default());
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(conf)));
store.register_late_pass(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf)));
store.register_late_pass(move |_| Box::new(escape::BoxedLocal::new(conf)));
store.register_late_pass(move |_| Box::new(vec::UselessVec::new(conf)));
store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf)));
store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
store.register_late_pass(|_| Box::new(derive::Derive));
store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf)));
store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums));
store.register_late_pass(|_| Box::<regex::Regex>::default());
store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf)));
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone())));
store.register_late_pass(|_| Box::new(swap::Swap));
store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks));
store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
store.register_early_pass(|| Box::new(functions::EarlyFunctions));
store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf)));
store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk));
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf)));
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())));
store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
store.register_late_pass(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf)));
store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(conf)));
store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf)));
store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf)));
store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf)));
store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf)));
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf)));
store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf)));
store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf)));
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf)));
store.register_late_pass(|_| Box::new(reference::DerefAddrOf));
store.register_early_pass(|| Box::new(double_parens::DoubleParens));
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())));
store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
store.register_early_pass(|| Box::new(formatting::Formatting));
store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit));
store.register_late_pass(|_| Box::new(returns::Return));
store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf)));
store.register_late_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_late_pass(|_| Box::new(needless_continue::NeedlessContinue));
store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
store.register_late_pass(|_| Box::new(create_dir::CreateDir));
store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf)));
store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf)));
store.register_late_pass(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf)));
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
store.register_late_pass(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf)));
store.register_late_pass(|_| Box::<default::Default>::default());
store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf)));
store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
store.register_late_pass(|_| Box::new(exit::Exit));
store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf)));
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf)));
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf)));
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
store.register_late_pass(|_| Box::new(as_conversions::AsConversions));
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
store.register_late_pass(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf)));
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf)));
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(conf)));
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn));
store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf)));
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf)));
store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
store.register_late_pass(|_| Box::<unwrap_in_result::UnwrapInResult>::default());
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
let attrs = attr_storage.clone();
store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone())));
store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf)));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop));
store.register_late_pass(|_| Box::new(strings::StrToString));
store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues));
store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default());
store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10));
store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf)));
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
store.register_early_pass(move || Box::new(module_style::ModStyle::default()));
store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default());
store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf)));
store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf)));
store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf)));
store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf)));
store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf)));
let format_args = format_args_storage.clone();
store.register_late_pass(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone())));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(conf)));
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(conf)));
let format_args = format_args_storage.clone();
store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone())));
store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf)));
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default()));
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf)));
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(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(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf)));
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf)));
store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate));
store.register_late_pass(move |_| Box::new(operators::Operators::new(conf)));
store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf)));
store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf)));
store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf)));
store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf)));
store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew));
store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable));
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
store.register_late_pass(|_| Box::new(box_default::BoxDefault));
store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods));
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(conf)));
store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf)));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf)));
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf)));
store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf)));
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
store.register_early_pass(|| Box::new(ref_patterns::RefPatterns));
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
store.register_early_pass(|| Box::new(needless_else::NeedlessElse));
store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug));
store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes));
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
store.register_late_pass(|_| Box::new(needless_ifs::NeedlessIfs));
store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf)));
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf)));
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf)));
store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx)));
store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf)));
store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf)));
store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf)));
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
store.register_early_pass(|| Box::new(visibility::Visibility));
store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf)));
store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf)));
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf)));
store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls));
store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing));
store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor));
store.register_late_pass(move |_| {
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
conf,
))
});
store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf)));
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default());
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes));
store.register_late_pass(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf)));
store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences));
store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions));
store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default());
store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf)));
store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf)));
store.register_late_pass(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf)));
store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl));
store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations));
store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf)));
store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses));
store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock));
store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf)));
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg));
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new()));
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
store.register_late_pass(|_| Box::new(useless_concat::UselessConcat));
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
store.register_late_pass(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default());
store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf)));
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg));
store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites));
store.register_late_pass(|_| Box::new(replace_box::ReplaceBox));
// add lints here, do not remove this comment, it's used in `new_lint`
let early_lints: [Box<dyn Fn() -> Box<dyn EarlyLintPass + 'static> + sync::DynSend + sync::DynSync>; _] = [
{
let format_args = format_args_storage.clone();
Box::new(move || {
Box::new(utils::format_args_collector::FormatArgsCollector::new(
format_args.clone(),
))
})
},
{
let attrs = attr_storage.clone();
Box::new(move || Box::new(AttrCollector::new(attrs.clone())))
},
Box::new(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))),
Box::new(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)),
Box::new(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))),
Box::new(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))),
Box::new(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))),
Box::new(|| Box::new(functions::EarlyFunctions)),
Box::new(move || Box::new(doc::Documentation::new(conf))),
Box::new(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)),
Box::new(|| Box::new(double_parens::DoubleParens)),
Box::new(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)),
Box::new(|| Box::new(else_if_without_else::ElseIfWithoutElse)),
Box::new(|| Box::new(int_plus_one::IntPlusOne)),
Box::new(|| Box::new(formatting::Formatting)),
Box::new(|| Box::new(misc_early::MiscEarlyLints)),
Box::new(|| Box::new(unused_unit::UnusedUnit)),
Box::new(|| Box::new(precedence::Precedence)),
Box::new(|| Box::new(redundant_else::RedundantElse)),
Box::new(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)),
Box::new(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))),
Box::new(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))),
Box::new(|| Box::new(tabs_in_doc_comments::TabsInDocComments)),
Box::new(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()),
Box::new(|| Box::new(option_env_unwrap::OptionEnvUnwrap)),
Box::new(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))),
Box::new(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))),
Box::new(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)),
Box::new(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)),
Box::new(move || Box::new(module_style::ModStyle::default())),
Box::new(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))),
Box::new(|| Box::new(octal_escapes::OctalEscapes)),
Box::new(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)),
Box::new(|| Box::new(crate_in_macro_def::CrateInMacroDef)),
Box::new(|| Box::new(pub_use::PubUse)),
Box::new(move || Box::new(large_include_file::LargeIncludeFile::new(conf))),
Box::new(|| Box::<duplicate_mod::DuplicateMod>::default()),
Box::new(|| Box::new(unused_rounding::UnusedRounding)),
Box::new(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))),
Box::new(|| Box::new(multi_assignments::MultiAssignments)),
Box::new(|| Box::new(partial_pub_fields::PartialPubFields)),
Box::new(|| Box::new(let_with_type_underscore::UnderscoreTyped)),
Box::new(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))),
Box::new(|| Box::new(ref_patterns::RefPatterns)),
Box::new(|| Box::new(needless_else::NeedlessElse)),
Box::new(move || Box::new(raw_strings::RawStrings::new(conf))),
Box::new(|| Box::new(visibility::Visibility)),
Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)),
Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)),
Box::new(|| Box::new(byte_char_slices::ByteCharSlice)),
Box::new(|| Box::new(cfg_not_test::CfgNotTest)),
Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())),
// add early passes here, used by `cargo dev new_lint`
];
store.early_passes.extend(early_lints);
#[expect(clippy::type_complexity)]
let late_lints: [Box<
dyn for<'tcx> Fn(TyCtxt<'tcx>) -> Box<dyn LateLintPass<'tcx> + 'tcx> + sync::DynSend + sync::DynSync,
>; _] = [
Box::new(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))),
Box::new(|_| Box::new(utils::dump_hir::DumpHir)),
Box::new(|_| Box::new(utils::author::Author)),
Box::new(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))),
Box::new(|_| Box::new(serde_api::SerdeApi)),
Box::new(move |_| Box::new(types::Types::new(conf))),
Box::new(move |_| Box::new(booleans::NonminimalBool::new(conf))),
Box::new(|_| Box::new(enum_clike::UnportableVariant)),
Box::new(move |_| Box::new(float_literal::FloatLiteral::new(conf))),
Box::new(|_| Box::new(ptr::Ptr)),
Box::new(|_| Box::new(needless_bool::NeedlessBool)),
Box::new(|_| Box::new(bool_comparison::BoolComparison)),
Box::new(|_| Box::new(needless_for_each::NeedlessForEach)),
Box::new(|_| Box::new(misc::LintPass)),
Box::new(|_| Box::new(eta_reduction::EtaReduction)),
Box::new(|_| Box::new(mut_mut::MutMut::default())),
Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)),
Box::new(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()),
Box::new(move |_| Box::new(len_zero::LenZero::new(conf))),
Box::new(move |_| Box::new(attrs::Attributes::new(conf))),
Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)),
Box::new(|_| Box::new(unicode::Unicode)),
Box::new(|_| Box::new(uninit_vec::UninitVec)),
Box::new(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)),
Box::new(|_| Box::new(strings::StringAdd)),
Box::new(|_| Box::new(implicit_return::ImplicitReturn)),
Box::new(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))),
Box::new(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)),
Box::new(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)),
Box::new(move |_| Box::new(approx_const::ApproxConstant::new(conf))),
Box::new(move |_| Box::new(matches::Matches::new(conf))),
Box::new(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))),
Box::new(move |_| Box::new(manual_strip::ManualStrip::new(conf))),
Box::new(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))),
Box::new(move |_| Box::new(mem_replace::MemReplace::new(conf))),
Box::new(move |_| Box::new(ranges::Ranges::new(conf))),
Box::new(move |_| Box::new(from_over_into::FromOverInto::new(conf))),
Box::new(move |_| Box::new(use_self::UseSelf::new(conf))),
Box::new(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))),
Box::new(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)),
Box::new(move |_| Box::new(casts::Casts::new(conf))),
Box::new(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)),
Box::new(|_| Box::new(same_name_method::SameNameMethod)),
Box::new(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))),
Box::new(|_| Box::<shadow::Shadow>::default()),
Box::new(move |_| {
Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new(
conf,
))
}),
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(methods::Methods::new(conf, format_args.clone())))
},
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone())))
},
Box::new(move |_| Box::new(loops::Loops::new(conf))),
Box::new(|_| Box::<main_recursion::MainRecursion>::default()),
Box::new(move |_| Box::new(lifetimes::Lifetimes::new(conf))),
Box::new(|_| Box::new(entry::HashMapPass)),
Box::new(|_| Box::new(minmax::MinMaxPass)),
Box::new(|_| Box::new(zero_div_zero::ZeroDiv)),
Box::new(|_| Box::new(mutex_atomic::Mutex)),
Box::new(|_| Box::new(needless_update::NeedlessUpdate)),
Box::new(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)),
Box::new(|_| Box::new(borrow_deref_ref::BorrowDerefRef)),
Box::new(|_| Box::<no_effect::NoEffect>::default()),
Box::new(|_| Box::new(temporary_assignment::TemporaryAssignment)),
Box::new(move |_| Box::new(transmute::Transmute::new(conf))),
Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))),
Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))),
Box::new(move |_| Box::new(vec::UselessVec::new(conf))),
Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))),
Box::new(|_| Box::new(strings::StringLitAsBytes)),
Box::new(|_| Box::new(derive::Derive)),
Box::new(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))),
Box::new(|_| Box::new(drop_forget_ref::DropForgetRef)),
Box::new(|_| Box::new(empty_enums::EmptyEnums)),
Box::new(|_| Box::<regex::Regex>::default()),
Box::new(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))),
Box::new(|_| Box::new(copy_iterator::CopyIterator)),
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(format::UselessFormat::new(format_args.clone())))
},
Box::new(|_| Box::new(swap::Swap)),
Box::new(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)),
Box::new(|_| Box::<new_without_default::NewWithoutDefault>::default()),
Box::new(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))),
Box::new(move |tcx| Box::new(functions::Functions::new(tcx, conf))),
Box::new(move |_| Box::new(doc::Documentation::new(conf))),
Box::new(|_| Box::new(neg_multiply::NegMultiply)),
Box::new(|_| Box::new(let_if_seq::LetIfSeq)),
Box::new(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)),
Box::new(move |_| Box::new(missing_doc::MissingDoc::new(conf))),
Box::new(|_| Box::new(missing_inline::MissingInline)),
Box::new(move |_| Box::new(exhaustive_items::ExhaustiveItems)),
Box::new(|_| Box::new(unused_result_ok::UnusedResultOk)),
Box::new(|_| Box::new(match_result_ok::MatchResultOk)),
Box::new(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)),
Box::new(|_| Box::new(unused_io_amount::UnusedIoAmount)),
Box::new(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))),
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())))
},
Box::new(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)),
Box::new(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))),
Box::new(|_| Box::new(ref_option_ref::RefOptionRef)),
Box::new(|_| Box::new(infinite_iter::InfiniteIter)),
Box::new(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)),
Box::new(|_| Box::<useless_conversion::UselessConversion>::default()),
Box::new(|_| Box::new(implicit_hasher::ImplicitHasher)),
Box::new(|_| Box::new(fallible_impl_from::FallibleImplFrom)),
Box::new(move |_| Box::new(question_mark::QuestionMark::new(conf))),
Box::new(|_| Box::new(question_mark_used::QuestionMarkUsed)),
Box::new(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)),
Box::new(|_| Box::new(map_unit_fn::MapUnit)),
Box::new(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))),
Box::new(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)),
Box::new(move |_| Box::new(unwrap::Unwrap::new(conf))),
Box::new(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))),
Box::new(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))),
Box::new(|_| Box::new(redundant_clone::RedundantClone)),
Box::new(|_| Box::new(slow_vector_initialization::SlowVectorInit)),
Box::new(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))),
Box::new(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))),
Box::new(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)),
Box::new(|_| Box::new(inherent_to_string::InherentToString)),
Box::new(move |_| Box::new(trait_bounds::TraitBounds::new(conf))),
Box::new(|_| Box::new(comparison_chain::ComparisonChain)),
Box::new(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))),
Box::new(|_| Box::new(reference::DerefAddrOf)),
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())))
},
Box::new(|_| Box::new(redundant_closure_call::RedundantClosureCall)),
Box::new(|_| Box::new(unused_unit::UnusedUnit)),
Box::new(|_| Box::new(returns::Return)),
Box::new(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))),
Box::new(|_| Box::new(items_after_statements::ItemsAfterStatements)),
Box::new(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)),
Box::new(|_| Box::new(needless_continue::NeedlessContinue)),
Box::new(|_| Box::new(create_dir::CreateDir)),
Box::new(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))),
Box::new(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))),
Box::new(|_| Box::<default::Default>::default()),
Box::new(move |_| Box::new(unused_self::UnusedSelf::new(conf))),
Box::new(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)),
Box::new(|_| Box::new(exit::Exit)),
Box::new(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))),
Box::new(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))),
Box::new(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))),
Box::new(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)),
Box::new(|_| Box::new(as_conversions::AsConversions)),
Box::new(|_| Box::new(let_underscore::LetUnderscore)),
Box::new(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))),
Box::new(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))),
Box::new(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()),
Box::new(|_| Box::<dereference::Dereferencing<'_>>::default()),
Box::new(|_| Box::new(option_if_let_else::OptionIfLetElse)),
Box::new(|_| Box::new(future_not_send::FutureNotSend)),
Box::new(move |_| Box::new(large_futures::LargeFuture::new(conf))),
Box::new(|_| Box::new(if_let_mutex::IfLetMutex)),
Box::new(|_| Box::new(if_not_else::IfNotElse)),
Box::new(|_| Box::new(equatable_if_let::PatternEquality)),
Box::new(|_| Box::new(manual_async_fn::ManualAsyncFn)),
Box::new(|_| Box::new(panic_in_result_fn::PanicInResultFn)),
Box::new(|_| Box::<macro_use::MacroUseImports>::default()),
Box::new(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)),
Box::new(|_| Box::<unwrap_in_result::UnwrapInResult>::default()),
Box::new(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)),
Box::new(|_| Box::new(async_yields_async::AsyncYieldsAsync)),
{
let attrs = attr_storage.clone();
Box::new(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone())))
},
Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))),
Box::new(|_| Box::new(empty_drop::EmptyDrop)),
Box::new(|_| Box::new(strings::StrToString)),
Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)),
Box::new(|_| Box::<vec_init_then_push::VecInitThenPush>::default()),
Box::new(|_| Box::new(redundant_slicing::RedundantSlicing)),
Box::new(|_| Box::new(from_str_radix_10::FromStrRadix10)),
Box::new(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))),
Box::new(|_| Box::new(bool_assert_comparison::BoolAssertComparison)),
Box::new(|_| Box::<unused_async::UnusedAsync>::default()),
Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))),
Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))),
Box::new(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)),
Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)),
Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)),
Box::new(move |_| Box::new(manual_assert::ManualAssert)),
Box::new(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))),
Box::new(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))),
{
let format_args = format_args_storage.clone();
Box::new(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone())))
},
Box::new(|_| Box::new(trailing_empty_array::TrailingEmptyArray)),
Box::new(|_| Box::new(needless_late_init::NeedlessLateInit)),
Box::new(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)),
Box::new(|_| Box::new(init_numbered_fields::NumberedFields)),
Box::new(move |_| Box::new(manual_bits::ManualBits::new(conf))),
Box::new(|_| Box::new(default_union_representation::DefaultUnionRepresentation)),
Box::new(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()),
Box::new(move |_| Box::new(dbg_macro::DbgMacro::new(conf))),
{
let format_args = format_args_storage.clone();
Box::new(move |_| Box::new(write::Write::new(conf, format_args.clone())))
},
Box::new(move |_| Box::new(cargo::Cargo::new(conf))),
Box::new(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())),
Box::new(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)),
Box::new(|_| Box::new(format_push_string::FormatPushString)),
Box::new(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))),
Box::new(|_| Box::new(strings::TrimSplitWhitespace)),
Box::new(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)),
Box::new(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)),
Box::new(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)),
Box::new(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)),
Box::new(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)),
Box::new(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))),
Box::new(move |_| Box::new(manual_retain::ManualRetain::new(conf))),
Box::new(move |_| Box::new(manual_rotate::ManualRotate)),
Box::new(move |_| Box::new(operators::Operators::new(conf))),
Box::new(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))),
Box::new(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))),
Box::new(|_| Box::new(partialeq_to_none::PartialeqToNone)),
Box::new(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))),
Box::new(move |_| Box::new(manual_clamp::ManualClamp::new(conf))),
Box::new(|_| Box::new(manual_string_new::ManualStringNew)),
Box::new(|_| Box::new(unused_peekable::UnusedPeekable)),
Box::new(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)),
Box::new(|_| Box::new(box_default::BoxDefault)),
Box::new(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)),
Box::new(|_| Box::new(missing_trait_methods::MissingTraitMethods)),
Box::new(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)),
Box::new(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)),
Box::new(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))),
Box::new(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))),
Box::new(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)),
Box::new(|_| Box::new(size_of_ref::SizeOfRef)),
Box::new(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)),
Box::new(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))),
Box::new(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)),
Box::new(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)),
Box::new(|_| Box::new(missing_assert_message::MissingAssertMessage)),
Box::new(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)),
Box::new(|_| Box::new(redundant_async_block::RedundantAsyncBlock)),
Box::new(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))),
Box::new(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)),
Box::new(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))),
Box::new(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)),
Box::new(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))),
Box::new(|_| Box::new(items_after_test_module::ItemsAfterTestModule)),
Box::new(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)),
Box::new(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)),
Box::new(|_| Box::new(endian_bytes::EndianBytes)),
Box::new(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)),
Box::new(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)),
Box::new(|_| Box::new(needless_ifs::NeedlessIfs)),
Box::new(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))),
Box::new(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))),
Box::new(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)),
Box::new(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))),
Box::new(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))),
Box::new(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))),
Box::new(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))),
Box::new(|_| Box::new(manual_range_patterns::ManualRangePatterns)),
Box::new(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))),
Box::new(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))),
Box::new(|_| Box::new(four_forward_slashes::FourForwardSlashes)),
Box::new(|_| Box::new(error_impl_error::ErrorImplError)),
Box::new(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))),
Box::new(|_| Box::new(redundant_locals::RedundantLocals)),
Box::new(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)),
Box::new(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default()),
Box::new(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)),
Box::new(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)),
Box::new(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)),
Box::new(move |_| {
Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new(
conf,
))
}),
Box::new(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))),
Box::new(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)),
Box::new(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default()),
Box::new(|_| Box::new(iter_over_hash_type::IterOverHashType)),
Box::new(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)),
Box::new(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))),
Box::new(|_| Box::new(uninhabited_references::UninhabitedReferences)),
Box::new(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)),
Box::new(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()),
Box::new(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))),
Box::new(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))),
Box::new(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))),
Box::new(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)),
Box::new(move |_| Box::new(assigning_clones::AssigningClones::new(conf))),
Box::new(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)),
Box::new(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))),
Box::new(move |_| Box::new(string_patterns::StringPatterns::new(conf))),
Box::new(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)),
Box::new(|_| Box::new(zombie_processes::ZombieProcesses)),
Box::new(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)),
Box::new(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))),
Box::new(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)),
Box::new(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)),
Box::new(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))),
Box::new(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)),
Box::new(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)),
Box::new(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))),
Box::new(|_| Box::new(useless_concat::UselessConcat)),
Box::new(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)),
Box::new(|_| Box::<unnecessary_semicolon::UnnecessarySemicolon>::default()),
Box::new(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))),
Box::new(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))),
Box::new(|_| Box::new(single_option_map::SingleOptionMap)),
Box::new(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)),
Box::new(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))),
Box::new(|_| Box::new(infallible_try_from::InfallibleTryFrom)),
Box::new(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)),
Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)),
Box::new(|_| Box::new(volatile_composites::VolatileComposites)),
Box::new(|_| Box::<replace_box::ReplaceBox>::default()),
// add late passes here, used by `cargo dev new_lint`
];
store.late_passes.extend(late_lints);
}

View file

@ -5,7 +5,7 @@ use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::implements_trait;
use clippy_utils::usage::contains_return_break_continue_macro;
use clippy_utils::{higher, peel_blocks_with_stmt};
use clippy_utils::{as_some_expr, higher, peel_blocks_with_stmt};
use rustc_errors::Applicability;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind};
@ -33,8 +33,7 @@ pub(super) fn check<'tcx>(
&& let [stmt] = block.stmts
&& let StmtKind::Semi(semi) = stmt.kind
&& let ExprKind::Ret(Some(ret_value)) = semi.kind
&& let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind
&& ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some(inner_ret) = as_some_expr(cx, ret_value)
&& inner_ret.res_local_id() == Some(binding_id)
&& !contains_return_break_continue_macro(cond)
&& let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr)

View file

@ -880,6 +880,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
missing_spin_loop::check(cx, condition, body);
manual_while_let_some::check(cx, condition, body, span);
}
if let ExprKind::MethodCall(path, recv, [arg], _) = expr.kind
&& matches!(
path.ident.name,
sym::all | sym::any | sym::filter_map | sym::find_map | sym::flat_map | sym::for_each | sym::map
)
{
unused_enumerate_index::check_method(cx, expr, recv, arg);
}
}
}
@ -908,7 +917,7 @@ impl Loops {
same_item_push::check(cx, pat, arg, body, expr, self.msrv);
manual_flatten::check(cx, pat, arg, body, span, self.msrv);
manual_find::check(cx, pat, arg, body, span, expr);
unused_enumerate_index::check(cx, pat, arg, body);
unused_enumerate_index::check(cx, arg, pat, None, body);
char_indices_as_byte_indices::check(cx, pat, arg, body);
}

View file

@ -1,42 +1,87 @@
use super::UNUSED_ENUMERATE_INDEX;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet;
use clippy_utils::{pat_is_wild, sugg};
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::{SpanRangeExt, walk_span_to_context};
use clippy_utils::{expr_or_init, pat_is_wild};
use rustc_errors::Applicability;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind, Pat, PatKind};
use rustc_hir::{Expr, ExprKind, Pat, PatKind, TyKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_span::{Span, SyntaxContext, sym};
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
///
/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) {
if let PatKind::Tuple([index, elem], _) = pat.kind
&& let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
&& let ty = cx.typeck_results().expr_ty(arg)
&& pat_is_wild(cx, &index.kind, body)
&& ty.is_diag_item(cx, sym::Enumerate)
&& let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
iter_expr: &'tcx Expr<'tcx>,
pat: &Pat<'tcx>,
ty_spans: Option<(Span, Span)>,
body: &'tcx Expr<'tcx>,
) {
if let PatKind::Tuple([idx_pat, inner_pat], _) = pat.kind
&& cx.typeck_results().expr_ty(iter_expr).is_diag_item(cx, sym::Enumerate)
&& pat_is_wild(cx, &idx_pat.kind, body)
&& let enumerate_call = expr_or_init(cx, iter_expr)
&& let ExprKind::MethodCall(_, _, [], enumerate_span) = enumerate_call.kind
&& let Some(enumerate_id) = cx.typeck_results().type_dependent_def_id(enumerate_call.hir_id)
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_id)
&& !enumerate_call.span.from_expansion()
&& !pat.span.from_expansion()
&& !idx_pat.span.from_expansion()
&& !inner_pat.span.from_expansion()
&& let Some(enumerate_range) = enumerate_span.map_range(cx, |_, text, range| {
text.get(..range.start)?
.ends_with('.')
.then_some(range.start - 1..range.end)
})
{
span_lint_and_then(
let enumerate_span = Span::new(enumerate_range.start, enumerate_range.end, SyntaxContext::root(), None);
span_lint_hir_and_then(
cx,
UNUSED_ENUMERATE_INDEX,
arg.span,
enumerate_call.hir_id,
enumerate_span,
"you seem to use `.enumerate()` and immediately discard the index",
|diag| {
let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
let mut spans = Vec::with_capacity(5);
spans.push((enumerate_span, String::new()));
spans.push((pat.span.with_hi(inner_pat.span.lo()), String::new()));
spans.push((pat.span.with_lo(inner_pat.span.hi()), String::new()));
if let Some((outer, inner)) = ty_spans {
spans.push((outer.with_hi(inner.lo()), String::new()));
spans.push((outer.with_lo(inner.hi()), String::new()));
}
diag.multipart_suggestion(
"remove the `.enumerate()` call",
vec![
(pat.span, snippet(cx, elem.span, "..").into_owned()),
(arg.span, base_iter.to_string()),
],
spans,
Applicability::MachineApplicable,
);
},
);
}
}
pub(super) fn check_method<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
recv: &'tcx Expr<'tcx>,
arg: &'tcx Expr<'tcx>,
) {
if let ExprKind::Closure(closure) = arg.kind
&& let body = cx.tcx.hir_body(closure.body)
&& let [param] = body.params
&& cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)
&& let [input] = closure.fn_decl.inputs
&& !arg.span.from_expansion()
&& !input.span.from_expansion()
&& !recv.span.from_expansion()
&& !param.span.from_expansion()
{
let ty_spans = if let TyKind::Tup([_, inner]) = input.kind {
let Some(inner) = walk_span_to_context(inner.span, SyntaxContext::root()) else {
return;
};
Some((input.span, inner))
} else {
None
};
check(cx, recv, param.pat, ty_spans, body.value);
}
}

View file

@ -5,11 +5,11 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::visitors::is_res_used;
use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable};
use clippy_utils::{as_some_pattern, get_enclosing_loop_or_multi_call_closure, higher, is_refutable};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp};
use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::ty::adjustment::Adjust;
@ -19,8 +19,7 @@ use rustc_span::symbol::sym;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr)
// check for `Some(..)` pattern
&& let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
&& cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some(some_pat) = as_some_pattern(cx, let_pat)
// check for call to `Iterator::next`
&& let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind
&& method_name.ident.name == sym::next

View file

@ -70,12 +70,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsPowerOfTwo {
if !expr.span.from_expansion()
&& let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq)
{
if let Some(a) = count_ones_receiver(cx, lhs)
&& is_integer_literal(rhs, 1)
if is_integer_literal(rhs, 1)
&& let Some(a) = count_ones_receiver(cx, lhs)
{
self.build_sugg(cx, expr, a);
} else if let Some(a) = count_ones_receiver(cx, rhs)
&& is_integer_literal(lhs, 1)
} else if is_integer_literal(lhs, 1)
&& let Some(a) = count_ones_receiver(cx, rhs)
{
self.build_sugg(cx, expr, a);
} else if is_integer_literal(rhs, 0)

View file

@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::Msrv;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::source::snippet_with_context;
use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym};
use clippy_utils::{as_some_pattern, is_none_pattern, msrvs, peel_hir_expr_refs, sym};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, QPath, is_range_literal};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, Symbol};
@ -154,10 +153,8 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) {
}
fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<Symbol> {
if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind
&& let Res::Def(DefKind::Ctor(..), def_id) = path.res
if let Some([binding]) = as_some_pattern(cx, pat)
&& let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind
&& clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome)
{
Some(ident.name)
} else {

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{higher, sym};
use clippy_utils::{as_some_pattern, higher, sym};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -55,10 +55,9 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
};
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
&& let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation
&& ok_path.ident.name == sym::ok
&& cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
&& cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some([ok_pat]) = as_some_pattern(cx, let_pat) //get operation
&& let ctxt = expr.span.ctxt()
&& let_expr.span.ctxt() == ctxt
&& let_pat.span.ctxt() == ctxt

View file

@ -1,8 +1,9 @@
use clippy_utils::as_some_expr;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::visitors::contains_unsafe_block;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_span::{SyntaxContext, sym};
@ -52,21 +53,19 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr)
}
// function called for each <expr> expression:
/// Checks whether <expr> resolves to `Some(target)`
// NOTE: called for each <expr> expression:
// Some(x) => if <cond> {
// <expr>
// } else {
// <expr>
// }
// Returns true if <expr> resolves to `Some(x)`, `false` otherwise
fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool {
if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr)
// there can be not statements in the block as they would be removed when switching to `.filter`
&& let ExprKind::Call(callee, [arg]) = inner_expr.kind
&& let Some(arg) = as_some_expr(cx, inner_expr)
{
return ctxt == expr.span.ctxt()
&& callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& arg.res_local_id() == Some(target);
return ctxt == expr.span.ctxt() && arg.res_local_id() == Some(target);
}
false
}

View file

@ -1,12 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs};
use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment};
use clippy_utils::{as_some_expr, get_parent_expr, is_none_expr, peel_blocks, span_contains_comment};
use rustc_ast::{BindingMode, Mutability};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
use rustc_hir::LangItem::ResultErr;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
use rustc_lint::{LateContext, LintContext};
@ -106,8 +106,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'
/// Check if `expr` contains `Some(ident)`, possibly as a block
fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool {
if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind
&& body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
if let Some(body_arg) = as_some_expr(cx, peel_blocks(expr))
&& cx.typeck_results().expr_ty(body_arg) == ty
&& let ExprKind::Path(QPath::Resolved(
_,
@ -124,7 +123,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t
/// Check if `expr` is `None`, possibly as a block
fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
is_none_expr(cx, peel_blocks(expr))
}
/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or

View file

@ -1,18 +1,17 @@
use crate::map_unit_fn::OPTION_MAP_UNIT_FN;
use crate::matches::MATCH_AS_REF;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs};
use clippy_utils::{
CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks,
peel_hir_expr_refs, peel_hir_expr_while,
CaptureKind, as_some_pattern, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed,
is_none_expr, is_none_pattern, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
};
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::def::Res;
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::{SyntaxContext, sym};
@ -44,16 +43,16 @@ where
try_parse_pattern(cx, then_pat, expr_ctxt),
else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
) {
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
(Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
(else_body, pattern, ref_count, true)
},
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
(Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => {
(else_body, pattern, ref_count, false)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_arm_body(cx, else_body) => {
(then_body, pattern, ref_count, true)
},
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
(Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_arm_body(cx, else_body) => {
(then_body, pattern, ref_count, false)
},
_ => return None,
@ -255,23 +254,9 @@ pub(super) fn try_parse_pattern<'tcx>(
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _, _) => f(cx, pat, ref_count + 1, ctxt),
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,
..
}) if cx
.qpath_res(qpath, *hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionNone) =>
{
Some(OptionPat::None)
},
PatKind::TupleStruct(ref qpath, [pattern], _)
if cx
.qpath_res(qpath, pat.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionSome)
&& pat.span.ctxt() == ctxt =>
_ if is_none_pattern(cx, pat) => Some(OptionPat::None),
_ if let Some([pattern]) = as_some_pattern(cx, pat)
&& pat.span.ctxt() == ctxt =>
{
Some(OptionPat::Some { pattern, ref_count })
},
@ -281,7 +266,7 @@ pub(super) fn try_parse_pattern<'tcx>(
f(cx, pat, 0, ctxt)
}
// Checks for the `None` value.
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
/// Checks for the `None` value, possibly in a block.
fn is_none_arm_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
is_none_expr(cx, peel_blocks(expr))
}

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::option_arg_ty;
use clippy_utils::{is_none_arm, peel_blocks};
use clippy_utils::{as_some_expr, as_some_pattern, is_none_arm, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, Mutability, PatKind, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty;
@ -82,14 +81,9 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<Mutability> {
if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind
&& cx
.qpath_res(qpath, arm.pat.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, LangItem::OptionSome)
if let Some([first_pat, ..]) = as_some_pattern(cx, arm.pat)
&& let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., ident, _) = first_pat.kind
&& let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome)
&& let Some(arg) = as_some_expr(cx, peel_blocks(arm.body))
&& let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind
&& path2.segments.len() == 1
&& ident.name == path2.segments[0].ident.name

View file

@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt};
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind};
use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, PatKind, Path, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_span::{Span, Symbol};
@ -307,26 +307,6 @@ fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
false
}
fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool {
let parent = cx.tcx.parent_hir_node(match_expr.hir_id);
if let Node::Expr(Expr {
kind: ExprKind::Closure(..) | ExprKind::Binary(..),
..
})
| Node::AnonConst(..) = parent
{
return true;
}
if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id)
&& let ExprKind::Match(..) = arm.body.kind
{
return true;
}
false
}
fn indent_of_nth_line(snippet: &str, nth: usize) -> Option<usize> {
snippet
.lines()
@ -379,14 +359,47 @@ fn sugg_with_curlies<'a>(
let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new());
if !expr_in_nested_block(cx, match_expr)
&& ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr))
{
let mut add_curlies = || {
cbrace_end = format!("\n{indent}}}");
// Fix body indent due to the closure
indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
cbrace_start = format!("{{\n{indent}");
snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty());
};
if !expr_in_nested_block(cx, match_expr) {
let mut parent = cx.tcx.parent_hir_node(match_expr.hir_id);
if let Node::Expr(Expr {
kind: ExprKind::Assign(..),
hir_id,
..
}) = parent
{
parent = cx.tcx.parent_hir_node(*hir_id);
}
if let Node::Stmt(stmt) = parent {
parent = cx.tcx.parent_hir_node(stmt.hir_id);
}
match parent {
Node::Block(..)
| Node::Expr(Expr {
kind: ExprKind::Block(..) | ExprKind::ConstBlock(..),
..
}) => {
if needs_var_binding && is_var_binding_used_later {
add_curlies();
}
},
Node::Expr(..)
| Node::AnonConst(..)
| Node::Item(Item {
kind: ItemKind::Const(..),
..
}) => add_curlies(),
Node::Arm(arm) if let ExprKind::Match(..) = arm.body.kind => add_curlies(),
_ => {},
}
}
format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}")

View file

@ -4,7 +4,7 @@ use crate::FxHashSet;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{first_line_of_span, indent_of, snippet};
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
use clippy_utils::{get_attr, is_lint_allowed, sym};
use clippy_utils::{get_builtin_attr, is_lint_allowed, sym};
use itertools::Itertools;
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxIndexSet;
@ -183,7 +183,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool {
if let Some(adt) = ty.ty_adt_def()
&& get_attr(
&& get_builtin_attr(
self.cx.sess(),
self.cx.tcx.get_all_attrs(adt.did()),
sym::has_significant_drop,

View file

@ -373,7 +373,10 @@ impl<'a> PatState<'a> {
},
// Patterns for things which can only contain a single sub-pattern.
PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
PatKind::Binding(_, _, _, Some(pat))
| PatKind::Ref(pat, _, _)
| PatKind::Box(pat)
| PatKind::Deref(pat) => {
self.add_pat(cx, pat)
},
PatKind::Tuple([sub_pat], pos)

View file

@ -1,13 +1,14 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_non_aggregate_primitive_type;
use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core};
use clippy_utils::{
as_some_expr, is_default_equivalent, is_expr_used_or_unified, is_none_expr, peel_ref_operators, std_or_core,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -128,7 +129,7 @@ impl_lint_pass!(MemReplace =>
[MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool {
if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
if is_none_expr(cx, src) {
// Since this is a late pass (already type-checked),
// and we already know that the second argument is an
// `Option`, we do not need to check the first
@ -161,8 +162,7 @@ fn check_replace_option_with_some(
expr_span: Span,
msrv: Msrv,
) -> bool {
if let ExprKind::Call(src_func, [src_arg]) = src.kind
&& src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
if let Some(src_arg) = as_some_expr(cx, src)
&& msrv.meets(cx, msrvs::OPTION_REPLACE)
{
// We do not have to check for a `const` context here, because `core::mem::replace()` and

View file

@ -1,7 +1,6 @@
use super::ERR_EXPECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::ty::has_debug_impl;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
@ -17,12 +16,10 @@ pub(super) fn check(
err_span: Span,
msrv: Msrv,
) {
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
// Grabs the `Result<T, E>` type
&& let result_type = cx.typeck_results().expr_ty(recv)
// Tests if the T type in a `Result<T, E>` is not None
&& let Some(data_type) = get_data_type(cx, result_type)
// Tests if the T type in a `Result<T, E>` implements debug
let result_ty = cx.typeck_results().expr_ty(recv);
// Grabs the `Result<T, E>` type
if let Some(data_type) = get_data_type(cx, result_ty)
// Tests if the T type in a `Result<T, E>` implements Debug
&& has_debug_impl(cx, data_type)
&& msrv.meets(cx, msrvs::EXPECT_ERR)
{
@ -41,7 +38,7 @@ pub(super) fn check(
/// Given a `Result<T, E>` type, return its data (`T`).
fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() {
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(),
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().next(),
_ => None,
}
}

View file

@ -1,13 +1,11 @@
use std::iter::once;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig};
use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym};
use clippy_utils::{as_some_expr, get_expr_use_or_unification_node, is_none_expr, std_or_core, sym};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::hir_id::HirId;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
@ -68,15 +66,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method
let item = match recv.kind {
ExprKind::Array([]) => None,
ExprKind::Array([e]) => Some(e),
ExprKind::Path(ref p)
if cx
.qpath_res(p, recv.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionNone) =>
{
None
},
ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg),
_ if is_none_expr(cx, recv) => None,
_ if let Some(arg) = as_some_expr(cx, recv) => Some(arg),
_ => return,
};
let iter_type = match method_name {

View file

@ -81,7 +81,8 @@ pub(super) fn check<'tcx>(
}
match it.kind {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, _, Mutability::Mut) => {
PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _)
| PatKind::Ref(_, _, Mutability::Mut) => {
to_be_discarded = true;
false
},

View file

@ -138,7 +138,6 @@ mod unnecessary_option_map_or_else;
mod unnecessary_result_map_or_else;
mod unnecessary_sort_by;
mod unnecessary_to_owned;
mod unused_enumerate_index;
mod unwrap_expect_used;
mod useless_asref;
mod useless_nonzero_new_unchecked;
@ -1084,7 +1083,7 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// In versions of the compiler before Rust 1.82.0, this bypasses the specialized
/// implementation of`ToString` and instead goes through the more expensive string
/// implementation of `ToString` and instead goes through the more expensive string
/// formatting facilities.
///
/// ### Example
@ -5026,7 +5025,6 @@ impl Methods {
zst_offset::check(cx, expr, recv);
},
(sym::all, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, true);
match method_call(recv) {
Some((sym::cloned, recv2, [], _, _)) => {
@ -5056,7 +5054,6 @@ impl Methods {
}
},
(sym::any, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, false);
match method_call(recv) {
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
@ -5170,7 +5167,7 @@ impl Methods {
},
(sym::expect, [_]) => {
match method_call(recv) {
Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv),
Some((sym::ok, recv_inner, [], _, _)) => ok_expect::check(cx, expr, recv, recv_inner),
Some((sym::err, recv, [], err_span, _)) => {
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
},
@ -5216,7 +5213,6 @@ impl Methods {
}
},
(sym::filter_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FilterMap);
filter_map_bool_then::check(cx, expr, arg, call_span);
filter_map_identity::check(cx, expr, arg, span);
@ -5231,11 +5227,9 @@ impl Methods {
);
},
(sym::find_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap);
},
(sym::flat_map, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span);
lines_filter_map_ok::check_filter_or_flat_map(
@ -5263,20 +5257,17 @@ impl Methods {
manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv);
unnecessary_fold::check(cx, expr, init, acc, span);
},
(sym::for_each, [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
match method_call(recv) {
Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(arg),
false,
),
_ => {},
}
(sym::for_each, [arg]) => match method_call(recv) {
Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
expr,
recv,
recv2,
iter_overeager_cloned::Op::NeedlessMove(arg),
false,
),
_ => {},
},
(sym::get, [arg]) => {
get_first::check(cx, expr, recv, arg);
@ -5337,7 +5328,6 @@ impl Methods {
},
(name @ (sym::map | sym::map_err), [m_arg]) => {
if name == sym::map {
unused_enumerate_index::check(cx, expr, recv, m_arg);
map_clone::check(cx, expr, recv, m_arg, self.msrv);
map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span);
manual_is_variant_and::check_map(cx, expr);

View file

@ -38,11 +38,14 @@ pub(super) fn check<'tcx>(
Node::Expr(parent) => {
check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr);
let sugg: String;
let mut app;
if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
let mut app = Applicability::MachineApplicable;
app = Applicability::MachineApplicable;
let collect_ty = cx.typeck_results().expr_ty(collect_expr);
let sugg: String = match name.ident.name {
sugg = match name.ident.name {
sym::len => {
if let Some(adt) = collect_ty.ty_adt_def()
&& matches!(
@ -78,17 +81,23 @@ pub(super) fn check<'tcx>(
},
_ => return,
};
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
call_span.with_hi(parent.span.hi()),
NEEDLESS_COLLECT_MSG,
"replace with",
sugg,
app,
);
} else if let ExprKind::Index(_, index, _) = parent.kind {
app = Applicability::MaybeIncorrect;
let snip = snippet_with_applicability(cx, index.span, "_", &mut app);
sugg = format!("nth({snip}).unwrap()");
} else {
return;
}
span_lint_and_sugg(
cx,
NEEDLESS_COLLECT,
call_span.with_hi(parent.span.hi()),
NEEDLESS_COLLECT_MSG,
"replace with",
sugg,
app,
);
},
Node::LetStmt(l) => {
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind

View file

@ -1,28 +1,35 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::res::MaybeDef;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::has_debug_impl;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::{self, Ty};
use rustc_span::sym;
use super::OK_EXPECT;
/// lint use of `ok().expect()` for `Result`s
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result)
// lint if the caller of `ok()` is a `Result`
&& let result_type = cx.typeck_results().expr_ty(recv)
&& let Some(error_type) = get_error_type(cx, result_type)
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, recv_inner: &hir::Expr<'_>) {
let result_ty = cx.typeck_results().expr_ty(recv_inner);
// lint if the caller of `ok()` is a `Result`
if let Some(error_type) = get_error_type(cx, result_ty)
&& has_debug_impl(cx, error_type)
&& let Some(span) = recv.span.trim_start(recv_inner.span)
{
span_lint_and_help(
span_lint_and_then(
cx,
OK_EXPECT,
expr.span,
"called `ok().expect()` on a `Result` value",
None,
"you can call `expect()` directly on the `Result`",
|diag| {
let span = cx.sess().source_map().span_extend_while_whitespace(span);
diag.span_suggestion_verbose(
span,
"call `expect()` directly on the `Result`",
String::new(),
Applicability::MachineApplicable,
);
},
);
}
}
@ -30,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
match ty.kind() {
ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1),
ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().nth(1),
_ => None,
}
}

View file

@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_none_expr;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionSome;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
@ -48,7 +49,7 @@ pub(super) fn check<'tcx>(
return;
}
if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) {
if !is_none_expr(cx, def_arg) {
// nothing to lint!
return;
}

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::peel_blocks;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::snippet;
use clippy_utils::{is_none_expr, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::LangItem::OptionSome;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind
&& let body = cx.tcx.hir_body(body)
// And finally we check that we return a `None` in the "else case".
&& peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
&& is_none_expr(cx, peel_blocks(body.value))
{
let msg = "called `map_or_else(|_| None, Some)` on a `Result` value";
let self_snippet = snippet(cx, recv.span, "..");

View file

@ -1,10 +1,10 @@
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes};
use clippy_utils::sym;
use clippy_utils::ty::{is_copy, option_arg_ty};
use clippy_utils::usage::mutated_variables;
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
use clippy_utils::{as_some_expr, sym};
use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
@ -47,8 +47,7 @@ pub(super) fn check<'tcx>(
let sugg = if !found_filtering {
// Check if the closure is .filter_map(|x| Some(x))
if kind.is_filter_map()
&& let hir::ExprKind::Call(expr, [arg]) = body.value.kind
&& expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
&& let Some(arg) = as_some_expr(cx, body.value)
&& let hir::ExprKind::Path(_) = arg.kind
{
span_lint(

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::{last_path_segment, sym};
use clippy_utils::{is_none_expr, last_path_segment, sym};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, AmbigArg};
use rustc_lint::LateContext;
@ -23,6 +23,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) -
}
}
#[expect(clippy::too_many_lines)]
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
@ -38,23 +39,24 @@ pub(super) fn check(
}
let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind {
let Some((qpath, hir_id)) = call.opt_qpath() else {
return;
};
let args = last_path_segment(qpath).args.map(|args| args.args);
let res = cx.qpath_res(qpath, hir_id);
if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) {
(sym::Some, call_args, get_ty_from_args(args, 0))
} else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) {
(sym::Ok, call_args, get_ty_from_args(args, 0))
} else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) {
(sym::Err, call_args, get_ty_from_args(args, 1))
if let Some((qpath, hir_id)) = call.opt_qpath()
&& let args = last_path_segment(qpath).args.map(|args| args.args)
&& let Some(did) = cx.qpath_res(qpath, hir_id).ctor_parent(cx).opt_def_id()
{
let lang_items = cx.tcx.lang_items();
if Some(did) == lang_items.option_some_variant() {
(sym::Some, call_args, get_ty_from_args(args, 0))
} else if Some(did) == lang_items.result_ok_variant() {
(sym::Ok, call_args, get_ty_from_args(args, 0))
} else if Some(did) == lang_items.result_err_variant() {
(sym::Err, call_args, get_ty_from_args(args, 1))
} else {
return;
}
} else {
return;
}
} else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) {
} else if is_none_expr(cx, init) {
let call_args: &[hir::Expr<'_>] = &[];
(sym::None, call_args, None)
} else {

View file

@ -6,7 +6,8 @@ use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind};
use rustc_middle::ty;
use rustc_middle::ty::GenericArgKind;
use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::iter;

View file

@ -1,138 +0,0 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::{SpanRangeExt, snippet};
use clippy_utils::{expr_or_init, pat_is_wild};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
use rustc_lint::LateContext;
use rustc_span::{Span, sym};
use crate::loops::UNUSED_ENUMERATE_INDEX;
/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops.
///
/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is
/// checked:
/// ```ignore
/// for (_, x) in some_iter.enumerate() {
/// // Index is ignored
/// }
/// ```
///
/// This `check` function checks for chained method calls constructs where we can detect that the
/// index is unused. Currently, this checks only for the following patterns:
/// ```ignore
/// some_iter.enumerate().map_function(|(_, x)| ..)
/// let x = some_iter.enumerate();
/// x.map_function(|(_, x)| ..)
/// ```
/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or
/// `map`.
///
/// # Preconditions
/// This function must be called not on the `enumerate` call expression itself, but on any of the
/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and
/// that the method call is one of the `std::iter::Iterator` trait.
///
/// * `call_expr`: The map function call expression
/// * `recv`: The receiver of the call
/// * `closure_arg`: The argument to the map function call containing the closure/function to apply
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
let recv_ty = cx.typeck_results().expr_ty(recv);
// If we call a method on a `std::iter::Enumerate` instance
if recv_ty.is_diag_item(cx, sym::Enumerate)
// If we are calling a method of the `Iterator` trait
&& cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator)
// And the map argument is a closure
&& let ExprKind::Closure(closure) = closure_arg.kind
&& let closure_body = cx.tcx.hir_body(closure.body)
// And that closure has one argument ...
&& let [closure_param] = closure_body.params
// .. which is a tuple of 2 elements
&& let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind
// And that the first element (the index) is either `_` or unused in the body
&& pat_is_wild(cx, &index.kind, closure_body)
// Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the
// first example below, `expr_or_init` would return `recv`.
// ```
// iter.enumerate().map(|(_, x)| x)
// ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate`
//
// let binding = iter.enumerate();
// ^^^^^^^^^^^^^^^^ `recv_init_expr`
// binding.map(|(_, x)| x)
// ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate`
// ```
&& let recv_init_expr = expr_or_init(cx, recv)
// Make sure the initializer is a method call. It may be that the `Enumerate` comes from something
// that we cannot control.
// This would for instance happen with:
// ```
// external_lib::some_function_returning_enumerate().map(|(_, x)| x)
// ```
&& let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
&& let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
// Make sure the method call is `std::iter::Iterator::enumerate`.
&& cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid)
{
// Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
// that would be explicitly in the closure.
let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
// We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
// Fallback to `..` if we fail getting either snippet.
Some(ty_span) => elem
.span
.get_source_text(cx)
.and_then(|binding_name| {
ty_span
.get_source_text(cx)
.map(|ty_name| format!("{binding_name}: {ty_name}"))
})
.unwrap_or_else(|| "..".to_string()),
// Otherwise, we have no explicit type. We can replace with the binding name of the element.
None => snippet(cx, elem.span, "..").into_owned(),
};
// Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we
// can get from the `MethodCall`.
span_lint_hir_and_then(
cx,
UNUSED_ENUMERATE_INDEX,
recv_init_expr.hir_id,
enumerate_span,
"you seem to use `.enumerate()` and immediately discard the index",
|diag| {
diag.multipart_suggestion(
"remove the `.enumerate()` call",
vec![
(closure_param.span, new_closure_param),
(
enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()),
String::new(),
),
],
Applicability::MachineApplicable,
);
},
);
}
}
/// Find the span of the explicit type of the element.
///
/// # Returns
/// If the tuple argument:
/// * Has no explicit type, returns `None`
/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None`
/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for
/// the element type.
fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option<Span> {
if let [tuple_ty] = fn_decl.inputs
&& let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind
&& !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer(()))
{
Some(elem_ty.span)
} else {
None
}
}

View file

@ -3,10 +3,11 @@ use std::ops::ControlFlow;
use clippy_utils::comparisons::{Rel, normalize_comparison};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
use clippy_utils::higher::{If, Range};
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call};
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{eq_expr_value, hash_expr, higher};
use clippy_utils::{eq_expr_value, hash_expr};
use rustc_ast::{BinOpKind, LitKind, RangeLimits};
use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnindexMap;
@ -15,7 +16,7 @@ use rustc_hir::{Block, Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_span::{Span, sym};
use rustc_span::{Span, Symbol, sym};
declare_clippy_lint! {
/// ### What it does
@ -134,15 +135,15 @@ fn len_comparison<'hir>(
fn assert_len_expr<'hir>(
cx: &LateContext<'_>,
expr: &'hir Expr<'hir>,
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>, Symbol)> {
let ((cmp, asserted_len, slice_len), macro_call) = if let Some(If { cond, then, .. }) = If::hir(expr)
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
// check if `then` block has a never type expression
&& let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
&& cx.typeck_results().expr_ty(then_expr).is_never()
{
len_comparison(bin_op.node, left, right)?
(len_comparison(bin_op.node, left, right)?, sym::assert_macro)
} else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| {
match cx.tcx.get_diagnostic_name(macro_call.def_id) {
Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)),
@ -151,7 +152,12 @@ fn assert_len_expr<'hir>(
}
}) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn)
{
len_comparison(bin_op, left, right)?
(
len_comparison(bin_op, left, right)?,
root_macro_call(expr.span)
.and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id))
.unwrap_or(sym::assert_macro),
)
} else {
return None;
};
@ -160,7 +166,7 @@ fn assert_len_expr<'hir>(
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
&& method.ident.name == sym::len
{
Some((cmp, asserted_len, recv))
Some((cmp, asserted_len, recv, macro_call))
} else {
None
}
@ -174,6 +180,7 @@ enum IndexEntry<'hir> {
comparison: LengthComparison,
assert_span: Span,
slice: &'hir Expr<'hir>,
macro_call: Symbol,
},
/// `assert!` with indexing
///
@ -187,6 +194,7 @@ enum IndexEntry<'hir> {
slice: &'hir Expr<'hir>,
indexes: Vec<Span>,
comparison: LengthComparison,
macro_call: Symbol,
},
/// Indexing without an `assert!`
IndexWithoutAssert {
@ -225,9 +233,9 @@ fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<usize> {
&& let LitKind::Int(Pu128(index), _) = lit.node
{
Some(index as usize)
} else if let Some(higher::Range {
} else if let Some(Range {
end: Some(end), limits, ..
}) = higher::Range::hir(cx, expr)
}) = Range::hir(cx, expr)
&& let ExprKind::Lit(lit) = &end.kind
&& let LitKind::Int(Pu128(index @ 1..), _) = lit.node
{
@ -258,6 +266,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
comparison,
assert_span,
slice,
macro_call,
} => {
if slice.span.lo() > assert_span.lo() {
*entry = IndexEntry::AssertWithIndex {
@ -268,6 +277,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
slice,
indexes: vec![expr.span],
comparison: *comparison,
macro_call: *macro_call,
};
}
},
@ -303,7 +313,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni
/// Checks if the expression is an `assert!` expression and adds it to `asserts`
fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
if let Some((comparison, asserted_len, slice, macro_call)) = assert_len_expr(cx, expr) {
let hash = hash_expr(cx, slice);
let indexes = map.entry(hash).or_default();
@ -326,6 +336,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
assert_span: expr.span.source_callsite(),
comparison,
asserted_len,
macro_call,
};
}
} else {
@ -334,6 +345,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
comparison,
assert_span: expr.span.source_callsite(),
slice,
macro_call,
});
}
}
@ -362,6 +374,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
comparison,
assert_span,
slice,
macro_call,
} if indexes.len() > 1 && !is_first_highest => {
// if we have found an `assert!`, let's also check that it's actually right
// and if it covers the highest index and if not, suggest the correct length
@ -382,11 +395,23 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>
snippet(cx, slice.span, "..")
)),
// `highest_index` here is rather a length, so we need to add 1 to it
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => Some(format!(
"assert!({}.len() == {})",
snippet(cx, slice.span, ".."),
highest_index + 1
)),
LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call {
sym::assert_eq_macro => Some(format!(
"assert_eq!({}.len(), {})",
snippet(cx, slice.span, ".."),
highest_index + 1
)),
sym::debug_assert_eq_macro => Some(format!(
"debug_assert_eq!({}.len(), {})",
snippet(cx, slice.span, ".."),
highest_index + 1
)),
_ => Some(format!(
"assert!({}.len() == {})",
snippet(cx, slice.span, ".."),
highest_index + 1
)),
},
_ => None,
};

View file

@ -4,6 +4,7 @@ use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, Attribute, find_attr};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::AssocContainer;
use rustc_session::config::CrateType;
use rustc_session::declare_lint_pass;
use rustc_span::Span;
@ -81,20 +82,20 @@ fn check_missing_inline_attrs(
}
}
fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
use rustc_session::config::CrateType;
cx.tcx
.crate_types()
.iter()
.any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
}
declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
impl<'tcx> LateLintPass<'tcx> for MissingInline {
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
if it.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) {
if it.span.in_external_macro(cx.sess().source_map()) {
return;
}
if cx
.tcx
.crate_types()
.iter()
.any(|t: &CrateType| matches!(t, CrateType::ProcMacro))
{
return;
}
@ -149,7 +150,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
if impl_item.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) {
if impl_item.span.in_external_macro(cx.sess().source_map())
|| cx
.tcx
.crate_types()
.iter()
.any(|t: &CrateType| matches!(t, CrateType::ProcMacro))
{
return;
}

View file

@ -4,7 +4,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym};
use std::path::{Path, PathBuf};
use std::path::{Component, Path, PathBuf};
use std::sync::Arc;
declare_clippy_lint! {
@ -150,7 +150,13 @@ fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile
/// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test)
/// for code-sharing between tests.
fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
if path.ends_with("mod.rs") && !path.starts_with("tests") {
if path.ends_with("mod.rs")
&& !path
.components()
.filter_map(|c| if let Component::Normal(d) = c { Some(d) } else { None })
.take_while(|&c| c != "src")
.any(|c| c == "tests")
{
span_lint_and_then(
cx,
MOD_MODULE_FILES,

View file

@ -99,10 +99,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id)
&& adj

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Param, PatKind, TyKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
use rustc_span::symbol::kw;
declare_clippy_lint! {
@ -65,52 +64,6 @@ enum Mode {
Value,
}
fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) {
if let [segment] = &path.segments[..]
&& segment.ident.name == kw::SelfUpper
{
// In case we have a named lifetime, we check if the name comes from expansion.
// If it does, at this point we know the rest of the parameter was written by the user,
// so let them decide what the name of the lifetime should be.
// See #6089 for more details.
let mut applicability = Applicability::MachineApplicable;
let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
if lifetime.ident.span.from_expansion() {
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
format!("&{lt_name} mut self")
}
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
if lifetime.ident.span.from_expansion() {
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability);
format!("&{lt_name} self")
}
},
(Mode::Value, Mutability::Mut) => "mut self".to_string(),
(Mode::Value, Mutability::Not) => "self".to_string(),
};
span_lint_and_sugg(
cx,
NEEDLESS_ARBITRARY_SELF_TYPE,
span,
"the type of the `self` parameter does not need to be arbitrary",
"consider to change this parameter to",
self_param,
applicability,
);
}
}
impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
// Bail out if the parameter it's not a receiver or was not written by the user
@ -118,20 +71,55 @@ impl EarlyLintPass for NeedlessArbitrarySelfType {
return;
}
match &p.ty.kind {
TyKind::Path(None, path) => {
if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind {
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl);
}
let (path, binding_mode, mutbl) = match &p.ty.kind {
TyKind::Path(None, path) if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind => {
(path, Mode::Value, mutbl)
},
TyKind::Ref(lifetime, mut_ty) => {
TyKind::Ref(lifetime, mut_ty)
if let TyKind::Path(None, path) = &mut_ty.ty.kind
&& let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind
{
check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl);
}
&& let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind =>
{
(path, Mode::Ref(*lifetime), mut_ty.mutbl)
},
_ => {},
_ => return,
};
let span = p.span.to(p.ty.span);
if let [segment] = &path.segments[..]
&& segment.ident.name == kw::SelfUpper
{
span_lint_and_then(
cx,
NEEDLESS_ARBITRARY_SELF_TYPE,
span,
"the type of the `self` parameter does not need to be arbitrary",
|diag| {
let mut applicability = Applicability::MachineApplicable;
let add = match binding_mode {
Mode::Value => String::new(),
Mode::Ref(None) => mutbl.ref_prefix_str().to_string(),
Mode::Ref(Some(lifetime)) => {
// In case we have a named lifetime, we check if the name comes from expansion.
// If it does, at this point we know the rest of the parameter was written by the user,
// so let them decide what the name of the lifetime should be.
// See #6089 for more details.
let lt_name = if lifetime.ident.span.from_expansion() {
applicability = Applicability::HasPlaceholders;
"'_".into()
} else {
snippet_with_applicability(cx, lifetime.ident.span, "'_", &mut applicability)
};
format!("&{lt_name} {mut_}", mut_ = mutbl.prefix_str())
},
};
let mut sugg = vec![(p.ty.span.with_lo(p.span.hi()), String::new())];
if !add.is_empty() {
sugg.push((p.span.shrink_to_lo(), add));
}
diag.multipart_suggestion_verbose("remove the type", sugg, applicability);
},
);
}
}
}

View file

@ -354,10 +354,7 @@ fn self_cmp_call<'tcx>(
needs_fully_qualified: &mut bool,
) -> bool {
match cmp_expr.kind {
ExprKind::Call(path, [_, _]) => path
.res(typeck)
.opt_def_id()
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)),
ExprKind::Call(path, [_, _]) => path.res(typeck).is_diag_item(cx, sym::ord_cmp_method),
ExprKind::MethodCall(_, recv, [_], ..) => {
let ExprKind::Path(path) = recv.kind else {
return false;

View file

@ -706,7 +706,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| !self.is_init_expr_freeze(
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| !self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
cx.tcx.typeck(item.owner_id),
@ -749,7 +749,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
!is_freeze
},
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| {
!self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),
@ -806,7 +806,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
// FIXME: we just assume mgca rhs's are freeze
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| {
_ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| {
!self.is_init_expr_freeze(
cx.tcx,
cx.typing_env(),

View file

@ -1,4 +1,5 @@
use super::ARITHMETIC_SIDE_EFFECTS;
use crate::clippy_utils::res::MaybeQPath as _;
use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint;
@ -6,7 +7,7 @@ use clippy_utils::res::MaybeDef;
use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Ty, UintTy};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, Symbol};
use {rustc_ast as ast, rustc_hir as hir};
@ -88,74 +89,16 @@ impl ArithmeticSideEffects {
self.allowed_unary.contains(ty_string_elem)
}
fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Adt(adt, substs) = ty.kind()
&& cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
&& let int_type = substs.type_at(0)
&& matches!(int_type.kind(), ty::Uint(_))
{
true
} else {
false
}
}
/// Verifies built-in types that have specific allowed operations
fn has_specific_allowed_type_and_operation<'tcx>(
cx: &LateContext<'tcx>,
lhs_ty: Ty<'tcx>,
op: hir::BinOpKind,
rhs_ty: Ty<'tcx>,
) -> bool {
let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping);
// If the RHS is `NonZero<u*>`, then division or module by zero will never occur.
if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem {
return true;
}
// `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module.
if is_sat_or_wrap(lhs_ty) {
return !is_div_or_rem;
}
false
}
// For example, 8i32 or &i64::MAX.
fn is_integral(ty: Ty<'_>) -> bool {
ty.peel_refs().is_integral()
}
// Common entry-point to avoid code duplication.
fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if is_from_proc_macro(cx, expr) {
return;
}
let msg = "arithmetic operation that can potentially result in unexpected side-effects";
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
self.expr_span = Some(expr.span);
}
/// Returns the numeric value of a literal integer originated from `expr`, if any.
///
/// Literal integers can be originated from adhoc declarations like `1`, associated constants
/// 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(lit) = actual.kind
&& let ast::LitKind::Int(n, _) = lit.node
{
return Some(n.get());
}
if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) {
return Some(n);
}
None
}
/// Methods like `add_assign` are send to their `BinOps` references.
fn manage_sugar_methods<'tcx>(
&mut self,
@ -213,59 +156,53 @@ impl ArithmeticSideEffects {
&& let hir::ExprKind::MethodCall(method, receiver, [], _) = actual_lhs.kind
&& method.ident.name == sym::get
&& let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs()
&& Self::is_non_zero_u(cx, receiver_ty)
&& let Some(1) = Self::literal_integer(cx, actual_rhs)
&& is_non_zero_u(cx, receiver_ty)
&& literal_integer(cx, actual_rhs) == Some(1)
{
return;
}
let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs();
let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs();
if self.has_allowed_binary(lhs_ty, rhs_ty) {
if self.has_allowed_binary(lhs_ty, rhs_ty)
| has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty)
| is_safe_due_to_smaller_source_type(cx, op, (actual_lhs, lhs_ty), actual_rhs)
| is_safe_due_to_smaller_source_type(cx, op, (actual_rhs, rhs_ty), actual_lhs)
{
return;
}
if Self::has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) {
return;
}
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
if is_integer(lhs_ty) && is_integer(rhs_ty) {
if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op {
// At least for integers, shifts are already handled by the CTFE
return;
}
match (
Self::literal_integer(cx, actual_lhs),
Self::literal_integer(cx, actual_rhs),
) {
(None, None) => false,
match (literal_integer(cx, actual_lhs), literal_integer(cx, actual_rhs)) {
(None, Some(n)) => match (&op, n) {
// Division and module are always valid if applied to non-zero integers
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
(hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => return,
// Adding or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
// Multiplication by 1 or 0 will never overflow
| (hir::BinOpKind::Mul, 0 | 1)
=> true,
_ => false,
=> return,
_ => {},
},
(Some(n), None) => match (&op, n) {
// Adding or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
// Multiplication by 1 or 0 will never overflow
| (hir::BinOpKind::Mul, 0 | 1)
=> true,
_ => false,
},
(Some(_), Some(_)) => {
matches!((lhs_ref_counter, rhs_ref_counter), (0, 0))
(Some(n), None)
if matches!(
(&op, n),
// Adding or subtracting zeros is always a no-op
(hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
// Multiplication by 1 or 0 will never overflow
| (hir::BinOpKind::Mul, 0 | 1)
) =>
{
return;
},
(Some(_), Some(_)) if matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) => return,
_ => {},
}
} else {
false
};
if !has_valid_op {
self.issue_lint(cx, expr);
}
self.issue_lint(cx, expr);
}
/// There are some integer methods like `wrapping_div` that will panic depending on the
@ -285,7 +222,7 @@ impl ArithmeticSideEffects {
return;
}
let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver);
if !Self::is_integral(instance_ty) {
if !is_integer(instance_ty) {
return;
}
self.manage_sugar_methods(cx, expr, receiver, ps, arg);
@ -293,7 +230,7 @@ impl ArithmeticSideEffects {
return;
}
let (actual_arg, _) = peel_hir_expr_refs(arg);
match Self::literal_integer(cx, actual_arg) {
match literal_integer(cx, actual_arg) {
None | Some(0) => self.issue_lint(cx, arg),
Some(_) => {},
}
@ -317,7 +254,7 @@ impl ArithmeticSideEffects {
return;
}
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
if Self::literal_integer(cx, actual_un_expr).is_some() {
if literal_integer(cx, actual_un_expr).is_some() {
return;
}
self.issue_lint(cx, expr);
@ -385,3 +322,120 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
}
}
}
/// Detects a type-casting conversion and returns the type of the original expression. For
/// example, `let foo = u64::from(bar)`.
fn find_original_primitive_ty<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<Ty<'tcx>> {
if let hir::ExprKind::Call(path, [arg]) = &expr.kind
&& path.res(cx).opt_def_id().is_diag_item(&cx.tcx, sym::from_fn)
{
Some(cx.typeck_results().expr_ty(arg))
} else {
None
}
}
/// Verifies built-in types that have specific allowed operations
fn has_specific_allowed_type_and_operation<'tcx>(
cx: &LateContext<'tcx>,
lhs_ty: Ty<'tcx>,
op: hir::BinOpKind,
rhs_ty: Ty<'tcx>,
) -> bool {
let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
let is_sat_or_wrap = |ty: Ty<'_>| matches!(ty.opt_diag_name(cx), Some(sym::Saturating | sym::Wrapping));
// If the RHS is `NonZero<u*>`, then division or module by zero will never occur.
if is_non_zero_u(cx, rhs_ty) && is_div_or_rem {
return true;
}
// `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module.
if is_sat_or_wrap(lhs_ty) {
return !is_div_or_rem;
}
false
}
// For example, `i8` or `u128` and possible associated references like `&&u16`.
fn is_integer(ty: Ty<'_>) -> bool {
ty.peel_refs().is_integral()
}
fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
if let ty::Adt(adt, substs) = ty.kind()
&& cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
&& let int_type = substs.type_at(0)
&& matches!(int_type.kind(), ty::Uint(_))
{
true
} else {
false
}
}
/// If one side is a literal it is possible to evaluate overflows as long as the other side has a
/// smaller type. `0` and `1` suffixes indicate different sides.
///
/// For example, `1000u64 + u64::from(some_runtime_variable_of_type_u8)`.
fn is_safe_due_to_smaller_source_type(
cx: &LateContext<'_>,
op: hir::BinOpKind,
(expr0, ty0): (&hir::Expr<'_>, Ty<'_>),
expr1: &hir::Expr<'_>,
) -> bool {
let Some(num0) = literal_integer(cx, expr0) else {
return false;
};
let Some(orig_ty1) = find_original_primitive_ty(cx, expr1) else {
return false;
};
let Some(num1) = max_int_num(orig_ty1) else {
return false;
};
let Some(rslt) = (match op {
hir::BinOpKind::Add => num0.checked_add(num1),
hir::BinOpKind::Mul => num0.checked_mul(num1),
_ => None,
}) else {
return false;
};
match ty0.peel_refs().kind() {
ty::Uint(UintTy::U16) => u16::try_from(rslt).is_ok(),
ty::Uint(UintTy::U32) => u32::try_from(rslt).is_ok(),
ty::Uint(UintTy::U64) => u64::try_from(rslt).is_ok(),
ty::Uint(UintTy::U128) => true,
ty::Uint(UintTy::Usize) => usize::try_from(rslt).is_ok(),
_ => false,
}
}
/// Returns the numeric value of a literal integer originated from `expr`, if any.
///
/// Literal integers can be originated from adhoc declarations like `1`, associated constants
/// 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(lit) = actual.kind
&& let ast::LitKind::Int(n, _) = lit.node
{
return Some(n.get());
}
if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) {
return Some(n);
}
None
}
fn max_int_num(ty: Ty<'_>) -> Option<u128> {
match ty.peel_refs().kind() {
ty::Uint(UintTy::U8) => Some(u8::MAX.into()),
ty::Uint(UintTy::U16) => Some(u16::MAX.into()),
ty::Uint(UintTy::U32) => Some(u32::MAX.into()),
ty::Uint(UintTy::U64) => Some(u64::MAX.into()),
ty::Uint(UintTy::U128) => Some(u128::MAX),
ty::Uint(UintTy::Usize) => usize::MAX.try_into().ok(),
_ => None,
}
}

View file

@ -6,16 +6,15 @@ use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_copy;
use clippy_utils::{
CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause,
is_in_const_context, peel_blocks, peel_hir_expr_while,
is_in_const_context, is_none_pattern, peel_blocks, peel_hir_expr_while,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::LangItem::ResultErr;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_expr, walk_path};
use rustc_hir::{
Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path,
QPath, UnOp,
Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
@ -313,11 +312,14 @@ impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
}
fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> {
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
let res = cx.qpath_res(qpath, pat.hir_id);
if res.ctor_parent(cx).is_lang_item(cx, OptionSome) {
if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind
&& let res = cx.qpath_res(qpath, pat.hir_id)
&& let Some(did) = res.ctor_parent(cx).opt_def_id()
{
let lang_items = cx.tcx.lang_items();
if Some(did) == lang_items.option_some_variant() {
return Some((inner_pat, false));
} else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) {
} else if Some(did) == lang_items.result_ok_variant() {
return Some((inner_pat, true));
}
}
@ -376,14 +378,7 @@ fn try_convert_match<'tcx>(
fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
match arm.pat.kind {
PatKind::Expr(PatExpr {
kind: PatExprKind::Path(qpath),
hir_id,
..
}) => cx
.qpath_res(qpath, *hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionNone),
_ if is_none_pattern(cx, arm.pat) => true,
PatKind::TupleStruct(ref qpath, [first_pat], _) => {
cx.qpath_res(qpath, arm.pat.hir_id)
.ctor_parent(cx)

View file

@ -0,0 +1,49 @@
use super::CMP_NULL;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_lint_allowed, sym};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
op: BinOpKind,
l: &Expr<'_>,
r: &Expr<'_>,
) -> bool {
let non_null_path_snippet = match (
is_lint_allowed(cx, CMP_NULL, expr.hir_id),
is_null_path(cx, l),
is_null_path(cx, r),
) {
(false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(),
(false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
_ => return false,
};
let invert = if op == BinOpKind::Eq { "" } else { "!" };
span_lint_and_sugg(
cx,
CMP_NULL,
expr.span,
"comparing with null is better expressed by the `.is_null()` method",
"try",
format!("{invert}{non_null_path_snippet}.is_null()",),
Applicability::MachineApplicable,
);
true
}
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(pathexp, []) = expr.kind {
matches!(
pathexp.basic_res().opt_diag_name(cx),
Some(sym::ptr_null | sym::ptr_null_mut)
)
} else {
false
}
}

202
clippy_lints/src/ptr/mod.rs Normal file
View file

@ -0,0 +1,202 @@
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, ImplItemKind, ItemKind, Node, TraitFn, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
mod cmp_null;
mod mut_from_ref;
mod ptr_arg;
mod ptr_eq;
declare_clippy_lint! {
/// ### What it does
/// This lint checks for function arguments of type `&String`, `&Vec`,
/// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
/// with the appropriate `.to_owned()`/`to_string()` calls.
///
/// ### Why is this bad?
/// Requiring the argument to be of the specific type
/// makes the function less useful for no benefit; slices in the form of `&[T]`
/// or `&str` usually suffice and can be obtained from other types, too.
///
/// ### Known problems
/// There may be `fn(&Vec)`-typed references pointing to your function.
/// If you have them, you will get a compiler error after applying this lint's
/// suggestions. You then have the choice to undo your changes or change the
/// type of the reference.
///
/// Note that if the function is part of your public interface, there may be
/// other crates referencing it, of which you may not be aware. Carefully
/// deprecate the function before applying the lint suggestions in this case.
///
/// ### Example
/// ```ignore
/// fn foo(&Vec<u32>) { .. }
/// ```
///
/// Use instead:
/// ```ignore
/// fn foo(&[u32]) { .. }
/// ```
#[clippy::version = "pre 1.29.0"]
pub PTR_ARG,
style,
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
}
declare_clippy_lint! {
/// ### What it does
/// This lint checks for equality comparisons with `ptr::null`
///
/// ### Why is this bad?
/// It's easier and more readable to use the inherent
/// `.is_null()`
/// method instead
///
/// ### Example
/// ```rust,ignore
/// use std::ptr;
///
/// if x == ptr::null {
/// // ..
/// }
/// ```
///
/// Use instead:
/// ```rust,ignore
/// if x.is_null() {
/// // ..
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub CMP_NULL,
style,
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
}
declare_clippy_lint! {
/// ### What it does
/// This lint checks for functions that take immutable references and return
/// mutable ones. This will not trigger if no unsafe code exists as there
/// are multiple safe functions which will do this transformation
///
/// To be on the conservative side, if there's at least one mutable
/// reference with the output lifetime, this lint will not trigger.
///
/// ### Why is this bad?
/// Creating a mutable reference which can be repeatably derived from an
/// immutable reference is unsound as it allows creating multiple live
/// mutable references to the same object.
///
/// This [error](https://github.com/rust-lang/rust/issues/39465) actually
/// lead to an interim Rust release 1.15.1.
///
/// ### Known problems
/// This pattern is used by memory allocators to allow allocating multiple
/// objects while returning mutable references to each one. So long as
/// different mutable references are returned each time such a function may
/// be safe.
///
/// ### Example
/// ```ignore
/// fn foo(&Foo) -> &mut Bar { .. }
/// ```
#[clippy::version = "pre 1.29.0"]
pub MUT_FROM_REF,
correctness,
"fns that create mutable refs from immutable ref args"
}
declare_clippy_lint! {
/// ### What it does
/// Use `std::ptr::eq` when applicable
///
/// ### Why is this bad?
/// `ptr::eq` can be used to compare `&T` references
/// (which coerce to `*const T` implicitly) by their address rather than
/// comparing the values they point to.
///
/// ### Example
/// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
/// assert!(a as *const _ as usize == b as *const _ as usize);
/// ```
/// Use instead:
/// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
/// assert!(std::ptr::eq(a, b));
/// ```
#[clippy::version = "1.49.0"]
pub PTR_EQ,
style,
"use `std::ptr::eq` when comparing raw pointers"
}
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]);
impl<'tcx> LateLintPass<'tcx> for Ptr {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
if matches!(trait_method, TraitFn::Provided(_)) {
// Handled by `check_body`.
return;
}
mut_from_ref::check(cx, sig, None);
ptr_arg::check_trait_item(cx, item.owner_id, sig);
}
}
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id);
let (item_id, sig, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn { sig, .. } = &i.kind {
(i.owner_id, sig, false)
} else {
return;
}
},
Some((_, Node::ImplItem(i))) => {
if !matches!(parents.next(),
Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
) {
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
(i.owner_id, sig, false)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
(i.owner_id, sig, true)
} else {
return;
}
},
_ => return,
};
mut_from_ref::check(cx, sig, Some(body));
ptr_arg::check_body(cx, body, item_id, sig, is_trait_item);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(op, l, r) = expr.kind
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
{
#[expect(
clippy::collapsible_if,
reason = "the outer `if`s check the HIR, the inner ones run lints"
)]
if !cmp_null::check(cx, expr, op.node, l, r) {
ptr_eq::check(cx, op.node, l, r, expr.span);
}
}
}
}

View file

@ -0,0 +1,75 @@
use super::MUT_FROM_REF;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::contains_unsafe_block;
use rustc_errors::MultiSpan;
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, Body, FnRetTy, FnSig, GenericArg, Lifetime, Mutability, TyKind};
use rustc_lint::LateContext;
use rustc_span::Span;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) {
let FnRetTy::Return(ty) = sig.decl.output else { return };
for (out, mutability, out_span) in get_lifetimes(ty) {
if mutability != Some(Mutability::Mut) {
continue;
}
let out_region = cx.tcx.named_bound_var(out.hir_id);
// `None` if one of the types contains `&'a mut T` or `T<'a>`.
// Else, contains all the locations of `&'a T` types.
let args_immut_refs: Option<Vec<Span>> = sig
.decl
.inputs
.iter()
.flat_map(get_lifetimes)
.filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
.map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span))
.collect();
if let Some(args_immut_refs) = args_immut_refs
&& !args_immut_refs.is_empty()
&& body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
{
span_lint_and_then(
cx,
MUT_FROM_REF,
out_span,
"mutable borrow from immutable input(s)",
|diag| {
let ms = MultiSpan::from_spans(args_immut_refs);
diag.span_note(ms, "immutable borrow here");
},
);
}
}
}
struct LifetimeVisitor<'tcx> {
result: Vec<(&'tcx Lifetime, Option<Mutability>, Span)>,
}
impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
if let TyKind::Ref(lt, ref m) = ty.kind {
self.result.push((lt, Some(m.mutbl), ty.span));
}
hir::intravisit::walk_ty(self, ty);
}
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
if let GenericArg::Lifetime(lt) = generic_arg {
self.result.push((lt, None, generic_arg.span()));
}
hir::intravisit::walk_generic_arg(self, generic_arg);
}
}
/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not.
///
/// The second field of the vector's elements indicate if the lifetime is attached to a
/// shared reference, a mutable reference, or neither.
fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option<Mutability>, Span)> {
use hir::intravisit::VisitorExt as _;
let mut visitor = LifetimeVisitor { result: Vec::new() };
visitor.visit_ty_unambig(ty);
visitor.result
}

View file

@ -1,24 +1,22 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use super::PTR_ARG;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::Sugg;
use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym};
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, sym};
use hir::LifetimeKind;
use rustc_abi::ExternAbi;
use rustc_errors::{Applicability, MultiSpan};
use rustc_errors::Applicability;
use rustc_hir::hir_id::{HirId, HirIdMap};
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind,
self as hir, AnonConst, BindingMode, Body, Expr, ExprKind, FnSig, GenericArg, Lifetime, Mutability, Node, OwnerId,
Param, PatKind, QPath, TyKind,
};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
use rustc_span::symbol::Symbol;
use rustc_trait_selection::infer::InferCtxtExt as _;
@ -27,260 +25,65 @@ use std::{fmt, iter};
use crate::vec::is_allowed_vec_method;
declare_clippy_lint! {
/// ### What it does
/// This lint checks for function arguments of type `&String`, `&Vec`,
/// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
/// with the appropriate `.to_owned()`/`to_string()` calls.
///
/// ### Why is this bad?
/// Requiring the argument to be of the specific type
/// makes the function less useful for no benefit; slices in the form of `&[T]`
/// or `&str` usually suffice and can be obtained from other types, too.
///
/// ### Known problems
/// There may be `fn(&Vec)`-typed references pointing to your function.
/// If you have them, you will get a compiler error after applying this lint's
/// suggestions. You then have the choice to undo your changes or change the
/// type of the reference.
///
/// Note that if the function is part of your public interface, there may be
/// other crates referencing it, of which you may not be aware. Carefully
/// deprecate the function before applying the lint suggestions in this case.
///
/// ### Example
/// ```ignore
/// fn foo(&Vec<u32>) { .. }
/// ```
///
/// Use instead:
/// ```ignore
/// fn foo(&[u32]) { .. }
/// ```
#[clippy::version = "pre 1.29.0"]
pub PTR_ARG,
style,
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
}
declare_clippy_lint! {
/// ### What it does
/// This lint checks for equality comparisons with `ptr::null`
///
/// ### Why is this bad?
/// It's easier and more readable to use the inherent
/// `.is_null()`
/// method instead
///
/// ### Example
/// ```rust,ignore
/// use std::ptr;
///
/// if x == ptr::null {
/// // ..
/// }
/// ```
///
/// Use instead:
/// ```rust,ignore
/// if x.is_null() {
/// // ..
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
pub CMP_NULL,
style,
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
}
declare_clippy_lint! {
/// ### What it does
/// This lint checks for functions that take immutable references and return
/// mutable ones. This will not trigger if no unsafe code exists as there
/// are multiple safe functions which will do this transformation
///
/// To be on the conservative side, if there's at least one mutable
/// reference with the output lifetime, this lint will not trigger.
///
/// ### Why is this bad?
/// Creating a mutable reference which can be repeatably derived from an
/// immutable reference is unsound as it allows creating multiple live
/// mutable references to the same object.
///
/// This [error](https://github.com/rust-lang/rust/issues/39465) actually
/// lead to an interim Rust release 1.15.1.
///
/// ### Known problems
/// This pattern is used by memory allocators to allow allocating multiple
/// objects while returning mutable references to each one. So long as
/// different mutable references are returned each time such a function may
/// be safe.
///
/// ### Example
/// ```ignore
/// fn foo(&Foo) -> &mut Bar { .. }
/// ```
#[clippy::version = "pre 1.29.0"]
pub MUT_FROM_REF,
correctness,
"fns that create mutable refs from immutable ref args"
}
declare_clippy_lint! {
/// ### What it does
/// Use `std::ptr::eq` when applicable
///
/// ### Why is this bad?
/// `ptr::eq` can be used to compare `&T` references
/// (which coerce to `*const T` implicitly) by their address rather than
/// comparing the values they point to.
///
/// ### Example
/// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
/// assert!(a as *const _ as usize == b as *const _ as usize);
/// ```
/// Use instead:
/// ```no_run
/// let a = &[1, 2, 3];
/// let b = &[1, 2, 3];
///
/// assert!(std::ptr::eq(a, b));
/// ```
#[clippy::version = "1.49.0"]
pub PTR_EQ,
style,
"use `std::ptr::eq` when comparing raw pointers"
}
declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]);
impl<'tcx> LateLintPass<'tcx> for Ptr {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
if matches!(trait_method, TraitFn::Provided(_)) {
// Handled by check body.
return;
}
check_mut_from_ref(cx, sig, None);
if !matches!(sig.header.abi, ExternAbi::Rust) {
// Ignore `extern` functions with non-Rust calling conventions
return;
}
for arg in check_fn_args(
cx,
cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(),
sig.decl.inputs,
&[],
)
.filter(|arg| arg.mutability() == Mutability::Not)
{
span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| {
diag.span_suggestion(
arg.span,
"change this to",
format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
Applicability::Unspecified,
);
});
}
}
pub(super) fn check_body<'tcx>(
cx: &LateContext<'tcx>,
body: &Body<'tcx>,
item_id: OwnerId,
sig: &FnSig<'tcx>,
is_trait_item: bool,
) {
if !matches!(sig.header.abi, ExternAbi::Rust) {
// Ignore `extern` functions with non-Rust calling conventions
return;
}
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id);
let (item_id, sig, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn { sig, .. } = &i.kind {
(i.owner_id, sig, false)
} else {
return;
}
},
Some((_, Node::ImplItem(i))) => {
if !matches!(parents.next(),
Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
) {
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
(i.owner_id, sig, false)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
(i.owner_id, sig, true)
} else {
return;
}
},
_ => return,
};
let decl = sig.decl;
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
.collect();
let results = check_ptr_arg_usage(cx, body, &lint_args);
check_mut_from_ref(cx, sig, Some(body));
if !matches!(sig.header.abi, ExternAbi::Rust) {
// Ignore `extern` functions with non-Rust calling conventions
return;
}
let decl = sig.decl;
let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
.collect();
let results = check_ptr_arg_usage(cx, body, &lint_args);
for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) {
span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
diag.multipart_suggestion(
"change this to",
iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
.chain(result.replacements.iter().map(|r| {
(
r.expr_span,
format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
)
}))
.collect(),
Applicability::Unspecified,
);
});
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Binary(op, l, r) = expr.kind
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
{
let non_null_path_snippet = match (
is_lint_allowed(cx, CMP_NULL, expr.hir_id),
is_null_path(cx, l),
is_null_path(cx, r),
) {
(false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(),
(false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(),
_ => return check_ptr_eq(cx, expr, op.node, l, r),
};
let invert = if op.node == BinOpKind::Eq { "" } else { "!" };
span_lint_and_sugg(
cx,
CMP_NULL,
expr.span,
"comparing with null is better expressed by the `.is_null()` method",
"try",
format!("{invert}{non_null_path_snippet}.is_null()",),
Applicability::MachineApplicable,
for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) {
span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
diag.multipart_suggestion(
"change this to",
iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
.chain(result.replacements.iter().map(|r| {
(
r.expr_span,
format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
)
}))
.collect(),
Applicability::Unspecified,
);
}
});
}
}
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item_id: OwnerId, sig: &FnSig<'tcx>) {
if !matches!(sig.header.abi, ExternAbi::Rust) {
// Ignore `extern` functions with non-Rust calling conventions
return;
}
for arg in check_fn_args(
cx,
cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(),
sig.decl.inputs,
&[],
)
.filter(|arg| arg.mutability() == Mutability::Not)
{
span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| {
diag.span_suggestion(
arg.span,
"change this to",
format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
Applicability::Unspecified,
);
});
}
}
@ -393,10 +196,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
hir_tys: &'tcx [hir::Ty<'tcx>],
params: &'tcx [Param<'tcx>],
) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
fn_sig
.inputs()
.iter()
.zip(hir_tys.iter())
iter::zip(fn_sig.inputs(), hir_tys)
.enumerate()
.filter_map(move |(i, (ty, hir_ty))| {
if let ty::Ref(_, ty, mutability) = *ty.kind()
@ -499,41 +299,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
})
}
fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) {
let FnRetTy::Return(ty) = sig.decl.output else { return };
for (out, mutability, out_span) in get_lifetimes(ty) {
if mutability != Some(Mutability::Mut) {
continue;
}
let out_region = cx.tcx.named_bound_var(out.hir_id);
// `None` if one of the types contains `&'a mut T` or `T<'a>`.
// Else, contains all the locations of `&'a T` types.
let args_immut_refs: Option<Vec<Span>> = sig
.decl
.inputs
.iter()
.flat_map(get_lifetimes)
.filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region)
.map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span))
.collect();
if let Some(args_immut_refs) = args_immut_refs
&& !args_immut_refs.is_empty()
&& body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value))
{
span_lint_and_then(
cx,
MUT_FROM_REF,
out_span,
"mutable borrow from immutable input(s)",
|diag| {
let ms = MultiSpan::from_spans(args_immut_refs);
diag.span_note(ms, "immutable borrow here");
},
);
}
}
}
#[expect(clippy::too_many_lines)]
fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
struct V<'cx, 'tcx> {
@ -658,11 +423,11 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
match param.pat.kind {
PatKind::Binding(BindingMode::NONE, id, ident, None)
if !is_lint_allowed(cx, PTR_ARG, param.hir_id)
// Let's not lint for the current parameter. The user may still intend to mutate
// (or, if not mutate, then perhaps call a method that's not otherwise available
// for) the referenced value behind the parameter with the underscore being only
// temporary.
&& !ident.name.as_str().starts_with('_') =>
// Let's not lint for the current parameter. The user may still intend to mutate
// (or, if not mutate, then perhaps call a method that's not otherwise available
// for) the referenced value behind the parameter with the underscore being only
// temporary.
&& !ident.name.as_str().starts_with('_') =>
{
Some((id, i))
},
@ -708,123 +473,3 @@ fn matches_preds<'tcx>(
.must_apply_modulo_regions(),
})
}
struct LifetimeVisitor<'tcx> {
result: Vec<(&'tcx Lifetime, Option<Mutability>, Span)>,
}
impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) {
if let TyKind::Ref(lt, ref m) = ty.kind {
self.result.push((lt, Some(m.mutbl), ty.span));
}
hir::intravisit::walk_ty(self, ty);
}
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
if let GenericArg::Lifetime(lt) = generic_arg {
self.result.push((lt, None, generic_arg.span()));
}
hir::intravisit::walk_generic_arg(self, generic_arg);
}
}
/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not.
///
/// The second field of the vector's elements indicate if the lifetime is attached to a
/// shared reference, a mutable reference, or neither.
fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option<Mutability>, Span)> {
use hir::intravisit::VisitorExt as _;
let mut visitor = LifetimeVisitor { result: Vec::new() };
visitor.visit_ty_unambig(ty);
visitor.result
}
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(pathexp, []) = expr.kind {
matches!(
pathexp.basic_res().opt_diag_name(cx),
Some(sym::ptr_null | sym::ptr_null_mut)
)
} else {
false
}
}
fn check_ptr_eq<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
op: BinOpKind,
left: &'tcx Expr<'_>,
right: &'tcx Expr<'_>,
) {
if expr.span.from_expansion() {
return;
}
// Remove one level of usize conversion if any
let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
(Some(lhs), Some(rhs)) => (lhs, rhs, true),
_ => (left, right, false),
};
// This lint concerns raw pointers
let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() {
return;
}
let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) =
(peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));
if !(usize_peeled || left_casts_peeled || right_casts_peeled) {
return;
}
let mut app = Applicability::MachineApplicable;
let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app);
let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app);
{
let Some(top_crate) = std_or_core(cx) else { return };
let invert = if op == BinOpKind::Eq { "" } else { "!" };
span_lint_and_sugg(
cx,
PTR_EQ,
expr.span,
format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
"try",
format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"),
app,
);
}
}
// If the given expression is a cast to a usize, return the lhs of the cast
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if !cast_expr.span.from_expansion()
&& cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
&& let ExprKind::Cast(expr, _) = cast_expr.kind
{
Some(expr)
} else {
None
}
}
// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been
// peeled or not.
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
if !expr.span.from_expansion()
&& let ExprKind::Cast(inner, _) = expr.kind
&& let ty::RawPtr(target_ty, _) = expr_ty.kind()
&& let inner_ty = cx.typeck_results().expr_ty(inner)
&& let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind()
&& target_ty == inner_target_ty
{
(peel_raw_casts(cx, inner, inner_ty).0, true)
} else {
(expr, false)
}
}

View file

@ -0,0 +1,87 @@
use super::PTR_EQ;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::std_or_core;
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
op: BinOpKind,
left: &'tcx Expr<'_>,
right: &'tcx Expr<'_>,
span: Span,
) {
if span.from_expansion() {
return;
}
// Remove one level of usize conversion if any
let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
(Some(lhs), Some(rhs)) => (lhs, rhs, true),
_ => (left, right, false),
};
// This lint concerns raw pointers
let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() {
return;
}
let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) =
(peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));
if !(usize_peeled || left_casts_peeled || right_casts_peeled) {
return;
}
let mut app = Applicability::MachineApplicable;
let ctxt = span.ctxt();
let left_snip = Sugg::hir_with_context(cx, left_var, ctxt, "_", &mut app);
let right_snip = Sugg::hir_with_context(cx, right_var, ctxt, "_", &mut app);
{
let Some(top_crate) = std_or_core(cx) else { return };
let invert = if op == BinOpKind::Eq { "" } else { "!" };
span_lint_and_sugg(
cx,
PTR_EQ,
span,
format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
"try",
format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"),
app,
);
}
}
// If the given expression is a cast to a usize, return the lhs of the cast
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if !cast_expr.span.from_expansion()
&& cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
&& let ExprKind::Cast(expr, _) = cast_expr.kind
{
Some(expr)
} else {
None
}
}
// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been
// peeled or not.
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
if !expr.span.from_expansion()
&& let ExprKind::Cast(inner, _) = expr.kind
&& let ty::RawPtr(target_ty, _) = expr_ty.kind()
&& let inner_ty = cx.typeck_results().expr_ty(inner)
&& let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind()
&& target_ty == inner_target_ty
{
(peel_raw_casts(cx, inner, inner_ty).0, true)
} else {
(expr, false)
}
}

View file

@ -150,7 +150,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren();
// Take care when binding is `ref`
let sugg = if let PatKind::Binding(
BindingMode(ByRef::Yes(_,ref_mutability), binding_mutability),
BindingMode(ByRef::Yes(_, ref_mutability), binding_mutability),
_hir_id,
ident,
subpattern,

View file

@ -3,11 +3,17 @@ use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_default_equivalent_call, local_is_initialized};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::smallvec::SmallVec;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath};
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, LangItem, QPath};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::sym;
use rustc_middle::hir::place::ProjectionKind;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
use rustc_span::{Symbol, sym};
declare_clippy_lint! {
/// ### What it does
@ -33,17 +39,57 @@ declare_clippy_lint! {
perf,
"assigning a newly created box to `Box<T>` is inefficient"
}
declare_lint_pass!(ReplaceBox => [REPLACE_BOX]);
#[derive(Default)]
pub struct ReplaceBox {
consumed_locals: FxHashSet<HirId>,
loaded_bodies: SmallVec<[BodyId; 2]>,
}
impl ReplaceBox {
fn get_consumed_locals(&mut self, cx: &LateContext<'_>) -> &FxHashSet<HirId> {
if let Some(body_id) = cx.enclosing_body
&& !self.loaded_bodies.contains(&body_id)
{
self.loaded_bodies.push(body_id);
ExprUseVisitor::for_clippy(
cx,
cx.tcx.hir_body_owner_def_id(body_id),
MovedVariablesCtxt {
consumed_locals: &mut self.consumed_locals,
},
)
.consume_body(cx.tcx.hir_body(body_id))
.into_ok();
}
&self.consumed_locals
}
}
impl_lint_pass!(ReplaceBox => [REPLACE_BOX]);
impl LateLintPass<'_> for ReplaceBox {
fn check_body_post(&mut self, _: &LateContext<'_>, body: &Body<'_>) {
if self.loaded_bodies.first().is_some_and(|&x| x == body.id()) {
self.consumed_locals.clear();
self.loaded_bodies.clear();
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Assign(lhs, rhs, _) = &expr.kind
&& !lhs.span.from_expansion()
&& !rhs.span.from_expansion()
&& let lhs_ty = cx.typeck_results().expr_ty(lhs)
&& let Some(inner_ty) = lhs_ty.boxed_ty()
// No diagnostic for late-initialized locals
&& lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local))
&& let Some(inner_ty) = lhs_ty.boxed_ty()
// No diagnostic if this is a local that has been moved, or the field
// of a local that has been moved, or several chained field accesses of a local
&& local_base(lhs).is_none_or(|(base_id, _)| {
!self.get_consumed_locals(cx).contains(&base_id)
})
{
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
&& implements_trait(cx, inner_ty, default_trait_id, &[])
@ -109,3 +155,46 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<
None
}
}
struct MovedVariablesCtxt<'a> {
consumed_locals: &'a mut FxHashSet<HirId>,
}
impl<'tcx> Delegate<'tcx> for MovedVariablesCtxt<'_> {
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
if let PlaceBase::Local(id) = cmt.place.base
&& let mut projections = cmt
.place
.projections
.iter()
.filter(|x| matches!(x.kind, ProjectionKind::Deref))
// Either no deref or multiple derefs
&& (projections.next().is_none() || projections.next().is_some())
{
self.consumed_locals.insert(id);
}
}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}
/// A local place followed by optional fields
type IdFields = (HirId, Vec<Symbol>);
/// If `expr` is a local variable with optional field accesses, return it.
fn local_base(expr: &Expr<'_>) -> Option<IdFields> {
match expr.kind {
ExprKind::Path(qpath) => qpath.res_local_id().map(|id| (id, Vec::new())),
ExprKind::Field(expr, field) => local_base(expr).map(|(id, mut fields)| {
fields.push(field.name);
(id, fields)
}),
_ => None,
}
}

View file

@ -3,7 +3,7 @@ use clippy_utils::res::MaybeResPath;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg};
use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_non_whitespace};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, PatKind, StmtKind};
@ -27,7 +27,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>)
&& !initexpr.span.in_external_macro(cx.sess().source_map())
&& !retexpr.span.in_external_macro(cx.sess().source_map())
&& !local.span.from_expansion()
&& !span_contains_cfg(cx, stmt.span.between(retexpr.span))
&& !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), true)
{
span_lint_hir_and_then(
cx,

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::{indent_of, snippet};
use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym};
use clippy_utils::{expr_or_init, get_builtin_attr, peel_hir_expr_unary, sym};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@ -167,7 +167,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool {
if let Some(adt) = ty.ty_adt_def() {
let mut iter = get_attr(
let mut iter = get_builtin_attr(
self.cx.sess(),
self.cx.tcx.get_all_attrs(adt.did()),
sym::has_significant_drop,

View file

@ -1,14 +1,15 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::VecArgs;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_no_std_crate, sym};
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr};
use rustc_hir::{Expr, ExprKind, StructTailExpr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::DesugaringKind;
use std::fmt::{self, Display, Formatter};
declare_clippy_lint! {
@ -86,19 +87,21 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
return;
};
let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else {
let ExprKind::Struct(_, [start, end], StructTailExpr::None) = inner_expr.kind else {
return;
};
if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range)
if inner_expr.span.is_desugaring(DesugaringKind::RangeExpr)
&& let ty = cx.typeck_results().expr_ty(start.expr)
&& let Some(snippet) = span.get_source_text(cx)
// `is_from_proc_macro` will skip any `vec![]`. Let's not!
&& snippet.starts_with(suggested_type.starts_with())
&& snippet.ends_with(suggested_type.ends_with())
&& let Some(start_snippet) = start.span.get_source_text(cx)
&& let Some(end_snippet) = end.span.get_source_text(cx)
{
let mut applicability = Applicability::MachineApplicable;
let (start_snippet, _) = snippet_with_context(cx, start.expr.span, span.ctxt(), "..", &mut applicability);
let (end_snippet, _) = snippet_with_context(cx, end.expr.span, span.ctxt(), "..", &mut applicability);
let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step)
&& implements_trait(cx, ty, step_def_id, &[])
{
@ -129,7 +132,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
span,
"if you wanted a `Vec` that contains the entire range, try",
format!("({start_snippet}..{end_snippet}).collect::<std::vec::Vec<{ty}>>()"),
Applicability::MaybeIncorrect,
applicability,
);
}
@ -138,7 +141,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
inner_expr.span,
format!("if you wanted {suggested_type} of len {end_snippet}, try"),
format!("{start_snippet}; {end_snippet}"),
Applicability::MaybeIncorrect,
applicability,
);
}
},

View file

@ -435,10 +435,11 @@ declare_clippy_lint! {
/// to infer a technically correct yet unexpected type.
///
/// ### Example
/// ```no_run
/// ```
/// # unsafe {
/// let mut x: i32 = 0;
/// // Avoid "naked" calls to `transmute()`!
/// let x: i32 = std::mem::transmute([1u16, 2u16]);
/// x = std::mem::transmute([1u16, 2u16]);
///
/// // `first_answers` is intended to transmute a slice of bool to a slice of u8.
/// // But the programmer forgot to index the first element of the outer slice,
@ -449,7 +450,7 @@ declare_clippy_lint! {
/// # }
/// ```
/// Use instead:
/// ```no_run
/// ```
/// # unsafe {
/// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]);
///

View file

@ -1,115 +1,59 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::qpath_generic_tys;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::res::MaybeResPath;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, QPath, TyKind};
use rustc_hir::{QPath, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use std::borrow::Cow;
use super::RC_BUFFER;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
let app = Applicability::Unspecified;
let name = cx.tcx.get_diagnostic_name(def_id);
if name == Some(sym::Rc) {
if let Some(alternate) = match_buffer_type(cx, qpath) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app);
},
);
} else {
let Some(ty) = qpath_generic_tys(qpath).next() else {
return false;
};
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
return false;
}
let TyKind::Path(qpath) = &ty.kind else { return false };
let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Rc<T>` when T is a buffer type",
|diag| {
let mut applicability = app;
diag.span_suggestion(
hir_ty.span,
"try",
format!(
"Rc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
app,
);
},
);
return true;
}
} else if name == Some(sym::Arc) {
if let Some(alternate) = match_buffer_type(cx, qpath) {
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
|diag| {
diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app);
},
);
} else if let Some(ty) = qpath_generic_tys(qpath).next() {
if !ty.basic_res().is_diag_item(cx, sym::Vec) {
return false;
}
let TyKind::Path(qpath) = &ty.kind else { return false };
let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
"usage of `Arc<T>` when T is a buffer type",
|diag| {
let mut applicability = app;
diag.span_suggestion(
hir_ty.span,
"try",
format!(
"Arc<[{}]>",
snippet_with_applicability(cx, inner_span, "..", &mut applicability)
),
app,
);
},
);
return true;
}
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
let mut app = Applicability::Unspecified;
let rc = match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::Rc) => "Rc",
Some(sym::Arc) => "Arc",
_ => return false,
};
if let Some(ty) = qpath_generic_tys(qpath).next()
&& let Some(alternate) = match_buffer_type(cx, ty, &mut app)
{
span_lint_and_then(
cx,
RC_BUFFER,
hir_ty.span,
format!("usage of `{rc}<T>` when `T` is a buffer type"),
|diag| {
diag.span_suggestion_verbose(ty.span, "try", alternate, app);
},
);
true
} else {
false
}
false
}
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
let ty = qpath_generic_tys(qpath).next()?;
fn match_buffer_type(
cx: &LateContext<'_>,
ty: &Ty<'_>,
applicability: &mut Applicability,
) -> Option<Cow<'static, str>> {
let id = ty.basic_res().opt_def_id()?;
let path = match cx.tcx.get_diagnostic_name(id) {
Some(sym::OsString) => "std::ffi::OsStr",
Some(sym::PathBuf) => "std::path::Path",
_ if Some(id) == cx.tcx.lang_items().string() => "str",
Some(sym::OsString) => "std::ffi::OsStr".into(),
Some(sym::PathBuf) => "std::path::Path".into(),
Some(sym::Vec) => {
let TyKind::Path(vec_qpath) = &ty.kind else {
return None;
};
let vec_generic_ty = qpath_generic_tys(vec_qpath).next()?;
let snippet = snippet_with_applicability(cx, vec_generic_ty.span, "_", applicability);
format!("[{snippet}]").into()
},
_ if Some(id) == cx.tcx.lang_items().string() => "str".into(),
_ => return None,
};
Some(path)

View file

@ -215,7 +215,13 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
}
}
fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, (span, help_span): (Span, Span), is_doc: bool) {
#[expect(clippy::too_many_lines)]
fn check_has_safety_comment<'tcx>(
cx: &LateContext<'tcx>,
item: &hir::Item<'tcx>,
(span, help_span): (Span, Span),
is_doc: bool,
) {
match &item.kind {
ItemKind::Impl(Impl {
of_trait: Some(of_trait),
@ -236,12 +242,14 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>
ItemKind::Impl(_) => {},
// const and static items only need a safety comment if their body is an unsafe block, lint otherwise
&ItemKind::Const(.., ct_rhs) => {
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) {
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) {
let expr = const_item_rhs_to_expr(cx.tcx, ct_rhs);
if let Some(expr) = expr && !matches!(
expr.kind, hir::ExprKind::Block(block, _)
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
) {
if let Some(expr) = expr
&& !matches!(
expr.kind, hir::ExprKind::Block(block, _)
if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
)
{
span_lint_and_then(
cx,
UNNECESSARY_SAFETY_COMMENT,
@ -256,8 +264,8 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>
);
}
}
}
&ItemKind::Static(.., body) => {
},
&ItemKind::Static(.., body) => {
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
let body = cx.tcx.hir_body(body);
if !matches!(

View file

@ -35,18 +35,17 @@ declare_lint_pass!(UnnecessaryMapOnConstructor => [UNNECESSARY_MAP_ON_CONSTRUCTO
impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if expr.span.from_expansion() {
return;
}
if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
if !expr.span.from_expansion()
&& let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind
&& !map_arg.span.from_expansion()
&& let hir::ExprKind::Path(fun) = map_arg.kind
&& let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx)
{
let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind
&& let hir::ExprKind::Path(constructor_path) = constructor.kind
&& !constructor.span.from_expansion()
&& !arg.span.from_expansion()
{
if constructor.span.from_expansion() || arg.span.from_expansion() {
return;
}
(constructor_path, arg)
} else {
return;
@ -67,29 +66,22 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor {
_ => return,
}
if let hir::ExprKind::Path(fun) = map_arg.kind {
if map_arg.span.from_expansion() {
return;
}
let mut applicability = Applicability::MachineApplicable;
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability);
let constructor_snippet =
snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability);
let constructor_arg_snippet =
snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability);
span_lint_and_sugg(
cx,
UNNECESSARY_MAP_ON_CONSTRUCTOR,
expr.span,
format!(
"unnecessary {} on constructor {constructor_snippet}(_)",
path.ident.name
),
"try",
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
applicability,
);
}
let mut app = Applicability::MachineApplicable;
let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut app);
let constructor_snippet = snippet_with_applicability(cx, constructor_path.span(), "_", &mut app);
let constructor_arg_snippet = snippet_with_applicability(cx, constructor_item.span, "_", &mut app);
span_lint_and_sugg(
cx,
UNNECESSARY_MAP_ON_CONSTRUCTOR,
expr.span,
format!(
"unnecessary `{}` on constructor `{constructor_snippet}(_)`",
path.ident.name
),
"try",
format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"),
app,
);
}
}
}

View file

@ -10,6 +10,7 @@ use rustc_ast::mut_visit::*;
use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind, Pinnedness};
use rustc_ast_pretty::pprust;
use rustc_data_structures::thin_vec::{ThinVec, thin_vec};
use rustc_data_structures::thinvec::ExtractIf;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::impl_lint_pass;
@ -98,7 +99,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
return;
}
let mut pat = Box::new(pat.clone());
let mut pat = pat.clone();
// Nix all the paren patterns everywhere so that they aren't in our way.
remove_all_parens(&mut pat);
@ -120,7 +121,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
}
/// Remove all `(p)` patterns in `pat`.
fn remove_all_parens(pat: &mut Box<Pat>) {
fn remove_all_parens(pat: &mut Pat) {
#[derive(Default)]
struct Visitor {
/// If is not in the outer most pattern. This is needed to avoid removing the outermost
@ -143,7 +144,7 @@ fn remove_all_parens(pat: &mut Box<Pat>) {
}
/// Insert parens where necessary according to Rust's precedence rules for patterns.
fn insert_necessary_parens(pat: &mut Box<Pat>) {
fn insert_necessary_parens(pat: &mut Pat) {
struct Visitor;
impl MutVisitor for Visitor {
fn visit_pat(&mut self, pat: &mut Pat) {
@ -152,7 +153,8 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
let target = match &mut pat.kind {
// `i @ a | b`, `box a | b`, and `& mut? a | b`.
Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)`
// `&(mut x)`
Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p,
_ => return,
};
target.kind = Paren(Box::new(take_pat(target)));
@ -163,7 +165,7 @@ fn insert_necessary_parens(pat: &mut Box<Pat>) {
/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
fn unnest_or_patterns(pat: &mut Box<Pat>) -> bool {
fn unnest_or_patterns(pat: &mut Pat) -> bool {
struct Visitor {
changed: bool,
}
@ -385,15 +387,14 @@ fn take_pat(from: &mut Pat) -> Pat {
/// in `tail_or` if there are any and return if there were.
fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec<Pat>) -> bool {
fn extend(target: &mut Pat, mut tail_or: ThinVec<Pat>) {
match target {
// On an existing or-pattern in the target, append to it.
Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
// Otherwise convert the target to an or-pattern.
target => {
let mut init_or = thin_vec![take_pat(target)];
init_or.append(&mut tail_or);
target.kind = Or(init_or);
},
// On an existing or-pattern in the target, append to it,
// otherwise convert the target to an or-pattern.
if let Or(ps) = &mut target.kind {
ps.append(&mut tail_or);
} else {
let mut init_or = thin_vec![take_pat(target)];
init_or.append(&mut tail_or);
target.kind = Or(init_or);
}
}
@ -416,26 +417,14 @@ fn drain_matching(
let mut tail_or = ThinVec::new();
let mut idx = 0;
// If `ThinVec` had the `drain_filter` method, this loop could be rewritten
// like so:
//
// for pat in alternatives.drain_filter(|p| {
// // Check if we should extract, but only if `idx >= start`.
// idx += 1;
// idx > start && predicate(&p.kind)
// }) {
// tail_or.push(extract(pat.into_inner().kind));
// }
let mut i = 0;
while i < alternatives.len() {
idx += 1;
// FIXME: once `thin-vec` releases a new version, change this to `alternatives.extract_if()`
// See https://github.com/mozilla/thin-vec/issues/77
for pat in ExtractIf::new(alternatives, |p| {
// Check if we should extract, but only if `idx >= start`.
if idx > start && predicate(&alternatives[i].kind) {
let pat = alternatives.remove(i);
tail_or.push(extract(pat.kind));
} else {
i += 1;
}
idx += 1;
idx > start && predicate(&p.kind)
}) {
tail_or.push(extract(pat.kind));
}
tail_or

View file

@ -1,5 +1,5 @@
use clippy_utils::res::MaybeQPath;
use clippy_utils::{get_attr, higher, sym};
use clippy_utils::{get_builtin_attr, higher, sym};
use itertools::Itertools;
use rustc_ast::LitIntType;
use rustc_ast::ast::{LitFloatType, LitKind};
@ -859,5 +859,5 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
let attrs = cx.tcx.hir_attrs(hir_id);
get_attr(cx.sess(), attrs, sym::author).count() > 0
get_builtin_attr(cx.sess(), attrs, sym::author).count() > 0
}

View file

@ -1,4 +1,4 @@
use clippy_utils::{get_attr, sym};
use clippy_utils::{get_builtin_attr, sym};
use hir::TraitItem;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -60,5 +60,5 @@ impl<'tcx> LateLintPass<'tcx> for DumpHir {
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
let attrs = cx.tcx.hir_attrs(hir_id);
get_attr(cx.sess(), attrs, sym::dump).count() > 0
get_builtin_attr(cx.sess(), attrs, sym::dump).count() > 0
}

View file

@ -1,734 +0,0 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node};
use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
use clippy_utils::{is_in_test, sym};
use rustc_ast::token::LitKind;
use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
FormatPlaceholder, FormatTrait,
};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::{BytePos, Span};
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `println!("")` to
/// print a newline.
///
/// ### Why is this bad?
/// You should use `println!()`, which is simpler.
///
/// ### Example
/// ```no_run
/// println!("");
/// ```
///
/// Use instead:
/// ```no_run
/// println!();
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINTLN_EMPTY_STRING,
style,
"using `println!(\"\")` with an empty string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `print!()` with a format
/// string that ends in a newline.
///
/// ### Why is this bad?
/// You should use `println!()` instead, which appends the
/// newline.
///
/// ### Example
/// ```no_run
/// # let name = "World";
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```no_run
/// # let name = "World";
/// println!("Hello {}!", name);
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_WITH_NEWLINE,
style,
"using `print!()` with a format string that ends in a single newline"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why restrict this?
/// People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// Only catches `print!` and `println!` calls.
///
/// ### Example
/// ```no_run
/// println!("Hello world!");
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_STDOUT,
restriction,
"printing on stdout"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for printing on *stderr*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why restrict this?
/// People often print on *stderr* while debugging an
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// Only catches `eprint!` and `eprintln!` calls.
///
/// ### Example
/// ```no_run
/// eprintln!("Hello world!");
/// ```
#[clippy::version = "1.50.0"]
pub PRINT_STDERR,
restriction,
"printing on stderr"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// ### Why restrict this?
/// The purpose of the `Debug` trait is to facilitate debugging Rust code,
/// and [no guarantees are made about its output][stability].
/// It should not be used in user-facing output.
///
/// ### Example
/// ```no_run
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
///
/// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
#[clippy::version = "pre 1.29.0"]
pub USE_DEBUG,
restriction,
"use of `Debug`-based formatting"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns about the use of literals as `print!`/`println!` args.
///
/// ### Why is this bad?
/// Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// ### Example
/// ```no_run
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```no_run
/// println!("foo");
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_LITERAL,
style,
"printing a literal with a format string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// ### Why is this bad?
/// You should use `writeln!(buf)`, which is simpler.
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "");
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf);
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITELN_EMPTY_STRING,
style,
"using `writeln!(buf, \"\")` with an empty string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// ### Why is this bad?
/// You should use `writeln!()` instead, which appends the
/// newline.
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// write!(buf, "Hello {}!\n", name);
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// writeln!(buf, "Hello {}!", name);
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITE_WITH_NEWLINE,
style,
"using `write!()` with a format string that ends in a single newline"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// ### Why is this bad?
/// Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "{}", "foo");
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "foo");
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITE_LITERAL,
style,
"writing a literal with a format string"
}
pub struct Write {
format_args: FormatArgsStorage,
// The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated
outermost_debug_impl: Option<OwnerId>,
allow_print_in_tests: bool,
}
impl Write {
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
Self {
format_args,
outermost_debug_impl: None,
allow_print_in_tests: conf.allow_print_in_tests,
}
}
fn in_debug_impl(&self) -> bool {
self.outermost_debug_impl.is_some()
}
}
impl_lint_pass!(Write => [
PRINT_WITH_NEWLINE,
PRINTLN_EMPTY_STRING,
PRINT_STDOUT,
PRINT_STDERR,
USE_DEBUG,
PRINT_LITERAL,
WRITE_WITH_NEWLINE,
WRITELN_EMPTY_STRING,
WRITE_LITERAL,
]);
impl<'tcx> LateLintPass<'tcx> for Write {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
// Only check for `impl Debug`s if we're not already in one
if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) {
self.outermost_debug_impl = Some(item.owner_id);
}
}
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
// Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl
if self.outermost_debug_impl == Some(item.owner_id) {
self.outermost_debug_impl = None;
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
return;
};
let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else {
return;
};
let Some(name) = diag_name.as_str().strip_suffix("_macro") else {
return;
};
let is_build_script = cx
.sess()
.opts
.crate_name
.as_ref()
.is_some_and(|crate_name| crate_name == "build_script_build");
let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
match diag_name {
sym::print_macro | sym::println_macro if !allowed_in_tests => {
if !is_build_script {
span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`"));
}
},
sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`"));
},
sym::write_macro | sym::writeln_macro => {},
_ => return,
}
if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
// ignore `writeln!(w)` and `write!(v, some_macro!())`
if format_args.span.from_expansion() {
return;
}
match diag_name {
sym::print_macro | sym::eprint_macro | sym::write_macro => {
check_newline(cx, format_args, &macro_call, name);
},
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
check_empty_string(cx, format_args, &macro_call, name);
},
_ => {},
}
check_literal(cx, format_args, name);
if !self.in_debug_impl() {
for piece in &format_args.template {
if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
span: Some(span),
format_trait: FormatTrait::Debug,
..
}) = piece
{
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
}
}
}
}
}
}
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Impl(Impl {
of_trait: Some(of_trait),
..
}) = &item.kind
&& let Some(trait_id) = of_trait.trait_ref.trait_def_id()
{
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
} else {
false
}
}
fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else {
return;
};
let count_vertical_whitespace = || {
format_args
.template
.iter()
.filter_map(|piece| match piece {
FormatArgsPiece::Literal(literal) => Some(literal),
FormatArgsPiece::Placeholder(_) => None,
})
.flat_map(|literal| literal.as_str().chars())
.filter(|ch| matches!(ch, '\r' | '\n'))
.count()
};
if last.as_str().ends_with('\n')
// ignore format strings with other internal vertical whitespace
&& count_vertical_whitespace() == 1
{
let mut format_string_span = format_args.span;
let lint = if name == "write" {
format_string_span = expand_past_previous_comma(cx, format_string_span);
WRITE_WITH_NEWLINE
} else {
PRINT_WITH_NEWLINE
};
span_lint_and_then(
cx,
lint,
macro_call.span,
format!("using `{name}!()` with a format string that ends in a single newline"),
|diag| {
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = format_string_span.get_source_text(cx) else {
return;
};
if format_args.template.len() == 1 && last == sym::LF {
// print!("\n"), write!(f, "\n")
diag.multipart_suggestion(
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
Applicability::MachineApplicable,
);
} else if format_snippet.ends_with("\\n\"") {
// print!("...\n"), write!(f, "...\n")
let hi = format_string_span.hi();
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
diag.multipart_suggestion(
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
Applicability::MachineApplicable,
);
}
},
);
}
}
fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] {
let mut span = format_args.span;
let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span);
WRITELN_EMPTY_STRING
} else {
PRINTLN_EMPTY_STRING
};
span_lint_and_then(
cx,
lint,
macro_call.span,
format!("empty string literal in `{name}!`"),
|diag| {
diag.span_suggestion(
span,
"remove the empty string",
String::new(),
Applicability::MachineApplicable,
);
},
);
}
}
fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
let lint_name = if name.starts_with("write") {
WRITE_LITERAL
} else {
PRINT_LITERAL
};
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece {
counts[arg_index(&placeholder.argument)] += 1;
}
}
let mut suggestion: Vec<(Span, String)> = vec![];
// holds index of replaced positional arguments; used to decrement the index of the remaining
// positional arguments.
let mut replaced_position: Vec<usize> = vec![];
let mut sug_span: Option<Span> = None;
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
span: Some(placeholder_span),
format_trait: FormatTrait::Display,
format_options,
}) = piece
&& *format_options == FormatOptions::default()
&& let index = arg_index(argument)
&& counts[index] == 1
&& let Some(arg) = format_args.arguments.by_index(index)
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
&& !arg.expr.span.from_expansion()
&& let Some(value_string) = arg.expr.span.get_source_text(cx)
{
let (replacement, replace_raw) = match lit.kind {
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
Some(extracted) => extracted,
None => return,
},
LitKind::Char => (
match lit.symbol {
sym::DOUBLE_QUOTE => "\\\"",
sym::BACKSLASH_SINGLE_QUOTE => "'",
_ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
Some(stripped) => stripped,
None => return,
},
}
.to_string(),
false,
),
LitKind::Bool => (lit.symbol.to_string(), false),
_ => continue,
};
let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
continue;
};
let format_string_is_raw = format_string_snippet.starts_with('r');
let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement),
(false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")),
(true, false) => match conservative_unescape(&replacement) {
Ok(unescaped) => Some(unescaped),
Err(UnescapeErr::Lint) => None,
Err(UnescapeErr::Ignore) => continue,
},
(true, true) => {
if replacement.contains(['#', '"']) {
None
} else {
Some(replacement)
}
},
};
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
if let Some((_, index)) = format_arg_piece_span(piece) {
replaced_position.push(index);
}
if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
&& let Some(removal_span) = format_arg_removal_span(format_args, index)
{
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
suggestion.push((*placeholder_span, replacement));
suggestion.push((removal_span, String::new()));
}
}
}
// Decrement the index of the remaining by the number of replaced positional arguments
if !suggestion.is_empty() {
for piece in &format_args.template {
relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
}
}
if let Some(span) = sug_span {
span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
if !suggestion.is_empty() {
diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
}
});
}
}
/// Extract Span and its index from the given `piece`
fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
match piece {
FormatArgsPiece::Placeholder(FormatPlaceholder {
argument: FormatArgPosition { index: Ok(index), .. },
span: Some(span),
..
}) => Some((*span, *index)),
_ => None,
}
}
/// Relocalizes the indexes of positional arguments in the format string
fn relocalize_format_args_indexes(
piece: &FormatArgsPiece,
suggestion: &mut Vec<(Span, String)>,
replaced_position: &[usize],
) {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument:
FormatArgPosition {
index: Ok(index),
// Only consider positional arguments
kind: FormatArgPositionKind::Number,
span: Some(span),
},
format_options,
..
}) = piece
{
if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
// If the span is already in the suggestion, we don't need to process it again
return;
}
// lambda to get the decremented index based on the replaced positions
let decremented_index = |index: usize| -> usize {
let decrement = replaced_position.iter().filter(|&&i| i < index).count();
index - decrement
};
suggestion.push((*span, decremented_index(*index).to_string()));
// If there are format options, we need to handle them as well
if *format_options != FormatOptions::default() {
// lambda to process width and precision format counts and add them to the suggestion
let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
if let Some(FormatCount::Argument(FormatArgPosition {
index: Ok(format_arg_index),
kind: FormatArgPositionKind::Number,
span: Some(format_arg_span),
})) = count
{
suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
}
};
process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
}
}
}
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
///
/// `r#"a"#` -> (`a`, true)
///
/// `"b"` -> (`b`, false)
fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
let (literal, raw) = match literal.strip_prefix('r') {
Some(stripped) => (stripped.trim_matches('#'), true),
None => (literal, false),
};
Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
}
enum UnescapeErr {
/// Should still be linted, can be manually resolved by author, e.g.
///
/// ```ignore
/// print!(r"{}", '"');
/// ```
Lint,
/// Should not be linted, e.g.
///
/// ```ignore
/// print!(r"{}", '\r');
/// ```
Ignore,
}
/// Unescape a normal string into a raw string
fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
let mut unescaped = String::with_capacity(literal.len());
let mut chars = literal.chars();
let mut err = false;
while let Some(ch) = chars.next() {
match ch {
'#' => err = true,
'\\' => match chars.next() {
Some('\\') => unescaped.push('\\'),
Some('"') => err = true,
_ => return Err(UnescapeErr::Ignore),
},
_ => unescaped.push(ch),
}
}
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
}
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in
/// `\u{xxxx}` are left unmodified
#[expect(clippy::match_same_arms)]
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
#[derive(Clone, Copy)]
enum State {
Normal,
Backslash,
UnicodeEscape,
}
let mut escaped = String::with_capacity(literal.len());
let mut state = State::Normal;
for ch in literal.chars() {
state = match (ch, state) {
// Escape braces outside of unicode escapes by doubling them up
('{' | '}', State::Normal) => {
escaped.push(ch);
State::Normal
},
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
//
// \u{aaaa} \\ \x01
// ^ ^ ^
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
// \u{aaaa}
// ^
('u', State::Backslash) => State::UnicodeEscape,
// \xAA \\
// ^ ^
(_, State::Backslash) => State::Normal,
// \u{aaaa}
// ^
('}', State::UnicodeEscape) => State::Normal,
_ => state,
};
escaped.push(ch);
}
escaped
}

View file

@ -0,0 +1,38 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::MacroCall;
use clippy_utils::source::expand_past_previous_comma;
use clippy_utils::sym;
use rustc_ast::{FormatArgs, FormatArgsPiece};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use super::{PRINTLN_EMPTY_STRING, WRITELN_EMPTY_STRING};
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] {
let mut span = format_args.span;
let lint = if name == "writeln" {
span = expand_past_previous_comma(cx, span);
WRITELN_EMPTY_STRING
} else {
PRINTLN_EMPTY_STRING
};
span_lint_and_then(
cx,
lint,
macro_call.span,
format!("empty string literal in `{name}!`"),
|diag| {
diag.span_suggestion(
span,
"remove the empty string",
String::new(),
Applicability::MachineApplicable,
);
},
);
}
}

View file

@ -0,0 +1,285 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::format_arg_removal_span;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sym;
use rustc_ast::token::LitKind;
use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions,
FormatPlaceholder, FormatTrait,
};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::Span;
use super::{PRINT_LITERAL, WRITE_LITERAL};
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos);
let lint_name = if name.starts_with("write") {
WRITE_LITERAL
} else {
PRINT_LITERAL
};
let mut counts = vec![0u32; format_args.arguments.all_args().len()];
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece {
counts[arg_index(&placeholder.argument)] += 1;
}
}
let mut suggestion: Vec<(Span, String)> = vec![];
// holds index of replaced positional arguments; used to decrement the index of the remaining
// positional arguments.
let mut replaced_position: Vec<usize> = vec![];
let mut sug_span: Option<Span> = None;
for piece in &format_args.template {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument,
span: Some(placeholder_span),
format_trait: FormatTrait::Display,
format_options,
}) = piece
&& *format_options == FormatOptions::default()
&& let index = arg_index(argument)
&& counts[index] == 1
&& let Some(arg) = format_args.arguments.by_index(index)
&& let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
&& !arg.expr.span.from_expansion()
&& let Some(value_string) = arg.expr.span.get_source_text(cx)
{
let (replacement, replace_raw) = match lit.kind {
LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
Some(extracted) => extracted,
None => return,
},
LitKind::Char => (
match lit.symbol {
sym::DOUBLE_QUOTE => "\\\"",
sym::BACKSLASH_SINGLE_QUOTE => "'",
_ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
Some(stripped) => stripped,
None => return,
},
}
.to_string(),
false,
),
LitKind::Bool => (lit.symbol.to_string(), false),
_ => continue,
};
let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
continue;
};
let format_string_is_raw = format_string_snippet.starts_with('r');
let replacement = match (format_string_is_raw, replace_raw) {
(false, false) => Some(replacement),
(false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")),
(true, false) => match conservative_unescape(&replacement) {
Ok(unescaped) => Some(unescaped),
Err(UnescapeErr::Lint) => None,
Err(UnescapeErr::Ignore) => continue,
},
(true, true) => {
if replacement.contains(['#', '"']) {
None
} else {
Some(replacement)
}
},
};
sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
if let Some((_, index)) = format_arg_piece_span(piece) {
replaced_position.push(index);
}
if let Some(replacement) = replacement
// `format!("{}", "a")`, `format!("{named}", named = "b")
// ~~~~~ ~~~~~~~~~~~~~
&& let Some(removal_span) = format_arg_removal_span(format_args, index)
{
let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw);
suggestion.push((*placeholder_span, replacement));
suggestion.push((removal_span, String::new()));
}
}
}
// Decrement the index of the remaining by the number of replaced positional arguments
if !suggestion.is_empty() {
for piece in &format_args.template {
relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position);
}
}
if let Some(span) = sug_span {
span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| {
if !suggestion.is_empty() {
diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable);
}
});
}
}
/// Extract Span and its index from the given `piece`
fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
match piece {
FormatArgsPiece::Placeholder(FormatPlaceholder {
argument: FormatArgPosition { index: Ok(index), .. },
span: Some(span),
..
}) => Some((*span, *index)),
_ => None,
}
}
/// Relocalizes the indexes of positional arguments in the format string
fn relocalize_format_args_indexes(
piece: &FormatArgsPiece,
suggestion: &mut Vec<(Span, String)>,
replaced_position: &[usize],
) {
if let FormatArgsPiece::Placeholder(FormatPlaceholder {
argument:
FormatArgPosition {
index: Ok(index),
// Only consider positional arguments
kind: FormatArgPositionKind::Number,
span: Some(span),
},
format_options,
..
}) = piece
{
if suggestion.iter().any(|(s, _)| s.overlaps(*span)) {
// If the span is already in the suggestion, we don't need to process it again
return;
}
// lambda to get the decremented index based on the replaced positions
let decremented_index = |index: usize| -> usize {
let decrement = replaced_position.iter().filter(|&&i| i < index).count();
index - decrement
};
suggestion.push((*span, decremented_index(*index).to_string()));
// If there are format options, we need to handle them as well
if *format_options != FormatOptions::default() {
// lambda to process width and precision format counts and add them to the suggestion
let mut process_format_count = |count: &Option<FormatCount>, formatter: &dyn Fn(usize) -> String| {
if let Some(FormatCount::Argument(FormatArgPosition {
index: Ok(format_arg_index),
kind: FormatArgPositionKind::Number,
span: Some(format_arg_span),
})) = count
{
suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index))));
}
};
process_format_count(&format_options.width, &|index: usize| format!("{index}$"));
process_format_count(&format_options.precision, &|index: usize| format!(".{index}$"));
}
}
}
/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
///
/// `r#"a"#` -> (`a`, true)
///
/// `"b"` -> (`b`, false)
fn extract_str_literal(literal: &str) -> Option<(String, bool)> {
let (literal, raw) = match literal.strip_prefix('r') {
Some(stripped) => (stripped.trim_matches('#'), true),
None => (literal, false),
};
Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw))
}
enum UnescapeErr {
/// Should still be linted, can be manually resolved by author, e.g.
///
/// ```ignore
/// print!(r"{}", '"');
/// ```
Lint,
/// Should not be linted, e.g.
///
/// ```ignore
/// print!(r"{}", '\r');
/// ```
Ignore,
}
/// Unescape a normal string into a raw string
fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
let mut unescaped = String::with_capacity(literal.len());
let mut chars = literal.chars();
let mut err = false;
while let Some(ch) = chars.next() {
match ch {
'#' => err = true,
'\\' => match chars.next() {
Some('\\') => unescaped.push('\\'),
Some('"') => err = true,
_ => return Err(UnescapeErr::Ignore),
},
_ => unescaped.push(ch),
}
}
if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
}
/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces
/// in `\u{xxxx}` are left unmodified
#[expect(clippy::match_same_arms)]
fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String {
#[derive(Clone, Copy)]
enum State {
Normal,
Backslash,
UnicodeEscape,
}
let mut escaped = String::with_capacity(literal.len());
let mut state = State::Normal;
for ch in literal.chars() {
state = match (ch, state) {
// Escape braces outside of unicode escapes by doubling them up
('{' | '}', State::Normal) => {
escaped.push(ch);
State::Normal
},
// If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise:
//
// \u{aaaa} \\ \x01
// ^ ^ ^
('\\', State::Normal) if preserve_unicode_escapes => State::Backslash,
// \u{aaaa}
// ^
('u', State::Backslash) => State::UnicodeEscape,
// \xAA \\
// ^ ^
(_, State::Backslash) => State::Normal,
// \u{aaaa}
// ^
('}', State::UnicodeEscape) => State::Normal,
_ => state,
};
escaped.push(ch);
}
escaped
}

View file

@ -0,0 +1,354 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::macros::{FormatArgsStorage, root_macro_call_first_node};
use clippy_utils::{is_in_test, sym};
use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
mod empty_string;
mod literal;
mod use_debug;
mod with_newline;
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `println!("")` to
/// print a newline.
///
/// ### Why is this bad?
/// You should use `println!()`, which is simpler.
///
/// ### Example
/// ```no_run
/// println!("");
/// ```
///
/// Use instead:
/// ```no_run
/// println!();
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINTLN_EMPTY_STRING,
style,
"using `println!(\"\")` with an empty string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `print!()` with a format
/// string that ends in a newline.
///
/// ### Why is this bad?
/// You should use `println!()` instead, which appends the
/// newline.
///
/// ### Example
/// ```no_run
/// # let name = "World";
/// print!("Hello {}!\n", name);
/// ```
/// use println!() instead
/// ```no_run
/// # let name = "World";
/// println!("Hello {}!", name);
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_WITH_NEWLINE,
style,
"using `print!()` with a format string that ends in a single newline"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why restrict this?
/// People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// Only catches `print!` and `println!` calls.
///
/// ### Example
/// ```no_run
/// println!("Hello world!");
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_STDOUT,
restriction,
"printing on stdout"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for printing on *stderr*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why restrict this?
/// People often print on *stderr* while debugging an
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
/// Only catches `eprint!` and `eprintln!` calls.
///
/// ### Example
/// ```no_run
/// eprintln!("Hello world!");
/// ```
#[clippy::version = "1.50.0"]
pub PRINT_STDERR,
restriction,
"printing on stderr"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// ### Why restrict this?
/// The purpose of the `Debug` trait is to facilitate debugging Rust code,
/// and [no guarantees are made about its output][stability].
/// It should not be used in user-facing output.
///
/// ### Example
/// ```no_run
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
///
/// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
#[clippy::version = "pre 1.29.0"]
pub USE_DEBUG,
restriction,
"use of `Debug`-based formatting"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns about the use of literals as `print!`/`println!` args.
///
/// ### Why is this bad?
/// Using literals as `println!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// ### Example
/// ```no_run
/// println!("{}", "foo");
/// ```
/// use the literal without formatting:
/// ```no_run
/// println!("foo");
/// ```
#[clippy::version = "pre 1.29.0"]
pub PRINT_LITERAL,
style,
"printing a literal with a format string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `writeln!(buf, "")` to
/// print a newline.
///
/// ### Why is this bad?
/// You should use `writeln!(buf)`, which is simpler.
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "");
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf);
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITELN_EMPTY_STRING,
style,
"using `writeln!(buf, \"\")` with an empty string"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns when you use `write!()` with a format
/// string that
/// ends in a newline.
///
/// ### Why is this bad?
/// You should use `writeln!()` instead, which appends the
/// newline.
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// write!(buf, "Hello {}!\n", name);
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let name = "World";
/// writeln!(buf, "Hello {}!", name);
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITE_WITH_NEWLINE,
style,
"using `write!()` with a format string that ends in a single newline"
}
declare_clippy_lint! {
/// ### What it does
/// This lint warns about the use of literals as `write!`/`writeln!` args.
///
/// ### Why is this bad?
/// Using literals as `writeln!` args is inefficient
/// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
/// (i.e., just put the literal in the format string)
///
/// ### Example
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "{}", "foo");
/// ```
///
/// Use instead:
/// ```no_run
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// writeln!(buf, "foo");
/// ```
#[clippy::version = "pre 1.29.0"]
pub WRITE_LITERAL,
style,
"writing a literal with a format string"
}
pub struct Write {
format_args: FormatArgsStorage,
// The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated
outermost_debug_impl: Option<OwnerId>,
allow_print_in_tests: bool,
}
impl Write {
pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self {
Self {
format_args,
outermost_debug_impl: None,
allow_print_in_tests: conf.allow_print_in_tests,
}
}
fn in_debug_impl(&self) -> bool {
self.outermost_debug_impl.is_some()
}
}
impl_lint_pass!(Write => [
PRINT_WITH_NEWLINE,
PRINTLN_EMPTY_STRING,
PRINT_STDOUT,
PRINT_STDERR,
USE_DEBUG,
PRINT_LITERAL,
WRITE_WITH_NEWLINE,
WRITELN_EMPTY_STRING,
WRITE_LITERAL,
]);
impl<'tcx> LateLintPass<'tcx> for Write {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
// Only check for `impl Debug`s if we're not already in one
if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) {
self.outermost_debug_impl = Some(item.owner_id);
}
}
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
// Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl
if self.outermost_debug_impl == Some(item.owner_id) {
self.outermost_debug_impl = None;
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
return;
};
let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else {
return;
};
let Some(name) = diag_name.as_str().strip_suffix("_macro") else {
return;
};
let is_build_script = cx
.sess()
.opts
.crate_name
.as_ref()
.is_some_and(|crate_name| crate_name == "build_script_build");
let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
match diag_name {
sym::print_macro | sym::println_macro if !allowed_in_tests => {
if !is_build_script {
span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`"));
}
},
sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`"));
},
sym::write_macro | sym::writeln_macro => {},
_ => return,
}
if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
// ignore `writeln!(w)` and `write!(v, some_macro!())`
if format_args.span.from_expansion() {
return;
}
match diag_name {
sym::print_macro | sym::eprint_macro | sym::write_macro => {
with_newline::check(cx, format_args, &macro_call, name);
},
sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
empty_string::check(cx, format_args, &macro_call, name);
},
_ => {},
}
literal::check(cx, format_args, name);
if !self.in_debug_impl() {
use_debug::check(cx, format_args);
}
}
}
}
fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Impl(Impl {
of_trait: Some(of_trait),
..
}) = &item.kind
&& let Some(trait_id) = of_trait.trait_ref.trait_def_id()
{
cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
} else {
false
}
}

View file

@ -0,0 +1,18 @@
use clippy_utils::diagnostics::span_lint;
use rustc_ast::{FormatArgs, FormatArgsPiece, FormatPlaceholder, FormatTrait};
use rustc_lint::LateContext;
use super::USE_DEBUG;
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs) {
for piece in &format_args.template {
if let &FormatArgsPiece::Placeholder(FormatPlaceholder {
span: Some(span),
format_trait: FormatTrait::Debug,
..
}) = piece
{
span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
}
}
}

View file

@ -0,0 +1,78 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::MacroCall;
use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma};
use clippy_utils::sym;
use rustc_ast::{FormatArgs, FormatArgsPiece};
use rustc_errors::Applicability;
use rustc_lint::{LateContext, LintContext};
use rustc_span::BytePos;
use super::{PRINT_WITH_NEWLINE, WRITE_WITH_NEWLINE};
pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) {
let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else {
return;
};
let count_vertical_whitespace = || {
format_args
.template
.iter()
.filter_map(|piece| match piece {
FormatArgsPiece::Literal(literal) => Some(literal),
FormatArgsPiece::Placeholder(_) => None,
})
.flat_map(|literal| literal.as_str().chars())
.filter(|ch| matches!(ch, '\r' | '\n'))
.count()
};
if last.as_str().ends_with('\n')
// ignore format strings with other internal vertical whitespace
&& count_vertical_whitespace() == 1
{
let mut format_string_span = format_args.span;
let lint = if name == "write" {
format_string_span = expand_past_previous_comma(cx, format_string_span);
WRITE_WITH_NEWLINE
} else {
PRINT_WITH_NEWLINE
};
span_lint_and_then(
cx,
lint,
macro_call.span,
format!("using `{name}!()` with a format string that ends in a single newline"),
|diag| {
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let Some(format_snippet) = format_string_span.get_source_text(cx) else {
return;
};
if format_args.template.len() == 1 && last == sym::LF {
// print!("\n"), write!(f, "\n")
diag.multipart_suggestion(
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
Applicability::MachineApplicable,
);
} else if format_snippet.ends_with("\\n\"") {
// print!("...\n"), write!(f, "...\n")
let hi = format_string_span.hi();
let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
diag.multipart_suggestion(
format!("use `{name}ln!` instead"),
vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
Applicability::MachineApplicable,
);
}
},
);
}
}

View file

@ -371,7 +371,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
&& both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs)
},
(
Fn(box ast::Fn {
@ -625,7 +625,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
&& eq_id(*li, *ri)
&& eq_generics(lg, rg)
&& eq_ty(lt, rt)
&& both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r))
&& both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs)
},
(
Fn(box ast::Fn {

View file

@ -1,3 +1,5 @@
//! Utility functions for attributes, including Clippy's built-in ones
use crate::source::SpanRangeExt;
use crate::{sym, tokenize_with_text};
use rustc_ast::attr;
@ -12,131 +14,59 @@ use rustc_session::Session;
use rustc_span::{Span, Symbol};
use std::str::FromStr;
/// Deprecation status of attributes known by Clippy.
pub enum DeprecationStatus {
/// Attribute is deprecated
Deprecated,
/// Attribute is deprecated and was replaced by the named attribute
Replaced(&'static str),
None,
}
#[rustfmt::skip]
pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[
(sym::author, DeprecationStatus::None),
(sym::version, DeprecationStatus::None),
(sym::cognitive_complexity, DeprecationStatus::None),
(sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")),
(sym::dump, DeprecationStatus::None),
(sym::msrv, DeprecationStatus::None),
// The following attributes are for the 3rd party crate authors.
// See book/src/attribs.md
(sym::has_significant_drop, DeprecationStatus::None),
(sym::format_args, DeprecationStatus::None),
];
pub struct LimitStack {
stack: Vec<u64>,
}
impl Drop for LimitStack {
fn drop(&mut self) {
assert_eq!(self.stack.len(), 1);
}
}
impl LimitStack {
#[must_use]
pub fn new(limit: u64) -> Self {
Self { stack: vec![limit] }
}
pub fn limit(&self) -> u64 {
*self.stack.last().expect("there should always be a value in the stack")
}
pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
let stack = &mut self.stack;
parse_attrs(sess, attrs, name, |val| stack.push(val));
}
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
let stack = &mut self.stack;
parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
}
}
pub fn get_attr<'a, A: AttributeExt + 'a>(
/// Given `attrs`, extract all the instances of a built-in Clippy attribute called `name`
pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
sess: &'a Session,
attrs: &'a [A],
name: Symbol,
) -> impl Iterator<Item = &'a A> {
attrs.iter().filter(move |attr| {
let Some(attr_segments) = attr.ident_path() else {
return false;
};
if let Some([clippy, segment2]) = attr.ident_path().as_deref()
&& clippy.name == sym::clippy
{
let new_name = match segment2.name {
sym::cyclomatic_complexity => Some("cognitive_complexity"),
sym::author
| sym::version
| sym::cognitive_complexity
| sym::dump
| sym::msrv
// The following attributes are for the 3rd party crate authors.
// See book/src/attribs.md
| sym::has_significant_drop
| sym::format_args => None,
_ => {
sess.dcx().span_err(segment2.span, "usage of unknown attribute");
return false;
},
};
if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy {
BUILTIN_ATTRIBUTES
.iter()
.find_map(|(builtin_name, deprecation_status)| {
if attr_segments[1].name == *builtin_name {
Some(deprecation_status)
} else {
None
}
})
.map_or_else(
|| {
sess.dcx().span_err(attr_segments[1].span, "usage of unknown attribute");
false
},
|deprecation_status| {
let mut diag = sess
.dcx()
.struct_span_err(attr_segments[1].span, "usage of deprecated attribute");
match *deprecation_status {
DeprecationStatus::Deprecated => {
diag.emit();
false
},
DeprecationStatus::Replaced(new_name) => {
diag.span_suggestion(
attr_segments[1].span,
"consider using",
new_name,
Applicability::MachineApplicable,
);
diag.emit();
false
},
DeprecationStatus::None => {
diag.cancel();
attr_segments[1].name == name
},
}
},
)
match new_name {
Some(new_name) => {
sess.dcx()
.struct_span_err(segment2.span, "usage of deprecated attribute")
.with_span_suggestion(
segment2.span,
"consider using",
new_name,
Applicability::MachineApplicable,
)
.emit();
false
},
None => segment2.name == name,
}
} else {
false
}
})
}
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
for attr in get_attr(sess, attrs, name) {
if let Some(value) = attr.value_str() {
if let Ok(value) = FromStr::from_str(value.as_str()) {
f(value);
} else {
sess.dcx().span_err(attr.span(), "not a number");
}
} else {
sess.dcx().span_err(attr.span(), "bad clippy attribute");
}
}
}
pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
/// If `attrs` contain exactly one instance of a built-in Clippy attribute called `name`,
/// returns that attribute, and `None` otherwise
pub fn get_unique_builtin_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> {
let mut unique_attr: Option<&A> = None;
for attr in get_attr(sess, attrs, name) {
for attr in get_builtin_attr(sess, attrs, name) {
if let Some(duplicate) = unique_attr {
sess.dcx()
.struct_span_err(attr.span(), format!("`{name}` is defined multiple times"))
@ -149,13 +79,13 @@ pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], n
unique_attr
}
/// Returns true if the attributes contain any of `proc_macro`,
/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
/// Checks whether `attrs` contain any of `proc_macro`, `proc_macro_derive` or
/// `proc_macro_attribute`
pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool {
attrs.iter().any(AttributeExt::is_proc_macro_attr)
}
/// Returns true if the attributes contain `#[doc(hidden)]`
/// Checks whether `attrs` contain `#[doc(hidden)]`
pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
attrs
.iter()
@ -164,6 +94,7 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool {
.any(|l| attr::list_contains_name(&l, sym::hidden))
}
/// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]`
pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
adt.is_variant_list_non_exhaustive()
|| find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..))
@ -176,7 +107,7 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
.any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..)))
}
/// Checks if the given span contains a `#[cfg(..)]` attribute
/// Checks whether the given span contains a `#[cfg(..)]` attribute
pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
s.check_source_text(cx, |src| {
let mut iter = tokenize_with_text(src);
@ -198,3 +129,52 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
false
})
}
/// Currently used to keep track of the current value of `#[clippy::cognitive_complexity(N)]`
pub struct LimitStack {
default: u64,
stack: Vec<u64>,
}
impl Drop for LimitStack {
fn drop(&mut self) {
debug_assert_eq!(self.stack, Vec::<u64>::new()); // avoid `.is_empty()`, for a nicer error message
}
}
#[expect(missing_docs, reason = "they're all trivial...")]
impl LimitStack {
#[must_use]
/// Initialize the stack starting with a default value, which usually comes from configuration
pub fn new(limit: u64) -> Self {
Self {
default: limit,
stack: vec![],
}
}
pub fn limit(&self) -> u64 {
self.stack.last().copied().unwrap_or(self.default)
}
pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
let stack = &mut self.stack;
parse_attrs(sess, attrs, name, |val| stack.push(val));
}
pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) {
let stack = &mut self.stack;
parse_attrs(sess, attrs, name, |val| debug_assert_eq!(stack.pop(), Some(val)));
}
}
fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) {
for attr in get_builtin_attr(sess, attrs, name) {
let Some(value) = attr.value_str() else {
sess.dcx().span_err(attr.span(), "bad clippy attribute");
continue;
};
let Ok(value) = u64::from_str(value.as_str()) else {
sess.dcx().span_err(attr.span(), "not a number");
continue;
};
f(value);
}
}

View file

@ -1138,9 +1138,8 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx
match ct_rhs {
ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value),
ConstItemRhs::TypeConst(const_arg) => match const_arg.kind {
ConstArgKind::Path(_) => None,
ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value),
ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None,
ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None,
},
}
}

View file

@ -211,12 +211,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
// Custom `Deref` impl might have side effects
ExprKind::Unary(UnOp::Deref, e)
if self
.cx
.typeck_results()
.expr_ty(e)
.builtin_deref(true)
.is_none() =>
if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() =>
{
self.eagerness |= NoChange;
},

View file

@ -480,10 +480,8 @@ impl HirEqInterExpr<'_, '_, '_> {
// Use explicit match for now since ConstArg is undergoing flux.
(ConstArgKind::Path(..), ConstArgKind::Anon(..))
| (ConstArgKind::Anon(..), ConstArgKind::Path(..))
| (ConstArgKind::Infer(..), _)
| (_, ConstArgKind::Infer(..))
| (ConstArgKind::Error(..), _)
| (_, ConstArgKind::Error(..)) => false,
| (ConstArgKind::Infer(..) | ConstArgKind::Error(..), _)
| (_, ConstArgKind::Infer(..) | ConstArgKind::Error(..)) => false,
}
}

View file

@ -50,6 +50,7 @@ extern crate rustc_span;
extern crate rustc_trait_selection;
pub mod ast_utils;
#[deny(missing_docs)]
pub mod attrs;
mod check_proc_macro;
pub mod comparisons;
@ -131,7 +132,7 @@ use crate::ast_utils::unordered_over;
use crate::consts::{ConstEvalCtxt, Constant};
use crate::higher::Range;
use crate::msrvs::Msrv;
use crate::res::{MaybeDef, MaybeResPath};
use crate::res::{MaybeDef, MaybeQPath, MaybeResPath};
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
use crate::visitors::for_each_expr_without_closures;
@ -300,6 +301,22 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
cx.tcx.lang_items().get(item) == Some(did)
}
/// Checks is `expr` is `None`
pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone)
}
/// If `expr` is `Some(inner)`, returns `inner`
pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Call(e, [arg]) = expr.kind
&& e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome)
{
Some(arg)
} else {
None
}
}
/// Checks if `expr` is an empty block or an empty tuple.
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
matches!(
@ -320,6 +337,25 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
matches!(pat.kind, PatKind::Wild)
}
/// If `pat` is:
/// - `Some(inner)`, returns `inner`
/// - it will _usually_ contain just one element, but could have two, given patterns like
/// `Some(inner, ..)` or `Some(.., inner)`
/// - `Some`, returns `[]`
/// - otherwise, returns `None`
pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> {
if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind
&& cx
.qpath_res(qpath, pat.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionSome)
{
Some(inner)
} else {
None
}
}
/// Checks if the `pat` is `None`.
pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
matches!(pat.kind,
@ -2782,11 +2818,7 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>(
pat: &'a Pat<'hir>,
else_body: &Expr<'_>,
) -> Option<&'a Pat<'hir>> {
if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind
&& cx
.qpath_res(&pat_path, pat.hir_id)
.ctor_parent(cx)
.is_lang_item(cx, OptionSome)
if let Some([inner_pat]) = as_some_pattern(cx, pat)
&& !is_refutable(cx, inner_pat)
&& let else_body = peel_blocks(else_body)
&& let ExprKind::Ret(Some(ret_val)) = else_body.kind

View file

@ -3,7 +3,7 @@
use std::sync::{Arc, OnceLock};
use crate::visitors::{Descend, for_each_expr_without_closures};
use crate::{get_unique_attr, sym};
use crate::{get_unique_builtin_attr, sym};
use arrayvec::ArrayVec;
use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
@ -42,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
} else {
// Allow users to tag any macro as being format!-like
// TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method
get_unique_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some()
get_unique_builtin_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some()
}
}

View file

@ -1,11 +1,16 @@
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use std::iter;
/// Represents the base of a numeric literal, used for parsing and formatting.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Radix {
/// A binary literal (e.g., `0b1010`)
Binary,
/// An octal literal (e.g., `0o670`)
Octal,
/// A decimal literal (e.g., `123`)
Decimal,
/// A hexadecimal literal (e.g., `0xFF`)
Hexadecimal,
}
@ -46,6 +51,7 @@ pub struct NumericLiteral<'a> {
}
impl<'a> NumericLiteral<'a> {
/// Attempts to parse a `NumericLiteral` from the source string of an `ast::LitKind`.
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
if lit_kind.is_numeric()
@ -63,6 +69,7 @@ impl<'a> NumericLiteral<'a> {
}
}
/// Parses a raw numeric literal string into its structured `NumericLiteral` parts.
#[must_use]
pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
let unsigned_lit = lit.trim_start_matches('-');
@ -102,11 +109,12 @@ impl<'a> NumericLiteral<'a> {
}
}
/// Checks if the literal's radix is `Radix::Decimal`
pub fn is_decimal(&self) -> bool {
self.radix == Radix::Decimal
}
pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
let mut integer = digits;
let mut fraction = None;
let mut exponent = None;
@ -180,7 +188,7 @@ impl<'a> NumericLiteral<'a> {
output
}
pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, zero_pad: bool) {
debug_assert!(group_size > 0);
let mut digits = input.chars().filter(|&c| c != '_');
@ -196,7 +204,7 @@ impl<'a> NumericLiteral<'a> {
if partial_group_first {
first_group_size = (digits.clone().count() - 1) % group_size + 1;
if pad {
if zero_pad {
for _ in 0..group_size - first_group_size {
output.push('0');
}

View file

@ -194,8 +194,7 @@ fn check_rvalue<'tcx>(
))
}
},
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _)
| Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(body, cx.tcx);
if ty.is_integral() || ty.is_bool() {

View file

@ -126,6 +126,7 @@ generate! {
copy_from_nonoverlapping,
copy_to,
copy_to_nonoverlapping,
core_arch,
count_ones,
create,
create_new,
@ -330,6 +331,7 @@ generate! {
splitn_mut,
sqrt,
starts_with,
std_detect,
step_by,
strlen,
style,

View file

@ -13,8 +13,8 @@ extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_span;
/// See docs in https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs
/// and https://github.com/rust-lang/rust/pull/146627 for why we need this `use` statement.
/// See docs in <https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc/src/main.rs>
/// and <https://github.com/rust-lang/rust/pull/146627> for why we need this `use` statement.
#[cfg(feature = "jemalloc")]
use tikv_jemalloc_sys as _;
@ -187,7 +187,6 @@ fn display_help() {
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml";
#[expect(clippy::too_many_lines)]
pub fn main() {
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());

View file

@ -17,6 +17,7 @@
extern crate proc_macro_derive;
use core::num::{NonZero, Saturating, Wrapping};
use core::time::Duration;
const ONE: i32 = 1;
const ZERO: i32 = 0;
@ -687,4 +688,59 @@ pub fn explicit_methods() {
//~^ arithmetic_side_effects
}
pub fn issue_15943(days: u8) -> Duration {
Duration::from_secs(86400 * u64::from(days))
}
pub fn type_conversion_add() {
let _ = u128::MAX + u128::from(1u8);
//~^ arithmetic_side_effects
let _ = 1u128 + u128::from(1u16);
let _ = 1u128 + u128::from(1u32);
let _ = 1u128 + u128::from(1u64);
let _ = 1u64 + u64::from(1u8);
let _ = 1u64 + u64::from(1u16);
let _ = 1u64 + u64::from(1u32);
let _ = 1u32 + u32::from(1u8);
let _ = 1u32 + u32::from(1u16);
let _ = 1u16 + u16::from(1u8);
}
pub fn type_conversion_mul() {
let _ = u128::MAX * u128::from(1u8);
//~^ arithmetic_side_effects
let _ = 1u128 * u128::from(1u16);
let _ = 1u128 * u128::from(1u32);
let _ = 1u128 * u128::from(1u64);
let _ = 1u64 * u64::from(1u8);
let _ = 1u64 * u64::from(1u16);
let _ = 1u64 * u64::from(1u32);
let _ = 1u32 * u32::from(1u8);
let _ = 1u32 * u32::from(1u16);
let _ = 1u16 * u16::from(1u8);
}
pub fn type_conversion_does_not_escape_its_context() {
struct Foo;
impl Foo {
fn from(n: u8) -> u64 {
u64::from(n)
}
}
let _ = Duration::from_secs(86400 * Foo::from(1));
//~^ arithmetic_side_effects
fn shift(x: u8) -> u64 {
1 << u64::from(x)
}
let _ = Duration::from_secs(86400 * shift(1));
//~^ arithmetic_side_effects
}
fn main() {}

View file

@ -1,5 +1,5 @@
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:166:13
--> tests/ui/arithmetic_side_effects.rs:167:13
|
LL | let _ = 1f16 + 1f16;
| ^^^^^^^^^^^
@ -8,766 +8,790 @@ LL | let _ = 1f16 + 1f16;
= help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]`
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:170:13
--> tests/ui/arithmetic_side_effects.rs:171:13
|
LL | let _ = 1f128 + 1f128;
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:175:13
--> tests/ui/arithmetic_side_effects.rs:176:13
|
LL | let _ = String::new() + &String::new();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:311:5
--> tests/ui/arithmetic_side_effects.rs:312:5
|
LL | _n += 1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:313:5
--> tests/ui/arithmetic_side_effects.rs:314:5
|
LL | _n += &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:315:5
--> tests/ui/arithmetic_side_effects.rs:316:5
|
LL | _n -= 1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:317:5
--> tests/ui/arithmetic_side_effects.rs:318:5
|
LL | _n -= &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:319:5
--> tests/ui/arithmetic_side_effects.rs:320:5
|
LL | _n /= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:321:5
--> tests/ui/arithmetic_side_effects.rs:322:5
|
LL | _n /= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:323:5
--> tests/ui/arithmetic_side_effects.rs:324:5
|
LL | _n %= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:325:5
--> tests/ui/arithmetic_side_effects.rs:326:5
|
LL | _n %= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:327:5
--> tests/ui/arithmetic_side_effects.rs:328:5
|
LL | _n *= 2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:329:5
--> tests/ui/arithmetic_side_effects.rs:330:5
|
LL | _n *= &2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:331:5
--> tests/ui/arithmetic_side_effects.rs:332:5
|
LL | _n += -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:333:5
--> tests/ui/arithmetic_side_effects.rs:334:5
|
LL | _n += &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:335:5
--> tests/ui/arithmetic_side_effects.rs:336:5
|
LL | _n -= -1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:337:5
--> tests/ui/arithmetic_side_effects.rs:338:5
|
LL | _n -= &-1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:339:5
--> tests/ui/arithmetic_side_effects.rs:340:5
|
LL | _n /= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:341:5
--> tests/ui/arithmetic_side_effects.rs:342:5
|
LL | _n /= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:343:5
--> tests/ui/arithmetic_side_effects.rs:344:5
|
LL | _n %= -0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:345:5
--> tests/ui/arithmetic_side_effects.rs:346:5
|
LL | _n %= &-0;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:347:5
--> tests/ui/arithmetic_side_effects.rs:348:5
|
LL | _n *= -2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:349:5
--> tests/ui/arithmetic_side_effects.rs:350:5
|
LL | _n *= &-2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:351:5
--> tests/ui/arithmetic_side_effects.rs:352:5
|
LL | _custom += Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:353:5
--> tests/ui/arithmetic_side_effects.rs:354:5
|
LL | _custom += &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:355:5
--> tests/ui/arithmetic_side_effects.rs:356:5
|
LL | _custom -= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:357:5
--> tests/ui/arithmetic_side_effects.rs:358:5
|
LL | _custom -= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:359:5
--> tests/ui/arithmetic_side_effects.rs:360:5
|
LL | _custom /= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:361:5
--> tests/ui/arithmetic_side_effects.rs:362:5
|
LL | _custom /= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:363:5
--> tests/ui/arithmetic_side_effects.rs:364:5
|
LL | _custom %= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:365:5
--> tests/ui/arithmetic_side_effects.rs:366:5
|
LL | _custom %= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:367:5
--> tests/ui/arithmetic_side_effects.rs:368:5
|
LL | _custom *= Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:369:5
--> tests/ui/arithmetic_side_effects.rs:370:5
|
LL | _custom *= &Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:371:5
--> tests/ui/arithmetic_side_effects.rs:372:5
|
LL | _custom >>= Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:373:5
--> tests/ui/arithmetic_side_effects.rs:374:5
|
LL | _custom >>= &Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:375:5
--> tests/ui/arithmetic_side_effects.rs:376:5
|
LL | _custom <<= Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:377:5
--> tests/ui/arithmetic_side_effects.rs:378:5
|
LL | _custom <<= &Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:379:5
--> tests/ui/arithmetic_side_effects.rs:380:5
|
LL | _custom += -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:381:5
--> tests/ui/arithmetic_side_effects.rs:382:5
|
LL | _custom += &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:383:5
--> tests/ui/arithmetic_side_effects.rs:384:5
|
LL | _custom -= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:385:5
--> tests/ui/arithmetic_side_effects.rs:386:5
|
LL | _custom -= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:387:5
--> tests/ui/arithmetic_side_effects.rs:388:5
|
LL | _custom /= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:389:5
--> tests/ui/arithmetic_side_effects.rs:390:5
|
LL | _custom /= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:391:5
--> tests/ui/arithmetic_side_effects.rs:392:5
|
LL | _custom %= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:393:5
--> tests/ui/arithmetic_side_effects.rs:394:5
|
LL | _custom %= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:395:5
--> tests/ui/arithmetic_side_effects.rs:396:5
|
LL | _custom *= -Custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:397:5
--> tests/ui/arithmetic_side_effects.rs:398:5
|
LL | _custom *= &-Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:399:5
--> tests/ui/arithmetic_side_effects.rs:400:5
|
LL | _custom >>= -Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:401:5
--> tests/ui/arithmetic_side_effects.rs:402:5
|
LL | _custom >>= &-Custom;
| ^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:403:5
--> tests/ui/arithmetic_side_effects.rs:404:5
|
LL | _custom <<= -Custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:405:5
--> tests/ui/arithmetic_side_effects.rs:406:5
|
LL | _custom <<= &-Custom;
| ^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:409:10
--> tests/ui/arithmetic_side_effects.rs:410:10
|
LL | _n = _n + 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:411:10
--> tests/ui/arithmetic_side_effects.rs:412:10
|
LL | _n = _n + &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:413:10
--> tests/ui/arithmetic_side_effects.rs:414:10
|
LL | _n = 1 + _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:415:10
--> tests/ui/arithmetic_side_effects.rs:416:10
|
LL | _n = &1 + _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:417:10
--> tests/ui/arithmetic_side_effects.rs:418:10
|
LL | _n = _n - 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:419:10
--> tests/ui/arithmetic_side_effects.rs:420:10
|
LL | _n = _n - &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:421:10
--> tests/ui/arithmetic_side_effects.rs:422:10
|
LL | _n = 1 - _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:423:10
--> tests/ui/arithmetic_side_effects.rs:424:10
|
LL | _n = &1 - _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:425:10
--> tests/ui/arithmetic_side_effects.rs:426:10
|
LL | _n = _n / 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:427:10
--> tests/ui/arithmetic_side_effects.rs:428:10
|
LL | _n = _n / &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:429:10
--> tests/ui/arithmetic_side_effects.rs:430:10
|
LL | _n = _n % 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:431:10
--> tests/ui/arithmetic_side_effects.rs:432:10
|
LL | _n = _n % &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:433:10
--> tests/ui/arithmetic_side_effects.rs:434:10
|
LL | _n = _n * 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:435:10
--> tests/ui/arithmetic_side_effects.rs:436:10
|
LL | _n = _n * &2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:437:10
--> tests/ui/arithmetic_side_effects.rs:438:10
|
LL | _n = 2 * _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:439:10
--> tests/ui/arithmetic_side_effects.rs:440:10
|
LL | _n = &2 * _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:441:10
--> tests/ui/arithmetic_side_effects.rs:442:10
|
LL | _n = 23 + &85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:443:10
--> tests/ui/arithmetic_side_effects.rs:444:10
|
LL | _n = &23 + 85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:445:10
--> tests/ui/arithmetic_side_effects.rs:446:10
|
LL | _n = &23 + &85;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:447:15
--> tests/ui/arithmetic_side_effects.rs:448:15
|
LL | _custom = _custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:449:15
--> tests/ui/arithmetic_side_effects.rs:450:15
|
LL | _custom = _custom + &_custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:451:15
--> tests/ui/arithmetic_side_effects.rs:452:15
|
LL | _custom = Custom + _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:453:15
--> tests/ui/arithmetic_side_effects.rs:454:15
|
LL | _custom = &Custom + _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:455:15
--> tests/ui/arithmetic_side_effects.rs:456:15
|
LL | _custom = _custom - Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:457:15
--> tests/ui/arithmetic_side_effects.rs:458:15
|
LL | _custom = _custom - &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:459:15
--> tests/ui/arithmetic_side_effects.rs:460:15
|
LL | _custom = Custom - _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:461:15
--> tests/ui/arithmetic_side_effects.rs:462:15
|
LL | _custom = &Custom - _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:463:15
--> tests/ui/arithmetic_side_effects.rs:464:15
|
LL | _custom = _custom / Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:465:15
--> tests/ui/arithmetic_side_effects.rs:466:15
|
LL | _custom = _custom / &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:467:15
--> tests/ui/arithmetic_side_effects.rs:468:15
|
LL | _custom = _custom % Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:469:15
--> tests/ui/arithmetic_side_effects.rs:470:15
|
LL | _custom = _custom % &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:471:15
--> tests/ui/arithmetic_side_effects.rs:472:15
|
LL | _custom = _custom * Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:473:15
--> tests/ui/arithmetic_side_effects.rs:474:15
|
LL | _custom = _custom * &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:475:15
--> tests/ui/arithmetic_side_effects.rs:476:15
|
LL | _custom = Custom * _custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:477:15
--> tests/ui/arithmetic_side_effects.rs:478:15
|
LL | _custom = &Custom * _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:479:15
--> tests/ui/arithmetic_side_effects.rs:480:15
|
LL | _custom = Custom + &Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:481:15
--> tests/ui/arithmetic_side_effects.rs:482:15
|
LL | _custom = &Custom + Custom;
| ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:483:15
--> tests/ui/arithmetic_side_effects.rs:484:15
|
LL | _custom = &Custom + &Custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:485:15
--> tests/ui/arithmetic_side_effects.rs:486:15
|
LL | _custom = _custom >> _custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:487:15
--> tests/ui/arithmetic_side_effects.rs:488:15
|
LL | _custom = _custom >> &_custom;
| ^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:489:15
--> tests/ui/arithmetic_side_effects.rs:490:15
|
LL | _custom = Custom << _custom;
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:491:15
--> tests/ui/arithmetic_side_effects.rs:492:15
|
LL | _custom = &Custom << _custom;
| ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:495:23
--> tests/ui/arithmetic_side_effects.rs:496:23
|
LL | _n.saturating_div(0);
| ^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:497:21
--> tests/ui/arithmetic_side_effects.rs:498:21
|
LL | _n.wrapping_div(0);
| ^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:499:21
--> tests/ui/arithmetic_side_effects.rs:500:21
|
LL | _n.wrapping_rem(0);
| ^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:501:28
--> tests/ui/arithmetic_side_effects.rs:502:28
|
LL | _n.wrapping_rem_euclid(0);
| ^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:504:23
--> tests/ui/arithmetic_side_effects.rs:505:23
|
LL | _n.saturating_div(_n);
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:506:21
--> tests/ui/arithmetic_side_effects.rs:507:21
|
LL | _n.wrapping_div(_n);
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:508:21
--> tests/ui/arithmetic_side_effects.rs:509:21
|
LL | _n.wrapping_rem(_n);
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:510:28
--> tests/ui/arithmetic_side_effects.rs:511:28
|
LL | _n.wrapping_rem_euclid(_n);
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:513:23
--> tests/ui/arithmetic_side_effects.rs:514:23
|
LL | _n.saturating_div(*Box::new(_n));
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:517:10
--> tests/ui/arithmetic_side_effects.rs:518:10
|
LL | _n = -_n;
| ^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:519:10
--> tests/ui/arithmetic_side_effects.rs:520:10
|
LL | _n = -&_n;
| ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:521:15
--> tests/ui/arithmetic_side_effects.rs:522:15
|
LL | _custom = -_custom;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:523:15
--> tests/ui/arithmetic_side_effects.rs:524:15
|
LL | _custom = -&_custom;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:525:9
--> tests/ui/arithmetic_side_effects.rs:526:9
|
LL | _ = -*Box::new(_n);
| ^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:535:5
--> tests/ui/arithmetic_side_effects.rs:536:5
|
LL | 1 + i;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:537:5
--> tests/ui/arithmetic_side_effects.rs:538:5
|
LL | i * 2;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:539:5
--> tests/ui/arithmetic_side_effects.rs:540:5
|
LL | 1 % i / 2;
| ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:541:5
--> tests/ui/arithmetic_side_effects.rs:542:5
|
LL | i - 2 + 2 - i;
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:543:5
--> tests/ui/arithmetic_side_effects.rs:544:5
|
LL | -i;
| ^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:555:5
--> tests/ui/arithmetic_side_effects.rs:556:5
|
LL | i += 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:557:5
--> tests/ui/arithmetic_side_effects.rs:558:5
|
LL | i -= 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:559:5
--> tests/ui/arithmetic_side_effects.rs:560:5
|
LL | i *= 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:562:5
--> tests/ui/arithmetic_side_effects.rs:563:5
|
LL | i /= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:565:5
--> tests/ui/arithmetic_side_effects.rs:566:5
|
LL | i /= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:567:5
--> tests/ui/arithmetic_side_effects.rs:568:5
|
LL | i /= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:570:5
--> tests/ui/arithmetic_side_effects.rs:571:5
|
LL | i %= 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:573:5
--> tests/ui/arithmetic_side_effects.rs:574:5
|
LL | i %= var1;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:575:5
--> tests/ui/arithmetic_side_effects.rs:576:5
|
LL | i %= var2;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:586:5
--> tests/ui/arithmetic_side_effects.rs:587:5
|
LL | 10 / a
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:641:9
--> tests/ui/arithmetic_side_effects.rs:642:9
|
LL | x / maybe_zero
| ^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:646:9
--> tests/ui/arithmetic_side_effects.rs:647:9
|
LL | x % maybe_zero
| ^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:658:5
--> tests/ui/arithmetic_side_effects.rs:659:5
|
LL | one.add_assign(1);
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:663:5
--> tests/ui/arithmetic_side_effects.rs:664:5
|
LL | one.sub_assign(1);
| ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:684:5
--> tests/ui/arithmetic_side_effects.rs:685:5
|
LL | one.add(&one);
| ^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:686:5
--> tests/ui/arithmetic_side_effects.rs:687:5
|
LL | Box::new(one).add(one);
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 128 previous errors
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:696:13
|
LL | let _ = u128::MAX + u128::from(1u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:713:13
|
LL | let _ = u128::MAX * u128::from(1u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:736:33
|
LL | let _ = Duration::from_secs(86400 * Foo::from(1));
| ^^^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
--> tests/ui/arithmetic_side_effects.rs:742:33
|
LL | let _ = Duration::from_secs(86400 * shift(1));
| ^^^^^^^^^^^^^^^^
error: aborting due to 132 previous errors

View file

@ -1,5 +1,4 @@
#![warn(clippy::cmp_null)]
#![allow(unused_mut)]
use std::ptr;
@ -18,7 +17,7 @@ fn main() {
}
let mut y = 0;
let mut m: *mut usize = &mut y;
let m: *mut usize = &mut y;
if m.is_null() {
//~^ cmp_null

View file

@ -1,5 +1,4 @@
#![warn(clippy::cmp_null)]
#![allow(unused_mut)]
use std::ptr;
@ -18,7 +17,7 @@ fn main() {
}
let mut y = 0;
let mut m: *mut usize = &mut y;
let m: *mut usize = &mut y;
if m == ptr::null_mut() {
//~^ cmp_null

View file

@ -1,5 +1,5 @@
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:9:8
--> tests/ui/cmp_null.rs:8:8
|
LL | if p == ptr::null() {
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
@ -8,31 +8,31 @@ LL | if p == ptr::null() {
= help: to override `-D warnings` add `#[allow(clippy::cmp_null)]`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:14:8
--> tests/ui/cmp_null.rs:13:8
|
LL | if ptr::null() == p {
| ^^^^^^^^^^^^^^^^ help: try: `p.is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:22:8
--> tests/ui/cmp_null.rs:21:8
|
LL | if m == ptr::null_mut() {
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:27:8
--> tests/ui/cmp_null.rs:26:8
|
LL | if ptr::null_mut() == m {
| ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:33:13
--> tests/ui/cmp_null.rs:32:13
|
LL | let _ = x as *const () == ptr::null();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()`
error: comparing with null is better expressed by the `.is_null()` method
--> tests/ui/cmp_null.rs:39:19
--> tests/ui/cmp_null.rs:38:19
|
LL | debug_assert!(f != std::ptr::null_mut());
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()`

View file

@ -178,4 +178,11 @@ const fn uncalled_len() {
//~^ incompatible_msrv
}
#[clippy::msrv = "1.0.0"]
fn vec_macro() {
let _: Vec<u32> = vec![];
let _: Vec<u32> = vec![1; 3];
let _: Vec<u32> = vec![1, 2];
}
fn main() {}

View file

@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
//~[edition2024]^ let_and_return
}
fn issue15987() -> i32 {
macro_rules! sample {
( $( $args:expr ),+ ) => {};
}
let r = 5;
sample!(r);
r
}
fn main() {}

View file

@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
//~[edition2024]^ let_and_return
}
fn issue15987() -> i32 {
macro_rules! sample {
( $( $args:expr ),+ ) => {};
}
let r = 5;
sample!(r);
r
}
fn main() {}

View file

@ -261,4 +261,14 @@ fn issue14164() -> Result<u32, ()> {
//~[edition2024]^ let_and_return
}
fn issue15987() -> i32 {
macro_rules! sample {
( $( $args:expr ),+ ) => {};
}
let r = 5;
sample!(r);
r
}
fn main() {}

View file

@ -139,3 +139,34 @@ fn main() {
}
println!("{}", val.get());
}
fn issue16062(bar: fn() -> bool) {
let foo;
//~^ useless_let_if_seq
if bar() {
foo = 42;
} else {
foo = 0;
}
}
fn issue16064(bar: fn() -> bool) {
macro_rules! mac {
($e:expr) => {
$e()
};
($base:expr, $lit:expr) => {
$lit * $base + 2
};
}
let foo;
//~^ useless_let_if_seq
if mac!(bar) {
foo = mac!(10, 4);
} else {
foo = 0;
}
let bar = 1;
}

View file

@ -52,5 +52,29 @@ LL | | }
|
= note: you might not need `mut` at all
error: aborting due to 4 previous errors
error: `if _ { .. } else { .. }` is an expression
--> tests/ui/let_if_seq.rs:144:5
|
LL | / let foo;
LL | |
LL | | if bar() {
LL | | foo = 42;
LL | | } else {
LL | | foo = 0;
LL | | }
| |_____^ help: it is more idiomatic to write: `let foo = if bar() { 42 } else { 0 };`
error: `if _ { .. } else { .. }` is an expression
--> tests/ui/let_if_seq.rs:163:5
|
LL | / let foo;
LL | |
LL | | if mac!(bar) {
LL | | foo = mac!(10, 4);
LL | | } else {
LL | | foo = 0;
LL | | }
| |_____^ help: it is more idiomatic to write: `let foo = if mac!(bar) { mac!(10, 4) } else { 0 };`
error: aborting due to 6 previous errors

View file

@ -265,3 +265,82 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
a < b
&& b < c
}
#[allow(
irrefutable_let_patterns,
clippy::blocks_in_conditions,
clippy::unused_unit,
clippy::let_unit_value,
clippy::unit_arg,
clippy::unnecessary_operation
)]
fn issue15537(a: i32) -> ((), (), ()) {
let y = (
{ todo!() },
{
{ a };
()
},
(),
);
let y = [
{ todo!() },
{
{ a };
()
},
(),
];
fn call(x: (), y: (), z: ()) {}
let y = call(
{ todo!() },
{
{ a };
()
},
(),
);
struct Foo;
impl Foo {
fn method(&self, x: (), y: (), z: ()) {}
}
let x = Foo;
x.method(
{ todo!() },
{
{ a };
()
},
(),
);
-{
{ a };
1
};
_ = { a };
1;
if let x = {
{ a };
1
} {}
if {
{ a };
true
} {
todo!()
}
[1, 2, 3][{
{ a };
1usize
}];
todo!()
}

View file

@ -342,3 +342,84 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool {
(a, b) => b < c,
}
}
#[allow(
irrefutable_let_patterns,
clippy::blocks_in_conditions,
clippy::unused_unit,
clippy::let_unit_value,
clippy::unit_arg,
clippy::unnecessary_operation
)]
fn issue15537(a: i32) -> ((), (), ()) {
let y = (
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);
let y = [
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
];
fn call(x: (), y: (), z: ()) {}
let y = call(
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);
struct Foo;
impl Foo {
fn method(&self, x: (), y: (), z: ()) {}
}
let x = Foo;
x.method(
{ todo!() },
match { a } {
//~^ match_single_binding
_ => (),
},
(),
);
-match { a } {
//~^ match_single_binding
_ => 1,
};
_ = match { a } {
//~^ match_single_binding
_ => 1,
};
if let x = match { a } {
//~^ match_single_binding
_ => 1,
} {}
if match { a } {
//~^ match_single_binding
_ => true,
} {
todo!()
}
[1, 2, 3][match { a } {
//~^ match_single_binding
_ => 1usize,
}];
todo!()
}

View file

@ -525,5 +525,161 @@ LL | | (a, b) => b < c,
LL | | }
| |_________^ help: consider using the match body instead: `b < c`
error: aborting due to 37 previous errors
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:357:9
|
LL | / match { a } {
LL | |
LL | | _ => (),
LL | | },
| |_________^
|
help: consider using the scrutinee and body instead
|
LL ~ {
LL + { a };
LL + ()
LL ~ },
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:366:9
|
LL | / match { a } {
LL | |
LL | | _ => (),
LL | | },
| |_________^
|
help: consider using the scrutinee and body instead
|
LL ~ {
LL + { a };
LL + ()
LL ~ },
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:376:9
|
LL | / match { a } {
LL | |
LL | | _ => (),
LL | | },
| |_________^
|
help: consider using the scrutinee and body instead
|
LL ~ {
LL + { a };
LL + ()
LL ~ },
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:390:9
|
LL | / match { a } {
LL | |
LL | | _ => (),
LL | | },
| |_________^
|
help: consider using the scrutinee and body instead
|
LL ~ {
LL + { a };
LL + ()
LL ~ },
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:397:6
|
LL | -match { a } {
| ______^
LL | |
LL | | _ => 1,
LL | | };
| |_____^
|
help: consider using the scrutinee and body instead
|
LL ~ -{
LL + { a };
LL + 1
LL ~ };
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:402:9
|
LL | _ = match { a } {
| _________^
LL | |
LL | | _ => 1,
LL | | };
| |_____^
|
help: consider using the scrutinee and body instead
|
LL ~ _ = { a };
LL ~ 1;
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:407:16
|
LL | if let x = match { a } {
| ________________^
LL | |
LL | | _ => 1,
LL | | } {}
| |_____^
|
help: consider using the scrutinee and body instead
|
LL ~ if let x = {
LL + { a };
LL + 1
LL ~ } {}
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:412:8
|
LL | if match { a } {
| ________^
LL | |
LL | | _ => true,
LL | | } {
| |_____^
|
help: consider using the scrutinee and body instead
|
LL ~ if {
LL + { a };
LL + true
LL ~ } {
|
error: this match could be replaced by its scrutinee and body
--> tests/ui/match_single_binding.rs:419:15
|
LL | [1, 2, 3][match { a } {
| _______________^
LL | |
LL | | _ => 1usize,
LL | | }];
| |_____^
|
help: consider using the scrutinee and body instead
|
LL ~ [1, 2, 3][{
LL + { a };
LL + 1usize
LL ~ }];
|
error: aborting due to 46 previous errors

View file

@ -150,9 +150,9 @@ fn highest_index_first(v1: &[u8]) {
}
fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
assert!(v1.len() == 3);
assert_eq!(v1.len(), 3);
assert_eq!(v2.len(), 4);
assert!(v3.len() == 3);
assert_eq!(v3.len(), 3);
assert_eq!(4, v4.len());
let _ = v1[0] + v1[1] + v1[2];
@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
let _ = v4[0] + v4[1] + v4[2];
}
mod issue15988 {
fn assert_eq_len(v: &[i32]) {
assert_eq!(v.len(), 3);
let _ = v[0] + v[1] + v[2];
//~^ missing_asserts_for_indexing
}
fn debug_assert_eq_len(v: &[i32]) {
debug_assert_eq!(v.len(), 3);
let _ = v[0] + v[1] + v[2];
//~^ missing_asserts_for_indexing
}
}
fn main() {}

View file

@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) {
let _ = v4[0] + v4[1] + v4[2];
}
mod issue15988 {
fn assert_eq_len(v: &[i32]) {
assert_eq!(v.len(), 2);
let _ = v[0] + v[1] + v[2];
//~^ missing_asserts_for_indexing
}
fn debug_assert_eq_len(v: &[i32]) {
debug_assert_eq!(v.len(), 2);
let _ = v[0] + v[1] + v[2];
//~^ missing_asserts_for_indexing
}
}
fn main() {}

View file

@ -305,7 +305,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover
--> tests/ui/missing_asserts_for_indexing.rs:158:13
|
LL | assert_eq!(v1.len(), 2);
| ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)`
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)`
...
LL | let _ = v1[0] + v1[1] + v1[2];
| ^^^^^^^^^^^^^^^^^^^^^
@ -331,7 +331,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover
--> tests/ui/missing_asserts_for_indexing.rs:163:13
|
LL | assert_eq!(2, v3.len());
| ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)`
| ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)`
...
LL | let _ = v3[0] + v3[1] + v3[2];
| ^^^^^^^^^^^^^^^^^^^^^
@ -353,5 +353,55 @@ LL | let _ = v3[0] + v3[1] + v3[2];
| ^^^^^
= note: asserting the length before indexing will elide bounds checks
error: aborting due to 13 previous errors
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
LL | assert_eq!(v.len(), 2);
| ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)`
LL | let _ = v[0] + v[1] + v[2];
| ^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:172:17
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:172:24
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:172:31
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: indexing into a slice multiple times with an `assert` that does not cover the highest index
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
LL | debug_assert_eq!(v.len(), 2);
| ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)`
LL | let _ = v[0] + v[1] + v[2];
| ^^^^^^^^^^^^^^^^^^
|
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:178:17
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:178:24
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
note: slice indexed here
--> tests/ui/missing_asserts_for_indexing.rs:178:31
|
LL | let _ = v[0] + v[1] + v[2];
| ^^^^
= note: asserting the length before indexing will elide bounds checks
error: aborting due to 15 previous errors

View file

@ -1,7 +1,6 @@
//@ check-pass
#![warn(clippy::missing_inline_in_public_items)]
pub fn foo() {}
//~^ missing_inline_in_public_items
fn main() {}

View file

@ -0,0 +1,11 @@
error: missing `#[inline]` for a function
--> tests/ui/missing_inline_executable.rs:3:1
|
LL | pub fn foo() {}
| ^^^^^^^^^^^^^^^
|
= note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]`
error: aborting due to 1 previous error

View file

@ -0,0 +1,10 @@
//@compile-flags: --test
//@check-pass
#![warn(clippy::missing_inline_in_public_items)]
#[expect(clippy::missing_inline_in_public_items)]
pub fn foo() -> u32 {
0
}
fn private_function() {}

View file

@ -13,8 +13,7 @@ impl A {}
//~^ multiple_inherent_impl
#[cfg(test)]
impl A {} // false positive
//~^ multiple_inherent_impl
impl A {}
#[cfg(test)]
impl A {}
@ -25,8 +24,7 @@ struct B;
impl B {}
#[cfg(test)]
impl B {} // false positive
//~^ multiple_inherent_impl
impl B {}
impl B {}
//~^ multiple_inherent_impl

Some files were not shown because too many files have changed in this diff Show more