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

This commit is contained in:
Philipp Krones 2026-01-08 18:33:46 +01:00
commit 3fac496339
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
149 changed files with 2070 additions and 512 deletions

View file

@ -1,6 +1,6 @@
// REUSE-IgnoreStart
Copyright 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license

View file

@ -42,7 +42,7 @@ walkdir = "2.3"
filetime = "0.2.9"
itertools = "0.12"
pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] }
askama = { version = "0.15", default-features = false, features = ["alloc", "config", "derive"] }
[dev-dependencies.toml]
version = "0.9.7"

View file

@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated

View file

@ -277,7 +277,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
<!-- REUSE-IgnoreStart -->
Copyright 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license

View file

@ -1,10 +1,10 @@
use super::ALLOW_ATTRIBUTES;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use rustc_ast::attr::AttributeExt;
use rustc_ast::{AttrStyle, Attribute};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, LintContext};
use rustc_ast::attr::AttributeExt;
// Separate each crate's features.
pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) {
@ -15,12 +15,7 @@ pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) {
{
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(cx, ALLOW_ATTRIBUTES, path_span, "#[allow] attribute found", |diag| {
diag.span_suggestion(
path_span,
"replace it with",
"expect",
Applicability::MachineApplicable,
);
diag.span_suggestion(path_span, "replace it with", "expect", Applicability::MachineApplicable);
});
}
}

View file

@ -16,7 +16,7 @@ mod utils;
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::msrvs::{self, Msrv, MsrvStack};
use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind, AttrItemKind};
use rustc_ast::{self as ast, AttrArgs, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind};
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -604,7 +604,9 @@ impl EarlyLintPass for PostExpansionEarlyAttributes {
if attr.has_name(sym::ignore)
&& match &attr.kind {
AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })),
AttrKind::Normal(normal_attr) => {
!matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. }))
},
AttrKind::DocComment(..) => true,
}
{

View file

@ -2,7 +2,7 @@ use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT};
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{AttrArgs, AttrKind, AttrItemKind};
use rustc_ast::{AttrArgs, AttrItemKind, AttrKind};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
use rustc_span::sym;

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
use clippy_utils::source::walk_span_to_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::sym;
use clippy_utils::ty::{implements_trait, is_copy};
@ -130,22 +131,24 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
if let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) {
let sugg = if bool_value ^ eq_macro {
!sugg.maybe_paren()
} else if ty::Bool == *non_lit_ty.kind() {
sugg
} else {
!!sugg.maybe_paren()
};
suggestions.push((non_lit_expr.span, sugg.to_string()));
let mut applicability = Applicability::MachineApplicable;
let sugg = Sugg::hir_with_context(cx, non_lit_expr, macro_call.span.ctxt(), "..", &mut applicability);
let sugg = if bool_value ^ eq_macro {
!sugg.maybe_paren()
} else if ty::Bool == *non_lit_ty.kind() {
sugg
} else {
!!sugg.maybe_paren()
};
let non_lit_expr_span =
walk_span_to_context(non_lit_expr.span, macro_call.span.ctxt()).unwrap_or(non_lit_expr.span);
suggestions.push((non_lit_expr_span, sugg.to_string()));
diag.multipart_suggestion(
format!("replace it with `{non_eq_mac}!(..)`"),
suggestions,
Applicability::MachineApplicable,
);
}
diag.multipart_suggestion(
format!("replace it with `{non_eq_mac}!(..)`"),
suggestions,
applicability,
);
},
);
}

View file

@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::impl_lint_pass;
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, Symbol, SyntaxContext};
use std::fmt::Write as _;
declare_clippy_lint! {
/// ### What it does
@ -356,7 +357,7 @@ impl SuggestContext<'_, '_, '_> {
if app != Applicability::MachineApplicable {
return None;
}
self.output.push_str(&(!snip).to_string());
let _cannot_fail = write!(&mut self.output, "{}", &(!snip));
}
},
True | False | Not(_) => {

View file

@ -132,7 +132,7 @@ declare_clippy_lint! {
/// Because this can be caused purely by the dependencies
/// themselves, it's not always possible to fix this issue.
/// In those cases, you can allow that specific crate using
/// the `allowed_duplicate_crates` configuration option.
/// the `allowed-duplicate-crates` configuration option.
///
/// ### Example
/// ```toml

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::attr::data_structures::CfgEntry;
use rustc_ast::{AttrItemKind, EarlyParsedAttribute};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::declare_lint_pass;
use rustc_ast::AttrItemKind;
use rustc_ast::EarlyParsedAttribute;
use rustc_span::sym;
use rustc_ast::attr::data_structures::CfgEntry;
declare_clippy_lint! {
/// ### What it does
@ -40,7 +39,7 @@ impl EarlyLintPass for CfgNotTest {
unreachable!()
};
if contains_not_test(&cfg, false) {
if contains_not_test(cfg, false) {
span_lint_and_then(
cx,
CFG_NOT_TEST,
@ -58,11 +57,9 @@ impl EarlyLintPass for CfgNotTest {
fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool {
match cfg {
CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| {
contains_not_test(item, not)
}),
CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| contains_not_test(item, not)),
CfgEntry::Not(sub, _) => contains_not_test(sub, !not),
CfgEntry::NameValue { name: sym::test, .. } => not,
_ => false
_ => false,
}
}

View file

@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
@ -80,7 +80,8 @@ impl LateLintPass<'_> for CheckedConversions {
&& self.msrv.meets(cx, msrvs::TRY_FROM)
{
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
let (snippet, _) =
snippet_with_context(cx, cv.expr_to_cast.span, item.span.ctxt(), "_", &mut applicability);
span_lint_and_sugg(
cx,
CHECKED_CONVERSIONS,

View file

@ -1,15 +1,16 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::fulfill_or_allowed;
use rustc_hir::{self as hir, HirId};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::{Span, sym};
use rustc_span::sym;
use super::DERIVE_ORD_XOR_PARTIAL_ORD;
/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
item: &hir::Item<'_>,
trait_ref: &hir::TraitRef<'_>,
ty: Ty<'tcx>,
adt_hir_id: HirId,
@ -19,6 +20,8 @@ pub(super) fn check<'tcx>(
&& let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait()
&& let Some(def_id) = &trait_ref.trait_def_id()
&& *def_id == ord_trait_def_id
&& let item_hir_id = cx.tcx.local_def_id_to_hir_id(item.owner_id)
&& !fulfill_or_allowed(cx, DERIVE_ORD_XOR_PARTIAL_ORD, [adt_hir_id])
{
// Look for the PartialOrd implementations for `ty`
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
@ -39,7 +42,7 @@ pub(super) fn check<'tcx>(
"you are deriving `Ord` but have implemented `PartialOrd` explicitly"
};
span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| {
span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, item_hir_id, item.span, mess, |diag| {
if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here");

View file

@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived);
derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived);
derive_ord_xor_partial_ord::check(cx, item, trait_ref, ty, adt_hir_id, is_automatically_derived);
if is_automatically_derived {
unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id);

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute, AttrItemKind};
use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, AttrStyle, Attribute};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;

View file

@ -869,10 +869,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
}),
true,
);
let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| {
add_doc_fragment(&mut acc, fragment);
acc
});
let mut doc = String::with_capacity(fragments.iter().map(|frag| frag.doc.as_str().len() + 1).sum());
for fragment in &fragments {
add_doc_fragment(&mut doc, fragment);
}
doc.pop();
if doc.trim().is_empty() {

View file

@ -114,6 +114,8 @@ fn check_source(cx: &EarlyContext<'_>, inner: &Expr) -> bool {
&& inner.starts_with('(')
&& inner.ends_with(')')
&& outer_after_inner.trim_start().starts_with(')')
// Don't lint macro repetition patterns like `($($result),*)` where parens are necessary
&& !inner.trim_start_matches('(').trim_start().starts_with("$(")
{
true
} else {

View file

@ -1,9 +1,13 @@
use std::borrow::Cow;
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::{Sugg, make_binop};
use clippy_utils::{
SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, sym,
SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, is_integer_literal_untyped,
peel_blocks, peel_blocks_with_stmt, sym,
};
use rustc_ast::ast::LitKind;
use rustc_data_structures::packed::Pu128;
@ -238,10 +242,21 @@ fn check_subtraction(
if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) {
// This part of the condition is voluntarily split from the one before to ensure that
// if `snippet_opt` fails, it won't try the next conditions.
if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST))
&& let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren)
&& let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr)
{
if !is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST) {
let mut applicability = Applicability::MachineApplicable;
let big_expr_sugg = (if is_integer_literal_untyped(big_expr) {
let get_snippet = |span: Span| {
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
let big_expr_ty = cx.typeck_results().expr_ty(big_expr);
Cow::Owned(format!("{snippet}_{big_expr_ty}"))
};
Sugg::hir_from_snippet(cx, big_expr, get_snippet)
} else {
Sugg::hir_with_applicability(cx, big_expr, "..", &mut applicability)
})
.maybe_paren();
let little_expr_sugg = Sugg::hir_with_applicability(cx, little_expr, "..", &mut applicability);
let sugg = format!(
"{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}",
if is_composited { "{ " } else { "" },
@ -254,7 +269,7 @@ fn check_subtraction(
"manual arithmetic check found",
"replace it with",
sugg,
Applicability::MachineApplicable,
applicability,
);
}
} else if eq_expr_value(cx, left, little_expr)

View file

@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::Msrv;
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_hir::attrs::AttributeKind;
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince, find_attr};
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};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
declare_clippy_lint! {
/// ### What it does
@ -270,6 +269,9 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
/// attribute.
fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool {
cx.tcx.hir_parent_id_iter(hir_id).any(|id| {
find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace)
find_attr!(
cx.tcx.hir_attrs(id),
AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace
)
})
}

