diff options
37 files changed, 1002 insertions, 162 deletions
diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index 4059f9603c3..b9b5cedb5aa 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -36,6 +36,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { | sym::unused_braces | sym::unused_import_braces | sym::unused_imports + | sym::redundant_imports ) { return; diff --git a/clippy_lints/src/empty_with_brackets.rs b/clippy_lints/src/empty_with_brackets.rs index 4414aebbf9a..f2757407ba5 100644 --- a/clippy_lints/src/empty_with_brackets.rs +++ b/clippy_lints/src/empty_with_brackets.rs @@ -92,8 +92,10 @@ impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VA impl LateLintPass<'_> for EmptyWithBrackets { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // FIXME: handle `struct $name {}` if let ItemKind::Struct(ident, _, var_data) = &item.kind && !item.span.from_expansion() + && !ident.span.from_expansion() && has_brackets(var_data) && let span_after_ident = item.span.with_lo(ident.span.hi()) && has_no_fields(cx, var_data, span_after_ident) @@ -116,10 +118,12 @@ impl LateLintPass<'_> for EmptyWithBrackets { } fn check_variant(&mut self, cx: &LateContext<'_>, variant: &Variant<'_>) { - // the span of the parentheses/braces - let span_after_ident = variant.span.with_lo(variant.ident.span.hi()); - - if has_no_fields(cx, &variant.data, span_after_ident) { + // FIXME: handle `$name {}` + if !variant.span.from_expansion() + && !variant.ident.span.from_expansion() + && let span_after_ident = variant.span.with_lo(variant.ident.span.hi()) + && has_no_fields(cx, &variant.data, span_after_ident) + { match variant.data { VariantData::Struct { .. } => { // Empty struct variants can be linted immediately diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index d959981a83c..b8d0cec5aeb 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -10,7 +10,7 @@ use rustc_span::{Span, sym}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; @@ -28,6 +28,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> if let hir::ItemKind::Fn { ref sig, body: ref body_id, + ident, .. } = item.kind { @@ -51,8 +52,8 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> sig.decl, cx.tcx.hir_body(*body_id), item.span, + ident.span, item.owner_id, - item.span.with_hi(sig.decl.output.span().hi()), "this function could have a `#[must_use]` attribute", ); } @@ -84,8 +85,8 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp sig.decl, cx.tcx.hir_body(*body_id), item.span, + item.ident.span, item.owner_id, - item.span.with_hi(sig.decl.output.span().hi()), "this method could have a `#[must_use]` attribute", ); } @@ -120,8 +121,8 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr sig.decl, body, item.span, + item.ident.span, item.owner_id, - item.span.with_hi(sig.decl.output.span().hi()), "this method could have a `#[must_use]` attribute", ); } @@ -198,8 +199,8 @@ fn check_must_use_candidate<'tcx>( decl: &'tcx hir::FnDecl<'_>, body: &'tcx hir::Body<'_>, item_span: Span, + ident_span: Span, item_id: hir::OwnerId, - fn_span: Span, msg: &'static str, ) { if has_mutable_arg(cx, body) @@ -208,18 +209,18 @@ fn check_must_use_candidate<'tcx>( || returns_unit(decl) || !cx.effective_visibilities.is_exported(item_id.def_id) || is_must_use_ty(cx, return_ty(cx, item_id)) + || item_span.from_expansion() { return; } - span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { - if let Some(snippet) = fn_span.get_source_text(cx) { - diag.span_suggestion( - fn_span, - "add the attribute", - format!("#[must_use] {snippet}"), - Applicability::MachineApplicable, - ); - } + span_lint_and_then(cx, MUST_USE_CANDIDATE, ident_span, msg, |diag| { + let indent = snippet_indent(cx, item_span).unwrap_or_default(); + diag.span_suggestion( + item_span.shrink_to_lo(), + "add the attribute", + format!("#[must_use] \n{indent}"), + Applicability::MachineApplicable, + ); }); } diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index addb1e7d755..116d63c3bb1 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -1,9 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr_data_structures::{RustcVersion, Stability, StableSince}; +use clippy_utils::{is_in_const_context, is_in_test}; +use rustc_attr_data_structures::{RustcVersion, StabilityLevel, StableSince}; use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::DefKind; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -80,7 +81,7 @@ enum Availability { pub struct IncompatibleMsrv { msrv: Msrv, - availability_cache: FxHashMap<DefId, Availability>, + availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, } @@ -96,18 +97,32 @@ impl IncompatibleMsrv { } /// Returns the availability of `def_id`, whether it is enabled through a feature or - /// available since a given version (the default being Rust 1.0.0). - fn get_def_id_availability(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> Availability { - if let Some(availability) = self.availability_cache.get(&def_id) { + /// available since a given version (the default being Rust 1.0.0). `needs_const` requires + /// the `const`-stability to be looked up instead of the stability in non-`const` contexts. + fn get_def_id_availability(&mut self, tcx: TyCtxt<'_>, def_id: DefId, needs_const: bool) -> Availability { + if let Some(availability) = self.availability_cache.get(&(def_id, needs_const)) { return *availability; } - let stability = tcx.lookup_stability(def_id); - let version = if stability.is_some_and(|stability| tcx.features().enabled(stability.feature)) { + let (feature, stability_level) = if needs_const { + tcx.lookup_const_stability(def_id) + .map(|stability| (stability.feature, stability.level)) + .unzip() + } else { + tcx.lookup_stability(def_id) + .map(|stability| (stability.feature, stability.level)) + .unzip() + }; + let version = if feature.is_some_and(|feature| tcx.features().enabled(feature)) { Availability::FeatureEnabled - } else if let Some(StableSince::Version(version)) = stability.as_ref().and_then(Stability::stable_since) { + } else if let Some(StableSince::Version(version)) = + stability_level.as_ref().and_then(StabilityLevel::stable_since) + { Availability::Since(version) + } else if needs_const { + // Fallback to regular stability + self.get_def_id_availability(tcx, def_id, false) } else if let Some(parent_def_id) = tcx.opt_parent(def_id) { - self.get_def_id_availability(tcx, parent_def_id) + self.get_def_id_availability(tcx, parent_def_id, needs_const) } else { Availability::Since(RustcVersion { major: 1, @@ -115,10 +130,11 @@ impl IncompatibleMsrv { patch: 0, }) }; - self.availability_cache.insert(def_id, version); + self.availability_cache.insert((def_id, needs_const), version); version } + /// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV. fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, node: HirId, span: Span) { if def_id.is_local() { // We don't check local items since their MSRV is supposed to always be valid. @@ -144,9 +160,13 @@ impl IncompatibleMsrv { return; } + let needs_const = cx.enclosing_body.is_some() + && is_in_const_context(cx) + && matches!(cx.tcx.def_kind(def_id), DefKind::AssocFn | DefKind::Fn); + if (self.check_in_tests || !is_in_test(cx.tcx, node)) && let Some(current) = self.msrv.current(cx) - && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id) + && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current { span_lint_and_then( @@ -154,7 +174,8 @@ impl IncompatibleMsrv { INCOMPATIBLE_MSRV, span, format!( - "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable since `{version}`" + "current MSRV (Minimum Supported Rust Version) is `{current}` but this item is stable{} since `{version}`", + if needs_const { " in a `const` context" } else { "" }, ), |diag| { if is_under_cfg_attribute(cx, node) { @@ -168,7 +189,6 @@ impl IncompatibleMsrv { impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - // TODO: check for const stability when in const context match expr.kind { ExprKind::MethodCall(_, _, _, span) => { if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index e85d779b488..c2b73943106 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_no_std_crate; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_copy}; use rustc_errors::Applicability; @@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { - let help_text = "consider boxing the large fields to reduce the total size of the enum"; + let help_text = "consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum"; span_lint_and_then( cx, LARGE_ENUM_VARIANT, @@ -117,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { ident.span, "boxing a variant would require the type no longer be `Copy`", ); - } else { + } else if !is_no_std_crate(cx) { let sugg: Vec<(Span, String)> = variants_size[0] .fields_size .iter() diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 7837b18bcd3..972b0b110e0 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -39,7 +39,9 @@ pub(super) fn check<'tcx>( var: canonical_id, indexed_mut: FxHashSet::default(), indexed_indirectly: FxHashMap::default(), + unnamed_indexed_indirectly: false, indexed_directly: FxIndexMap::default(), + unnamed_indexed_directly: false, referenced: FxHashSet::default(), nonindex: false, prefer_mutable: false, @@ -47,7 +49,11 @@ pub(super) fn check<'tcx>( walk_expr(&mut visitor, body); // linting condition: we only indexed one variable, and indexed it directly - if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 { + if visitor.indexed_indirectly.is_empty() + && !visitor.unnamed_indexed_indirectly + && !visitor.unnamed_indexed_directly + && visitor.indexed_directly.len() == 1 + { let (indexed, (indexed_extent, indexed_ty)) = visitor .indexed_directly .into_iter() @@ -217,6 +223,7 @@ fn is_end_eq_array_len<'tcx>( false } +#[expect(clippy::struct_excessive_bools)] struct VarVisitor<'a, 'tcx> { /// context reference cx: &'a LateContext<'tcx>, @@ -226,9 +233,13 @@ struct VarVisitor<'a, 'tcx> { indexed_mut: FxHashSet<Symbol>, /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>, + /// indirectly indexed literals, like `[1, 2, 3][(i + 4) % N]` + unnamed_indexed_indirectly: bool, /// subset of `indexed` of vars that are indexed directly: `v[i]` /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]` indexed_directly: FxIndexMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>, + /// directly indexed literals, like `[1, 2, 3][i]` + unnamed_indexed_directly: bool, /// Any names that are used outside an index operation. /// Used to detect things like `&mut vec` used together with `vec[i]` referenced: FxHashSet<Symbol>, @@ -242,6 +253,7 @@ struct VarVisitor<'a, 'tcx> { impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { + let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name && let QPath::Resolved(None, seqvar) = *seqpath @@ -251,7 +263,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); } - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); match res { Res::Local(hir_id) => { @@ -286,6 +297,13 @@ impl<'tcx> VarVisitor<'_, 'tcx> { }, _ => (), } + } else if let ExprKind::Repeat(..) | ExprKind::Array(..) = seqexpr.kind { + if index_used_directly { + self.unnamed_indexed_directly = true; + } else { + self.unnamed_indexed_indirectly = true; + } + return false; } true } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 69c84bc7038..8a253ae5810 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -6,9 +6,11 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; -use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr}; +use rustc_hir::{ + Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, +}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_span::{BytePos, Span, sym}; use std::iter::once; use std::ops::ControlFlow; @@ -20,7 +22,7 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::Diverging => { + NeverLoopResult::Diverging { ref break_spans } => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -38,10 +40,15 @@ pub(super) fn check<'tcx>( Applicability::Unspecified }; - diag.span_suggestion_verbose( + let mut suggestions = vec![( for_span.with_hi(iterator.span.hi()), - "if you need the first element of the iterator, try writing", for_to_if_let_sugg(cx, iterator, pat), + )]; + // Make sure to clear up the diverging sites when we remove a loopp. + suggestions.extend(break_spans.iter().map(|span| (*span, String::new()))); + diag.multipart_suggestion_verbose( + "if you need the first element of the iterator, try writing", + suggestions, app, ); } @@ -70,22 +77,22 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool { /// The first two bits of information are in this enum, and the last part is in the /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by /// scope. -#[derive(Copy, Clone)] +#[derive(Clone)] enum NeverLoopResult { /// A continue may occur for the main loop. MayContinueMainLoop, /// We have not encountered any main loop continue, /// but we are diverging (subsequent control flow is not reachable) - Diverging, + Diverging { break_spans: Vec<Span> }, /// We have not encountered any main loop continue, /// and subsequent control flow is (possibly) reachable Normal, } #[must_use] -fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { +fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult { match arg { - NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, + NeverLoopResult::Diverging { .. } | NeverLoopResult::Normal => NeverLoopResult::Normal, NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop, } } @@ -94,7 +101,7 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult { #[must_use] fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) -> NeverLoopResult { match first { - NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop => first, + NeverLoopResult::Diverging { .. } | NeverLoopResult::MayContinueMainLoop => first, NeverLoopResult::Normal => second(), } } @@ -103,7 +110,7 @@ fn combine_seq(first: NeverLoopResult, second: impl FnOnce() -> NeverLoopResult) #[must_use] fn combine_seq_many(iter: impl IntoIterator<Item = NeverLoopResult>) -> NeverLoopResult { for e in iter { - if let NeverLoopResult::Diverging | NeverLoopResult::MayContinueMainLoop = e { + if let NeverLoopResult::Diverging { .. } | NeverLoopResult::MayContinueMainLoop = e { return e; } } @@ -118,7 +125,19 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult NeverLoopResult::MayContinueMainLoop }, (NeverLoopResult::Normal, _) | (_, NeverLoopResult::Normal) => NeverLoopResult::Normal, - (NeverLoopResult::Diverging, NeverLoopResult::Diverging) => NeverLoopResult::Diverging, + ( + NeverLoopResult::Diverging { + break_spans: mut break_spans1, + }, + NeverLoopResult::Diverging { + break_spans: mut break_spans2, + }, + ) => { + break_spans1.append(&mut break_spans2); + NeverLoopResult::Diverging { + break_spans: break_spans1, + } + }, } } @@ -136,7 +155,7 @@ fn never_loop_block<'tcx>( combine_seq_many(iter.map(|(e, els)| { let e = never_loop_expr(cx, e, local_labels, main_loop_id); // els is an else block in a let...else binding - els.map_or(e, |els| { + els.map_or(e.clone(), |els| { combine_seq(e, || match never_loop_block(cx, els, local_labels, main_loop_id) { // Returning MayContinueMainLoop here means that // we will not evaluate the rest of the body @@ -144,7 +163,7 @@ fn never_loop_block<'tcx>( // An else block always diverges, so the Normal case should not happen, // but the analysis is approximate so it might return Normal anyway. // Returning Normal here says that nothing more happens on the main path - NeverLoopResult::Diverging | NeverLoopResult::Normal => NeverLoopResult::Normal, + NeverLoopResult::Diverging { .. } | NeverLoopResult::Normal => NeverLoopResult::Normal, }) }) })) @@ -159,6 +178,45 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'t } } +fn stmt_source_span(stmt: &Stmt<'_>) -> Span { + let call_span = stmt.span.source_callsite(); + // if it is a macro call, the span will be missing the trailing semicolon + if stmt.span == call_span { + return call_span; + } + + // An expression without a trailing semi-colon (must have unit type). + if let StmtKind::Expr(..) = stmt.kind { + return call_span; + } + + call_span.with_hi(call_span.hi() + BytePos(1)) +} + +/// Returns a Vec of all the individual spans after the highlighted expression in a block +fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec<Span> { + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) { + if let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) { + return block + .stmts + .iter() + .skip_while(|inner| inner.hir_id != stmt.hir_id) + .map(stmt_source_span) + .chain(if let Some(e) = block.expr { vec![e.span] } else { vec![] }) + .collect(); + } + + return vec![stmt.span]; + } + + vec![] +} + +fn is_label_for_block(cx: &LateContext<'_>, dest: &Destination) -> bool { + dest.target_id + .is_ok_and(|hir_id| matches!(cx.tcx.hir_node(hir_id), Node::Block(_))) +} + #[allow(clippy::too_many_lines)] fn never_loop_expr<'tcx>( cx: &LateContext<'tcx>, @@ -197,7 +255,7 @@ fn never_loop_expr<'tcx>( ExprKind::Loop(b, _, _, _) => { // We don't attempt to track reachability after a loop, // just assume there may have been a break somewhere - absorb_break(never_loop_block(cx, b, local_labels, main_loop_id)) + absorb_break(&never_loop_block(cx, b, local_labels, main_loop_id)) }, ExprKind::If(e, e2, e3) => { let e1 = never_loop_expr(cx, e, local_labels, main_loop_id); @@ -212,9 +270,10 @@ fn never_loop_expr<'tcx>( ExprKind::Match(e, arms, _) => { let e = never_loop_expr(cx, e, local_labels, main_loop_id); combine_seq(e, || { - arms.iter().fold(NeverLoopResult::Diverging, |a, b| { - combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) - }) + arms.iter() + .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| { + combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) + }) }) }, ExprKind::Block(b, _) => { @@ -224,7 +283,7 @@ fn never_loop_expr<'tcx>( let ret = never_loop_block(cx, b, local_labels, main_loop_id); let jumped_to = b.targeted_by_break && local_labels.pop().unwrap().1; match ret { - NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal, + NeverLoopResult::Diverging { .. } if jumped_to => NeverLoopResult::Normal, _ => ret, } }, @@ -235,25 +294,39 @@ fn never_loop_expr<'tcx>( if id == main_loop_id { NeverLoopResult::MayContinueMainLoop } else { - NeverLoopResult::Diverging + NeverLoopResult::Diverging { + break_spans: all_spans_after_expr(cx, expr), + } } }, - ExprKind::Break(_, e) | ExprKind::Ret(e) => { + ExprKind::Ret(e) => { let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| { never_loop_expr(cx, e, local_labels, main_loop_id) }); combine_seq(first, || { // checks if break targets a block instead of a loop - if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind - && let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) - { - *reachable = true; + mark_block_as_reachable(expr, local_labels); + NeverLoopResult::Diverging { break_spans: vec![] } + }) + }, + ExprKind::Break(dest, e) => { + let first = e.as_ref().map_or(NeverLoopResult::Normal, |e| { + never_loop_expr(cx, e, local_labels, main_loop_id) + }); + combine_seq(first, || { + // checks if break targets a block instead of a loop + mark_block_as_reachable(expr, local_labels); + NeverLoopResult::Diverging { + break_spans: if is_label_for_block(cx, &dest) { + vec![] + } else { + all_spans_after_expr(cx, expr) + }, } - NeverLoopResult::Diverging }) }, ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { - NeverLoopResult::Diverging + NeverLoopResult::Diverging { break_spans: vec![] } }), ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { @@ -283,12 +356,12 @@ fn never_loop_expr<'tcx>( }; let result = combine_seq(result, || { if cx.typeck_results().expr_ty(expr).is_never() { - NeverLoopResult::Diverging + NeverLoopResult::Diverging { break_spans: vec![] } } else { NeverLoopResult::Normal } }); - if let NeverLoopResult::Diverging = result + if let NeverLoopResult::Diverging { .. } = result && let Some(macro_call) = root_macro_call_first_node(cx, expr) && let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id) { @@ -316,3 +389,11 @@ fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) format!("if let Some({pat_snippet}) = {iter_snippet}.next()") } + +fn mark_block_as_reachable(expr: &Expr<'_>, local_labels: &mut [(HirId, bool)]) { + if let ExprKind::Break(Destination { target_id: Ok(t), .. }, _) = expr.kind + && let Some((_, reachable)) = local_labels.iter_mut().find(|(label, _)| *label == t) + { + *reachable = true; + } +} diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index 08c0caa4266..7e530e98ac4 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -152,21 +152,26 @@ fn report_single_pattern( }) if lit.node.is_str() || lit.node.is_bytestr() => pat_ref_count + 1, _ => pat_ref_count, }; - // References are only implicitly added to the pattern, so no overflow here. - // e.g. will work: match &Some(_) { Some(_) => () } - // will not: match Some(_) { &Some(_) => () } - let ref_count_diff = ty_ref_count - pat_ref_count; - // Try to remove address of expressions first. - let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); - let ref_count_diff = ref_count_diff - removed; + // References are implicitly removed when `deref_patterns` are used. + // They are implicitly added when match ergonomics are used. + let (ex, ref_or_deref_adjust) = if ty_ref_count > pat_ref_count { + let ref_count_diff = ty_ref_count - pat_ref_count; + + // Try to remove address of expressions first. + let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff); + + (ex, String::from(if ref_count_diff == removed { "" } else { "&" })) + } else { + (ex, "*".repeat(pat_ref_count - ty_ref_count)) + }; let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`"; let sugg = format!( "if {} == {}{} {}{els_str}", snippet_with_context(cx, ex.span, ctxt, "..", &mut app).0, // PartialEq for different reference counts may not exist. - "&".repeat(ref_count_diff), + ref_or_deref_adjust, snippet_with_applicability(cx, arm.pat.span, "..", &mut app), expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app), ); diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs index 965993808f6..94944bd9445 100644 --- a/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,6 +1,6 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, is_trait_method, peel_blocks, @@ -45,9 +45,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() && let Some(param_snippet) = param.span.get_source_text(cx) - && let Some(filter) = recv.span.get_source_text(cx) - && let Some(map) = then_body.span.get_source_text(cx) { + let mut applicability = Applicability::MachineApplicable; + let (filter, _) = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut applicability); + let (map, _) = snippet_with_context(cx, then_body.span, expr.span.ctxt(), "..", &mut applicability); + span_lint_and_then( cx, FILTER_MAP_BOOL_THEN, @@ -62,7 +64,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & "filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})", derefs = "*".repeat(needed_derefs) ), - Applicability::MachineApplicable, + applicability, ); } else { diag.help("consider using `filter` then `map` instead"); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 25c95d23436..ca54fc693e7 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_attr_data_structures::{AttributeKind, find_attr}; -use rustc_hir as hir; -use rustc_hir::Attribute; +use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, Attribute}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocItemContainer; use rustc_session::declare_lint_pass; @@ -97,6 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } match it.kind { hir::ItemKind::Fn { .. } => { + if fn_is_externally_exported(cx, it.owner_id.to_def_id()) { + return; + } + let desc = "a function"; let attrs = cx.tcx.hir_attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); @@ -173,3 +177,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { check_missing_inline_attrs(cx, attrs, impl_item.span, desc); } } + +/// Checks if this function is externally exported, where #[inline] wouldn't have the desired effect +/// and a rustc warning would be triggered, see #15301 +fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { + let attrs = cx.tcx.codegen_fn_attrs(def_id); + attrs.contains_extern_indicator() +} diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 19d9acfc930..4197680dd04 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -96,6 +96,12 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Match(_, arms, _) = expr.kind { + // if the match is generated by an external macro, the writer does not control + // how the scrutinee (`match &scrutiny { ... }`) is matched + if expr.span.in_external_macro(cx.sess().source_map()) { + return; + } + for arm in arms { let pat = &arm.pat; if apply_lint(cx, pat, DerefPossible::Possible) { diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 8a8218c6976..eee2cef2aaf 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -261,6 +261,7 @@ generate! { read_to_end, read_to_string, read_unaligned, + redundant_imports, redundant_pub_crate, regex, rem_euclid, diff --git a/tests/ui-toml/enum_variant_size/enum_variant_size.stderr b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr index 020b3cc7878..a5dfd7015a3 100644 --- a/tests/ui-toml/enum_variant_size/enum_variant_size.stderr +++ b/tests/ui-toml/enum_variant_size/enum_variant_size.stderr @@ -12,7 +12,7 @@ LL | | } | = note: `-D clippy::large-enum-variant` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B([u8; 501]), LL + B(Box<[u8; 501]>), diff --git a/tests/ui/filter_map_bool_then.fixed b/tests/ui/filter_map_bool_then.fixed index b3e112f19eb..d370b85a67e 100644 --- a/tests/ui/filter_map_bool_then.fixed +++ b/tests/ui/filter_map_bool_then.fixed @@ -89,3 +89,24 @@ fn issue11503() { let _: Vec<usize> = bools.iter().enumerate().filter(|&(i, b)| ****b).map(|(i, b)| i).collect(); //~^ filter_map_bool_then } + +fn issue15047() { + #[derive(Clone, Copy)] + enum MyEnum { + A, + B, + C, + } + + macro_rules! foo { + ($e:expr) => { + $e + 1 + }; + } + + let x = 1; + let _ = [(MyEnum::A, "foo", 1i32)] + .iter() + .filter(|&(t, s, i)| matches!(t, MyEnum::A if s.starts_with("bar"))).map(|(t, s, i)| foo!(x)); + //~^ filter_map_bool_then +} diff --git a/tests/ui/filter_map_bool_then.rs b/tests/ui/filter_map_bool_then.rs index d996b3cb3c5..12295cc2482 100644 --- a/tests/ui/filter_map_bool_then.rs +++ b/tests/ui/filter_map_bool_then.rs @@ -89,3 +89,24 @@ fn issue11503() { let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); //~^ filter_map_bool_then } + +fn issue15047() { + #[derive(Clone, Copy)] + enum MyEnum { + A, + B, + C, + } + + macro_rules! foo { + ($e:expr) => { + $e + 1 + }; + } + + let x = 1; + let _ = [(MyEnum::A, "foo", 1i32)] + .iter() + .filter_map(|(t, s, i)| matches!(t, MyEnum::A if s.starts_with("bar")).then(|| foo!(x))); + //~^ filter_map_bool_then +} diff --git a/tests/ui/filter_map_bool_then.stderr b/tests/ui/filter_map_bool_then.stderr index aeb1baeb35e..edf6c655939 100644 --- a/tests/ui/filter_map_bool_then.stderr +++ b/tests/ui/filter_map_bool_then.stderr @@ -61,5 +61,11 @@ error: usage of `bool::then` in `filter_map` LL | let _: Vec<usize> = bools.iter().enumerate().filter_map(|(i, b)| b.then(|| i)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(i, b)| ****b).map(|(i, b)| i)` -error: aborting due to 10 previous errors +error: usage of `bool::then` in `filter_map` + --> tests/ui/filter_map_bool_then.rs:110:10 + | +LL | .filter_map(|(t, s, i)| matches!(t, MyEnum::A if s.starts_with("bar")).then(|| foo!(x))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&(t, s, i)| matches!(t, MyEnum::A if s.starts_with("bar"))).map(|(t, s, i)| foo!(x))` + +error: aborting due to 11 previous errors diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 1f9069c7c1c..f7f21e1850d 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -4,6 +4,7 @@ #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] +use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::future::Future; @@ -128,4 +129,43 @@ fn non_fn_items() { //~^ incompatible_msrv } +#[clippy::msrv = "1.87.0"] +fn msrv_non_ok_in_const() { + { + let c = Cell::new(42); + _ = c.get(); + } + const { + let c = Cell::new(42); + _ = c.get(); + //~^ incompatible_msrv + } +} + +#[clippy::msrv = "1.88.0"] +fn msrv_ok_in_const() { + { + let c = Cell::new(42); + _ = c.get(); + } + const { + let c = Cell::new(42); + _ = c.get(); + } +} + +#[clippy::msrv = "1.86.0"] +fn enum_variant_not_ok() { + let _ = std::io::ErrorKind::InvalidFilename; + //~^ incompatible_msrv + let _ = const { std::io::ErrorKind::InvalidFilename }; + //~^ incompatible_msrv +} + +#[clippy::msrv = "1.87.0"] +fn enum_variant_ok() { + let _ = std::io::ErrorKind::InvalidFilename; + let _ = const { std::io::ErrorKind::InvalidFilename }; +} + fn main() {} diff --git a/tests/ui/incompatible_msrv.stderr b/tests/ui/incompatible_msrv.stderr index fff1b956561..e42360d296f 100644 --- a/tests/ui/incompatible_msrv.stderr +++ b/tests/ui/incompatible_msrv.stderr @@ -1,5 +1,5 @@ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0` - --> tests/ui/incompatible_msrv.rs:15:39 + --> tests/ui/incompatible_msrv.rs:16:39 | LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); | ^^^^^ @@ -8,37 +8,37 @@ LL | assert_eq!(map.entry("poneyland").key(), &"poneyland"); = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]` error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0` - --> tests/ui/incompatible_msrv.rs:21:11 + --> tests/ui/incompatible_msrv.rs:22:11 | LL | v.into_key(); | ^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0` - --> tests/ui/incompatible_msrv.rs:25:5 + --> tests/ui/incompatible_msrv.rs:26:5 | LL | sleep(Duration::new(1, 0)); | ^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0` - --> tests/ui/incompatible_msrv.rs:30:33 + --> tests/ui/incompatible_msrv.rs:31:33 | LL | static NO_BODY_BAD_MSRV: Option<Duration> = None; | ^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.2.0` but this item is stable since `1.3.0` - --> tests/ui/incompatible_msrv.rs:37:19 + --> tests/ui/incompatible_msrv.rs:38:19 | LL | let _: Option<Duration> = None; | ^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:61:17 + --> tests/ui/incompatible_msrv.rs:62:17 | LL | let _ = core::iter::once_with(|| 0); | ^^^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:68:21 + --> tests/ui/incompatible_msrv.rs:69:21 | LL | let _ = core::iter::once_with(|| $msg); | ^^^^^^^^^^^^^^^^^^^^^ @@ -49,25 +49,25 @@ LL | my_panic!("foo"); = note: this error originates in the macro `my_panic` (in Nightly builds, run with -Z macro-backtrace for more info) error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.43.0` - --> tests/ui/incompatible_msrv.rs:75:13 + --> tests/ui/incompatible_msrv.rs:76:13 | LL | assert!(core::iter::once_with(|| 0).next().is_some()); | ^^^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.80.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:88:13 + --> tests/ui/incompatible_msrv.rs:89:13 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:99:13 + --> tests/ui/incompatible_msrv.rs:100:13 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:104:17 + --> tests/ui/incompatible_msrv.rs:105:17 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ @@ -75,22 +75,40 @@ LL | let _ = std::iter::repeat_n((), 5); = note: you may want to conditionally increase the MSRV considered by Clippy using the `clippy::msrv` attribute error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.82.0` - --> tests/ui/incompatible_msrv.rs:109:17 + --> tests/ui/incompatible_msrv.rs:110:17 | LL | let _ = std::iter::repeat_n((), 5); | ^^^^^^^^^^^^^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.78.0` but this item is stable since `1.84.0` - --> tests/ui/incompatible_msrv.rs:122:7 + --> tests/ui/incompatible_msrv.rs:123:7 | LL | r.isqrt() | ^^^^^^^ error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.85.0` - --> tests/ui/incompatible_msrv.rs:127:13 + --> tests/ui/incompatible_msrv.rs:128:13 | LL | let _ = std::io::ErrorKind::CrossesDevices; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 14 previous errors +error: current MSRV (Minimum Supported Rust Version) is `1.87.0` but this item is stable in a `const` context since `1.88.0` + --> tests/ui/incompatible_msrv.rs:140:15 + | +LL | _ = c.get(); + | ^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0` + --> tests/ui/incompatible_msrv.rs:159:13 + | +LL | let _ = std::io::ErrorKind::InvalidFilename; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: current MSRV (Minimum Supported Rust Version) is `1.86.0` but this item is stable since `1.87.0` + --> tests/ui/incompatible_msrv.rs:161:21 + | +LL | let _ = const { std::io::ErrorKind::InvalidFilename }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors diff --git a/tests/ui/large_enum_variant.32bit.stderr b/tests/ui/large_enum_variant.32bit.stderr index 80ca5daa1d5..ac1ed27a6b3 100644 --- a/tests/ui/large_enum_variant.32bit.stderr +++ b/tests/ui/large_enum_variant.32bit.stderr @@ -12,7 +12,7 @@ LL | | } | = note: `-D clippy::large-enum-variant` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B([i32; 8000]), LL + B(Box<[i32; 8000]>), @@ -30,7 +30,7 @@ LL | | ContainingLargeEnum(LargeEnum), LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingLargeEnum(LargeEnum), LL + ContainingLargeEnum(Box<LargeEnum>), @@ -49,7 +49,7 @@ LL | | StructLikeLittle { x: i32, y: i32 }, LL | | } | |_^ the entire enum is at least 70008 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), LL + ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), @@ -67,7 +67,7 @@ LL | | StructLikeLarge { x: [i32; 8000], y: i32 }, LL | | } | |_^ the entire enum is at least 32008 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - StructLikeLarge { x: [i32; 8000], y: i32 }, LL + StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, @@ -85,7 +85,7 @@ LL | | StructLikeLarge2 { x: [i32; 8000] }, LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - StructLikeLarge2 { x: [i32; 8000] }, LL + StructLikeLarge2 { x: Box<[i32; 8000]> }, @@ -104,7 +104,7 @@ LL | | C([u8; 200]), LL | | } | |_^ the entire enum is at least 1256 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B([u8; 1255]), LL + B(Box<[u8; 1255]>), @@ -122,7 +122,7 @@ LL | | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; LL | | } | |_^ the entire enum is at least 70132 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), LL + ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), @@ -140,7 +140,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -158,7 +158,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32000 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -176,7 +176,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32000 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -199,7 +199,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum CopyableLargeEnum { | ^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:118:5 | LL | B([u64; 8000]), @@ -222,7 +222,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum ManuallyCopyLargeEnum { | ^^^^^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:124:5 | LL | B([u64; 8000]), @@ -245,7 +245,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum SomeGenericPossiblyCopyEnum<T> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:138:5 | LL | B([u64; 4000]), @@ -263,7 +263,7 @@ LL | | Large((T, [u8; 512])), LL | | } | |_^ the entire enum is at least 512 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large((T, [u8; 512])), LL + Large(Box<(T, [u8; 512])>), @@ -281,7 +281,7 @@ LL | | Small(u8), LL | | } | |_^ the entire enum is at least 516 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large([Foo<u64>; 64]), LL + Large(Box<[Foo<u64>; 64]>), @@ -299,7 +299,7 @@ LL | | Error(PossiblyLargeEnumWithConst<256>), LL | | } | |_^ the entire enum is at least 514 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Error(PossiblyLargeEnumWithConst<256>), LL + Error(Box<PossiblyLargeEnumWithConst<256>>), @@ -317,7 +317,7 @@ LL | | Recursive(Box<WithRecursion>), LL | | } | |_^ the entire enum is at least 516 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large([u64; 64]), LL + Large(Box<[u64; 64]>), @@ -335,7 +335,7 @@ LL | | Error(WithRecursionAndGenerics<u64>), LL | | } | |_^ the entire enum is at least 516 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Error(WithRecursionAndGenerics<u64>), LL + Error(Box<WithRecursionAndGenerics<u64>>), diff --git a/tests/ui/large_enum_variant.64bit.stderr b/tests/ui/large_enum_variant.64bit.stderr index 559bdf2a2f5..d8199f9090f 100644 --- a/tests/ui/large_enum_variant.64bit.stderr +++ b/tests/ui/large_enum_variant.64bit.stderr @@ -12,7 +12,7 @@ LL | | } | = note: `-D clippy::large-enum-variant` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B([i32; 8000]), LL + B(Box<[i32; 8000]>), @@ -30,7 +30,7 @@ LL | | ContainingLargeEnum(LargeEnum), LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingLargeEnum(LargeEnum), LL + ContainingLargeEnum(Box<LargeEnum>), @@ -49,7 +49,7 @@ LL | | StructLikeLittle { x: i32, y: i32 }, LL | | } | |_^ the entire enum is at least 70008 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]), LL + ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>), @@ -67,7 +67,7 @@ LL | | StructLikeLarge { x: [i32; 8000], y: i32 }, LL | | } | |_^ the entire enum is at least 32008 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - StructLikeLarge { x: [i32; 8000], y: i32 }, LL + StructLikeLarge { x: Box<[i32; 8000]>, y: i32 }, @@ -85,7 +85,7 @@ LL | | StructLikeLarge2 { x: [i32; 8000] }, LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - StructLikeLarge2 { x: [i32; 8000] }, LL + StructLikeLarge2 { x: Box<[i32; 8000]> }, @@ -104,7 +104,7 @@ LL | | C([u8; 200]), LL | | } | |_^ the entire enum is at least 1256 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B([u8; 1255]), LL + B(Box<[u8; 1255]>), @@ -122,7 +122,7 @@ LL | | ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; LL | | } | |_^ the entire enum is at least 70132 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]), LL + ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]), @@ -140,7 +140,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32004 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -158,7 +158,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32000 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -176,7 +176,7 @@ LL | | B(Struct2), LL | | } | |_^ the entire enum is at least 32000 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - B(Struct2), LL + B(Box<Struct2>), @@ -199,7 +199,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum CopyableLargeEnum { | ^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:118:5 | LL | B([u64; 8000]), @@ -222,7 +222,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum ManuallyCopyLargeEnum { | ^^^^^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:124:5 | LL | B([u64; 8000]), @@ -245,7 +245,7 @@ note: boxing a variant would require the type no longer be `Copy` | LL | enum SomeGenericPossiblyCopyEnum<T> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum --> tests/ui/large_enum_variant.rs:138:5 | LL | B([u64; 4000]), @@ -263,7 +263,7 @@ LL | | Large((T, [u8; 512])), LL | | } | |_^ the entire enum is at least 512 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large((T, [u8; 512])), LL + Large(Box<(T, [u8; 512])>), @@ -281,7 +281,7 @@ LL | | Small(u8), LL | | } | |_^ the entire enum is at least 520 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large([Foo<u64>; 64]), LL + Large(Box<[Foo<u64>; 64]>), @@ -299,7 +299,7 @@ LL | | Error(PossiblyLargeEnumWithConst<256>), LL | | } | |_^ the entire enum is at least 514 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Error(PossiblyLargeEnumWithConst<256>), LL + Error(Box<PossiblyLargeEnumWithConst<256>>), @@ -317,7 +317,7 @@ LL | | Recursive(Box<WithRecursion>), LL | | } | |_^ the entire enum is at least 520 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Large([u64; 64]), LL + Large(Box<[u64; 64]>), @@ -335,7 +335,7 @@ LL | | Error(WithRecursionAndGenerics<u64>), LL | | } | |_^ the entire enum is at least 520 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - Error(WithRecursionAndGenerics<u64>), LL + Error(Box<WithRecursionAndGenerics<u64>>), @@ -353,7 +353,7 @@ LL | | _SmallBoi(u8), LL | | } | |_____^ the entire enum is at least 296 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - BigBoi(PublishWithBytes), LL + BigBoi(Box<PublishWithBytes>), @@ -371,7 +371,7 @@ LL | | _SmallBoi(u8), LL | | } | |_____^ the entire enum is at least 224 bytes | -help: consider boxing the large fields to reduce the total size of the enum +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum | LL - BigBoi(PublishWithVec), LL + BigBoi(Box<PublishWithVec>), diff --git a/tests/ui/large_enum_variant_no_std.rs b/tests/ui/large_enum_variant_no_std.rs new file mode 100644 index 00000000000..ff0213155b6 --- /dev/null +++ b/tests/ui/large_enum_variant_no_std.rs @@ -0,0 +1,8 @@ +#![no_std] +#![warn(clippy::large_enum_variant)] + +enum Myenum { + //~^ ERROR: large size difference between variants + Small(u8), + Large([u8; 1024]), +} diff --git a/tests/ui/large_enum_variant_no_std.stderr b/tests/ui/large_enum_variant_no_std.stderr new file mode 100644 index 00000000000..4f32e3e4835 --- /dev/null +++ b/tests/ui/large_enum_variant_no_std.stderr @@ -0,0 +1,22 @@ +error: large size difference between variants + --> tests/ui/large_enum_variant_no_std.rs:4:1 + | +LL | / enum Myenum { +LL | | +LL | | Small(u8), + | | --------- the second-largest variant contains at least 1 bytes +LL | | Large([u8; 1024]), + | | ----------------- the largest variant contains at least 1024 bytes +LL | | } + | |_^ the entire enum is at least 1025 bytes + | +help: consider boxing the large fields or introducing indirection in some other way to reduce the total size of the enum + --> tests/ui/large_enum_variant_no_std.rs:7:5 + | +LL | Large([u8; 1024]), + | ^^^^^^^^^^^^^^^^^ + = note: `-D clippy::large-enum-variant` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::large_enum_variant)]` + +error: aborting due to 1 previous error + diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index c1801005b77..223c7447975 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -80,3 +80,20 @@ impl PubFoo { // do not lint this since users cannot control the external code #[derive(Debug)] pub struct S; + +pub mod issue15301 { + #[unsafe(no_mangle)] + pub extern "C" fn call_from_c() { + println!("Just called a Rust function from C!"); + } + + #[unsafe(no_mangle)] + pub extern "Rust" fn call_from_rust() { + println!("Just called a Rust function from Rust!"); + } + + #[unsafe(no_mangle)] + pub fn call_from_rust_no_extern() { + println!("Just called a Rust function from Rust!"); + } +} diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 4c1d6b1ccb5..1e8589cf39d 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -13,13 +13,15 @@ use std::sync::atomic::{AtomicBool, Ordering}; pub struct MyAtomic(AtomicBool); pub struct MyPure; -#[must_use] pub fn pure(i: u8) -> u8 { +#[must_use] +pub fn pure(i: u8) -> u8 { //~^ must_use_candidate i } impl MyPure { - #[must_use] pub fn inherent_pure(&self) -> u8 { + #[must_use] + pub fn inherent_pure(&self) -> u8 { //~^ must_use_candidate 0 } @@ -51,7 +53,8 @@ pub fn with_callback<F: Fn(u32) -> bool>(f: &F) -> bool { f(0) } -#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { +#[must_use] +pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { //~^ must_use_candidate true } @@ -64,7 +67,8 @@ pub fn atomics(b: &AtomicBool) -> bool { b.load(Ordering::SeqCst) } -#[must_use] pub fn rcd(_x: Rc<u32>) -> bool { +#[must_use] +pub fn rcd(_x: Rc<u32>) -> bool { //~^ must_use_candidate true } @@ -73,7 +77,8 @@ pub fn rcmut(_x: Rc<&mut u32>) -> bool { true } -#[must_use] pub fn arcd(_x: Arc<u32>) -> bool { +#[must_use] +pub fn arcd(_x: Arc<u32>) -> bool { //~^ must_use_candidate false } diff --git a/tests/ui/must_use_candidates.stderr b/tests/ui/must_use_candidates.stderr index 590253d95f9..5ddbd026062 100644 --- a/tests/ui/must_use_candidates.stderr +++ b/tests/ui/must_use_candidates.stderr @@ -1,35 +1,64 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:1 + --> tests/ui/must_use_candidates.rs:16:8 | LL | pub fn pure(i: u8) -> u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` + | ^^^^ | = note: `-D clippy::must-use-candidate` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::must_use_candidate)]` +help: add the attribute + | +LL + #[must_use] +LL | pub fn pure(i: u8) -> u8 { + | error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:22:5 + --> tests/ui/must_use_candidates.rs:22:12 | LL | pub fn inherent_pure(&self) -> u8 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` + | ^^^^^^^^^^^^^ + | +help: add the attribute + | +LL ~ #[must_use] +LL ~ pub fn inherent_pure(&self) -> u8 { + | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:54:1 + --> tests/ui/must_use_candidates.rs:54:8 + | +LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { + | ^^^^^^^^^^^ + | +help: add the attribute | +LL + #[must_use] LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` + | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:1 + --> tests/ui/must_use_candidates.rs:67:8 | LL | pub fn rcd(_x: Rc<u32>) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool` + | ^^^ + | +help: add the attribute + | +LL + #[must_use] +LL | pub fn rcd(_x: Rc<u32>) -> bool { + | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:76:1 + --> tests/ui/must_use_candidates.rs:76:8 | LL | pub fn arcd(_x: Arc<u32>) -> bool { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool` + | ^^^^ + | +help: add the attribute + | +LL + #[must_use] +LL | pub fn arcd(_x: Arc<u32>) -> bool { + | error: aborting due to 5 previous errors diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 8a1c1be289c..70cf9fa7369 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -185,3 +185,28 @@ mod issue_2496 { unimplemented!() } } + +fn needless_loop() { + use std::hint::black_box; + let x = [0; 64]; + for i in 0..64 { + let y = [0; 64]; + + black_box(x[i]); + black_box(y[i]); + } + + for i in 0..64 { + black_box(x[i]); + black_box([0; 64][i]); + } + + for i in 0..64 { + black_box(x[i]); + black_box([1, 2, 3, 4, 5, 6, 7, 8][i]); + } + + for i in 0..64 { + black_box([1, 2, 3, 4, 5, 6, 7, 8][i]); + } +} diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index e0f54ef899b..48d4b8ad151 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -466,3 +466,35 @@ fn main() { test13(); test14(); } + +fn issue15059() { + 'a: for _ in 0..1 { + //~^ never_loop + break 'a; + } + + let mut b = 1; + 'a: for i in 0..1 { + //~^ never_loop + match i { + 0 => { + b *= 2; + break 'a; + }, + x => { + b += x; + break 'a; + }, + } + } + + #[allow(clippy::unused_unit)] + for v in 0..10 { + //~^ never_loop + break; + println!("{v}"); + // This is comment and should be kept + println!("This is a comment"); + () + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index bc9a7ec48b4..54b463266a3 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -176,8 +176,10 @@ LL | | } | help: if you need the first element of the iterator, try writing | -LL - for v in 0..10 { -LL + if let Some(v) = (0..10).next() { +LL ~ if let Some(v) = (0..10).next() { +LL | +LL ~ +LL ~ | error: this loop never actually loops @@ -232,5 +234,68 @@ LL | | break 'inner; LL | | } | |_________^ -error: aborting due to 21 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:471:5 + | +LL | / 'a: for _ in 0..1 { +LL | | +LL | | break 'a; +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL ~ if let Some(_) = (0..1).next() { +LL | +LL ~ + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:477:5 + | +LL | / 'a: for i in 0..1 { +LL | | +LL | | match i { +LL | | 0 => { +... | +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL ~ if let Some(i) = (0..1).next() { +LL | +... +LL | b *= 2; +LL ~ +LL | }, +LL | x => { +LL | b += x; +LL ~ + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:492:5 + | +LL | / for v in 0..10 { +LL | | +LL | | break; +LL | | println!("{v}"); +... | +LL | | () +LL | | } + | |_____^ + | +help: if you need the first element of the iterator, try writing + | +LL ~ if let Some(v) = (0..10).next() { +LL | +LL ~ +LL ~ +LL | // This is comment and should be kept +LL ~ +LL ~ + | + +error: aborting due to 24 previous errors diff --git a/tests/ui/pattern_type_mismatch/auxiliary/external.rs b/tests/ui/pattern_type_mismatch/auxiliary/external.rs new file mode 100644 index 00000000000..cd27c5c74aa --- /dev/null +++ b/tests/ui/pattern_type_mismatch/auxiliary/external.rs @@ -0,0 +1,13 @@ +//! **FAKE** external macro crate. + +#[macro_export] +macro_rules! macro_with_match { + ( $p:pat ) => { + let something = (); + + match &something { + $p => true, + _ => false, + } + }; +} diff --git a/tests/ui/pattern_type_mismatch/syntax.rs b/tests/ui/pattern_type_mismatch/syntax.rs index 49ea1d3f7a6..aa988a577df 100644 --- a/tests/ui/pattern_type_mismatch/syntax.rs +++ b/tests/ui/pattern_type_mismatch/syntax.rs @@ -6,6 +6,9 @@ clippy::single_match )] +//@aux-build:external.rs +use external::macro_with_match; + fn main() {} fn syntax_match() { @@ -159,3 +162,9 @@ fn macro_expansion() { let value = &Some(23); matching_macro!(value); } + +fn external_macro_expansion() { + macro_with_match! { + () + }; +} diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr index cd604d604c1..636841e0a21 100644 --- a/tests/ui/pattern_type_mismatch/syntax.stderr +++ b/tests/ui/pattern_type_mismatch/syntax.stderr @@ -1,5 +1,5 @@ error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:16:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:19:9 | LL | Some(_) => (), | ^^^^^^^ @@ -9,7 +9,7 @@ LL | Some(_) => (), = help: to override `-D warnings` add `#[allow(clippy::pattern_type_mismatch)]` error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:36:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:39:12 | LL | if let Some(_) = ref_value {} | ^^^^^^^ @@ -17,7 +17,7 @@ LL | if let Some(_) = ref_value {} = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:48:15 + --> tests/ui/pattern_type_mismatch/syntax.rs:51:15 | LL | while let Some(_) = ref_value { | ^^^^^^^ @@ -25,7 +25,7 @@ LL | while let Some(_) = ref_value { = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:68:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:71:9 | LL | for (_a, _b) in slice.iter() {} | ^^^^^^^^ @@ -33,7 +33,7 @@ LL | for (_a, _b) in slice.iter() {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:79:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:82:9 | LL | let (_n, _m) = ref_value; | ^^^^^^^^ @@ -41,7 +41,7 @@ LL | let (_n, _m) = ref_value; = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:89:12 + --> tests/ui/pattern_type_mismatch/syntax.rs:92:12 | LL | fn foo((_a, _b): &(i32, i32)) {} | ^^^^^^^^ @@ -49,7 +49,7 @@ LL | fn foo((_a, _b): &(i32, i32)) {} = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:104:10 + --> tests/ui/pattern_type_mismatch/syntax.rs:107:10 | LL | foo(|(_a, _b)| ()); | ^^^^^^^^ @@ -57,7 +57,7 @@ LL | foo(|(_a, _b)| ()); = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:121:9 + --> tests/ui/pattern_type_mismatch/syntax.rs:124:9 | LL | Some(_) => (), | ^^^^^^^ @@ -65,7 +65,7 @@ LL | Some(_) => (), = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings error: type of pattern does not match the expression type - --> tests/ui/pattern_type_mismatch/syntax.rs:142:17 + --> tests/ui/pattern_type_mismatch/syntax.rs:145:17 | LL | Some(_) => (), | ^^^^^^^ diff --git a/tests/ui/single_match_else_deref_patterns.fixed b/tests/ui/single_match_else_deref_patterns.fixed new file mode 100644 index 00000000000..7a9f8063096 --- /dev/null +++ b/tests/ui/single_match_else_deref_patterns.fixed @@ -0,0 +1,53 @@ +#![feature(deref_patterns)] +#![allow( + incomplete_features, + clippy::eq_op, + clippy::op_ref, + clippy::deref_addrof, + clippy::borrow_deref_ref, + clippy::needless_if +)] +#![deny(clippy::single_match_else)] + +fn string() { + if *"" == *"" {} + + if *&*&*&*"" == *"" {} + + if ***&&"" == *"" {} + + if *&*&*"" == *"" {} + + if **&&*"" == *"" {} +} + +fn int() { + if &&&1 == &&&2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if &&1 == &&2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if &&1 == &&2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if &1 == &2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if &1 == &2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if 1 == 2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else + if 1 == 2 { unreachable!() } else { + // ok + } + //~^^^^^^ single_match_else +} diff --git a/tests/ui/single_match_else_deref_patterns.rs b/tests/ui/single_match_else_deref_patterns.rs new file mode 100644 index 00000000000..ef19c7cbde2 --- /dev/null +++ b/tests/ui/single_match_else_deref_patterns.rs @@ -0,0 +1,94 @@ +#![feature(deref_patterns)] +#![allow( + incomplete_features, + clippy::eq_op, + clippy::op_ref, + clippy::deref_addrof, + clippy::borrow_deref_ref, + clippy::needless_if +)] +#![deny(clippy::single_match_else)] + +fn string() { + match *"" { + //~^ single_match + "" => {}, + _ => {}, + } + + match *&*&*&*"" { + //~^ single_match + "" => {}, + _ => {}, + } + + match ***&&"" { + //~^ single_match + "" => {}, + _ => {}, + } + + match *&*&*"" { + //~^ single_match + "" => {}, + _ => {}, + } + + match **&&*"" { + //~^ single_match + "" => {}, + _ => {}, + } +} + +fn int() { + match &&&1 { + &&&2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&&1 { + &&2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&1 { + &&2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&&1 { + &2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&1 { + &2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&&1 { + 2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else + match &&1 { + 2 => unreachable!(), + _ => { + // ok + }, + } + //~^^^^^^ single_match_else +} diff --git a/tests/ui/single_match_else_deref_patterns.stderr b/tests/ui/single_match_else_deref_patterns.stderr new file mode 100644 index 00000000000..a47df55459b --- /dev/null +++ b/tests/ui/single_match_else_deref_patterns.stderr @@ -0,0 +1,188 @@ +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:13:5 + | +LL | / match *"" { +LL | | +LL | | "" => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if *"" == *"" {}` + | + = note: you might want to preserve the comments from inside the `match` + = note: `-D clippy::single-match` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::single_match)]` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:19:5 + | +LL | / match *&*&*&*"" { +LL | | +LL | | "" => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if *&*&*&*"" == *"" {}` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:25:5 + | +LL | / match ***&&"" { +LL | | +LL | | "" => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if ***&&"" == *"" {}` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:31:5 + | +LL | / match *&*&*"" { +LL | | +LL | | "" => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if *&*&*"" == *"" {}` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:37:5 + | +LL | / match **&&*"" { +LL | | +LL | | "" => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: try: `if **&&*"" == *"" {}` + | + = note: you might want to preserve the comments from inside the `match` + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:45:5 + | +LL | / match &&&1 { +LL | | &&&2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +note: the lint level is defined here + --> tests/ui/single_match_else_deref_patterns.rs:10:9 + | +LL | #![deny(clippy::single_match_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +help: try + | +LL ~ if &&&1 == &&&2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:52:5 + | +LL | / match &&&1 { +LL | | &&2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if &&1 == &&2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:59:5 + | +LL | / match &&1 { +LL | | &&2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if &&1 == &&2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:66:5 + | +LL | / match &&&1 { +LL | | &2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if &1 == &2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:73:5 + | +LL | / match &&1 { +LL | | &2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if &1 == &2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:80:5 + | +LL | / match &&&1 { +LL | | 2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if 1 == 2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: you seem to be trying to use `match` for an equality check. Consider using `if` + --> tests/ui/single_match_else_deref_patterns.rs:87:5 + | +LL | / match &&1 { +LL | | 2 => unreachable!(), +LL | | _ => { +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ if 1 == 2 { unreachable!() } else { +LL + // ok +LL + } + | + +error: aborting due to 12 previous errors + diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index a96c8f46f55..830aa3c976f 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -146,3 +146,15 @@ pub mod unknown_namespace { #[allow(rustc::non_glob_import_of_type_ir_inherent)] use some_module::SomeType; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/15316 +pub mod redundant_imports_issue { + macro_rules! empty { + () => {}; + } + + #[expect(redundant_imports)] + pub(crate) use empty; + + empty!(); +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index b26410134bb..14c69ccf2ed 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -146,3 +146,15 @@ pub mod unknown_namespace { #[allow(rustc::non_glob_import_of_type_ir_inherent)] use some_module::SomeType; } + +// Regression test for https://github.com/rust-lang/rust-clippy/issues/15316 +pub mod redundant_imports_issue { + macro_rules! empty { + () => {}; + } + + #[expect(redundant_imports)] + pub(crate) use empty; + + empty!(); +} diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index cfc5fb7b27c..d3204967531 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -208,7 +208,6 @@ const LEVEL_FILTERS_DEFAULT = { allow: true, warn: true, deny: true, - none: true, }; const APPLICABILITIES_FILTER_DEFAULT = { Unspecified: true, |