View file

@ -101,7 +101,21 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
InherentImplLintScope::Crate => Criterion::Crate,
};
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)) {
let predicates = {
// Gets the predicates (bounds) for the given impl block,
// sorted for consistent comparison to allow distinguishing between impl blocks
// with different generic bounds.
let mut predicates = cx
.tcx
.predicates_of(impl_id)
.predicates
.iter()
.map(|(clause, _)| *clause)
.collect::<Vec<_>>();
predicates.sort_by_key(|c| format!("{c:?}"));
predicates
};
match type_map.entry((impl_ty, predicates, 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));
@ -152,15 +166,12 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
let id = cx.tcx.local_def_id_to_hir_id(id);
if let Node::Item(&Item {
kind: ItemKind::Impl(impl_item),
kind: ItemKind::Impl(_),
span,
..
}) = cx.tcx.hir_node(id)
{
(!span.from_expansion()
&& impl_item.generics.params.is_empty()
&& !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id]))
.then_some(span)
(!span.from_expansion() && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])).then_some(span)
} else {
None
}

View file

@ -1,9 +1,8 @@
use rustc_ast::AttrItemKind;
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet_opt;
use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind};
use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, Attribute, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::impl_lint_pass;

View file

@ -94,6 +94,15 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
})
&& u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)
{
// libtest might generate a large array containing the test cases, and no span will be associated
// to it. In this case it is better not to complain.
//
// Note that this condition is not checked explicitly by a unit test. Do not remove it without
// ensuring that <https://github.com/rust-lang/rust-clippy/issues/13774> stays fixed.
if expr.span.is_dummy() {
return;
}
span_lint_and_then(
cx,
LARGE_STACK_ARRAYS,

View file

@ -1,16 +1,22 @@
use super::FOR_KV_MAP;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet;
use clippy_utils::source::{snippet_with_applicability, walk_span_to_context};
use clippy_utils::{pat_is_wild, sugg};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::sym;
use rustc_span::{Span, sym};
/// Checks for the `FOR_KV_MAP` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
body: &'tcx Expr<'_>,
span: Span,
) {
let pat_span = pat.span;
if let PatKind::Tuple(pat, _) = pat.kind
@ -34,21 +40,25 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
_ => arg,
};
if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) {
if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap))
&& let Some(arg_span) = walk_span_to_context(arg_span, span.ctxt())
{
span_lint_and_then(
cx,
FOR_KV_MAP,
arg_span,
format!("you seem to want to iterate on a map's {kind}s"),
|diag| {
let map = sugg::Sugg::hir(cx, arg, "map");
let mut applicability = Applicability::MachineApplicable;
let map = sugg::Sugg::hir_with_context(cx, arg, span.ctxt(), "map", &mut applicability);
let pat = snippet_with_applicability(cx, new_pat_span, kind, &mut applicability);
diag.multipart_suggestion(
"use the corresponding method",
vec![
(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
(pat_span, pat.to_string()),
(arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())),
],
Applicability::MachineApplicable,
applicability,
);
},
);

View file

@ -943,7 +943,7 @@ impl Loops {
explicit_counter_loop::check(cx, pat, arg, body, expr, label);
}
self.check_for_loop_arg(cx, pat, arg);
for_kv_map::check(cx, pat, arg, body);
for_kv_map::check(cx, pat, arg, body, span);
mut_range_bound::check(cx, arg, body);
single_element_loop::check(cx, pat, arg, body, expr);
same_item_push::check(cx, pat, arg, body, expr, self.msrv);

View file

@ -1,7 +1,7 @@
use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sym;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
@ -111,14 +111,12 @@ impl LateLintPass<'_> for ManualIgnoreCaseCmp {
"manual case-insensitive ASCII comparison",
|diag| {
let mut app = Applicability::MachineApplicable;
let (left_snip, _) = snippet_with_context(cx, left_span, expr.span.ctxt(), "..", &mut app);
let (right_snip, _) = snippet_with_context(cx, right_span, expr.span.ctxt(), "..", &mut app);
diag.span_suggestion_verbose(
expr.span,
"consider using `.eq_ignore_ascii_case()` instead",
format!(
"{neg}{}.eq_ignore_ascii_case({deref}{})",
snippet_with_applicability(cx, left_span, "_", &mut app),
snippet_with_applicability(cx, right_span, "_", &mut app)
),
format!("{neg}{left_snip}.eq_ignore_ascii_case({deref}{right_snip})"),
app,
);
},

View file

@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{is_from_proc_macro, sym};
use rustc_ast::LitKind;
use rustc_data_structures::packed::Pu128;
@ -102,7 +102,7 @@ impl LateLintPass<'_> for ManualIlog2 {
fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, full_expr: &Expr<'_>) {
let mut app = Applicability::MachineApplicable;
let recv = snippet_with_applicability(cx, recv.span, "_", &mut app);
let (recv, _) = snippet_with_context(cx, recv.span, full_expr.span.ctxt(), "_", &mut app);
span_lint_and_sugg(
cx,
MANUAL_ILOG2,

View file

@ -135,7 +135,7 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok
} else {
Applicability::MachineApplicable
};
let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren();
let scrut = Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_paren();
let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee);
let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty);

View file

@ -46,6 +46,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" };
let mut applicability = Applicability::MachineApplicable;
let ctxt = expr.span.ctxt();
span_lint_and_then(
cx,
MATCH_AS_REF,
@ -59,7 +60,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
"use `Option::as_ref()`",
format!(
"{}.as_ref(){cast}",
Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(),
Sugg::hir_with_context(cx, ex, ctxt, "_", &mut applicability).maybe_paren(),
),
applicability,
);
@ -69,7 +70,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
format!("use `Option::{method}()` directly"),
format!(
"{}.{method}(){cast}",
Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(),
Sugg::hir_with_context(cx, ex, ctxt, "_", &mut applicability).maybe_paren(),
),
applicability,
);

View file

@ -16,6 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
&& arms
.iter()
.all(|arm| arm.pat.walk_short(|p| !matches!(p.kind, PatKind::Binding(..))))
&& arms.len() == 2
{
span_lint_and_then(
cx,
@ -23,59 +24,58 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]
expr.span,
"`match` on a boolean expression",
move |diag| {
if arms.len() == 2 {
let mut app = Applicability::MachineApplicable;
let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app);
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
match &lit.node {
LitKind::Bool(true) => Some(test),
LitKind::Bool(false) => Some(!test),
_ => None,
}
.map(|test| {
if let Some(guard) = &arms[0]
.guard
.map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app))
{
test.and(guard)
} else {
test
}
})
} else {
None
let mut app = Applicability::MachineApplicable;
let ctxt = expr.span.ctxt();
let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind {
let test = Sugg::hir_with_context(cx, scrutinee, ctxt, "_", &mut app);
if let PatExprKind::Lit { lit, .. } = arm_bool.kind {
match &lit.node {
LitKind::Bool(true) => Some(test),
LitKind::Bool(false) => Some(!test),
_ => None,
}
.map(|test| {
if let Some(guard) = &arms[0]
.guard
.map(|g| Sugg::hir_with_context(cx, g, ctxt, "_", &mut app))
{
test.and(guard)
} else {
test
}
})
} else {
None
}
} else {
None
};
if let Some(test_sugg) = test_sugg {
let ctxt = expr.span.ctxt();
let (true_expr, false_expr) = (arms[0].body, arms[1].body);
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => Some(format!(
"if {} {} else {}",
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(false, true) => Some(format!(
"if {} {}",
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, false) => Some(format!(
"if {} {}",
!test_sugg,
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, true) => None,
};
if let Some(test_sugg) = test_sugg {
let ctxt = expr.span.ctxt();
let (true_expr, false_expr) = (arms[0].body, arms[1].body);
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => Some(format!(
"if {} {} else {}",
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app),
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(false, true) => Some(format!(
"if {} {}",
test_sugg,
expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, false) => Some(format!(
"if {} {}",
!test_sugg,
expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app)
)),
(true, true) => None,
};
if let Some(sugg) = sugg {
diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app);
}
if let Some(sugg) = sugg {
diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app);
}
}
},

View file

@ -1,7 +1,6 @@
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
use clippy_utils::source::walk_span_to_context;
use clippy_utils::sugg::{Sugg, make_unop};
use clippy_utils::ty::needs_ordered_drop;
use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures};
@ -25,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
..
}) = higher::WhileLet::hir(expr)
{
find_method_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
find_method_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false, let_span);
find_if_let_true(cx, let_pat, let_expr, let_span);
}
}
@ -39,7 +38,7 @@ pub(super) fn check_if_let<'tcx>(
let_span: Span,
) {
find_if_let_true(cx, pat, scrutinee, let_span);
find_method_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else);
find_method_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else, let_span);
}
/// Looks for:
@ -182,6 +181,7 @@ fn find_method_sugg_for_if_let<'tcx>(
let_expr: &'tcx Expr<'_>,
keyword: &'static str,
has_else: bool,
let_span: Span,
) {
// also look inside refs
// if we have &None for example, peel it so we can detect "if let None = x"
@ -239,15 +239,9 @@ fn find_method_sugg_for_if_let<'tcx>(
let expr_span = expr.span;
let ctxt = expr.span.ctxt();
// if/while let ... = ... { ... }
// ^^^
let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else {
return;
};
// if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(res_span.shrink_to_hi());
let span = expr_span.until(let_span.shrink_to_hi());
let mut app = if needs_drop {
Applicability::MaybeIncorrect
@ -273,13 +267,14 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized
&& let Some((good_method, maybe_guard)) = found_good_method(cx, arms)
{
let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span));
let expr_span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span);
let result_expr = match &op.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed,
_ => op,
};
let mut app = Applicability::MachineApplicable;
let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren();
let receiver_sugg = Sugg::hir_with_context(cx, result_expr, expr_span.ctxt(), "_", &mut app).maybe_paren();
let mut sugg = format!("{receiver_sugg}.{good_method}");
if let Some(guard) = maybe_guard {
@ -302,14 +297,14 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
return;
}
let guard = Sugg::hir(cx, guard, "..");
let guard = Sugg::hir_with_context(cx, guard, expr_span.ctxt(), "..", &mut app);
let _ = write!(sugg, " && {}", guard.maybe_paren());
}
span_lint_and_sugg(
cx,
REDUNDANT_PATTERN_MATCHING,
span,
expr_span,
format!("redundant pattern matching, consider using `{good_method}`"),
"try",
sugg,

View file

@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::macros::{is_assert_macro, root_macro_call};
use clippy_utils::res::MaybeResPath;
use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context};
use rustc_hir::{Expr, HirId};
use rustc_lint::{LateContext, LintContext};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use rustc_hir::{Expr, HirId, find_attr};
use rustc_lint::{LateContext, LintContext};
use super::CONST_IS_EMPTY;

View file

@ -2,7 +2,7 @@ use super::ITER_KV_MAP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::{pat_is_wild, sym};
use rustc_hir::{Body, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
@ -58,6 +58,8 @@ pub(super) fn check<'tcx>(
applicability,
);
} else {
let (body_snippet, _) =
snippet_with_context(cx, body_expr.span, expr.span.ctxt(), "..", &mut applicability);
span_lint_and_sugg(
cx,
ITER_KV_MAP,
@ -65,9 +67,8 @@ pub(super) fn check<'tcx>(
format!("iterating on a map's {replacement_kind}s"),
"try",
format!(
"{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {})",
"{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {body_snippet})",
annotation.prefix_str(),
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)
),
applicability,
);

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_blocks, strip_pat_refs};
use rustc_ast::ast;
use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_errors::{Applicability, Diag};
use rustc_hir as hir;
use rustc_hir::PatKind;
use rustc_hir::def::{DefKind, Res};
@ -59,6 +59,34 @@ struct Replacement {
method_name: &'static str,
has_args: bool,
has_generic_return: bool,
is_short_circuiting: bool,
}
impl Replacement {
fn default_applicability(&self) -> Applicability {
if self.is_short_circuiting {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
}
}
fn maybe_add_note(&self, diag: &mut Diag<'_, ()>) {
if self.is_short_circuiting {
diag.note(format!(
"the `{}` method is short circuiting and may change the program semantics if the iterator has side effects",
self.method_name
));
}
}
fn maybe_turbofish(&self, ty: Ty<'_>) -> String {
if self.has_generic_return {
format!("::<{ty}>")
} else {
String::new()
}
}
}
fn check_fold_with_op(
@ -86,32 +114,30 @@ fn check_fold_with_op(
&& left_expr.res_local_id() == Some(first_arg_id)
&& (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id))
{
let mut applicability = Applicability::MachineApplicable;
let turbofish = if replacement.has_generic_return {
format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs())
} else {
String::new()
};
let sugg = if replacement.has_args {
format!(
"{method}{turbofish}(|{second_arg_ident}| {r})",
method = replacement.method_name,
r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability),
)
} else {
format!("{method}{turbofish}()", method = replacement.method_name)
};
span_lint_and_sugg(
let span = fold_span.with_hi(expr.span.hi());
span_lint_and_then(
cx,
UNNECESSARY_FOLD,
fold_span.with_hi(expr.span.hi()),
span,
"this `.fold` can be written more succinctly using another method",
"try",
sugg,
applicability,
|diag| {
let mut applicability = replacement.default_applicability();
let turbofish =
replacement.maybe_turbofish(cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs());
let (r_snippet, _) =
snippet_with_context(cx, right_expr.span, expr.span.ctxt(), "EXPR", &mut applicability);
let sugg = if replacement.has_args {
format!(
"{method}{turbofish}(|{second_arg_ident}| {r_snippet})",
method = replacement.method_name,
)
} else {
format!("{method}{turbofish}()", method = replacement.method_name)
};
diag.span_suggestion(span, "try", sugg, applicability);
replacement.maybe_add_note(diag);
},
);
return true;
}
@ -131,22 +157,25 @@ fn check_fold_with_method(
// Check if the function belongs to the operator
&& cx.tcx.is_diagnostic_item(method, fn_did)
{
let applicability = Applicability::MachineApplicable;
let turbofish = if replacement.has_generic_return {
format!("::<{}>", cx.typeck_results().expr_ty(expr))
} else {
String::new()
};
span_lint_and_sugg(
let span = fold_span.with_hi(expr.span.hi());
span_lint_and_then(
cx,
UNNECESSARY_FOLD,
fold_span.with_hi(expr.span.hi()),
span,
"this `.fold` can be written more succinctly using another method",
"try",
format!("{method}{turbofish}()", method = replacement.method_name),
applicability,
|diag| {
diag.span_suggestion(
span,
"try",
format!(
"{method}{turbofish}()",
method = replacement.method_name,
turbofish = replacement.maybe_turbofish(cx.typeck_results().expr_ty(expr))
),
replacement.default_applicability(),
);
replacement.maybe_add_note(diag);
},
);
}
}
@ -171,6 +200,7 @@ pub(super) fn check<'tcx>(
method_name: "any",
has_args: true,
has_generic_return: false,
is_short_circuiting: true,
};
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, replacement);
},
@ -179,6 +209,7 @@ pub(super) fn check<'tcx>(
method_name: "all",
has_args: true,
has_generic_return: false,
is_short_circuiting: true,
};
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, replacement);
},
@ -187,6 +218,7 @@ pub(super) fn check<'tcx>(
method_name: "sum",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
is_short_circuiting: false,
};
if !check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, replacement) {
check_fold_with_method(cx, expr, acc, fold_span, sym::add, replacement);
@ -197,6 +229,7 @@ pub(super) fn check<'tcx>(
method_name: "product",
has_args: false,
has_generic_return: needs_turbofish(cx, expr),
is_short_circuiting: false,
};
if !check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, replacement) {
check_fold_with_method(cx, expr, acc, fold_span, sym::mul, replacement);

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

@ -82,7 +82,8 @@ impl LateLintPass<'_> for ImportRename {
&& let Some(import) = match snip.split_once(" as ") {
None => Some(snip.as_str()),
Some((import, rename)) => {
if rename.trim() == name.as_str() {
let trimmed_rename = rename.trim();
if trimmed_rename == "_" || trimmed_rename == name.as_str() {
None
} else {
Some(import.trim())

View file

@ -31,7 +31,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.78.0"]
pub MULTIPLE_BOUND_LOCATIONS,
suspicious,
style,
"defining generic bounds in multiple locations"
}

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

@ -143,7 +143,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &T
&& new.ident.name == sym::new
{
let mut applicability = Applicability::MaybeIncorrect;
let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability);
let arg = Sugg::hir_with_context(cx, arg, expr.span.ctxt(), "_", &mut applicability);
let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))];
match ty_ascription {
TypeAscriptionKind::Required(ty_ascription) => {

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{
SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call,
@ -171,8 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
&& SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
{
let mut applicability = Applicability::MachineApplicable;
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability);
let cond = Sugg::hir_with_context(cx, cond, e.span.ctxt(), "..", &mut applicability);
let (lhs, _) = snippet_with_context(cx, lhs_a.span, e.span.ctxt(), "..", &mut applicability);
let mut sugg = if a == b {
format!("{cond}; {lhs} = {a:?};")
} else {

View file

@ -56,8 +56,20 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
&& let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind {
check_expr(cx, expr, stmt.span);
}
}
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
if let Some(expr) = block.expr {
check_expr(cx, expr, expr.span);
}
}
}
fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>, outer_span: Span) {
if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind
&& let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind
// Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
// `v.foo().iter().for_each()` must be skipped.
@ -76,69 +88,74 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
// Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
// and suggesting `for … in … { unsafe { } }` is a little ugly.
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
{
let mut applicability = Applicability::MachineApplicable;
// If any closure parameter has an explicit type specified, applying the lint would necessarily
// remove that specification, possibly breaking type inference
if fn_decl
.inputs
.iter()
.any(|input| matches!(input.kind, TyKind::Infer(..)))
{
let mut applicability = Applicability::MachineApplicable;
applicability = Applicability::MaybeIncorrect;
}
// If any closure parameter has an explicit type specified, applying the lint would necessarily
// remove that specification, possibly breaking type inference
if fn_decl
.inputs
.iter()
.any(|input| matches!(input.kind, TyKind::Infer(..)))
{
applicability = Applicability::MaybeIncorrect;
}
let mut ret_collector = RetCollector::default();
ret_collector.visit_expr(body.value);
let mut ret_collector = RetCollector::default();
ret_collector.visit_expr(body.value);
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
if ret_collector.ret_in_loop {
return;
}
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
if ret_collector.ret_in_loop {
return;
}
let ret_suggs = if ret_collector.spans.is_empty() {
None
} else {
applicability = Applicability::MaybeIncorrect;
Some(
ret_collector
.spans
.into_iter()
.map(|span| (span, "continue".to_string()))
.collect(),
)
};
let ret_suggs = if ret_collector.spans.is_empty() {
None
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
let (body_value_sugg, is_macro_call) =
snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability);
let sugg = format!(
"for {} in {} {}",
body_param_sugg,
for_each_rev_sugg,
if is_macro_call {
format!("{{ {body_value_sugg}; }}")
} else {
applicability = Applicability::MaybeIncorrect;
Some(
ret_collector
.spans
.into_iter()
.map(|span| (span, "continue".to_string()))
.collect(),
)
};
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
let (body_value_sugg, is_macro_call) =
snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability);
let sugg = format!(
"for {} in {} {}",
body_param_sugg,
for_each_rev_sugg,
if is_macro_call {
format!("{{ {body_value_sugg}; }}")
} else {
match body.value.kind {
ExprKind::Block(block, _) if is_let_desugar(block) => {
format!("{{ {body_value_sugg} }}")
},
ExprKind::Block(_, _) => body_value_sugg.to_string(),
_ => format!("{{ {body_value_sugg}; }}"),
}
match body.value.kind {
ExprKind::Block(block, _) if is_let_desugar(block) => {
format!("{{ {body_value_sugg} }}")
},
ExprKind::Block(_, _) => body_value_sugg.to_string(),
_ => format!("{{ {body_value_sugg}; }}"),
}
);
}
);
span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
diag.span_suggestion(stmt.span, "try", sugg, applicability);
span_lint_and_then(
cx,
NEEDLESS_FOR_EACH,
outer_span,
"needless use of `for_each`",
|diag| {
diag.span_suggestion(outer_span, "try", sugg, applicability);
if let Some(ret_suggs) = ret_suggs {
diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability);
}
});
}
},
);
}
}

View file

@ -1,16 +1,15 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::return_ty;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
use clippy_utils::sugg::DiagExt;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::HirIdSet;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::{Attribute, HirIdSet};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::AssocKind;
use rustc_session::impl_lint_pass;
use rustc_span::sym;
use rustc_hir::Attribute;
use rustc_hir::attrs::AttributeKind;
declare_clippy_lint! {
/// ### What it does
@ -59,6 +58,7 @@ pub struct NewWithoutDefault {
impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
#[expect(clippy::too_many_lines)]
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let hir::ItemKind::Impl(hir::Impl {
of_trait: None,
@ -139,16 +139,34 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app));
sugg.push('\n');
}
}
sugg
};
let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app);
let where_clause_sugg = if generics.has_where_clause_predicates {
format!(
"\n{}\n",
snippet_with_applicability(cx, generics.where_clause_span, "", &mut app)
)
let where_clause_sugg =
snippet_with_applicability(cx, generics.where_clause_span, "", &mut app).to_string();
let mut where_clause_sugg = reindent_multiline(&where_clause_sugg, true, Some(4));
if impl_item.generics.has_where_clause_predicates {
if !where_clause_sugg.ends_with(',') {
where_clause_sugg.push(',');
}
let additional_where_preds =
snippet_with_applicability(cx, impl_item.generics.where_clause_span, "", &mut app);
let ident = indent_of(cx, generics.where_clause_span).unwrap_or(0);
// Remove the leading `where ` keyword
let additional_where_preds = additional_where_preds.trim_start_matches("where").trim_start();
where_clause_sugg.push('\n');
where_clause_sugg.extend(std::iter::repeat_n(' ', ident));
where_clause_sugg.push_str(additional_where_preds);
}
format!("\n{where_clause_sugg}\n")
} else if impl_item.generics.has_where_clause_predicates {
let where_clause_sugg =
snippet_with_applicability(cx, impl_item.generics.where_clause_span, "", &mut app);
let where_clause_sugg = reindent_multiline(&where_clause_sugg, true, Some(4));
format!("\n{}\n", where_clause_sugg.trim_start())
} else {
String::new()
};

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::res::MaybeQPath;
use clippy_utils::source::snippet;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
@ -94,51 +94,37 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
return;
}
let arg_snip = snippet(cx, arg_span, "..");
let expr_snip;
let eq_impl;
if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() {
expr_snip = format!("*{arg_snip}");
eq_impl = with_deref;
let mut applicability = Applicability::MachineApplicable;
let (arg_snip, _) = snippet_with_context(cx, arg_span, expr.span.ctxt(), "..", &mut applicability);
let (expr_snip, eq_impl) = if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() {
(format!("*{arg_snip}"), with_deref)
} else {
expr_snip = arg_snip.to_string();
eq_impl = without_deref;
}
(arg_snip.to_string(), without_deref)
};
let span;
let hint;
if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
span = expr.span;
hint = expr_snip;
let (span, hint) = if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
(expr.span, expr_snip)
} else {
span = expr.span.to(other.span);
let span = expr.span.to(other.span);
let cmp_span = if other.span < expr.span {
other.span.between(expr.span)
} else {
expr.span.between(other.span)
};
if eq_impl.ty_eq_other {
hint = format!(
"{expr_snip}{}{}",
snippet(cx, cmp_span, ".."),
snippet(cx, other.span, "..")
);
} else {
hint = format!(
"{}{}{expr_snip}",
snippet(cx, other.span, ".."),
snippet(cx, cmp_span, "..")
);
}
}
diag.span_suggestion(
span,
"try",
hint,
Applicability::MachineApplicable, // snippet
);
let (cmp_snippet, _) = snippet_with_context(cx, cmp_span, expr.span.ctxt(), "..", &mut applicability);
let (other_snippet, _) =
snippet_with_context(cx, other.span, expr.span.ctxt(), "..", &mut applicability);
if eq_impl.ty_eq_other {
(span, format!("{expr_snip}{cmp_snippet}{other_snippet}"))
} else {
(span, format!("{other_snippet}{cmp_snippet}{expr_snip}"))
}
};
diag.span_suggestion(span, "try", hint, applicability);
},
);
}

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::sugg::{Sugg, has_enclosing_paren};
use clippy_utils::{SpanlessEq, sym};
use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp};
@ -7,7 +8,7 @@ use rustc_data_structures::packed::Pu128;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self};
use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Spanned;
use super::MANUAL_DIV_CEIL;
@ -16,59 +17,84 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: &
let mut applicability = Applicability::MachineApplicable;
if op == BinOpKind::Div
&& check_int_ty_and_feature(cx, lhs)
&& check_int_ty_and_feature(cx, rhs)
&& let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = lhs.kind
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(lhs))
&& check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(rhs))
&& msrv.meets(cx, msrvs::DIV_CEIL)
{
// (x + (y - 1)) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
return;
}
match lhs.kind {
ExprKind::Binary(inner_op, inner_lhs, inner_rhs) => {
// (x + (y - 1)) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
return;
}
// ((y - 1) + x) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
return;
}
// ((y - 1) + x) / y
if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Add
&& sub_op.node == BinOpKind::Sub
&& check_literal(sub_rhs)
&& check_eq_expr(cx, sub_lhs, rhs)
{
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
return;
}
// (x + y - 1) / y
if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Sub
&& add_op.node == BinOpKind::Add
&& check_literal(inner_rhs)
&& check_eq_expr(cx, add_rhs, rhs)
{
build_suggestion(cx, expr, add_lhs, rhs, &mut applicability);
}
// (x + y - 1) / y
if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind
&& inner_op.node == BinOpKind::Sub
&& add_op.node == BinOpKind::Add
&& check_literal(inner_rhs)
&& check_eq_expr(cx, add_rhs, rhs)
{
build_suggestion(cx, expr, add_lhs, rhs, &mut applicability);
}
// (x + (Y - 1)) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) {
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}
// (x + (Y - 1)) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) {
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}
// ((Y - 1) + x) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) {
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
}
// ((Y - 1) + x) / Y
if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) {
build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability);
}
// (x - (-Y - 1)) / Y
if inner_op.node == BinOpKind::Sub
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind
&& differ_by_one(abs_div_rhs, inner_rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
// (x - (-Y - 1)) / Y
if inner_op.node == BinOpKind::Sub
&& let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind
&& differ_by_one(abs_div_rhs, inner_rhs)
{
build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability);
}
},
ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) => {
// x.next_multiple_of(Y) / Y
if method.ident.name == sym::next_multiple_of
&& check_int_ty(cx.typeck_results().expr_ty(receiver))
&& check_eq_expr(cx, next_multiple_of_arg, rhs)
{
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
}
},
ExprKind::Call(callee, [receiver, next_multiple_of_arg]) => {
// int_type::next_multiple_of(x, Y) / Y
if let Some(impl_ty_binder) = callee
.ty_rel_def_if_named(cx, sym::next_multiple_of)
.assoc_fn_parent(cx)
.opt_impl_ty(cx)
&& check_int_ty(impl_ty_binder.skip_binder())
&& check_eq_expr(cx, next_multiple_of_arg, rhs)
{
build_suggestion(cx, expr, receiver, rhs, &mut applicability);
}
},
_ => (),
}
}
}
@ -91,8 +117,11 @@ fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool {
}
}
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr);
fn check_int_ty(expr_ty: Ty<'_>) -> bool {
matches!(expr_ty.peel_refs().kind(), ty::Int(_) | ty::Uint(_))
}
fn check_int_ty_and_feature(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool {
match expr_ty.peel_refs().kind() {
ty::Uint(_) => true,
ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings),

View file

@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(
{
let mut app = Applicability::MachineApplicable;
let divisor = deref_sugg(
Sugg::hir_with_applicability(cx, operand_right, "_", &mut app),
Sugg::hir_with_context(cx, operand_right, expr.span.ctxt(), "_", &mut app),
cx.typeck_results().expr_ty_adjusted(operand_right),
);
span_lint_and_sugg(
@ -47,7 +47,7 @@ pub(super) fn check<'tcx>(
format!(
"{}{}.is_multiple_of({divisor})",
if op == BinOpKind::Eq { "" } else { "!" },
Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren()
Sugg::hir_with_context(cx, operand_left, expr.span.ctxt(), "_", &mut app).maybe_paren()
),
app,
);

View file

@ -5,7 +5,7 @@ use clippy_config::types::MatchLintBehaviour;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_copy};
use clippy_utils::usage::local_used_after_expr;
@ -147,7 +147,8 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
&& !span_contains_cfg(cx, els.span)
{
let mut applicability = Applicability::MaybeIncorrect;
let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren();
let init_expr_str =
Sugg::hir_with_context(cx, init_expr, stmt.span.ctxt(), "..", &mut applicability).maybe_paren();
// Take care when binding is `ref`
let sugg = if let PatKind::Binding(
BindingMode(ByRef::Yes(_, ref_mutability), binding_mutability),
@ -295,7 +296,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
&& (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block))
{
let mut applicability = Applicability::MachineApplicable;
let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
let receiver_str = snippet_with_context(cx, caller.span, expr.span.ctxt(), "..", &mut applicability).0;
let by_ref = !cx.type_is_copy_modulo_regions(caller_ty)
&& !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
let sugg = if let Some(else_inner) = r#else {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::res::{MaybeDef, MaybeQPath};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::{
SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym,
};
@ -273,6 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
let string_expression = &expressions[0].0;
let snippet_app = snippet_with_applicability(cx, string_expression.span, "..", &mut applicability);
let (right_snip, _) = snippet_with_context(cx, right.span, e.span.ctxt(), "..", &mut applicability);
span_lint_and_sugg(
cx,
@ -280,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
e.span,
"calling a slice of `as_bytes()` with `from_utf8` should be not necessary",
"try",
format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")),
format!("Some(&{snippet_app}[{right_snip}])"),
applicability,
);
}
@ -404,7 +405,8 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
"`to_string()` called on a `&str`",
|diag| {
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability);
let (snippet, _) =
snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut applicability);
diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability);
},
);

View file

@ -14,8 +14,8 @@ declare_clippy_lint! {
/// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead.
///
/// ### Why is this bad?
/// This avoids calling an unsafe `libc` function.
/// Currently, it also avoids calculating the length.
/// libc::strlen is an unsafe function, which we don't need to call
/// if all we want to know is the length of the c-string.
///
/// ### Example
/// ```rust, ignore

View file

@ -2,7 +2,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_integer_const;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{ConstBlock, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::symbol::sym;
@ -42,5 +42,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
return true;
}
// Catching:
// `std::mem::transmute({ 0 as *const u64 })` and similar const blocks
if let ExprKind::Block(block, _) = arg.kind
&& block.stmts.is_empty()
&& let Some(inner) = block.expr
{
// Run again with the inner expression
return check(cx, expr, inner, to_ty);
}
// Catching:
// `std::mem::transmute(const { u64::MIN as *const u64 });`
if let ExprKind::ConstBlock(ConstBlock { body, .. }) = arg.kind {
// Strip out the const and run again
let block = cx.tcx.hir_body(body).value;
return check(cx, expr, block, to_ty);
}
false
}

View file

@ -456,13 +456,25 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>)
fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String {
let mut prefix = String::new();
for adj in cx.typeck_results().expr_adjustments(expr) {
let adjustments = cx.typeck_results().expr_adjustments(expr);
let [.., last] = adjustments else { return prefix };
let target = last.target;
for adj in adjustments {
match adj.kind {
Adjust::Deref(_) => prefix = format!("*{prefix}"),
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) => prefix = format!("&mut {prefix}"),
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)) => prefix = format!("&{prefix}"),
_ => {},
}
// Stop once we reach the final target type.
// This prevents over-adjusting (e.g. suggesting &**y instead of *y).
if adj.target == target {
break;
}
}
prefix
}

View file

@ -250,8 +250,8 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<
if let hir::Attribute::Unparsed(attr_kind) = &attr
// Identify attribute
&& let [tool_name, attr_name] = &attr_kind.path.segments[..]
&& tool_name.name == sym::clippy
&& attr_name.name == sym::version
&& tool_name == &sym::clippy
&& attr_name == &sym::version
&& let Some(version) = attr.value_str()
{
Some(version)

View file

@ -30,7 +30,7 @@ Function signatures can change or be removed without replacement without any pri
<!-- REUSE-IgnoreStart -->
Copyright 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Licensed under the Apache License, Version 2.0
<[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license

View file

@ -976,7 +976,9 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
l.style == r.style
&& match (&l.kind, &r.kind) {
(DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
(Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args),
(Normal(l), Normal(r)) => {
eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args)
},
_ => false,
}
}

View file

@ -23,7 +23,9 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
if let [clippy, segment2] = &*attr.path()
&& *clippy == sym::clippy
{
let path_span = attr.path_span().expect("Clippy attributes are unparsed and have a span");
let path_span = attr
.path_span()
.expect("Clippy attributes are unparsed and have a span");
let new_name = match *segment2 {
sym::cyclomatic_complexity => Some("cognitive_complexity"),
sym::author
@ -48,7 +50,7 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>(
.with_span_suggestion(
path_span,
"consider using",
format!("clippy::{}", new_name),
format!("clippy::{new_name}"),
Applicability::MachineApplicable,
)
.emit();

View file

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

@ -671,14 +671,14 @@ impl HirEqInterExpr<'_, '_, '_> {
.iter()
.zip(*inits_b)
.all(|(init_a, init_b)| self.eq_const_arg(init_a.expr, init_b.expr))
}
},
(ConstArgKind::TupleCall(path_a, args_a), ConstArgKind::TupleCall(path_b, args_b)) => {
self.eq_qpath(path_a, path_b)
&& args_a
.iter()
.zip(*args_b)
.all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b))
}
},
// Use explicit match for now since ConstArg is undergoing flux.
(
ConstArgKind::Path(..)

View file

@ -90,13 +90,13 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
use itertools::Itertools;
use rustc_abi::Integer;
use rustc_ast::ast::{self, LitKind, RangeLimits};
use rustc_ast::join_path_syms;
use rustc_ast::{LitIntType, join_path_syms};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexmap;
use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnindexMap;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::attrs::{AttributeKind, CfgEntry};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::definitions::{DefPath, DefPathData};
@ -121,7 +121,6 @@ use rustc_middle::ty::{
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
};
use rustc_hir::attrs::CfgEntry;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{Ident, Symbol, kw};
@ -1386,6 +1385,17 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
false
}
/// Checks whether the given expression is an untyped integer literal.
pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(spanned) = expr.kind
&& let LitKind::Int(_, suffix) = spanned.node
{
return suffix == LitIntType::Unsuffixed;
}
false
}
/// Checks whether the given expression is a constant literal of the given value.
pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool {
if let ExprKind::Lit(spanned) = expr.kind
@ -2403,7 +2413,10 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool {
/// use [`is_in_cfg_test`]
pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool {
if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs)
&& cfgs.iter().any(|(cfg, _)| { matches!(cfg, CfgEntry::NameValue { name: sym::test, ..})}) {
&& cfgs
.iter()
.any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. }))
{
true
} else {
false
@ -2423,9 +2436,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
/// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied.
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..))
|| find_attr!(tcx
.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), AttributeKind::CfgTrace(..))
|| find_attr!(
tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
AttributeKind::CfgTrace(..)
)
}
/// Walks up the HIR tree from the given expression in an attempt to find where the value is

View file

@ -301,7 +301,7 @@ impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> {
fn opt_qpath(self) -> Option<QPathId<'tcx>> {
match &self.kind {
PatExprKind::Path(qpath) => Some((qpath, self.hir_id)),
_ => None,
PatExprKind::Lit { .. } => None,
}
}
}
@ -419,7 +419,7 @@ impl<'a> MaybeResPath<'a> for &PatExpr<'a> {
fn opt_res_path(self) -> OptResPath<'a> {
match &self.kind {
PatExprKind::Path(qpath) => qpath.opt_res_path(),
_ => (None, None),
PatExprKind::Lit { .. } => (None, None),
}
}
}

View file

@ -127,7 +127,7 @@ impl<'a> Sugg<'a> {
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(
pub fn hir_from_snippet(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
mut get_snippet: impl FnMut(Span) -> Cow<'a, str>,

View file

@ -248,6 +248,7 @@ generate! {
next_back,
next_if,
next_if_eq,
next_multiple_of,
next_tuple,
nth,
ok,

View file

@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under:
<!-- REUSE-IgnoreStart -->
Copyright 2014-2025 The Rust Project Developers
Copyright (c) The Rust Project Contributors
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license

View file

@ -6,5 +6,6 @@ enforced-import-renames = [
{ path = "std::clone", rename = "foo" },
{ path = "std::thread::sleep", rename = "thread_sleep" },
{ path = "std::any::type_name", rename = "ident" },
{ path = "std::sync::Mutex", rename = "StdMutie" }
{ path = "std::sync::Mutex", rename = "StdMutie" },
{ path = "std::io::Write", rename = "to_test_rename_as_underscore" }
]

View file

@ -15,6 +15,7 @@ use std::{
sync :: Mutex as StdMutie,
//~^ missing_enforced_import_renames
};
use std::io::Write as _;
fn main() {
use std::collections::BTreeMap as Map;

View file

@ -15,6 +15,7 @@ use std::{
sync :: Mutex,
//~^ missing_enforced_import_renames
};
use std::io::Write as _;
fn main() {
use std::collections::BTreeMap as OopsWrongRename;

View file

@ -32,7 +32,7 @@ LL | sync :: Mutex,
| ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie`
error: this import should be renamed
--> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:20:5
--> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:21:5
|
LL | use std::collections::BTreeMap as OopsWrongRename;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map`

View file

@ -216,3 +216,16 @@ fn main() {
assert!(!(b + b));
//~^ bool_assert_comparison
}
fn issue16279() {
macro_rules! is_empty {
($x:expr) => {
$x.is_empty()
};
}
assert!(!is_empty!("a"));
//~^ bool_assert_comparison
assert!(is_empty!(""));
//~^ bool_assert_comparison
}

View file

@ -216,3 +216,16 @@ fn main() {
assert_eq!(b + b, false);
//~^ bool_assert_comparison
}
fn issue16279() {
macro_rules! is_empty {
($x:expr) => {
$x.is_empty()
};
}
assert_eq!(is_empty!("a"), false);
//~^ bool_assert_comparison
assert_eq!(is_empty!(""), true);
//~^ bool_assert_comparison
}

View file

@ -444,5 +444,29 @@ LL - assert_eq!(b + b, false);
LL + assert!(!(b + b));
|
error: aborting due to 37 previous errors
error: used `assert_eq!` with a literal bool
--> tests/ui/bool_assert_comparison.rs:227:5
|
LL | assert_eq!(is_empty!("a"), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!(is_empty!("a"), false);
LL + assert!(!is_empty!("a"));
|
error: used `assert_eq!` with a literal bool
--> tests/ui/bool_assert_comparison.rs:229:5
|
LL | assert_eq!(is_empty!(""), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!(is_empty!(""), true);
LL + assert!(is_empty!(""));
|
error: aborting due to 39 previous errors

View file

@ -1,6 +1,7 @@
#![allow(
clippy::cast_lossless,
clippy::legacy_numeric_constants,
clippy::no_effect,
unused,
// Int::max_value will be deprecated in the future
deprecated,
@ -105,4 +106,19 @@ fn msrv_1_34() {
//~^ checked_conversions
}
fn issue16293() {
struct Outer {
inner: u32,
}
let outer = Outer { inner: 42 };
macro_rules! dot_inner {
($obj:expr) => {
$obj.inner
};
}
i32::try_from(dot_inner!(outer)).is_ok();
//~^ checked_conversions
}
fn main() {}

View file

@ -1,6 +1,7 @@
#![allow(
clippy::cast_lossless,
clippy::legacy_numeric_constants,
clippy::no_effect,
unused,
// Int::max_value will be deprecated in the future
deprecated,
@ -105,4 +106,19 @@ fn msrv_1_34() {
//~^ checked_conversions
}
fn issue16293() {
struct Outer {
inner: u32,
}
let outer = Outer { inner: 42 };
macro_rules! dot_inner {
($obj:expr) => {
$obj.inner
};
}
dot_inner!(outer) <= i32::MAX as u32;
//~^ checked_conversions
}
fn main() {}

View file

@ -1,5 +1,5 @@
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:15:13
--> tests/ui/checked_conversions.rs:16:13
|
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
@ -8,100 +8,106 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
= help: to override `-D warnings` add `#[allow(clippy::checked_conversions)]`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:17:13
--> tests/ui/checked_conversions.rs:18:13
|
LL | let _ = value <= (u32::MAX as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:22:13
--> tests/ui/checked_conversions.rs:23:13
|
LL | let _ = value <= i64::from(u16::max_value()) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:24:13
--> tests/ui/checked_conversions.rs:25:13
|
LL | let _ = value <= i64::from(u16::MAX) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:29:13
--> tests/ui/checked_conversions.rs:30:13
|
LL | let _ = value <= (u8::max_value() as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:31:13
--> tests/ui/checked_conversions.rs:32:13
|
LL | let _ = value <= (u8::MAX as isize) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:38:13
--> tests/ui/checked_conversions.rs:39:13
|
LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:40:13
--> tests/ui/checked_conversions.rs:41:13
|
LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:45:13
--> tests/ui/checked_conversions.rs:46:13
|
LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:47:13
--> tests/ui/checked_conversions.rs:48:13
|
LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:54:13
--> tests/ui/checked_conversions.rs:55:13
|
LL | let _ = value <= i32::max_value() as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:56:13
--> tests/ui/checked_conversions.rs:57:13
|
LL | let _ = value <= i32::MAX as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:61:13
--> tests/ui/checked_conversions.rs:62:13
|
LL | let _ = value <= isize::max_value() as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:63:13
--> tests/ui/checked_conversions.rs:64:13
|
LL | let _ = value <= isize::MAX as usize && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:68:13
--> tests/ui/checked_conversions.rs:69:13
|
LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:70:13
--> tests/ui/checked_conversions.rs:71:13
|
LL | let _ = value <= u16::MAX as u32 && value as i32 == 5;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()`
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:104:13
--> tests/ui/checked_conversions.rs:105:13
|
LL | let _ = value <= (u32::max_value() as i64) && value >= 0;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
error: aborting due to 17 previous errors
error: checked cast can be simplified
--> tests/ui/checked_conversions.rs:120:5
|
LL | dot_inner!(outer) <= i32::MAX as u32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(dot_inner!(outer)).is_ok()`
error: aborting due to 18 previous errors

View file

@ -83,3 +83,32 @@ fn issue_8103() {
let _ = foo1 == foo2;
//~^ cmp_owned
}
macro_rules! issue16322_macro_generator {
($locale:ident) => {
mod $locale {
macro_rules! _make {
($token:tt) => {
stringify!($token)
};
}
pub(crate) use _make;
}
macro_rules! t {
($token:tt) => {
crate::$locale::_make!($token)
};
}
};
}
issue16322_macro_generator!(de);
fn issue16322(item: String) {
if item == t!(frohes_neu_Jahr) {
//~^ cmp_owned
println!("Ja!");
}
}

View file

@ -83,3 +83,32 @@ fn issue_8103() {
let _ = foo1 == foo2.to_owned();
//~^ cmp_owned
}
macro_rules! issue16322_macro_generator {
($locale:ident) => {
mod $locale {
macro_rules! _make {
($token:tt) => {
stringify!($token)
};
}
pub(crate) use _make;
}
macro_rules! t {
($token:tt) => {
crate::$locale::_make!($token)
};
}
};
}
issue16322_macro_generator!(de);
fn issue16322(item: String) {
if item == t!(frohes_neu_Jahr).to_string() {
//~^ cmp_owned
println!("Ja!");
}
}

View file

@ -49,5 +49,11 @@ error: this creates an owned instance just for comparison
LL | let _ = foo1 == foo2.to_owned();
| ^^^^^^^^^^^^^^^ help: try: `foo2`
error: aborting due to 8 previous errors
error: this creates an owned instance just for comparison
--> tests/ui/cmp_owned/with_suggestion.rs:110:16
|
LL | if item == t!(frohes_neu_Jahr).to_string() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)`
error: aborting due to 9 previous errors

View file

@ -91,3 +91,17 @@ mod issue15708 {
}
}
}
mod issue16298 {
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
struct Normalized<S>(S);
impl<S: Eq> Eq for Normalized<S> {}
#[expect(clippy::derive_ord_xor_partial_ord)]
impl<S: Eq + PartialOrd> Ord for Normalized<S> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
}

View file

@ -161,4 +161,20 @@ fn issue15940() {
pub struct Person;
}
fn issue16224() {
fn test() -> i32 { 42 }
macro_rules! call {
($matcher:pat $(=> $result:expr)?) => {
match test() {
$matcher => Result::Ok(($($result),*)),
_ => Result::Err("No match".to_string()),
}
};
}
let _: Result<(), String> = call!(_);
let _: Result<i32, String> = call!(_ => 42);
}
fn main() {}

View file

@ -161,4 +161,20 @@ fn issue15940() {
pub struct Person;
}
fn issue16224() {
fn test() -> i32 { 42 }
macro_rules! call {
($matcher:pat $(=> $result:expr)?) => {
match test() {
$matcher => Result::Ok(($($result),*)),
_ => Result::Err("No match".to_string()),
}
};
}
let _: Result<(), String> = call!(_);
let _: Result<i32, String> = call!(_ => 42);
}
fn main() {}

View file

@ -69,3 +69,20 @@ fn main() {
let _v = v;
}
}
fn wrongly_unmangled_macros() {
use std::collections::HashMap;
macro_rules! test_map {
($val:expr) => {
&*$val
};
}
let m: HashMap<u64, u64> = HashMap::new();
let wrapped = Rc::new(m);
for v in test_map!(wrapped).values() {
//~^ for_kv_map
let _v = v;
}
}

View file

@ -69,3 +69,20 @@ fn main() {
let _v = v;
}
}
fn wrongly_unmangled_macros() {
use std::collections::HashMap;
macro_rules! test_map {
($val:expr) => {
&*$val
};
}
let m: HashMap<u64, u64> = HashMap::new();
let wrapped = Rc::new(m);
for (_, v) in test_map!(wrapped) {
//~^ for_kv_map
let _v = v;
}
}

View file

@ -72,5 +72,17 @@ LL - 'label: for (k, _value) in rm {
LL + 'label: for k in rm.keys() {
|
error: aborting due to 6 previous errors
error: you seem to want to iterate on a map's values
--> tests/ui/for_kv_map.rs:84:19
|
LL | for (_, v) in test_map!(wrapped) {
| ^^^^^^^^^^^^^^^^^^
|
help: use the corresponding method
|
LL - for (_, v) in test_map!(wrapped) {
LL + for v in test_map!(wrapped).values() {
|
error: aborting due to 7 previous errors

View file

@ -14,6 +14,7 @@ impl MyStruct {
}
impl<'a> MyStruct {
//~^ multiple_inherent_impl
fn lifetimed() {}
}
@ -90,10 +91,12 @@ struct Lifetime<'s> {
}
impl Lifetime<'_> {}
impl Lifetime<'_> {} // false negative
impl Lifetime<'_> {}
//~^ multiple_inherent_impl
impl<'a> Lifetime<'a> {}
impl<'a> Lifetime<'a> {} // false negative
impl<'a> Lifetime<'a> {}
//~^ multiple_inherent_impl
impl<'b> Lifetime<'b> {} // false negative?
@ -104,6 +107,39 @@ struct Generic<G> {
}
impl<G> Generic<G> {}
impl<G> Generic<G> {} // false negative
impl<G> Generic<G> {}
//~^ multiple_inherent_impl
use std::fmt::Debug;
#[derive(Debug)]
struct GenericWithBounds<T: Debug>(T);
impl<T: Debug> GenericWithBounds<T> {
fn make_one(_one: T) -> Self {
todo!()
}
}
impl<T: Debug> GenericWithBounds<T> {
//~^ multiple_inherent_impl
fn make_two(_two: T) -> Self {
todo!()
}
}
struct MultipleTraitBounds<T>(T);
impl<T: Debug> MultipleTraitBounds<T> {
fn debug_fn() {}
}
impl<T: Clone> MultipleTraitBounds<T> {
fn clone_fn() {}
}
impl<T: Debug + Clone> MultipleTraitBounds<T> {
fn debug_clone_fn() {}
}
fn main() {}

View file

@ -19,7 +19,24 @@ LL | | }
= help: to override `-D warnings` add `#[allow(clippy::multiple_inherent_impl)]`
error: multiple implementations of this structure
--> tests/ui/impl.rs:26:5
--> tests/ui/impl.rs:16:1
|
LL | / impl<'a> MyStruct {
LL | |
LL | | fn lifetimed() {}
LL | | }
| |_^
|
note: first implementation here
--> tests/ui/impl.rs:6:1
|
LL | / impl MyStruct {
LL | | fn first() {}
LL | | }
| |_^
error: multiple implementations of this structure
--> tests/ui/impl.rs:27:5
|
LL | / impl super::MyStruct {
LL | |
@ -37,7 +54,7 @@ LL | | }
| |_^
error: multiple implementations of this structure
--> tests/ui/impl.rs:48:1
--> tests/ui/impl.rs:49:1
|
LL | / impl WithArgs<u64> {
LL | |
@ -47,7 +64,7 @@ LL | | }
| |_^
|
note: first implementation here
--> tests/ui/impl.rs:45:1
--> tests/ui/impl.rs:46:1
|
LL | / impl WithArgs<u64> {
LL | | fn f2() {}
@ -55,28 +72,85 @@ LL | | }
| |_^
error: multiple implementations of this structure
--> tests/ui/impl.rs:71:1
--> tests/ui/impl.rs:72:1
|
LL | impl OneAllowedImpl {}
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: first implementation here
--> tests/ui/impl.rs:68:1
--> tests/ui/impl.rs:69:1
|
LL | impl OneAllowedImpl {}
| ^^^^^^^^^^^^^^^^^^^^^^
error: multiple implementations of this structure
--> tests/ui/impl.rs:84:1
--> tests/ui/impl.rs:85:1
|
LL | impl OneExpected {}
| ^^^^^^^^^^^^^^^^^^^
|
note: first implementation here
--> tests/ui/impl.rs:81:1
--> tests/ui/impl.rs:82:1
|
LL | impl OneExpected {}
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to 5 previous errors
error: multiple implementations of this structure
--> tests/ui/impl.rs:94:1
|
LL | impl Lifetime<'_> {}
| ^^^^^^^^^^^^^^^^^^^^
|
note: first implementation here
--> tests/ui/impl.rs:93:1
|
LL | impl Lifetime<'_> {}
| ^^^^^^^^^^^^^^^^^^^^
error: multiple implementations of this structure
--> tests/ui/impl.rs:98:1
|
LL | impl<'a> Lifetime<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first implementation here
--> tests/ui/impl.rs:97:1
|
LL | impl<'a> Lifetime<'a> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: multiple implementations of this structure
--> tests/ui/impl.rs:110:1
|
LL | impl<G> Generic<G> {}
| ^^^^^^^^^^^^^^^^^^^^^
|
note: first implementation here
--> tests/ui/impl.rs:109:1
|
LL | impl<G> Generic<G> {}
| ^^^^^^^^^^^^^^^^^^^^^
error: multiple implementations of this structure
--> tests/ui/impl.rs:124:1
|
LL | / impl<T: Debug> GenericWithBounds<T> {
LL | |
LL | | fn make_two(_two: T) -> Self {
LL | | todo!()
LL | | }
LL | | }
| |_^
|
note: first implementation here
--> tests/ui/impl.rs:118:1
|
LL | / impl<T: Debug> GenericWithBounds<T> {
LL | | fn make_one(_one: T) -> Self {
LL | | todo!()
LL | | }
LL | | }
| |_^
error: aborting due to 10 previous errors

View file

@ -252,3 +252,11 @@ fn arbitrary_expression() {
0
};
}
fn issue16307() {
let x: u8 = 100;
let y = 100_u8.saturating_sub(x);
//~^ implicit_saturating_sub
println!("{y}");
}

View file

@ -326,3 +326,11 @@ fn arbitrary_expression() {
0
};
}
fn issue16307() {
let x: u8 = 100;
let y = if x >= 100 { 0 } else { 100 - x };
//~^ implicit_saturating_sub
println!("{y}");
}

View file

@ -238,5 +238,11 @@ error: manual arithmetic check found
LL | let _ = if a < b * 2 { 0 } else { a - b * 2 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)`
error: aborting due to 27 previous errors
error: manual arithmetic check found
--> tests/ui/implicit_saturating_sub.rs:332:13
|
LL | let y = if x >= 100 { 0 } else { 100 - x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `100_u8.saturating_sub(x)`
error: aborting due to 28 previous errors

View file

@ -189,3 +189,9 @@ fn issue14595() {
let _ = map.as_ref().values().copied().collect::<Vec<_>>();
//~^ iter_kv_map
}
fn issue16340() {
let hm: HashMap<&str, &str> = HashMap::new();
let _ = hm.keys().map(|key| vec![key]);
//~^ iter_kv_map
}

View file

@ -193,3 +193,9 @@ fn issue14595() {
let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>();
//~^ iter_kv_map
}
fn issue16340() {
let hm: HashMap<&str, &str> = HashMap::new();
let _ = hm.iter().map(|(key, _)| vec![key]);
//~^ iter_kv_map
}

View file

@ -269,5 +269,11 @@ error: iterating on a map's values
LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()`
error: aborting due to 39 previous errors
error: iterating on a map's keys
--> tests/ui/iter_kv_map.rs:199:13
|
LL | let _ = hm.iter().map(|(key, _)| vec![key]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hm.keys().map(|key| vec![key])`
error: aborting due to 40 previous errors

View file

@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) {
let _ = size.div_ceil(*c);
//~^ manual_div_ceil
}
struct MyStruct(u32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: u32) -> u32 {
self.0.next_multiple_of(y)
}
}
fn issue_16219() {
let x = 33u32;
// Lint.
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let y = &x;
let _ = y.div_ceil(8);
//~^ manual_div_ceil
// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;
let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}

View file

@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) {
let _ = (size + c - 1) / c;
//~^ manual_div_ceil
}
struct MyStruct(u32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: u32) -> u32 {
self.0.next_multiple_of(y)
}
}
fn issue_16219() {
let x = 33u32;
// Lint.
let _ = x.next_multiple_of(8) / 8;
//~^ manual_div_ceil
let _ = u32::next_multiple_of(x, 8) / 8;
//~^ manual_div_ceil
let y = &x;
let _ = y.next_multiple_of(8) / 8;
//~^ manual_div_ceil
// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;
let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}

View file

@ -131,5 +131,23 @@ error: manually reimplementing `div_ceil`
LL | let _ = (size + c - 1) / c;
| ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)`
error: aborting due to 20 previous errors
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:121:13
|
LL | let _ = x.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:123:13
|
LL | let _ = u32::next_multiple_of(x, 8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil.rs:127:13
|
LL | let _ = y.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)`
error: aborting due to 23 previous errors

View file

@ -84,3 +84,32 @@ fn issue_13950() {
let _ = y.div_ceil(-7);
//~^ manual_div_ceil
}
struct MyStruct(i32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: i32) -> i32 {
self.0.next_multiple_of(y)
}
}
fn issue_16219() {
let x = 33i32;
// Lint.
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let _ = x.div_ceil(8);
//~^ manual_div_ceil
let y = &x;
let _ = y.div_ceil(8);
//~^ manual_div_ceil
// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;
let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}

View file

@ -84,3 +84,32 @@ fn issue_13950() {
let _ = (y - 8) / -7;
//~^ manual_div_ceil
}
struct MyStruct(i32);
impl MyStruct {
// Method matching name on different type should not trigger lint
fn next_multiple_of(self, y: i32) -> i32 {
self.0.next_multiple_of(y)
}
}
fn issue_16219() {
let x = 33i32;
// Lint.
let _ = x.next_multiple_of(8) / 8;
//~^ manual_div_ceil
let _ = i32::next_multiple_of(x, 8) / 8;
//~^ manual_div_ceil
let y = &x;
let _ = y.next_multiple_of(8) / 8;
//~^ manual_div_ceil
// No lint.
let _ = x.next_multiple_of(8) / 7;
let _ = x.next_multiple_of(7) / 8;
let z = MyStruct(x);
let _ = z.next_multiple_of(8) / 8;
}

View file

@ -139,5 +139,23 @@ error: manually reimplementing `div_ceil`
LL | let _ = (y - 8) / -7;
| ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)`
error: aborting due to 23 previous errors
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:100:13
|
LL | let _ = x.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:102:13
|
LL | let _ = i32::next_multiple_of(x, 8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)`
error: manually reimplementing `div_ceil`
--> tests/ui/manual_div_ceil_with_feature.rs:106:13
|
LL | let _ = y.next_multiple_of(8) / 8;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)`
error: aborting due to 26 previous errors

View file

@ -160,3 +160,23 @@ fn ref_osstring(a: OsString, b: &OsString) {
b.eq_ignore_ascii_case(a);
//~^ manual_ignore_case_cmp
}
fn wrongly_unmangled_macros(a: &str, b: &str) -> bool {
struct S<'a> {
inner: &'a str,
}
let a = S { inner: a };
let b = S { inner: b };
macro_rules! dot_inner {
($s:expr) => {
$s.inner
};
}
dot_inner!(a).eq_ignore_ascii_case(dot_inner!(b))
//~^ manual_ignore_case_cmp
|| dot_inner!(a).eq_ignore_ascii_case("abc")
//~^ manual_ignore_case_cmp
}

View file

@ -160,3 +160,23 @@ fn ref_osstring(a: OsString, b: &OsString) {
b.to_ascii_lowercase() == a.to_ascii_lowercase();
//~^ manual_ignore_case_cmp
}
fn wrongly_unmangled_macros(a: &str, b: &str) -> bool {
struct S<'a> {
inner: &'a str,
}
let a = S { inner: a };
let b = S { inner: b };
macro_rules! dot_inner {
($s:expr) => {
$s.inner
};
}
dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase()
//~^ manual_ignore_case_cmp
|| dot_inner!(a).to_ascii_lowercase() == "abc"
//~^ manual_ignore_case_cmp
}

View file

@ -588,5 +588,29 @@ LL - b.to_ascii_lowercase() == a.to_ascii_lowercase();
LL + b.eq_ignore_ascii_case(a);
|
error: aborting due to 49 previous errors
error: manual case-insensitive ASCII comparison
--> tests/ui/manual_ignore_case_cmp.rs:178:5
|
LL | dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider using `.eq_ignore_ascii_case()` instead
|
LL - dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase()
LL + dot_inner!(a).eq_ignore_ascii_case(dot_inner!(b))
|
error: manual case-insensitive ASCII comparison
--> tests/ui/manual_ignore_case_cmp.rs:180:12
|
LL | || dot_inner!(a).to_ascii_lowercase() == "abc"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: consider using `.eq_ignore_ascii_case()` instead
|
LL - || dot_inner!(a).to_ascii_lowercase() == "abc"
LL + || dot_inner!(a).eq_ignore_ascii_case("abc")
|
error: aborting due to 51 previous errors

View file

@ -30,3 +30,20 @@ fn foo(a: u32, b: u64) {
external!($a.ilog(2));
with_span!(span; a.ilog(2));
}
fn wrongly_unmangled_macros() {
struct S {
inner: u32,
}
let x = S { inner: 42 };
macro_rules! access {
($s:expr) => {
$s.inner
};
}
let log = access!(x).ilog2();
//~^ manual_ilog2
let log = access!(x).ilog2();
//~^ manual_ilog2
}

View file

@ -30,3 +30,20 @@ fn foo(a: u32, b: u64) {
external!($a.ilog(2));
with_span!(span; a.ilog(2));
}
fn wrongly_unmangled_macros() {
struct S {
inner: u32,
}
let x = S { inner: 42 };
macro_rules! access {
($s:expr) => {
$s.inner
};
}
let log = 31 - access!(x).leading_zeros();
//~^ manual_ilog2
let log = access!(x).ilog(2);
//~^ manual_ilog2
}

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