diff options
| author | 许杰友 Jieyou Xu (Joe) <39484203+jieyouxu@users.noreply.github.com> | 2025-06-03 20:19:44 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-03 20:19:44 +0800 |
| commit | 033b829e0a5b33f0b383a3711b7635a7464cd6fc (patch) | |
| tree | 6c3a4faecdcff30608e6efd6a41b3b5fcc900aa0 | |
| parent | 10f2bcc95738423fbdf01ce3bb00e4092102c438 (diff) | |
| parent | b65ceb28e0cc639e3adc9d93b11ecc3228fac4c2 (diff) | |
| download | rust-033b829e0a5b33f0b383a3711b7635a7464cd6fc.tar.gz rust-033b829e0a5b33f0b383a3711b7635a7464cd6fc.zip | |
Merge pull request #2451 from jieyouxu/rustc-pull
217 files changed, 3224 insertions, 1602 deletions
diff --git a/.mailmap b/.mailmap index c3ce111bfe3..3b57f88ecab 100644 --- a/.mailmap +++ b/.mailmap @@ -653,7 +653,7 @@ Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com> Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com> Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com> Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt> -Tshepang Mbambo <tshepang@gmail.com> +Tshepang Mbambo <hopsi@tuta.io> <tshepang@gmail.com> Ty Overby <ty@pre-alpha.com> Tyler Mandry <tmandry@gmail.com> <tmandry@google.com> Tyler Ruckinger <t.ruckinger@gmail.com> diff --git a/Cargo.lock b/Cargo.lock index e486f6148cc..43f5f40925b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,24 +179,11 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "askama" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7" -dependencies = [ - "askama_derive 0.13.1", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - -[[package]] -name = "askama" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" dependencies = [ - "askama_derive 0.14.0", + "askama_derive", "itoa", "percent-encoding", "serde", @@ -205,28 +192,11 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac" -dependencies = [ - "askama_parser 0.13.0", - "basic-toml", - "memchr", - "proc-macro2", - "quote", - "rustc-hash 2.1.1", - "serde", - "serde_derive", - "syn 2.0.101", -] - -[[package]] -name = "askama_derive" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" dependencies = [ - "askama_parser 0.14.0", + "askama_parser", "basic-toml", "memchr", "proc-macro2", @@ -239,18 +209,6 @@ dependencies = [ [[package]] name = "askama_parser" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f" -dependencies = [ - "memchr", - "serde", - "serde_derive", - "winnow 0.7.10", -] - -[[package]] -name = "askama_parser" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" @@ -582,7 +540,7 @@ name = "clippy" version = "0.1.89" dependencies = [ "anstream", - "askama 0.13.1", + "askama", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", @@ -1432,7 +1390,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", - "askama 0.14.0", + "askama", "cargo_metadata 0.18.1", "serde", "serde_json", @@ -4676,7 +4634,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama 0.14.0", + "askama", "base64", "expect-test", "indexmap", diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d3aacaa15a8..d1a2ddbdb34 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -3,10 +3,11 @@ use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin}; use rustc_index::{IndexSlice, IndexVec}; +use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; @@ -527,7 +528,22 @@ impl<'hir> LoweringContext<'_, 'hir> { } UseTreeKind::Glob => { let res = self.expect_full_res(id); - let res = smallvec![self.lower_res(res)]; + let res = self.lower_res(res); + // Put the result in the appropriate namespace. + let res = match res { + Res::Def(DefKind::Mod | DefKind::Trait, _) => { + PerNS { type_ns: Some(res), value_ns: None, macro_ns: None } + } + Res::Def(DefKind::Enum, _) => { + PerNS { type_ns: None, value_ns: Some(res), macro_ns: None } + } + Res::Err => { + // Propagate the error to all namespaces, just to be sure. + let err = Some(Res::Err); + PerNS { type_ns: err, value_ns: err, macro_ns: err } + } + _ => span_bug!(path.span, "bad glob res {:?}", res), + }; let path = Path { segments, span: path.span, tokens: None }; let path = self.lower_use_path(res, &path, ParamMode::Explicit); hir::ItemKind::Use(path, hir::UseKind::Glob) @@ -601,7 +617,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { // For non-empty lists we can just drop all the data, the prefix is already // present in HIR as a part of nested imports. - self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span }) + self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span }) }; hir::ItemKind::Use(path, hir::UseKind::ListStem) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 422e79ca82f..f41627e479f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -64,7 +64,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::parse::{add_feature_diagnostics, feature_err}; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, DesugaringKind, Span}; -use smallvec::{SmallVec, smallvec}; +use smallvec::SmallVec; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; @@ -705,14 +705,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res()) } - fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> { - let res = self.resolver.get_import_res(id).present_items(); - let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect(); - if res.is_empty() { + fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS<Option<Res>> { + let per_ns = self.resolver.get_import_res(id); + let per_ns = per_ns.map(|res| res.map(|res| self.lower_res(res))); + if per_ns.is_empty() { + // Propagate the error to all namespaces, just to be sure. self.dcx().span_delayed_bug(span, "no resolution for an import"); - return smallvec![Res::Err]; + let err = Some(Res::Err); + return PerNS { type_ns: err, value_ns: err, macro_ns: err }; } - res + per_ns } fn make_lang_item_qpath( diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 5cda64ce7b4..c80ef275c80 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -1,13 +1,13 @@ use std::sync::Arc; use rustc_ast::{self as ast, *}; -use rustc_hir::def::{DefKind, PartialRes, Res}; +use rustc_hir::def::{DefKind, PartialRes, PerNS, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg}; use rustc_middle::{span_bug, ty}; use rustc_session::parse::add_feature_diagnostics; use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; -use smallvec::{SmallVec, smallvec}; +use smallvec::smallvec; use tracing::{debug, instrument}; use super::errors::{ @@ -226,11 +226,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pub(crate) fn lower_use_path( &mut self, - res: SmallVec<[Res; 3]>, + res: PerNS<Option<Res>>, p: &Path, param_mode: ParamMode, ) -> &'hir hir::UsePath<'hir> { - assert!((1..=3).contains(&res.len())); + assert!(!res.is_empty()); self.arena.alloc(hir::UsePath { res, segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| { diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 56cc4327585..a5c9bad3ac2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -840,14 +840,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } else { bug!("not an upvar") }; - err.span_label( - *span, - format!( - "calling `{}` requires mutable binding due to {}", - self.describe_place(the_place_err).unwrap(), - reason - ), - ); + // sometimes we deliberately don't store the name of a place when coming from a macro in + // another crate. We generally want to limit those diagnostics a little, to hide + // implementation details (such as those from pin!() or format!()). In that case show a + // slightly different error message, or none at all if something else happened. In other + // cases the message is likely not useful. + if let Some(place_name) = self.describe_place(the_place_err) { + err.span_label( + *span, + format!("calling `{place_name}` requires mutable binding due to {reason}"), + ); + } else if span.from_expansion() { + err.span_label( + *span, + format!("a call in this macro requires a mutable binding due to {reason}",), + ); + } } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index ff1ae2d9d79..73be25ba92b 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -23,7 +23,7 @@ use rustc_codegen_ssa::traits::{ use rustc_middle::bug; #[cfg(feature = "master")] use rustc_middle::ty::layout::FnAbiOf; -use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; +use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; use rustc_target::callconv::{ArgAbi, PassMode}; @@ -205,21 +205,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc span: Span, ) -> Result<(), Instance<'tcx>> { let tcx = self.tcx; - let callee_ty = instance.ty(tcx, self.typing_env()); - let (def_id, fn_args) = match *callee_ty.kind() { - ty::FnDef(def_id, fn_args) => (def_id, fn_args), - _ => bug!("expected fn item type, found {}", callee_ty), - }; - - let sig = callee_ty.fn_sig(tcx); - let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig); - let arg_tys = sig.inputs(); - let ret_ty = sig.output(); - let name = tcx.item_name(def_id); + let name = tcx.item_name(instance.def_id()); let name_str = name.as_str(); - - let llret_ty = self.layout_of(ret_ty).gcc_type(self); + let fn_args = instance.args; let simple = get_simple_intrinsic(self, name); let simple_func = get_simple_function(self, name); @@ -320,8 +309,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc | sym::rotate_right | sym::saturating_add | sym::saturating_sub => { - let ty = arg_tys[0]; - match int_type_width_signed(ty, self) { + match int_type_width_signed(args[0].layout.ty, self) { Some((width, signed)) => match name { sym::ctlz | sym::cttz => { let func = self.current_func.borrow().expect("func"); @@ -400,7 +388,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, - ty, + ty: args[0].layout.ty, }); return Ok(()); } @@ -492,7 +480,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } _ if name_str.starts_with("simd_") => { - match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { + match generic_simd_intrinsic( + self, + name, + args, + result.layout.ty, + result.layout.gcc_type(self), + span, + ) { Ok(value) => value, Err(()) => return Ok(()), } @@ -503,13 +498,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc }; if result.layout.ty.is_bool() { - OperandRef::from_immediate_or_packed_pair(self, value, result.layout) - .val - .store(self, result); + let val = self.from_immediate(value); + self.store_to_place(val, result.val); } else if !result.layout.ty.is_unit() { - let ptr_llty = self.type_ptr_to(result.layout.gcc_type(self)); - let ptr = self.pointercast(result.val.llval, ptr_llty); - self.store(value, ptr, result.val.align); + self.store_to_place(value, result.val); } Ok(()) } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index b897d079249..82ef0d0b13a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -28,7 +28,6 @@ use crate::context::CodegenCx; pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, - callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, @@ -54,24 +53,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( }; } - let tcx = bx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions( - ty::TypingEnv::fully_monomorphized(), - callee_ty.fn_sig(tcx), - ); - let arg_tys = sig.inputs(); - if name == sym::simd_select_bitmask { require_simd!( - arg_tys[1], - InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + args[1].layout.ty, + InvalidMonomorphization::SimdArgument { span, name, ty: args[1].layout.ty } ); - let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx()); let expected_int_bits = (len.max(8) - 1).next_power_of_two(); let expected_bytes = len / 8 + ((len % 8 > 0) as u64); - let mask_ty = arg_tys[0]; + let mask_ty = args[0].layout.ty; let mut mask = match *mask_ty.kind() { ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), @@ -121,8 +113,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } // every intrinsic below takes a SIMD vector as its first argument - require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] }); - let in_ty = arg_tys[0]; + require_simd!( + args[0].layout.ty, + InvalidMonomorphization::SimdInput { span, name, ty: args[0].layout.ty } + ); + let in_ty = args[0].layout.ty; let comparison = match name { sym::simd_eq => Some(BinOp::Eq), @@ -134,7 +129,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( _ => None, }; - let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); + let (in_len, in_elem) = args[0].layout.ty.simd_size_and_type(bx.tcx()); if let Some(cmp_op) = comparison { require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); @@ -401,13 +396,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( #[cfg(feature = "master")] if name == sym::simd_insert || name == sym::simd_insert_dyn { require!( - in_elem == arg_tys[2], + in_elem == args[2].layout.ty, InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, - out_ty: arg_tys[2] + out_ty: args[2].layout.ty } ); @@ -439,10 +434,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( let m_elem_ty = in_elem; let m_len = in_len; require_simd!( - arg_tys[1], - InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + args[1].layout.ty, + InvalidMonomorphization::SimdArgument { span, name, ty: args[1].layout.ty } ); - let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (v_len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx()); require!( m_len == v_len, InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } @@ -911,18 +906,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); require_simd!( - arg_tys[1], - InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + args[1].layout.ty, + InvalidMonomorphization::SimdSecond { span, name, ty: args[1].layout.ty } ); require_simd!( - arg_tys[2], - InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + args[2].layout.ty, + InvalidMonomorphization::SimdThird { span, name, ty: args[2].layout.ty } ); require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); // Of the same length: - let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); + let (out_len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx()); + let (out_len2, _) = args[2].layout.ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, InvalidMonomorphization::SecondArgumentLength { @@ -930,7 +925,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[1], + arg_ty: args[1].layout.ty, out_len } ); @@ -941,7 +936,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[2], + arg_ty: args[2].layout.ty, out_len: out_len2 } ); @@ -970,8 +965,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); - let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (_, element_ty0) = args[0].layout.ty.simd_size_and_type(bx.tcx()); + let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx()); let (pointer_count, underlying_ty) = match *element_ty1.kind() { ty::RawPtr(p_ty, _) if p_ty == in_elem => { (ptr_count(element_ty1), non_ptr(element_ty1)) @@ -983,7 +978,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( span, name, expected_element: element_ty1, - second_arg: arg_tys[1], + second_arg: args[1].layout.ty, in_elem, in_ty, mutability: ExpectedPointerMutability::Not, @@ -998,7 +993,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // The element type of the third argument must be an integer type of any width: // TODO: also support unsigned integers. - let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); + let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx()); match *element_ty2.kind() { ty::Int(_) => (), _ => { @@ -1030,17 +1025,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // All types must be simd vector types require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); require_simd!( - arg_tys[1], - InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + args[1].layout.ty, + InvalidMonomorphization::SimdSecond { span, name, ty: args[1].layout.ty } ); require_simd!( - arg_tys[2], - InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + args[2].layout.ty, + InvalidMonomorphization::SimdThird { span, name, ty: args[2].layout.ty } ); // Of the same length: - let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); + let (element_len1, _) = args[1].layout.ty.simd_size_and_type(bx.tcx()); + let (element_len2, _) = args[2].layout.ty.simd_size_and_type(bx.tcx()); require!( in_len == element_len1, InvalidMonomorphization::SecondArgumentLength { @@ -1048,7 +1043,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[1], + arg_ty: args[1].layout.ty, out_len: element_len1 } ); @@ -1059,7 +1054,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[2], + arg_ty: args[2].layout.ty, out_len: element_len2 } ); @@ -1082,9 +1077,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument - let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); - let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); + let (_, element_ty0) = args[0].layout.ty.simd_size_and_type(bx.tcx()); + let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx()); + let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx()); let (pointer_count, underlying_ty) = match *element_ty1.kind() { ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => { (ptr_count(element_ty1), non_ptr(element_ty1)) @@ -1096,7 +1091,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( span, name, expected_element: element_ty1, - second_arg: arg_tys[1], + second_arg: args[1].layout.ty, in_elem, in_ty, mutability: ExpectedPointerMutability::Mut, @@ -1194,8 +1189,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( return_error!(InvalidMonomorphization::ExpectedVectorElementType { span, name, - expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1, - vector_type: arg_tys[0], + expected_element: args[0].layout.ty.simd_size_and_type(bx.tcx()).1, + vector_type: args[0].layout.ty, }); } }; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index e8629aeebb9..989752eb78e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -169,19 +169,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { span: Span, ) -> Result<(), ty::Instance<'tcx>> { let tcx = self.tcx; - let callee_ty = instance.ty(tcx, self.typing_env()); - let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else { - bug!("expected fn item type, found {}", callee_ty); - }; - - let sig = callee_ty.fn_sig(tcx); - let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig); - let arg_tys = sig.inputs(); - let ret_ty = sig.output(); - let name = tcx.item_name(def_id); - - let llret_ty = self.layout_of(ret_ty).llvm_type(self); + let name = tcx.item_name(instance.def_id()); + let fn_args = instance.args; let simple = get_simple_intrinsic(self, name); let llval = match name { @@ -265,22 +255,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { BackendRepr::Scalar(scalar) => { match scalar.primitive() { Primitive::Int(..) => { - if self.cx().size_of(ret_ty).bytes() < 4 { + if self.cx().size_of(result.layout.ty).bytes() < 4 { // `va_arg` should not be called on an integer type // less than 4 bytes in length. If it is, promote // the integer to an `i32` and truncate the result // back to the smaller type. let promoted_result = emit_va_arg(self, args[0], tcx.types.i32); - self.trunc(promoted_result, llret_ty) + self.trunc(promoted_result, result.layout.llvm_type(self)) } else { - emit_va_arg(self, args[0], ret_ty) + emit_va_arg(self, args[0], result.layout.ty) } } Primitive::Float(Float::F16) => { bug!("the va_arg intrinsic does not work with `f16`") } Primitive::Float(Float::F64) | Primitive::Pointer(_) => { - emit_va_arg(self, args[0], ret_ty) + emit_va_arg(self, args[0], result.layout.ty) } // `va_arg` should never be used with the return type f32. Primitive::Float(Float::F32) => { @@ -384,7 +374,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::rotate_right | sym::saturating_add | sym::saturating_sub => { - let ty = arg_tys[0]; + let ty = args[0].layout.ty; if !ty.is_integral() { tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, @@ -403,26 +393,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &[args[0].immediate(), y], ); - self.intcast(ret, llret_ty, false) + self.intcast(ret, result.layout.llvm_type(self), false) } sym::ctlz_nonzero => { let y = self.const_bool(true); let llvm_name = &format!("llvm.ctlz.i{width}"); let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) + self.intcast(ret, result.layout.llvm_type(self), false) } sym::cttz_nonzero => { let y = self.const_bool(true); let llvm_name = &format!("llvm.cttz.i{width}"); let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) + self.intcast(ret, result.layout.llvm_type(self), false) } sym::ctpop => { let ret = self.call_intrinsic( &format!("llvm.ctpop.i{width}"), &[args[0].immediate()], ); - self.intcast(ret, llret_ty, false) + self.intcast(ret, result.layout.llvm_type(self), false) } sym::bswap => { if width == 8 { @@ -554,16 +544,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // Unpack non-power-of-2 #[repr(packed, simd)] arguments. // This gives them the expected layout of a regular #[repr(simd)] vector. let mut loaded_args = Vec::new(); - for (ty, arg) in arg_tys.iter().zip(args) { + for arg in args { loaded_args.push( // #[repr(packed, simd)] vectors are passed like arrays (as references, // with reduced alignment and no padding) rather than as immediates. // We can use a vector load to fix the layout and turn the argument // into an immediate. - if ty.is_simd() + if arg.layout.ty.is_simd() && let OperandValue::Ref(place) = arg.val { - let (size, elem_ty) = ty.simd_size_and_type(self.tcx()); + let (size, elem_ty) = arg.layout.ty.simd_size_and_type(self.tcx()); let elem_ll_ty = match elem_ty.kind() { ty::Float(f) => self.type_float_from_ty(*f), ty::Int(i) => self.type_int_from_ty(*i), @@ -580,10 +570,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ); } - let llret_ty = if ret_ty.is_simd() - && let BackendRepr::Memory { .. } = self.layout_of(ret_ty).layout.backend_repr + let llret_ty = if result.layout.ty.is_simd() + && let BackendRepr::Memory { .. } = result.layout.backend_repr { - let (size, elem_ty) = ret_ty.simd_size_and_type(self.tcx()); + let (size, elem_ty) = result.layout.ty.simd_size_and_type(self.tcx()); let elem_ll_ty = match elem_ty.kind() { ty::Float(f) => self.type_float_from_ty(*f), ty::Int(i) => self.type_int_from_ty(*i), @@ -593,16 +583,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; self.type_vector(elem_ll_ty, size) } else { - llret_ty + result.layout.llvm_type(self) }; match generic_simd_intrinsic( self, name, - callee_ty, fn_args, &loaded_args, - ret_ty, + result.layout.ty, llret_ty, span, ) { @@ -621,9 +610,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; if result.layout.ty.is_bool() { - OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) - .val - .store(self, result); + let val = self.from_immediate(llval); + self.store_to_place(val, result.val); } else if !result.layout.ty.is_unit() { self.store_to_place(llval, result.val); } @@ -1151,7 +1139,6 @@ fn get_rust_try_fn<'a, 'll, 'tcx>( fn generic_simd_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, name: Symbol, - callee_ty: Ty<'tcx>, fn_args: GenericArgsRef<'tcx>, args: &[OperandRef<'tcx, &'ll Value>], ret_ty: Ty<'tcx>, @@ -1222,26 +1209,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>( bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)) } - let tcx = bx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx)); - let arg_tys = sig.inputs(); - // Sanity-check: all vector arguments must be immediates. if cfg!(debug_assertions) { - for (ty, arg) in arg_tys.iter().zip(args) { - if ty.is_simd() { + for arg in args { + if arg.layout.ty.is_simd() { assert_matches!(arg.val, OperandValue::Immediate(_)); } } } if name == sym::simd_select_bitmask { - let (len, _) = require_simd!(arg_tys[1], SimdArgument); + let (len, _) = require_simd!(args[1].layout.ty, SimdArgument); let expected_int_bits = len.max(8).next_power_of_two(); let expected_bytes = len.div_ceil(8); - let mask_ty = arg_tys[0]; + let mask_ty = args[0].layout.ty; let mask = match mask_ty.kind() { ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(), @@ -1275,8 +1258,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } // every intrinsic below takes a SIMD vector as its first argument - let (in_len, in_elem) = require_simd!(arg_tys[0], SimdInput); - let in_ty = arg_tys[0]; + let (in_len, in_elem) = require_simd!(args[0].layout.ty, SimdInput); + let in_ty = args[0].layout.ty; let comparison = match name { sym::simd_eq => Some(BinOp::Eq), @@ -1407,13 +1390,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_insert || name == sym::simd_insert_dyn { require!( - in_elem == arg_tys[2], + in_elem == args[2].layout.ty, InvalidMonomorphization::InsertedType { span, name, in_elem, in_ty, - out_ty: arg_tys[2] + out_ty: args[2].layout.ty } ); @@ -1464,7 +1447,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_select { let m_elem_ty = in_elem; let m_len = in_len; - let (v_len, _) = require_simd!(arg_tys[1], SimdArgument); + let (v_len, _) = require_simd!(args[1].layout.ty, SimdArgument); require!( m_len == v_len, InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } @@ -1665,9 +1648,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument let (_, element_ty0) = require_simd!(in_ty, SimdFirst); - let (out_len, element_ty1) = require_simd!(arg_tys[1], SimdSecond); + let (out_len, element_ty1) = require_simd!(args[1].layout.ty, SimdSecond); // The element type of the third argument must be a signed integer type of any width: - let (out_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird); + let (out_len2, element_ty2) = require_simd!(args[2].layout.ty, SimdThird); require_simd!(ret_ty, SimdReturn); // Of the same length: @@ -1678,7 +1661,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[1], + arg_ty: args[1].layout.ty, out_len } ); @@ -1689,7 +1672,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[2], + arg_ty: args[2].layout.ty, out_len: out_len2 } ); @@ -1709,7 +1692,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span, name, expected_element: element_ty1, - second_arg: arg_tys[1], + second_arg: args[1].layout.ty, in_elem, in_ty, mutability: ExpectedPointerMutability::Not, @@ -1770,10 +1753,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let (mask_len, mask_elem) = (in_len, in_elem); // The second argument must be a pointer matching the element type - let pointer_ty = arg_tys[1]; + let pointer_ty = args[1].layout.ty; // The last argument is a passthrough vector providing values for disabled lanes - let values_ty = arg_tys[2]; + let values_ty = args[2].layout.ty; let (values_len, values_elem) = require_simd!(values_ty, SimdThird); require_simd!(ret_ty, SimdReturn); @@ -1861,10 +1844,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let (mask_len, mask_elem) = (in_len, in_elem); // The second argument must be a pointer matching the element type - let pointer_ty = arg_tys[1]; + let pointer_ty = args[1].layout.ty; // The last argument specifies the values to store to memory - let values_ty = arg_tys[2]; + let values_ty = args[2].layout.ty; let (values_len, values_elem) = require_simd!(values_ty, SimdThird); // Of the same length: @@ -1944,8 +1927,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // The second argument must be a simd vector with an element type that's a pointer // to the element type of the first argument let (_, element_ty0) = require_simd!(in_ty, SimdFirst); - let (element_len1, element_ty1) = require_simd!(arg_tys[1], SimdSecond); - let (element_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird); + let (element_len1, element_ty1) = require_simd!(args[1].layout.ty, SimdSecond); + let (element_len2, element_ty2) = require_simd!(args[2].layout.ty, SimdThird); // Of the same length: require!( @@ -1955,7 +1938,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[1], + arg_ty: args[1].layout.ty, out_len: element_len1 } ); @@ -1966,7 +1949,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( name, in_len, in_ty, - arg_ty: arg_tys[2], + arg_ty: args[2].layout.ty, out_len: element_len2 } ); @@ -1981,7 +1964,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span, name, expected_element: element_ty1, - second_arg: arg_tys[1], + second_arg: args[1].layout.ty, in_elem, in_ty, mutability: ExpectedPointerMutability::Mut, @@ -2503,7 +2486,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ptrs = args[0].immediate(); // The second argument must be a ptr-sized integer. // (We don't care about the signedness, this is wrapping anyway.) - let (_offsets_len, offsets_elem) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (_offsets_len, offsets_elem) = args[1].layout.ty.simd_size_and_type(bx.tcx()); if !matches!(offsets_elem.kind(), ty::Int(ty::IntTy::Isize) | ty::Uint(ty::UintTy::Usize)) { span_bug!( span, @@ -2527,8 +2510,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return_error!(InvalidMonomorphization::ExpectedVectorElementType { span, name, - expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1, - vector_type: arg_tys[0] + expected_element: args[0].layout.ty.simd_size_and_type(bx.tcx()).1, + vector_type: args[0].layout.ty }); } }; diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 58fa3c392ca..1a39a0c3fda 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3,7 +3,7 @@ mod raw_dylib; use std::collections::BTreeSet; use std::ffi::OsString; use std::fs::{File, OpenOptions, read}; -use std::io::{BufWriter, Write}; +use std::io::{BufReader, BufWriter, Write}; use std::ops::{ControlFlow, Deref}; use std::path::{Path, PathBuf}; use std::process::{Output, Stdio}; @@ -184,6 +184,12 @@ pub fn link_binary( ); } + if sess.target.binary_format == BinaryFormat::Elf { + if let Err(err) = warn_if_linked_with_gold(sess, &out_filename) { + info!(?err, "Error while checking if gold was the linker"); + } + } + if output.is_stdout() { if output.is_tty() { sess.dcx().emit_err(errors::BinaryOutputToTty { @@ -3375,3 +3381,54 @@ fn add_lld_args( } } } + +// gold has been deprecated with binutils 2.44 +// and is known to behave incorrectly around Rust programs. +// There have been reports of being unable to bootstrap with gold: +// https://github.com/rust-lang/rust/issues/139425 +// Additionally, gold miscompiles SHF_GNU_RETAIN sections, which are +// emitted with `#[used(linker)]`. +fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn std::error::Error>> { + use object::read::elf::{FileHeader, SectionHeader}; + use object::read::{ReadCache, ReadRef, Result}; + use object::{Endianness, elf}; + + fn elf_has_gold_version_note<'a>( + elf: &impl FileHeader, + data: impl ReadRef<'a>, + ) -> Result<bool> { + let endian = elf.endian()?; + + let section = + elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version"); + if let Some((_, section)) = section { + if let Some(mut notes) = section.notes(endian, data)? { + return Ok(notes.any(|note| { + note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION) + })); + } + } + + Ok(false) + } + + let data = ReadCache::new(BufReader::new(File::open(path)?)); + + let was_linked_with_gold = if sess.target.pointer_width == 64 { + let elf = elf::FileHeader64::<Endianness>::parse(&data)?; + elf_has_gold_version_note(elf, &data)? + } else if sess.target.pointer_width == 32 { + let elf = elf::FileHeader32::<Endianness>::parse(&data)?; + elf_has_gold_version_note(elf, &data)? + } else { + return Ok(()); + }; + + if was_linked_with_gold { + let mut warn = + sess.dcx().struct_warn("the gold linker is deprecated and has known bugs with Rust"); + warn.help("consider using LLD or ld from GNU binutils instead"); + warn.emit(); + } + Ok(()) +} diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index a79d67bb6cd..8c6f52084c2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,7 +1,7 @@ use rustc_abi::WrappingRange; +use rustc_middle::bug; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::sym; @@ -60,18 +60,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info: SourceInfo, ) -> Result<(), ty::Instance<'tcx>> { let span = source_info.span; - let callee_ty = instance.ty(bx.tcx(), bx.typing_env()); - let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else { - span_bug!(span, "expected fn item type, found {}", callee_ty); - }; - - let sig = callee_ty.fn_sig(bx.tcx()); - let sig = bx.tcx().normalize_erasing_late_bound_regions(bx.typing_env(), sig); - let arg_tys = sig.inputs(); - let ret_ty = sig.output(); - let name = bx.tcx().item_name(def_id); + let name = bx.tcx().item_name(instance.def_id()); let name_str = name.as_str(); + let fn_args = instance.args; // If we're swapping something that's *not* an `OperandValue::Ref`, // then we can do it directly and avoid the alloca. @@ -97,13 +89,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); - let ret_llval = |bx: &mut Bx, llval| { if result.layout.ty.is_bool() { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val - .store(bx, result); + let val = bx.from_immediate(llval); + bx.store_to_place(val, result.val); } else if !result.layout.ty.is_unit() { bx.store_to_place(llval, result.val); } @@ -143,7 +132,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN, _ => bug!(), }; - let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable, callee_ty); + let value = meth::VirtualIndex::from_index(idx).get_usize( + bx, + vtable, + instance.ty(bx.tcx(), bx.typing_env()), + ); match name { // Size is always <= isize::MAX. sym::vtable_size => { @@ -164,7 +157,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::type_name | sym::variant_count => { let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap(); - OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx) + OperandRef::from_const(bx, value, result.layout.ty).immediate_or_packed_pair(bx) } sym::arith_offset => { let ty = fn_args.type_at(0); @@ -248,7 +241,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.or_disjoint(a, b) } sym::exact_div => { - let ty = arg_tys[0]; + let ty = args[0].layout.ty; match int_type_width_signed(ty, bx.tcx()) { Some((_width, signed)) => { if signed { @@ -268,7 +261,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { - match float_type_width(arg_tys[0]) { + match float_type_width(args[0].layout.ty) { Some(_width) => match name { sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()), sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()), @@ -281,7 +274,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { span, name, - ty: arg_tys[0], + ty: args[0].layout.ty, }); return Ok(()); } @@ -291,7 +284,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::fsub_algebraic | sym::fmul_algebraic | sym::fdiv_algebraic - | sym::frem_algebraic => match float_type_width(arg_tys[0]) { + | sym::frem_algebraic => match float_type_width(args[0].layout.ty) { Some(_width) => match name { sym::fadd_algebraic => { bx.fadd_algebraic(args[0].immediate(), args[1].immediate()) @@ -314,31 +307,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { span, name, - ty: arg_tys[0], + ty: args[0].layout.ty, }); return Ok(()); } }, sym::float_to_int_unchecked => { - if float_type_width(arg_tys[0]).is_none() { + if float_type_width(args[0].layout.ty).is_none() { bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, - ty: arg_tys[0], + ty: args[0].layout.ty, }); return Ok(()); } - let Some((_width, signed)) = int_type_width_signed(ret_ty, bx.tcx()) else { + let Some((_width, signed)) = int_type_width_signed(result.layout.ty, bx.tcx()) + else { bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, - ty: ret_ty, + ty: result.layout.ty, }); return Ok(()); }; if signed { - bx.fptosi(args[0].immediate(), llret_ty) + bx.fptosi(args[0].immediate(), bx.backend_type(result.layout)) } else { - bx.fptoui(args[0].immediate(), llret_ty) + bx.fptoui(args[0].immediate(), bx.backend_type(result.layout)) } } diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index f33f6b7cac1..60ae7ad115a 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -4,7 +4,18 @@ //! green/native threading. This is just a bare-bones enough solution for //! librustdoc, it is not production quality at all. -cfg_select! { +// cfg(bootstrap) +macro_rules! cfg_select_dispatch { + ($($tokens:tt)*) => { + #[cfg(bootstrap)] + cfg_match! { $($tokens)* } + + #[cfg(not(bootstrap))] + cfg_select! { $($tokens)* } + }; +} + +cfg_select_dispatch! { target_os = "linux" => { mod linux; use linux as imp; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index b34a7fdb9e4..b7447e24731 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,6 +10,8 @@ #![allow(internal_features)] #![allow(rustc::default_hash_types)] #![allow(rustc::potential_query_instability)] +#![cfg_attr(bootstrap, feature(cfg_match))] +#![cfg_attr(not(bootstrap), feature(cfg_select))] #![deny(unsafe_op_in_unsafe_fn)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] @@ -19,7 +21,6 @@ #![feature(ascii_char_variants)] #![feature(assert_matches)] #![feature(auto_traits)] -#![feature(cfg_select)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(extend_one)] diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 36649a36070..e3a01e4035c 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -859,8 +859,19 @@ fn get_thread_id() -> u32 { std::thread::current().id().as_u64().get() as u32 } +// cfg(bootstrap) +macro_rules! cfg_select_dispatch { + ($($tokens:tt)*) => { + #[cfg(bootstrap)] + cfg_match! { $($tokens)* } + + #[cfg(not(bootstrap))] + cfg_select! { $($tokens)* } + }; +} + // Memory reporting -cfg_select! { +cfg_select_dispatch! { windows => { pub fn get_resident_set_size() -> Option<usize> { use windows::{ diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 88c0d223fd3..b0dff635a9b 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -8,7 +8,6 @@ macro_rules! arena_types { [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_hir::Attribute, [] owner_info: rustc_hir::OwnerInfo<'tcx>, - [] use_path: rustc_hir::UsePath<'tcx>, [] lit: rustc_hir::Lit, [] macro_def: rustc_ast::MacroDef, ]); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 507c94aca8b..98ec1ccd6ba 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -584,7 +584,7 @@ impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace { } /// Just a helper ‒ separate structure for each namespace. -#[derive(Copy, Clone, Default, Debug)] +#[derive(Copy, Clone, Default, Debug, HashStable_Generic)] pub struct PerNS<T> { pub value_ns: T, pub type_ns: T, @@ -596,10 +596,16 @@ impl<T> PerNS<T> { PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) } } + /// Note: Do you really want to use this? Often you know which namespace a + /// name will belong in, and you can consider just that namespace directly, + /// rather than iterating through all of them. pub fn into_iter(self) -> IntoIter<T, 3> { [self.value_ns, self.type_ns, self.macro_ns].into_iter() } + /// Note: Do you really want to use this? Often you know which namespace a + /// name will belong in, and you can consider just that namespace directly, + /// rather than iterating through all of them. pub fn iter(&self) -> IntoIter<&T, 3> { [&self.value_ns, &self.type_ns, &self.macro_ns].into_iter() } @@ -634,6 +640,10 @@ impl<T> PerNS<Option<T>> { } /// Returns an iterator over the items which are `Some`. + /// + /// Note: Do you really want to use this? Often you know which namespace a + /// name will belong in, and you can consider just that namespace directly, + /// rather than iterating through all of them. pub fn present_items(self) -> impl Iterator<Item = T> { [self.type_ns, self.value_ns, self.macro_ns].into_iter().flatten() } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b4fcc16c09c..4f05e1c816c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -30,7 +30,7 @@ use thin_vec::ThinVec; use tracing::debug; use crate::LangItem; -use crate::def::{CtorKind, DefKind, Res}; +use crate::def::{CtorKind, DefKind, PerNS, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; @@ -347,7 +347,7 @@ pub struct Path<'hir, R = Res> { } /// Up to three resolutions for type, value and macro namespaces. -pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>; +pub type UsePath<'hir> = Path<'hir, PerNS<Option<Res>>>; impl Path<'_> { pub fn is_global(&self) -> bool { @@ -2370,6 +2370,10 @@ impl Expr<'_> { // Lang item paths cannot currently be local variables or statics. ExprKind::Path(QPath::LangItem(..)) => false, + // Suppress errors for bad expressions. + ExprKind::Err(_guar) + | ExprKind::Let(&LetExpr { recovered: ast::Recovered::Yes(_guar), .. }) => true, + // Partially qualified paths in expressions can only legally // refer to associated items which are always rvalues. ExprKind::Path(QPath::TypeRelative(..)) @@ -2401,8 +2405,7 @@ impl Expr<'_> { | ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::Cast(..) - | ExprKind::DropTemps(..) - | ExprKind::Err(_) => false, + | ExprKind::DropTemps(..) => false, } } diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 1fd44e44b9c..b6ebd61301c 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1148,7 +1148,7 @@ pub fn walk_use<'v, V: Visitor<'v>>( hir_id: HirId, ) -> V::Result { let UsePath { segments, ref res, span } = *path; - for &res in res { + for res in res.present_items() { try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id)); } V::Result::output() diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 75d92ae7a2e..5bc7559d29a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1012,10 +1012,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { { tcx.ensure_ok().mir_drops_elaborated_and_const_checked(def_id); } - }); - }); - sess.time("coroutine_obligations", || { - tcx.par_hir_body_owners(|def_id| { if tcx.is_coroutine(def_id.to_def_id()) { tcx.ensure_ok().mir_coroutine_witnesses(def_id); let _ = tcx.ensure_ok().check_coroutine_obligations( diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 068d96c860f..558f13a832c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -715,6 +715,7 @@ fn test_unstable_options_tracking_hash() { untracked!(no_analysis, true); untracked!(no_leak_check, true); untracked!(no_parallel_backend, true); + untracked!(no_steal_thir, true); untracked!(parse_crate_root_only, true); // `pre_link_arg` is omitted because it just forwards to `pre_link_args`. untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 1d4be24ea9f..1805a674d68 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -328,16 +328,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return }; - let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id); + let is_mod_inherent = |res: Res| { + res.opt_def_id() + .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id)) + }; // Path segments except for the final. - if let Some(seg) = - path.segments.iter().find(|seg| seg.res.opt_def_id().is_some_and(is_mod_inherent)) - { + if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) { cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage); } // Final path resolutions, like `use rustc_type_ir::inherent` - else if path.res.iter().any(|res| res.opt_def_id().is_some_and(is_mod_inherent)) { + else if let Some(type_ns) = path.res.type_ns + && is_mod_inherent(type_ns) + { cx.emit_span_lint( USAGE_OF_TYPE_IR_INHERENT, path.segments.last().unwrap().ident.span, @@ -346,13 +349,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr { } let (lo, hi, snippet) = match path.segments { - [.., penultimate, segment] - if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) => - { + [.., penultimate, segment] if is_mod_inherent(penultimate.res) => { (segment.ident.span, item.kind.ident().unwrap().span, "*") } [.., segment] - if path.res.iter().flat_map(Res::opt_def_id).any(is_mod_inherent) + if let Some(type_ns) = path.res.type_ns + && is_mod_inherent(type_ns) && let rustc_hir::UseKind::Single(ident) = kind => { let (lo, snippet) = diff --git a/compiler/rustc_lint/src/unqualified_local_imports.rs b/compiler/rustc_lint/src/unqualified_local_imports.rs index 50c5119285f..0076cae3cff 100644 --- a/compiler/rustc_lint/src/unqualified_local_imports.rs +++ b/compiler/rustc_lint/src/unqualified_local_imports.rs @@ -1,4 +1,3 @@ -use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::kw; @@ -47,17 +46,15 @@ declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]); impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { let hir::ItemKind::Use(path, _kind) = item.kind else { return }; - // `path` has three resolutions for the type, module, value namespaces. - // Check if any of them qualifies: local crate, and not a macro. - // (Macros can't be imported any other way so we don't complain about them.) - let is_local_import = |res: &Res| { - matches!( - res, - hir::def::Res::Def(def_kind, def_id) - if def_id.is_local() && !matches!(def_kind, DefKind::Macro(_)), - ) - }; - if !path.res.iter().any(is_local_import) { + // Check the type and value namespace resolutions for a local crate. + let is_local_import = matches!( + path.res.type_ns, + Some(hir::def::Res::Def(_, def_id)) if def_id.is_local() + ) || matches!( + path.res.value_ns, + Some(hir::def::Res::Def(_, def_id)) if def_id.is_local() + ); + if !is_local_import { return; } // So this does refer to something local. Let's check whether it starts with `self`, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 279033ee072..542653efd30 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -535,7 +535,8 @@ rustc_queries! { separate_provide_extern } - /// Fetch the THIR for a given body. + /// Fetch the THIR for a given body. The THIR body gets stolen by unsafety checking unless + /// `-Zno-steal-thir` is on. query thir_body(key: LocalDefId) -> Result<(&'tcx Steal<thir::Thir<'tcx>>, thir::ExprId), ErrorGuaranteed> { // Perf tests revealed that hashing THIR is inefficient (see #85729). no_hash diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 9d0681b19b9..d5061b71699 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -201,9 +201,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body. fn visit_inner_body(&mut self, def: LocalDefId) { if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) { - // Runs all other queries that depend on THIR. + // Run all other queries that depend on THIR. self.tcx.ensure_done().mir_built(def); - let inner_thir = &inner_thir.steal(); + let inner_thir = if self.tcx.sess.opts.unstable_opts.no_steal_thir { + &inner_thir.borrow() + } else { + // We don't have other use for the THIR. Steal it to reduce memory usage. + &inner_thir.steal() + }; let hir_context = self.tcx.local_def_id_to_hir_id(def); let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe); let mut inner_visitor = UnsafetyVisitor { @@ -1157,7 +1162,12 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { let Ok((thir, expr)) = tcx.thir_body(def) else { return }; // Runs all other queries that depend on THIR. tcx.ensure_done().mir_built(def); - let thir = &thir.steal(); + let thir = if tcx.sess.opts.unstable_opts.no_steal_thir { + &thir.borrow() + } else { + // We don't have other use for the THIR. Steal it to reduce memory usage. + &thir.steal() + }; let hir_id = tcx.local_def_id_to_hir_id(def); let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 78583a402fe..245bd866030 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1047,26 +1047,21 @@ fn find_fallback_pattern_typo<'tcx>( let hir::ItemKind::Use(path, _) = item.kind else { continue; }; - for res in &path.res { - if let Res::Def(DefKind::Const, id) = res - && infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity()) - { - if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) { - // The original const is accessible, suggest using it directly. - let item_name = cx.tcx.item_name(*id); - accessible.push(item_name); - accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id))); - } else if cx - .tcx - .visibility(item.owner_id) - .is_accessible_from(parent, cx.tcx) - { - // The const is accessible only through the re-export, point at - // the `use`. - let ident = item.kind.ident().unwrap(); - imported.push(ident.name); - imported_spans.push(ident.span); - } + if let Some(value_ns) = path.res.value_ns + && let Res::Def(DefKind::Const, id) = value_ns + && infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity()) + { + if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) { + // The original const is accessible, suggest using it directly. + let item_name = cx.tcx.item_name(id); + accessible.push(item_name); + accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id))); + } else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) { + // The const is accessible only through the re-export, point at + // the `use`. + let ident = item.kind.ident().unwrap(); + imported.push(ident.name); + imported_spans.push(ident.span); } } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index 211e2a92f73..c15d7d6f732 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -1,6 +1,7 @@ use std::{fmt, iter, mem}; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; +use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; use rustc_index::Idx; use rustc_middle::mir::*; @@ -254,8 +255,19 @@ where // impl_item_refs may be empty if drop fn is not implemented in 'impl AsyncDrop for ...' // (#140974). // Such code will report error, so just generate sync drop here and return - let Some(drop_fn_def_id) = - tcx.associated_item_def_ids(drop_trait).into_iter().nth(0).copied() + let Some(drop_fn_def_id) = tcx + .associated_item_def_ids(drop_trait) + .first() + .and_then(|def_id| { + if tcx.def_kind(def_id) == DefKind::AssocFn + && tcx.check_args_compatible(*def_id, trait_args) + { + Some(def_id) + } else { + None + } + }) + .copied() else { tcx.dcx().span_delayed_bug( self.elaborator.body().span, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 0777d442b59..5b7d45bb152 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2312,7 +2312,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { - let name = attr.name().unwrap(); + let Some(name) = attr.name() else { + return; + }; match target { Target::ExternCrate | Target::Mod => {} _ => { diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index 2bb698689be..b9a3849f32f 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -132,7 +132,7 @@ impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> { self.add_exportable(def_id); } hir::ItemKind::Use(path, _) => { - for res in &path.res { + for res in path.res.present_items() { // Only local items are exportable. if let Some(res_id) = res.opt_def_id() && let Some(res_id) = res_id.as_local() diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 0579e91c0d6..9b824572b66 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -118,9 +118,10 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { ast::UseTreeKind::Simple(Some(ident)) => { if ident.name == kw::Underscore && !self.r.import_res_map.get(&id).is_some_and(|per_ns| { - per_ns.iter().filter_map(|res| res.as_ref()).any(|res| { - matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) - }) + matches!( + per_ns.type_ns, + Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) + ) }) { self.unused_import(self.base_id).add(id); @@ -193,6 +194,16 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { continue; } + let module = self + .r + .get_nearest_non_block_module(self.r.local_def_id(extern_crate.id).to_def_id()); + if module.no_implicit_prelude { + // If the module has `no_implicit_prelude`, then we don't suggest + // replacing the extern crate with a use, as it would not be + // inserted into the prelude. User writes `extern` style deliberately. + continue; + } + let vis_span = extern_crate .vis_span .find_ancestor_inside(extern_crate.span) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 744e99c86e1..23ede3cdbad 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4898,11 +4898,28 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_expr(e, Some(expr)); } - ExprKind::Let(ref pat, ref scrutinee, _, _) => { + ExprKind::Let(ref pat, ref scrutinee, _, Recovered::No) => { self.visit_expr(scrutinee); self.resolve_pattern_top(pat, PatternSource::Let); } + ExprKind::Let(ref pat, ref scrutinee, _, Recovered::Yes(_)) => { + self.visit_expr(scrutinee); + // This is basically a tweaked, inlined `resolve_pattern_top`. + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + self.resolve_pattern(pat, PatternSource::Let, &mut bindings); + // We still collect the bindings in this `let` expression which is in + // an invalid position (and therefore shouldn't declare variables into + // its parent scope). To avoid unnecessary errors though, we do just + // reassign the resolutions to `Res::Err`. + for (_, bindings) in &mut bindings { + for (_, binding) in bindings { + *binding = Res::Err; + } + } + self.apply_pattern_bindings(bindings); + } + ExprKind::If(ref cond, ref then, ref opt_else) => { self.with_rib(ValueNS, RibKind::Normal, |this| { let old = this.diag_metadata.in_if_condition.replace(cond); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5b4068740a1..12fa05118ca 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2366,6 +2366,8 @@ options! { "run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"), no_profiler_runtime: bool = (false, parse_no_value, [TRACKED], "prevent automatic injection of the profiler_builtins crate"), + no_steal_thir: bool = (false, parse_bool, [UNTRACKED], + "don't steal the THIR when we're done with it; useful for rustc drivers (default: no)"), no_trait_vptr: bool = (false, parse_no_value, [TRACKED], "disable generation of trait vptr in vtable for upcasting"), no_unique_section_names: bool = (false, parse_bool, [TRACKED], diff --git a/compiler/rustc_span/src/analyze_source_file.rs b/compiler/rustc_span/src/analyze_source_file.rs index c32593a6d95..55d899c9ada 100644 --- a/compiler/rustc_span/src/analyze_source_file.rs +++ b/compiler/rustc_span/src/analyze_source_file.rs @@ -29,7 +29,18 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec<RelativeBytePos>, Vec<Multi (lines, multi_byte_chars) } -cfg_select! { +// cfg(bootstrap) +macro_rules! cfg_select_dispatch { + ($($tokens:tt)*) => { + #[cfg(bootstrap)] + cfg_match! { $($tokens)* } + + #[cfg(not(bootstrap))] + cfg_select! { $($tokens)* } + }; +} + +cfg_select_dispatch! { any(target_arch = "x86", target_arch = "x86_64") => { fn analyze_source_file_dispatch( src: &str, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 906462a0d22..e950493f135 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -17,10 +17,11 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(cfg_match))] +#![cfg_attr(not(bootstrap), feature(cfg_select))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(array_windows)] -#![feature(cfg_select)] #![feature(core_io_borrowed_buf)] #![feature(hash_set_entry)] #![feature(if_let_guard)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 970faf2997c..4e842a8f93a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -937,8 +937,10 @@ symbols! { external_doc, f, f128, + f128_epsilon, f128_nan, f16, + f16_epsilon, f16_nan, f16c_target_feature, f32, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 700fb0f386e..9991b76cd0a 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -200,7 +200,7 @@ pub trait Unsize<T: ?Sized> { /// /// Constants are only allowed as patterns if (a) their type implements /// `PartialEq`, and (b) interpreting the value of the constant as a pattern -/// is equialent to calling `PartialEq`. This ensures that constants used as +/// is equivalent to calling `PartialEq`. This ensures that constants used as /// patterns cannot expose implementation details in an unexpected way or /// cause semver hazards. /// diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 7d519384e37..02bb8179293 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -84,7 +84,7 @@ use crate::ptr; /// use std::mem::ManuallyDrop; /// /// pub struct BadOption<T> { -/// // Invariant: Has been dropped iff `is_some` is false. +/// // Invariant: Has been dropped if `is_some` is false. /// value: ManuallyDrop<T>, /// is_some: bool, /// } diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index 6b9b2ba8689..58de62a8be8 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -171,6 +171,7 @@ impl f128 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS #[unstable(feature = "f128", issue = "116909")] + #[rustc_diagnostic_item = "f128_epsilon"] pub const EPSILON: f128 = 1.92592994438723585305597794258492732e-34_f128; /// Smallest finite `f128` value. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index eb7af993c60..45f402d4967 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -168,6 +168,7 @@ impl f16 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f16::MANTISSA_DIGITS #[unstable(feature = "f16", issue = "116909")] + #[rustc_diagnostic_item = "f16_epsilon"] pub const EPSILON: f16 = 9.7656e-4_f16; /// Smallest finite `f16` value. diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index 324b5451873..a5c3a6c46a4 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -159,8 +159,11 @@ impl<T> OnceLock<T> { /// Gets the mutable reference to the underlying value. /// - /// Returns `None` if the cell is uninitialized, or being initialized. - /// This method never blocks. + /// Returns `None` if the cell is uninitialized. + /// + /// This method never blocks. Since it borrows the `OnceLock` mutably, + /// it is statically guaranteed that no active borrows to the `OnceLock` + /// exist, including from other threads. #[inline] #[stable(feature = "once_cell", since = "1.70.0")] pub fn get_mut(&mut self) -> Option<&mut T> { @@ -315,7 +318,9 @@ impl<T> OnceLock<T> { /// Gets the mutable reference of the contents of the cell, initializing /// it to `f()` if the cell was uninitialized. /// - /// This method never blocks. + /// This method never blocks. Since it borrows the `OnceLock` mutably, + /// it is statically guaranteed that no active borrows to the `OnceLock` + /// exist, including from other threads. /// /// # Panics /// @@ -405,7 +410,9 @@ impl<T> OnceLock<T> { /// it to `f()` if the cell was uninitialized. If the cell was uninitialized /// and `f()` failed, an error is returned. /// - /// This method never blocks. + /// This method never blocks. Since it borrows the `OnceLock` mutably, + /// it is statically guaranteed that no active borrows to the `OnceLock` + /// exist, including from other threads. /// /// # Panics /// @@ -469,7 +476,8 @@ impl<T> OnceLock<T> { /// /// Has no effect and returns `None` if the `OnceLock` was uninitialized. /// - /// Safety is guaranteed by requiring a mutable reference. + /// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that + /// no active borrows to the `OnceLock` exist, including from other threads. /// /// # Examples /// diff --git a/src/bootstrap/defaults/bootstrap.library.toml b/src/bootstrap/defaults/bootstrap.library.toml index b43796d6f20..6edd00922ae 100644 --- a/src/bootstrap/defaults/bootstrap.library.toml +++ b/src/bootstrap/defaults/bootstrap.library.toml @@ -1,17 +1,18 @@ # These defaults are meant for contributors to the standard library and documentation. [build] -# When building the standard library, you almost never want to build the compiler itself. -build-stage = 0 -test-stage = 0 -bench-stage = 0 +build-stage = 1 +test-stage = 1 +bench-stage = 1 [rust] # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. incremental = true # Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown. lto = "off" -# Download rustc by default for library profile if compiler-affecting -# directories are not modified. For CI this is disabled. +# When building the standard library, you almost never want to build the compiler itself. +# +# If compiler-affecting directories are not modified, use precompiled rustc to speed up +# library development by skipping compiler builds. download-rustc = "if-unchanged" [llvm] diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in index 08a288170fa..1ef3e07acf0 100644 --- a/src/bootstrap/mk/Makefile.in +++ b/src/bootstrap/mk/Makefile.in @@ -54,13 +54,15 @@ check-aux: src/etc/test-float-parse \ $(BOOTSTRAP_ARGS) # Run standard library tests in Miri. - $(Q)$(BOOTSTRAP) miri --stage 2 \ + $(Q)MIRIFLAGS="-Zmiri-strict-provenance" \ + $(BOOTSTRAP) miri --stage 2 \ library/coretests \ library/alloctests \ library/alloc \ $(BOOTSTRAP_ARGS) \ --no-doc - # Some doctests use file system operations to demonstrate dealing with `Result`. + # Some doctests use file system operations to demonstrate dealing with `Result`, + # so we have to run them with isolation disabled. $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ $(BOOTSTRAP) miri --stage 2 \ library/coretests \ @@ -70,22 +72,19 @@ check-aux: --doc # In `std` we cannot test everything, so we skip some modules. $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ - $(BOOTSTRAP) miri --stage 2 library/std \ - $(BOOTSTRAP_ARGS) \ - --no-doc -- \ - --skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal:: - $(Q)MIRIFLAGS="-Zmiri-disable-isolation" \ - $(BOOTSTRAP) miri --stage 2 library/std \ + $(BOOTSTRAP) miri --stage 2 \ + library/std \ $(BOOTSTRAP_ARGS) \ - --doc -- \ + -- \ --skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal:: # Also test some very target-specific modules on other targets # (making sure to cover an i686 target as well). $(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \ - $(BOOTSTRAP) miri --stage 2 library/std \ - $(BOOTSTRAP_ARGS) \ + $(BOOTSTRAP) miri --stage 2 \ + library/std \ --target aarch64-apple-darwin,i686-pc-windows-msvc \ - --no-doc -- \ + $(BOOTSTRAP_ARGS) \ + -- \ time:: sync:: thread:: env:: dist: $(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS) diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index 374884d8a9a..0671a8467e8 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -120,14 +120,12 @@ fn main() { }; cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); - if let Some(crate_name) = crate_name { - if let Some(target) = env::var_os("RUSTC_TIME") { - if target == "all" - || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name) - { - cmd.arg("-Ztime-passes"); - } - } + if let Some(crate_name) = crate_name + && let Some(target) = env::var_os("RUSTC_TIME") + && (target == "all" + || target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)) + { + cmd.arg("-Ztime-passes"); } // Print backtrace in case of ICE @@ -242,10 +240,10 @@ fn main() { } } - if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() { - if let Some("rustc_driver") = crate_name { - cmd.arg("-Clink-args=-Wl,-q"); - } + if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() + && let Some("rustc_driver") = crate_name + { + cmd.arg("-Clink-args=-Wl,-q"); } let is_test = args.iter().any(|a| a == "--test"); @@ -282,25 +280,24 @@ fn main() { (child, status) }; - if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() - || env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some() + if (env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some() + || env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some()) + && let Some(crate_name) = crate_name { - if let Some(crate_name) = crate_name { - let dur = start.elapsed(); - // If the user requested resource usage data, then - // include that in addition to the timing output. - let rusage_data = - env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child)); - eprintln!( - "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}", - crate_name, - is_test, - dur.as_secs(), - dur.subsec_millis(), - if rusage_data.is_some() { " " } else { "" }, - rusage_data.unwrap_or_default(), - ); - } + let dur = start.elapsed(); + // If the user requested resource usage data, then + // include that in addition to the timing output. + let rusage_data = + env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child)); + eprintln!( + "[RUSTC-TIMING] {} test:{} {}.{:03}{}{}", + crate_name, + is_test, + dur.as_secs(), + dur.subsec_millis(), + if rusage_data.is_some() { " " } else { "" }, + rusage_data.unwrap_or_default(), + ); } if status.success() { diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index fa848c492b4..922578f309a 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -1,5 +1,6 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. +use crate::core::build_steps::compile; use crate::core::build_steps::compile::{ add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, }; @@ -45,10 +46,12 @@ impl Step for Std { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + let stage = run.builder.top_stage; run.crate_or_deps("sysroot") .crate_or_deps("coretests") .crate_or_deps("alloctests") .path("library") + .default_condition(stage != 0) } fn make_run(run: RunConfig<'_>) { @@ -62,6 +65,12 @@ impl Step for Std { let target = self.target; let compiler = builder.compiler(builder.top_stage, builder.config.build); + if builder.top_stage == 0 { + // Reuse the stage0 libstd + builder.ensure(compile::Std::new(compiler, target)); + return; + } + let mut cargo = builder::Cargo::new( builder, compiler, diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 07fd51919d4..5e8d6bba841 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -207,16 +207,18 @@ impl Step for Rustc { let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; - if compiler.stage != 0 { - // If we're not in stage 0, then we won't have a std from the beta - // compiler around. That means we need to make sure there's one in - // the sysroot for the compiler to find. Otherwise, we're going to - // fail when building crates that need to generate code (e.g., build - // scripts and their dependencies). - builder.ensure(compile::Std::new(compiler, compiler.host)); - builder.ensure(compile::Std::new(compiler, target)); - } else { - builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check))); + if !builder.download_rustc() { + if compiler.stage != 0 { + // If we're not in stage 0, then we won't have a std from the beta + // compiler around. That means we need to make sure there's one in + // the sysroot for the compiler to find. Otherwise, we're going to + // fail when building crates that need to generate code (e.g., build + // scripts and their dependencies). + builder.ensure(compile::Std::new(compiler, compiler.host)); + builder.ensure(compile::Std::new(compiler, target)); + } else { + builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check))); + } } let mut cargo = builder::Cargo::new( @@ -286,7 +288,9 @@ macro_rules! lint_any { let compiler = builder.compiler(builder.top_stage, builder.config.build); let target = self.target; - builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check))); + if !builder.download_rustc() { + builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check))); + }; let cargo = prepare_tool_cargo( builder, diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 8668550bddf..a782d0f6b1a 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -147,14 +147,27 @@ impl Step for Std { )] fn run(self, builder: &Builder<'_>) { let target = self.target; - let compiler = self.compiler; + + // We already have std ready to be used for stage 0. + if self.compiler.stage == 0 { + let compiler = self.compiler; + builder.ensure(StdLink::from_std(self, compiler)); + + return; + } + + let compiler = if builder.download_rustc() && self.force_recompile { + // When there are changes in the library tree with CI-rustc, we want to build + // the stageN library and that requires using stageN-1 compiler. + builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.build) + } else { + self.compiler + }; // When using `download-rustc`, we already have artifacts for the host available. Don't // recompile them. - if builder.download_rustc() && builder.config.is_host_target(target) - // NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so - // its artifacts can't be reused. - && compiler.stage != 0 + if builder.download_rustc() + && builder.config.is_host_target(target) && !self.force_recompile { let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false }); @@ -189,7 +202,13 @@ impl Step for Std { let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); trace!(?compiler_to_use); - if compiler_to_use != compiler { + if compiler_to_use != compiler + // Never uplift std unless we have compiled stage 1; if stage 1 is compiled, + // uplift it from there. + // + // FIXME: improve `fn compiler_for` to avoid adding stage condition here. + && compiler.stage > 1 + { trace!(?compiler_to_use, ?compiler, "compiler != compiler_to_use, uplifting library"); builder.ensure(Std::new(compiler_to_use, target)); @@ -222,27 +241,6 @@ impl Step for Std { target_deps.extend(self.copy_extra_objects(builder, &compiler, target)); - // The LLD wrappers and `rust-lld` are self-contained linking components that can be - // necessary to link the stdlib on some targets. We'll also need to copy these binaries to - // the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target. - if compiler.stage == 0 && builder.config.is_host_target(compiler.host) { - trace!( - "(build == host) copying linking components to `stage0-sysroot` for bootstrapping" - ); - // We want to copy the host `bin` folder within the `rustlib` folder in the sysroot. - let src_sysroot_bin = builder - .rustc_snapshot_sysroot() - .join("lib") - .join("rustlib") - .join(compiler.host) - .join("bin"); - if src_sysroot_bin.exists() { - let target_sysroot_bin = builder.sysroot_target_bindir(compiler, target); - t!(fs::create_dir_all(&target_sysroot_bin)); - builder.cp_link_r(&src_sysroot_bin, &target_sysroot_bin); - } - } - // We build a sysroot for mir-opt tests using the same trick that Miri does: A check build // with -Zalways-encode-mir. This frees us from the need to have a target linker, and the // fact that this is a check build integrates nicely with run_cargo. @@ -628,18 +626,18 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // Help the libc crate compile by assisting it in finding various // sysroot native libraries. - if target.contains("musl") { - if let Some(p) = builder.musl_libdir(target) { - let root = format!("native={}", p.to_str().unwrap()); - cargo.rustflag("-L").rustflag(&root); - } + if target.contains("musl") + && let Some(p) = builder.musl_libdir(target) + { + let root = format!("native={}", p.to_str().unwrap()); + cargo.rustflag("-L").rustflag(&root); } - if target.contains("-wasi") { - if let Some(dir) = builder.wasi_libdir(target) { - let root = format!("native={}", dir.to_str().unwrap()); - cargo.rustflag("-L").rustflag(&root); - } + if target.contains("-wasi") + && let Some(dir) = builder.wasi_libdir(target) + { + let root = format!("native={}", dir.to_str().unwrap()); + cargo.rustflag("-L").rustflag(&root); } } @@ -737,7 +735,7 @@ impl Step for StdLink { let target = self.target; // NOTE: intentionally does *not* check `target == builder.build` to avoid having to add the same check in `test::Crate`. - let (libdir, hostdir) = if self.force_recompile && builder.download_rustc() { + let (libdir, hostdir) = if !self.force_recompile && builder.download_rustc() { // NOTE: copies part of `sysroot_libdir` to avoid having to add a new `force_recompile` argument there too let lib = builder.sysroot_libdir_relative(self.compiler); let sysroot = builder.ensure(crate::core::build_steps::compile::Sysroot { @@ -753,23 +751,16 @@ impl Step for StdLink { (libdir, hostdir) }; - add_to_sysroot( - builder, - &libdir, - &hostdir, - &build_stamp::libstd_stamp(builder, compiler, target), - ); + let is_downloaded_beta_stage0 = builder + .build + .config + .initial_rustc + .starts_with(builder.out.join(compiler.host).join("stage0/bin")); // Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0` // work for stage0-sysroot. We only do this if the stage0 compiler comes from beta, // and is not set to a custom path. - if compiler.stage == 0 - && builder - .build - .config - .initial_rustc - .starts_with(builder.out.join(compiler.host).join("stage0/bin")) - { + if compiler.stage == 0 && is_downloaded_beta_stage0 { // Copy bin files from stage0/bin to stage0-sysroot/bin let sysroot = builder.out.join(compiler.host).join("stage0-sysroot"); @@ -779,21 +770,9 @@ impl Step for StdLink { t!(fs::create_dir_all(&sysroot_bin_dir)); builder.cp_link_r(&stage0_bin_dir, &sysroot_bin_dir); - // Copy all files from stage0/lib to stage0-sysroot/lib let stage0_lib_dir = builder.out.join(host).join("stage0/lib"); - if let Ok(files) = fs::read_dir(stage0_lib_dir) { - for file in files { - let file = t!(file); - let path = file.path(); - if path.is_file() { - builder.copy_link( - &path, - &sysroot.join("lib").join(path.file_name().unwrap()), - FileType::Regular, - ); - } - } - } + t!(fs::create_dir_all(sysroot.join("lib"))); + builder.cp_link_r(&stage0_lib_dir, &sysroot.join("lib")); // Copy codegen-backends from stage0 let sysroot_codegen_backends = builder.sysroot_codegen_backends(compiler); @@ -807,6 +786,30 @@ impl Step for StdLink { if stage0_codegen_backends.exists() { builder.cp_link_r(&stage0_codegen_backends, &sysroot_codegen_backends); } + } else if compiler.stage == 0 { + let sysroot = builder.out.join(compiler.host.triple).join("stage0-sysroot"); + + if builder.local_rebuild { + // On local rebuilds this path might be a symlink to the project root, + // which can be read-only (e.g., on CI). So remove it before copying + // the stage0 lib. + let _ = fs::remove_dir_all(sysroot.join("lib/rustlib/src/rust")); + } + + builder.cp_link_r(&builder.initial_sysroot.join("lib"), &sysroot.join("lib")); + } else { + if builder.download_rustc() { + // Ensure there are no CI-rustc std artifacts. + let _ = fs::remove_dir_all(&libdir); + let _ = fs::remove_dir_all(&hostdir); + } + + add_to_sysroot( + builder, + &libdir, + &hostdir, + &build_stamp::libstd_stamp(builder, compiler, target), + ); } } } @@ -1029,7 +1032,7 @@ impl Step for Rustc { let compiler = self.compiler; let target = self.target; - // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler, + // NOTE: the ABI of the stage0 compiler is different from the ABI of the downloaded compiler, // so its artifacts can't be reused. if builder.download_rustc() && compiler.stage != 0 { trace!(stage = compiler.stage, "`download_rustc` requested"); @@ -1388,12 +1391,13 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect // found. This is to avoid the linker errors about undefined references to // `__llvm_profile_instrument_memop` when linking `rustc_driver`. let mut llvm_linker_flags = String::new(); - if builder.config.llvm_profile_generate && target.is_msvc() { - if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl { - // Add clang's runtime library directory to the search path - let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path); - llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display())); - } + if builder.config.llvm_profile_generate + && target.is_msvc() + && let Some(ref clang_cl_path) = builder.config.llvm_clang_cl + { + // Add clang's runtime library directory to the search path + let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path); + llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display())); } // The config can also specify its own llvm linker flags. @@ -1785,9 +1789,9 @@ impl Step for Sysroot { t!(fs::create_dir_all(&sysroot)); // In some cases(see https://github.com/rust-lang/rust/issues/109314), when the stage0 - // compiler relies on more recent version of LLVM than the beta compiler, it may not + // compiler relies on more recent version of LLVM than the stage0 compiler, it may not // be able to locate the correct LLVM in the sysroot. This situation typically occurs - // when we upgrade LLVM version while the beta compiler continues to use an older version. + // when we upgrade LLVM version while the stage0 compiler continues to use an older version. // // Make sure to add the correct version of LLVM into the stage0 sysroot. if compiler.stage == 0 { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 50dc8e5ac9b..587fca80374 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2276,11 +2276,12 @@ impl Step for LlvmTools { let target = self.target; // Run only if a custom llvm-config is not used - if let Some(config) = builder.config.target_config.get(&target) { - if !builder.config.llvm_from_ci && config.llvm_config.is_some() { - builder.info(&format!("Skipping LlvmTools ({target}): external LLVM")); - return None; - } + if let Some(config) = builder.config.target_config.get(&target) + && !builder.config.llvm_from_ci + && config.llvm_config.is_some() + { + builder.info(&format!("Skipping LlvmTools ({target}): external LLVM")); + return None; } if !builder.config.dry_run() { @@ -2398,11 +2399,11 @@ impl Step for RustDev { let target = self.target; /* run only if llvm-config isn't used */ - if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref _s) = config.llvm_config { - builder.info(&format!("Skipping RustDev ({target}): external LLVM")); - return None; - } + if let Some(config) = builder.config.target_config.get(&target) + && let Some(ref _s) = config.llvm_config + { + builder.info(&format!("Skipping RustDev ({target}): external LLVM")); + return None; } if !builder.config.dry_run() { diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 1c317ce4b86..61268df7336 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -318,10 +318,10 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { // `into_path` produces an absolute path. Try to strip `cwd` to get a shorter // relative path. let mut path = entry.clone().into_path(); - if let Ok(cwd) = cwd { - if let Ok(path2) = path.strip_prefix(cwd) { - path = path2.to_path_buf(); - } + if let Ok(cwd) = cwd + && let Ok(path2) = path.strip_prefix(cwd) + { + path = path2.to_path_buf(); } path.display().to_string() }); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 979ff399267..20a4d1a1515 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -107,18 +107,18 @@ pub fn prebuilt_llvm_config( // If we're using a custom LLVM bail out here, but we can only use a // custom LLVM for the build triple. - if let Some(config) = builder.config.target_config.get(&target) { - if let Some(ref s) = config.llvm_config { - check_llvm_version(builder, s); - let llvm_config = s.to_path_buf(); - let mut llvm_cmake_dir = llvm_config.clone(); - llvm_cmake_dir.pop(); - llvm_cmake_dir.pop(); - llvm_cmake_dir.push("lib"); - llvm_cmake_dir.push("cmake"); - llvm_cmake_dir.push("llvm"); - return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir }); - } + if let Some(config) = builder.config.target_config.get(&target) + && let Some(ref s) = config.llvm_config + { + check_llvm_version(builder, s); + let llvm_config = s.to_path_buf(); + let mut llvm_cmake_dir = llvm_config.clone(); + llvm_cmake_dir.pop(); + llvm_cmake_dir.pop(); + llvm_cmake_dir.push("lib"); + llvm_cmake_dir.push("cmake"); + llvm_cmake_dir.push("llvm"); + return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir }); } if handle_submodule_when_needed { @@ -468,10 +468,10 @@ impl Step for Llvm { cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";")); } - if let Some(num_linkers) = builder.config.llvm_link_jobs { - if num_linkers > 0 { - cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string()); - } + if let Some(num_linkers) = builder.config.llvm_link_jobs + && num_linkers > 0 + { + cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string()); } // https://llvm.org/docs/HowToCrossCompileLLVM.html @@ -597,10 +597,10 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok()); - if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { - if major >= 19 { - return; - } + if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) + && major >= 19 + { + return; } panic!("\n\nbad LLVM version: {version}, need >=19\n\n") } @@ -730,11 +730,9 @@ fn configure_cmake( // If ccache is configured we inform the build a little differently how // to invoke ccache while also invoking our compilers. - if use_compiler_launcher { - if let Some(ref ccache) = builder.config.ccache { - cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache) - .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache); - } + if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache { + cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache) + .define("CMAKE_CXX_COMPILER_LAUNCHER", ccache); } cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc)) .define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx)) @@ -792,20 +790,20 @@ fn configure_cmake( cxxflags.push(format!(" --target={target}")); } cfg.define("CMAKE_CXX_FLAGS", cxxflags); - if let Some(ar) = builder.ar(target) { - if ar.is_absolute() { - // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it - // tries to resolve this path in the LLVM build directory. - cfg.define("CMAKE_AR", sanitize_cc(&ar)); - } + if let Some(ar) = builder.ar(target) + && ar.is_absolute() + { + // LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it + // tries to resolve this path in the LLVM build directory. + cfg.define("CMAKE_AR", sanitize_cc(&ar)); } - if let Some(ranlib) = builder.ranlib(target) { - if ranlib.is_absolute() { - // LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it - // tries to resolve this path in the LLVM build directory. - cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib)); - } + if let Some(ranlib) = builder.ranlib(target) + && ranlib.is_absolute() + { + // LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it + // tries to resolve this path in the LLVM build directory. + cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib)); } if let Some(ref flags) = builder.config.llvm_ldflags { @@ -1038,13 +1036,14 @@ impl Step for Lld { // when doing PGO on CI, cmake or clang-cl don't automatically link clang's // profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid // linking errors, much like LLVM's cmake setup does in that situation. - if builder.config.llvm_profile_generate && target.is_msvc() { - if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() { - // Find clang's runtime library directory and push that as a search path to the - // cmake linker flags. - let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path); - ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display())); - } + if builder.config.llvm_profile_generate + && target.is_msvc() + && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() + { + // Find clang's runtime library directory and push that as a search path to the + // cmake linker flags. + let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path); + ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display())); } // LLD is built as an LLVM tool, but is distributed outside of the `llvm-tools` component, diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 71cdb665ed4..14c7b7cf5e9 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -154,10 +154,10 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); let compiler = builder.compiler(builder.top_stage, builder.config.build); builder.ensure(Std::new(compiler, builder.config.build)); - if let Some(opts) = args.cmd.shared_opts() { - if opts.profiles.contains(&Profile::Doc) { - builder.ensure(Rustdoc { compiler }); - } + if let Some(opts) = args.cmd.shared_opts() + && opts.profiles.contains(&Profile::Doc) + { + builder.ensure(Rustdoc { compiler }); } let sysroot = builder.ensure(Sysroot::new(compiler)); diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index 5e22c2d1acb..25adfdf1601 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -241,10 +241,10 @@ impl Step for Link { if run.builder.config.dry_run() { return; } - if let [cmd] = &run.paths[..] { - if cmd.assert_single_path().path.as_path().as_os_str() == "link" { - run.builder.ensure(Link); - } + if let [cmd] = &run.paths[..] + && cmd.assert_single_path().path.as_path().as_os_str() == "link" + { + run.builder.ensure(Link); } } fn run(self, builder: &Builder<'_>) -> Self::Output { @@ -457,10 +457,10 @@ impl Step for Hook { } fn make_run(run: RunConfig<'_>) { - if let [cmd] = &run.paths[..] { - if cmd.assert_single_path().path.as_path().as_os_str() == "hook" { - run.builder.ensure(Hook); - } + if let [cmd] = &run.paths[..] + && cmd.assert_single_path().path.as_path().as_os_str() == "hook" + { + run.builder.ensure(Hook); } } @@ -672,10 +672,10 @@ impl Step for Editor { if run.builder.config.dry_run() { return; } - if let [cmd] = &run.paths[..] { - if cmd.assert_single_path().path.as_path().as_os_str() == "editor" { - run.builder.ensure(Editor); - } + if let [cmd] = &run.paths[..] + && cmd.assert_single_path().path.as_path().as_os_str() == "editor" + { + run.builder.ensure(Editor); } } diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index bb682751a2e..22ab3e56a9c 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1576,7 +1576,7 @@ impl Step for Compiletest { if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() { eprintln!("\ -ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail +ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail HELP: to test the compiler, use `--stage 1` instead HELP: to test the standard library, use `--stage 0 library/std` instead NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`." @@ -1604,9 +1604,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the // running compiler in stage 2 when plugins run. let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 { - // At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead - // finding an incorrect compiler path on cross-targets, as the stage 0 beta compiler is - // always equal to `build.build` in the configuration. + // At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead + // finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to + // `build.build` in the configuration. let build = builder.build.build; compiler = builder.compiler(compiler.stage - 1, build); let test_stage = compiler.stage + 1; @@ -1692,7 +1692,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } if mode == "rustdoc-json" { - // Use the beta compiler for jsondocck + // Use the stage0 compiler for jsondocck let json_compiler = compiler.with_stage(0); cmd.arg("--jsondocck-path") .arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }).tool_path); @@ -2417,10 +2417,10 @@ impl Step for ErrorIndex { } fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> bool { - if let Ok(contents) = fs::read_to_string(markdown) { - if !contents.contains("```") { - return true; - } + if let Ok(contents) = fs::read_to_string(markdown) + && !contents.contains("```") + { + return true; } builder.verbose(|| println!("doc tests for: {}", markdown.display())); diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 76025d4020e..173b3ff0816 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -329,9 +329,9 @@ pub(crate) fn get_tool_rustc_compiler( return target_compiler; } - if builder.download_rustc() && target_compiler.stage > 0 { - // We already have the stage N compiler, we don't need to cut the stage. - return builder.compiler(target_compiler.stage, builder.config.build); + if builder.download_rustc() && target_compiler.stage == 1 { + // We shouldn't drop to stage0 compiler when using CI rustc. + return builder.compiler(1, builder.config.build); } // Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 51556525bc9..1e9af68a92d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -988,15 +988,15 @@ impl Builder<'_> { // requirement, but the `-L` library path is not propagated across // separate Cargo projects. We can add LLVM's library path to the // rustc args as a workaround. - if mode == Mode::ToolRustc || mode == Mode::Codegen { - if let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = - command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); - if target.is_msvc() { - rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); - } else { - rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}")); - } + if (mode == Mode::ToolRustc || mode == Mode::Codegen) + && let Some(llvm_config) = self.llvm_config(target) + { + let llvm_libdir = + command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout(); + if target.is_msvc() { + rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); + } else { + rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}")); } } @@ -1231,12 +1231,11 @@ impl Builder<'_> { _ => None, }; - if let Some(limit) = limit { - if stage == 0 - || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm" - { - rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); - } + if let Some(limit) = limit + && (stage == 0 + || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm") + { + rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}")); } } diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 51852099dc3..baa22fc7f72 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -237,7 +237,7 @@ fn alias_and_path_for_library() { ); assert_eq!( first(cache.all::<doc::Std>()), - &[doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0)] + &[doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1)] ); } @@ -255,19 +255,6 @@ fn ci_rustc_if_unchanged_invalidate_on_compiler_changes() { } #[test] -fn ci_rustc_if_unchanged_invalidate_on_library_changes_in_ci() { - git_test(|ctx| { - prepare_rustc_checkout(ctx); - ctx.create_upstream_merge(&["compiler/bar"]); - // This change should invalidate download-ci-rustc - ctx.create_nonupstream_merge(&["library/foo"]); - - let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true); - assert_eq!(config.download_rustc_commit, None); - }); -} - -#[test] fn ci_rustc_if_unchanged_do_not_invalidate_on_library_changes_outside_ci() { git_test(|ctx| { prepare_rustc_checkout(ctx); @@ -433,14 +420,14 @@ mod defaults { assert_eq!(first(cache.all::<doc::ErrorIndex>()), &[doc::ErrorIndex { target: a },]); assert_eq!( first(cache.all::<tool::ErrorIndex>()), - &[tool::ErrorIndex { compiler: Compiler::new(0, a) }] + &[tool::ErrorIndex { compiler: Compiler::new(1, a) }] ); - // docs should be built with the beta compiler, not with the stage0 artifacts. + // docs should be built with the stage0 compiler, not with the stage0 artifacts. // recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to, // not the one it was built by. assert_eq!( first(cache.all::<tool::Rustdoc>()), - &[tool::Rustdoc { compiler: Compiler::new(0, a) },] + &[tool::Rustdoc { compiler: Compiler::new(1, a) },] ); } } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index d4b5a809215..899ffde8adf 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -45,6 +45,7 @@ use crate::utils::helpers::{self, exe, output, t}; /// final output/compiler, which can be significantly affected by changes made to the bootstrap sources. #[rustfmt::skip] // We don't want rustfmt to oneline this list pub(crate) const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[ + ":!library", ":!src/tools", ":!src/librustdoc", ":!src/rustdoc-json-types", @@ -1699,20 +1700,20 @@ impl Config { }; // We want to be able to set string values without quotes, // like in `configure.py`. Try adding quotes around the right hand side - if let Some((key, value)) = option.split_once('=') { - if !value.contains('"') { - match get_table(&format!(r#"{key}="{value}""#)) { - Ok(v) => { - override_toml.merge( - None, - &mut Default::default(), - v, - ReplaceOpt::ErrorOnDuplicate, - ); - continue; - } - Err(e) => err = e, + if let Some((key, value)) = option.split_once('=') + && !value.contains('"') + { + match get_table(&format!(r#"{key}="{value}""#)) { + Ok(v) => { + override_toml.merge( + None, + &mut Default::default(), + v, + ReplaceOpt::ErrorOnDuplicate, + ); + continue; } + Err(e) => err = e, } } eprintln!("failed to parse override `{option}`: `{err}"); @@ -2056,16 +2057,15 @@ impl Config { || (matches!(debug_toml, Some(true)) && !matches!(rustc_debug_assertions_toml, Some(false))); - if debug_assertions_requested { - if let Some(ref opt) = download_rustc { - if opt.is_string_or_true() { - eprintln!( - "WARN: currently no CI rustc builds have rustc debug assertions \ + if debug_assertions_requested + && let Some(ref opt) = download_rustc + && opt.is_string_or_true() + { + eprintln!( + "WARN: currently no CI rustc builds have rustc debug assertions \ enabled. Please either set `rust.debug-assertions` to `false` if you \ want to use download CI rustc or set `rust.download-rustc` to `false`." - ); - } - } + ); } config.download_rustc_commit = config.download_ci_rustc_commit( @@ -2176,19 +2176,17 @@ impl Config { // We need to override `rust.channel` if it's manually specified when using the CI rustc. // This is because if the compiler uses a different channel than the one specified in bootstrap.toml, // tests may fail due to using a different channel than the one used by the compiler during tests. - if let Some(commit) = &config.download_rustc_commit { - if is_user_configured_rust_channel { - println!( - "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." - ); + if let Some(commit) = &config.download_rustc_commit + && is_user_configured_rust_channel + { + println!( + "WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel." + ); - let channel = config - .read_file_by_commit(Path::new("src/ci/channel"), commit) - .trim() - .to_owned(); + let channel = + config.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned(); - config.channel = channel; - } + config.channel = channel; } if let Some(llvm) = toml.llvm { @@ -2533,10 +2531,12 @@ impl Config { || bench_stage.is_some(); // See https://github.com/rust-lang/compiler-team/issues/326 config.stage = match config.cmd { - Subcommand::Check { .. } => flags.stage.or(check_stage).unwrap_or(0), + Subcommand::Check { .. } | Subcommand::Clippy { .. } | Subcommand::Fix => { + flags.stage.or(check_stage).unwrap_or(1) + } // `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden. Subcommand::Doc { .. } => { - flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 0 }) + flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 1 }) } Subcommand::Build => { flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 }) @@ -2551,8 +2551,6 @@ impl Config { // These are all bootstrap tools, which don't depend on the compiler. // The stage we pass shouldn't matter, but use 0 just in case. Subcommand::Clean { .. } - | Subcommand::Clippy { .. } - | Subcommand::Fix | Subcommand::Run { .. } | Subcommand::Setup { .. } | Subcommand::Format { .. } @@ -2698,10 +2696,10 @@ impl Config { let bindir = &self.bindir; if bindir.is_absolute() { // Try to make it relative to the prefix. - if let Some(prefix) = &self.prefix { - if let Ok(stripped) = bindir.strip_prefix(prefix) { - return stripped; - } + if let Some(prefix) = &self.prefix + && let Ok(stripped) = bindir.strip_prefix(prefix) + { + return stripped; } } bindir @@ -3150,24 +3148,10 @@ impl Config { } }; - // RUSTC_IF_UNCHANGED_ALLOWED_PATHS - let mut allowed_paths = RUSTC_IF_UNCHANGED_ALLOWED_PATHS.to_vec(); - - // In CI, disable ci-rustc if there are changes in the library tree. But for non-CI, allow - // these changes to speed up the build process for library developers. This provides consistent - // functionality for library developers between `download-rustc=true` and `download-rustc="if-unchanged"` - // options. - // - // If you update "library" logic here, update `builder::tests::ci_rustc_if_unchanged_logic` test - // logic accordingly. - if !self.is_running_on_ci { - allowed_paths.push(":!library"); - } - let commit = if self.rust_info.is_managed_git_subrepository() { // Look for a version to compare to based on the current commit. // Only commits merged by bors will have CI artifacts. - let freshness = self.check_path_modifications(&allowed_paths); + let freshness = self.check_path_modifications(RUSTC_IF_UNCHANGED_ALLOWED_PATHS); self.verbose(|| { eprintln!("rustc freshness: {freshness:?}"); }); @@ -3493,19 +3477,19 @@ fn check_incompatible_options_for_ci_rustc( // We always build the in-tree compiler on cross targets, so we only care // about the host target here. let host_str = host.to_string(); - if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str)) { - if current_cfg.profiler.is_some() { - let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str)); - let ci_cfg = ci_target_toml.ok_or(format!( - "Target specific config for '{host_str}' is not present for CI-rustc" - ))?; + if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str)) + && current_cfg.profiler.is_some() + { + let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str)); + let ci_cfg = ci_target_toml.ok_or(format!( + "Target specific config for '{host_str}' is not present for CI-rustc" + ))?; - let profiler = &ci_cfg.profiler; - err!(current_cfg.profiler, profiler, "build"); + let profiler = &ci_cfg.profiler; + err!(current_cfg.profiler, profiler, "build"); - let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; - err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); - } + let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins; + err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build"); } let (Some(current_rust_config), Some(ci_rust_config)) = diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index e0c9877cd55..d942334d1c3 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -666,7 +666,7 @@ impl Config { } }; - // For the beta compiler, put special effort into ensuring the checksums are valid. + // For the stage0 compiler, put special effort into ensuring the checksums are valid. let checksum = if should_verify { let error = format!( "src/stage0 doesn't contain a checksum for {url}. \ @@ -709,10 +709,10 @@ download-rustc = false "; } self.download_file(&format!("{base_url}/{url}"), &tarball, help_on_error); - if let Some(sha256) = checksum { - if !self.verify(&tarball, sha256) { - panic!("failed to verify {}", tarball.display()); - } + if let Some(sha256) = checksum + && !self.verify(&tarball, sha256) + { + panic!("failed to verify {}", tarball.display()); } self.unpack(&tarball, &bin_root, prefix); diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 7cce14841eb..e4643653d97 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1451,23 +1451,23 @@ Executed at: {executed_at}"#, // Look for Wasmtime, and for its default options be sure to disable // its caching system since we're executing quite a lot of tests and // ideally shouldn't pollute the cache too much. - if let Some(path) = finder.maybe_have("wasmtime") { - if let Ok(mut path) = path.into_os_string().into_string() { - path.push_str(" run -C cache=n --dir ."); - // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is - // required for libtest to work on beta/stable channels. - // - // NB: with Wasmtime 20 this can change to `-S inherit-env` to - // inherit the entire environment rather than just this single - // environment variable. - path.push_str(" --env RUSTC_BOOTSTRAP"); - - if target.contains("wasip2") { - path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup"); - } - - return Some(path); + if let Some(path) = finder.maybe_have("wasmtime") + && let Ok(mut path) = path.into_os_string().into_string() + { + path.push_str(" run -C cache=n --dir ."); + // Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is + // required for libtest to work on beta/stable channels. + // + // NB: with Wasmtime 20 this can change to `-S inherit-env` to + // inherit the entire environment rather than just this single + // environment variable. + path.push_str(" --env RUSTC_BOOTSTRAP"); + + if target.contains("wasip2") { + path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup"); } + + return Some(path); } None @@ -1637,12 +1637,12 @@ Executed at: {executed_at}"#, /// sha, version, etc. fn rust_version(&self) -> String { let mut version = self.rust_info().version(self, &self.version); - if let Some(ref s) = self.config.description { - if !s.is_empty() { - version.push_str(" ("); - version.push_str(s); - version.push(')'); - } + if let Some(ref s) = self.config.description + && !s.is_empty() + { + version.push_str(" ("); + version.push_str(s); + version.push(')'); } version } @@ -1760,14 +1760,14 @@ Executed at: {executed_at}"#, pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) { self.copy_link_internal(src, dst, false); - if file_type.could_have_split_debuginfo() { - if let Some(dbg_file) = split_debuginfo(src) { - self.copy_link_internal( - &dbg_file, - &dst.with_extension(dbg_file.extension().unwrap()), - false, - ); - } + if file_type.could_have_split_debuginfo() + && let Some(dbg_file) = split_debuginfo(src) + { + self.copy_link_internal( + &dbg_file, + &dst.with_extension(dbg_file.extension().unwrap()), + false, + ); } } @@ -1779,13 +1779,14 @@ Executed at: {executed_at}"#, if src == dst { return; } - if let Err(e) = fs::remove_file(dst) { - if cfg!(windows) && e.kind() != io::ErrorKind::NotFound { - // workaround for https://github.com/rust-lang/rust/issues/127126 - // if removing the file fails, attempt to rename it instead. - let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)); - let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos())); - } + if let Err(e) = fs::remove_file(dst) + && cfg!(windows) + && e.kind() != io::ErrorKind::NotFound + { + // workaround for https://github.com/rust-lang/rust/issues/127126 + // if removing the file fails, attempt to rename it instead. + let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)); + let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos())); } let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display())); let mut src = src.to_path_buf(); @@ -1894,10 +1895,10 @@ Executed at: {executed_at}"#, chmod(&dst, file_type.perms()); // If this file can have debuginfo, look for split debuginfo and install it too. - if file_type.could_have_split_debuginfo() { - if let Some(dbg_file) = split_debuginfo(src) { - self.install(&dbg_file, dstdir, FileType::Regular); - } + if file_type.could_have_split_debuginfo() + && let Some(dbg_file) = split_debuginfo(src) + { + self.install(&dbg_file, dstdir, FileType::Regular); } } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 1d0ea3ebf61..459a34d14cc 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -46,10 +46,10 @@ pub fn find_recent_config_change_ids(current_id: usize) -> &'static [ChangeInfo] // an empty list (it may be due to switching from a recent branch to an // older one); otherwise, return the full list (assuming the user provided // the incorrect change-id by accident). - if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id) { - if current_id > config.change_id { - return &[]; - } + if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id) + && current_id > config.change_id + { + return &[]; } CONFIG_CHANGE_HISTORY @@ -411,4 +411,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`./x run` now supports running in-tree `rustfmt`, e.g., `./x run rustfmt -- --check /path/to/file.rs`.", }, + ChangeInfo { + change_id: 119899, + severity: ChangeSeverity::Warning, + summary: "Stage0 library no longer matches the in-tree library, which means stage1 compiler now uses the beta library.", + }, ]; diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs index 60cbf50c7a3..2884ae08ea8 100644 --- a/src/ci/citool/src/jobs.rs +++ b/src/ci/citool/src/jobs.rs @@ -13,7 +13,7 @@ use crate::utils::load_env_var; #[derive(serde::Deserialize, Debug, Clone)] #[serde(deny_unknown_fields)] pub struct Job { - /// Name of the job, e.g. mingw-check + /// Name of the job, e.g. mingw-check-1 pub name: String, /// GitHub runner on which the job should be executed pub os: String, diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index c644f885be3..fcdca899e06 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -40,7 +40,7 @@ try-job: dist-i686-msvc"#, fn pr_jobs() { let stdout = get_matrix("pull_request", "commit", "refs/heads/pr/1234"); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"mingw-check","full_name":"PR - mingw-check","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true,"doc_url":"https://foo.bar"}] + jobs=[{"name":"mingw-check-1","full_name":"PR - mingw-check-1","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-2","full_name":"PR - mingw-check-2","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=pr "#); } @@ -51,6 +51,8 @@ fn get_matrix(event_name: &str, commit_msg: &str, branch_ref: &str) -> String { .env("GITHUB_EVENT_NAME", event_name) .env("COMMIT_MESSAGE", commit_msg) .env("GITHUB_REF", branch_ref) + .env("GITHUB_RUN_ID", "123") + .env("GITHUB_RUN_ATTEMPT", "1") .stdout(Stdio::piped()) .output() .expect("Failed to execute command"); diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index d81be88b708..d262da11102 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -64,7 +64,9 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - name: mingw-check + - name: mingw-check-1 + <<: *job-linux-4c + - name: mingw-check-2 <<: *job-linux-4c - name: mingw-check-tidy continue_on_error: true diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile index df73c7382b5..a877de1f7b2 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check-1/Dockerfile @@ -34,31 +34,23 @@ RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/ +COPY host-x86_64/mingw-check-1/reuse-requirements.txt /tmp/ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt -COPY host-x86_64/mingw-check/check-default-config-profiles.sh /scripts/ -COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/ -COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ +COPY host-x86_64/mingw-check-1/check-default-config-profiles.sh /scripts/ +COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/ +COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/ # Check library crates on all tier 1 targets. # We disable optimized compiler built-ins because that requires a C toolchain for the target. # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs. ENV SCRIPT \ - python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/check-default-config-profiles.sh && \ - python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ - python3 ../x.py check --target=x86_64-pc-windows-gnu --host=x86_64-pc-windows-gnu && \ - python3 ../x.py clippy ci && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ - python3 ../x.py test --stage 0 core alloc std test proc_macro && \ - # Build both public and internal documentation. - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library && \ - mkdir -p /checkout/obj/staging/doc && \ - cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \ - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \ - RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library/test && \ + python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ + python3 ../x.py check --stage 1 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check --stage 1 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ /scripts/validate-toolstate.sh && \ /scripts/validate-error-codes.sh && \ reuse --include-submodules lint && \ diff --git a/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh b/src/ci/docker/host-x86_64/mingw-check-1/check-default-config-profiles.sh index 0c85d4b449d..0c85d4b449d 100755 --- a/src/ci/docker/host-x86_64/mingw-check/check-default-config-profiles.sh +++ b/src/ci/docker/host-x86_64/mingw-check-1/check-default-config-profiles.sh diff --git a/src/ci/docker/host-x86_64/mingw-check/reuse-requirements.in b/src/ci/docker/host-x86_64/mingw-check-1/reuse-requirements.in index d7c2d3fde5b..d7c2d3fde5b 100644 --- a/src/ci/docker/host-x86_64/mingw-check/reuse-requirements.in +++ b/src/ci/docker/host-x86_64/mingw-check-1/reuse-requirements.in diff --git a/src/ci/docker/host-x86_64/mingw-check/reuse-requirements.txt b/src/ci/docker/host-x86_64/mingw-check-1/reuse-requirements.txt index 8784e18864b..8784e18864b 100644 --- a/src/ci/docker/host-x86_64/mingw-check/reuse-requirements.txt +++ b/src/ci/docker/host-x86_64/mingw-check-1/reuse-requirements.txt diff --git a/src/ci/docker/host-x86_64/mingw-check/validate-error-codes.sh b/src/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh index e9aa948eb87..e9aa948eb87 100755 --- a/src/ci/docker/host-x86_64/mingw-check/validate-error-codes.sh +++ b/src/ci/docker/host-x86_64/mingw-check-1/validate-error-codes.sh diff --git a/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh b/src/ci/docker/host-x86_64/mingw-check-1/validate-toolstate.sh index a5691da8cda..a5691da8cda 100755 --- a/src/ci/docker/host-x86_64/mingw-check/validate-toolstate.sh +++ b/src/ci/docker/host-x86_64/mingw-check-1/validate-toolstate.sh diff --git a/src/ci/docker/host-x86_64/mingw-check-2/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-2/Dockerfile new file mode 100644 index 00000000000..0c75f116aa0 --- /dev/null +++ b/src/ci/docker/host-x86_64/mingw-check-2/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + python3-pip \ + python3-pkg-resources \ + git \ + cmake \ + sudo \ + gdb \ + xz-utils \ + libssl-dev \ + pkg-config \ + mingw-w64 \ + && rm -rf /var/lib/apt/lists/* + +ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV SCRIPT \ + python3 ../x.py clippy ci && \ + python3 ../x.py test --stage 1 core alloc std test proc_macro && \ + # Build both public and internal documentation. + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \ + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library && \ + mkdir -p /checkout/obj/staging/doc && \ + cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \ + RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library/test diff --git a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile index d17f7ed7171..006a697af21 100644 --- a/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check-tidy/Dockerfile @@ -34,12 +34,12 @@ COPY host-x86_64/mingw-check-tidy/eslint.version /tmp/ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/ +COPY host-x86_64/mingw-check-1/reuse-requirements.txt /tmp/ RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt \ && pip3 install virtualenv -COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/ -COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ +COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/ +COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/ # NOTE: intentionally uses python2 for x.py so we can test it still works. # validate-toolstate only runs in our CI, so it's ok for it to only support python3. diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile index d8113e06723..1b57ae7c8da 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-nopt/Dockerfile @@ -29,5 +29,5 @@ RUN echo "optimize = false" >> /config/nopt-std-config.toml ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \ --disable-optimize-tests \ --set rust.test-compare-mode -ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ +ENV SCRIPT python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std \ && python3 ../x.py --stage 2 test diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 9eedbd451d8..b44915f8555 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -111,7 +111,9 @@ envs: # These jobs automatically inherit envs.pr, to avoid repeating # it in each job definition. pr: - - name: mingw-check + - name: mingw-check-1 + <<: *job-linux-4c + - name: mingw-check-2 <<: *job-linux-4c - name: mingw-check-tidy continue_on_error: true @@ -125,7 +127,7 @@ pr: env: IMAGE: aarch64-gnu-llvm-19 DOCKER_SCRIPT: stage_2_test_set1.sh - <<: *job-aarch64-linux-8c + <<: *job-aarch64-linux - name: aarch64-gnu-llvm-19-2 env: IMAGE: aarch64-gnu-llvm-19 @@ -281,11 +283,14 @@ auto: env: IMAGE: i686-gnu-nopt DOCKER_SCRIPT: >- - python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std && + python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std && /scripts/stage_2_test_set2.sh <<: *job-linux-4c - - name: mingw-check + - name: mingw-check-1 + <<: *job-linux-4c + + - name: mingw-check-2 <<: *job-linux-4c - name: test-various diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index b1e9eec529e..8b48bd518bd 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -99e7c15e81385b38a8186b51edc4577d5d7b5bdd +c68032fd4c442d275f4daa571ba19c076106b490 diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index a2930b3e427..2793ad43815 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -45,13 +45,13 @@ compiler. ```mermaid graph TD - s0c["stage0 compiler (1.63)"]:::downloaded -->|A| s0l("stage0 std (1.64)"):::with-s0c; + s0c["stage0 compiler (1.86.0-beta.1)"]:::downloaded -->|A| s0l("stage0 std (1.86.0-beta.1)"):::downloaded; s0c & s0l --- stepb[ ]:::empty; - stepb -->|B| s0ca["stage0 compiler artifacts (1.64)"]:::with-s0c; - s0ca -->|copy| s1c["stage1 compiler (1.64)"]:::with-s0c; - s1c -->|C| s1l("stage1 std (1.64)"):::with-s1c; + stepb -->|B| s0ca["stage0 compiler artifacts (1.87.0-dev)"]:::with-s0c; + s0ca -->|copy| s1c["stage1 compiler (1.87.0-dev)"]:::with-s0c; + s1c -->|C| s1l("stage1 std (1.87.0-dev)"):::with-s1c; s1c & s1l --- stepd[ ]:::empty; - stepd -->|D| s1ca["stage1 compiler artifacts (1.64)"]:::with-s1c; + stepd -->|D| s1ca["stage1 compiler artifacts (1.87.0-dev)"]:::with-s1c; s1ca -->|copy| s2c["stage2 compiler"]:::with-s1c; classDef empty width:0px,height:0px; @@ -62,19 +62,21 @@ graph TD ### Stage 0: the pre-compiled compiler -The stage0 compiler is usually the current _beta_ `rustc` compiler and its +The stage0 compiler is by default the very recent _beta_ `rustc` compiler and its associated dynamic libraries, which `./x.py` will download for you. (You can -also configure `./x.py` to use something else.) +also configure `./x.py` to change stage0 to something else.) -The stage0 compiler is then used only to compile [`src/bootstrap`], -[`library/std`], and [`compiler/rustc`]. When assembling the libraries and -binaries that will become the stage1 `rustc` compiler, the freshly compiled -`std` and `rustc` are used. There are two concepts at play here: a compiler -(with its set of dependencies) and its 'target' or 'object' libraries (`std` and -`rustc`). Both are staged, but in a staggered manner. +The precompiled stage0 compiler is then used only to compile [`src/bootstrap`] and [`compiler/rustc`] +with precompiled stage0 std. + +Note that to build the stage1 compiler we use the precompiled stage0 compiler and std. +Therefore, to use a compiler with a std that is freshly built from the tree, you need to +build the stage2 compiler. + +There are two concepts at play here: a compiler (with its set of dependencies) and its +'target' or 'object' libraries (`std` and `rustc`). Both are staged, but in a staggered manner. [`compiler/rustc`]: https://github.com/rust-lang/rust/tree/master/compiler/rustc -[`library/std`]: https://github.com/rust-lang/rust/tree/master/library/std [`src/bootstrap`]: https://github.com/rust-lang/rust/tree/master/src/bootstrap ### Stage 1: from current code, by an earlier compiler @@ -84,16 +86,14 @@ The rustc source code is then compiled with the `stage0` compiler to produce the ### Stage 2: the truly current compiler -We then rebuild our `stage1` compiler with itself to produce the `stage2` +We then rebuild the compiler using `stage1` compiler with in-tree std to produce the `stage2` compiler. -In theory, the `stage1` compiler is functionally identical to the `stage2` -compiler, but in practice there are subtle differences. In particular, the -`stage1` compiler itself was built by `stage0` and hence not by the source in -your working directory. This means that the ABI generated by the `stage0` -compiler may not match the ABI that would have been made by the `stage1` -compiler, which can cause problems for dynamic libraries, tests, and tools using -`rustc_private`. +The `stage1` compiler itself was built by precompiled `stage0` compiler and std +and hence not by the source in your working directory. This means that the ABI +generated by the `stage0` compiler may not match the ABI that would have been made +by the `stage1` compiler, which can cause problems for dynamic libraries, tests +and tools using `rustc_private`. Note that the `proc_macro` crate avoids this issue with a `C` FFI layer called `proc_macro::bridge`, allowing it to be used with `stage1`. @@ -101,9 +101,10 @@ Note that the `proc_macro` crate avoids this issue with a `C` FFI layer called The `stage2` compiler is the one distributed with `rustup` and all other install methods. However, it takes a very long time to build because one must first build the new compiler with an older compiler and then use that to build the new -compiler with itself. For development, you usually only want the `stage1` -compiler, which you can build with `./x build library`. See [Building the -compiler](../how-to-build-and-run.html#building-the-compiler). +compiler with itself. + +For development, you usually only want to use `--stage 1` flag to build things. +See [Building the compiler](../how-to-build-and-run.html#building-the-compiler). ### Stage 3: the same-result test @@ -114,10 +115,11 @@ something has broken. ### Building the stages The script [`./x`] tries to be helpful and pick the stage you most likely meant -for each subcommand. These defaults are as follows: +for each subcommand. Here are some `x` commands with their default stages: -- `check`: `--stage 0` -- `doc`: `--stage 0` +- `check`: `--stage 1` +- `clippy`: `--stage 1` +- `doc`: `--stage 1` - `build`: `--stage 1` - `test`: `--stage 1` - `dist`: `--stage 2` @@ -191,8 +193,8 @@ include, but are not limited to: without building `rustc` from source ('build with `stage0`, then test the artifacts'). If you're working on the standard library, this is normally the test command you want. -- `./x build --stage 0` means to build with the beta `rustc`. -- `./x doc --stage 0` means to document using the beta `rustdoc`. +- `./x build --stage 0` means to build with the stage0 `rustc`. +- `./x doc --stage 0` means to document using the stage0 `rustdoc`. #### Examples of what *not* to do @@ -208,9 +210,6 @@ include, but are not limited to: ### Building vs. running -Note that `build --stage N compiler/rustc` **does not** build the stage N -compiler: instead it builds the stage N+1 compiler _using_ the stage N compiler. - In short, _stage 0 uses the `stage0` compiler to create `stage0` artifacts which will later be uplifted to be the stage1 compiler_. @@ -268,23 +267,6 @@ However, when cross-compiling, `stage1` `std` will only run on the host. So the (See in the table how `stage2` only builds non-host `std` targets). -### Why does only libstd use `cfg(bootstrap)`? - -For docs on `cfg(bootstrap)` itself, see [Complications of -Bootstrapping](#complications-of-bootstrapping). - -The `rustc` generated by the `stage0` compiler is linked to the freshly-built -`std`, which means that for the most part only `std` needs to be `cfg`-gated, so -that `rustc` can use features added to `std` immediately after their addition, -without need for them to get into the downloaded `beta` compiler. - -Note this is different from any other Rust program: `stage1` `rustc` is built by -the _beta_ compiler, but using the _master_ version of `libstd`! - -The only time `rustc` uses `cfg(bootstrap)` is when it adds internal lints that -use diagnostic items, or when it uses unstable library features that were -recently changed. - ### What is a 'sysroot'? When you build a project with `cargo`, the build artifacts for dependencies are @@ -459,7 +441,6 @@ compiler itself uses to run. These aren't actually used by artifacts the new compiler generates. This step also copies the `rustc` and `rustdoc` binaries we generated into `build/$HOST/stage/bin`. -The `stage1/bin/rustc` is a fully functional compiler, but it doesn't yet have -any libraries to link built binaries or libraries to. The next 3 steps will -provide those libraries for it; they are mostly equivalent to constructing the -`stage1/bin` compiler so we don't go through them individually here. +The `stage1/bin/rustc` is a fully functional compiler built with stage0 (precompiled) compiler and std. +To use a compiler built entirely from source with the in-tree compiler and std, you need to build the +stage2 compiler, which is compiled using the stage1 (in-tree) compiler and std. diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index c3c1c41e3f6..c4783002b85 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -217,7 +217,6 @@ probably the best "go to" command for building a local compiler: This may *look* like it only builds the standard library, but that is not the case. What this command does is the following: -- Build `std` using the stage0 compiler - Build `rustc` using the stage0 compiler - This produces the stage1 compiler - Build `std` using the stage1 compiler @@ -241,8 +240,7 @@ build. The **full** `rustc` build (what you get with `./x build --stage 2 compiler/rustc`) has quite a few more steps: - Build `rustc` with the stage1 compiler. - - The resulting compiler here is called the "stage2" compiler. -- Build `std` with stage2 compiler. + - The resulting compiler here is called the "stage2" compiler, which uses stage1 std from the previous command. - Build `librustdoc` and a bunch of other things with the stage2 compiler. You almost never need to do this. @@ -250,14 +248,14 @@ You almost never need to do this. ### Build specific components If you are working on the standard library, you probably don't need to build -the compiler unless you are planning to use a recently added nightly feature. -Instead, you can just build using the bootstrap compiler. +every other default component. Instead, you can build a specific component by +providing its name, like this: ```bash -./x build --stage 0 library +./x build --stage 1 library ``` -If you choose the `library` profile when running `x setup`, you can omit `--stage 0` (it's the +If you choose the `library` profile when running `x setup`, you can omit `--stage 1` (it's the default). ## Creating a rustup toolchain @@ -271,7 +269,6 @@ you will likely need to build at some point; for example, if you want to run the entire test suite). ```bash -rustup toolchain link stage0 build/host/stage0-sysroot # beta compiler + stage0 std rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index 09ffbe8c882..8d323ba9646 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -85,7 +85,7 @@ Look for existing targets to use as examples. After adding your target to the `rustc_target` crate you may want to add `core`, `std`, ... with support for your new target. In that case you will probably need access to some `target_*` cfg. Unfortunately when building with -stage0 (the beta compiler), you'll get an error that the target cfg is +stage0 (a precompiled compiler), you'll get an error that the target cfg is unexpected because stage0 doesn't know about the new target specification and we pass `--check-cfg` in order to tell it to check. diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 1da433d3b68..76c39608449 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -307,51 +307,15 @@ lets you use `cargo fmt`. [the section on vscode]: suggested.md#configuring-rust-analyzer-for-rustc [the section on rustup]: how-to-build-and-run.md?highlight=rustup#creating-a-rustup-toolchain -## Faster builds with `--keep-stage`. - -Sometimes just checking whether the compiler builds is not enough. A common -example is that you need to add a `debug!` statement to inspect the value of -some state or better understand the problem. In that case, you don't really need -a full build. By bypassing bootstrap's cache invalidation, you can often get -these builds to complete very fast (e.g., around 30 seconds). The only catch is -this requires a bit of fudging and may produce compilers that don't work (but -that is easily detected and fixed). - -The sequence of commands you want is as follows: - -- Initial build: `./x build library` - - As [documented previously], this will build a functional stage1 compiler as - part of running all stage0 commands (which include building a `std` - compatible with the stage1 compiler) as well as the first few steps of the - "stage 1 actions" up to "stage1 (sysroot stage1) builds std". -- Subsequent builds: `./x build library --keep-stage 1` - - Note that we added the `--keep-stage 1` flag here - -[documented previously]: ./how-to-build-and-run.md#building-the-compiler - -As mentioned, the effect of `--keep-stage 1` is that we just _assume_ that the -old standard library can be re-used. If you are editing the compiler, this is -almost always true: you haven't changed the standard library, after all. But -sometimes, it's not true: for example, if you are editing the "metadata" part of -the compiler, which controls how the compiler encodes types and other states -into the `rlib` files, or if you are editing things that wind up in the metadata -(such as the definition of the MIR). - -**The TL;DR is that you might get weird behavior from a compile when using -`--keep-stage 1`** -- for example, strange [ICEs](../appendix/glossary.html#ice) -or other panics. In that case, you should simply remove the `--keep-stage 1` -from the command and rebuild. That ought to fix the problem. - -You can also use `--keep-stage 1` when running tests. Something like this: - -- Initial test run: `./x test tests/ui` -- Subsequent test run: `./x test tests/ui --keep-stage 1` - -### Iterating the standard library with `--keep-stage` - -If you are making changes to the standard library, you can use `./x build ---keep-stage 0 library` to iteratively rebuild the standard library without -rebuilding the compiler. +## Faster Builds with CI-rustc + +If you are not working on the compiler, you often don't need to build the compiler tree. +For example, you can skip building the compiler and only build the `library` tree or the +tools under `src/tools`. To achieve that, you have to enable this by setting the `download-rustc` +option in your configuration. This tells bootstrap to use the latest nightly compiler for `stage > 0` +steps, meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler +for `stage > 0` steps. This way, it will never need to build the in-tree compiler. As a result, your +build time will be significantly reduced by not building the in-tree compiler. ## Using incremental compilation diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index edfc870e8df..96e4edc17a5 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -66,9 +66,9 @@ kinds of builds (sets of jobs). ### Pull Request builds After each push to a pull request, a set of `pr` jobs are executed. Currently, -these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `mingw-check` and -`mingw-check-tidy` jobs, all running on Linux. These execute a relatively short -(~30 minutes) and lightweight test suite that should catch common issues. More +these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `mingw-check-1`, `mingw-check-2` +and `mingw-check-tidy` jobs, all running on Linux. These execute a relatively short +(~40 minutes) and lightweight test suite that should catch common issues. More specifically, they run a set of lints, they try to perform a cross-compile check build to Windows mingw (without producing any artifacts) and they test the compiler using a *system* version of LLVM. Unfortunately, it would take too many diff --git a/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md b/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md index e52ad1ce828..42662fbc0a1 100644 --- a/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md +++ b/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md @@ -11,7 +11,7 @@ based on the ABI defined by Fortanix for the [Enclave Development Platform [@jethrogb](https://github.com/jethrogb) [@raoulstrackx](https://github.com/raoulstrackx) -[@mzohreva](https://github.com/mzohreva) +[@aditijannu](https://github.com/aditijannu) Further contacts: diff --git a/src/doc/unstable-book/src/compiler-flags/no-steal-thir.md b/src/doc/unstable-book/src/compiler-flags/no-steal-thir.md new file mode 100644 index 00000000000..d83677d1711 --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/no-steal-thir.md @@ -0,0 +1,7 @@ +# `no-steal-thir` + +By default, to save on memory, the THIR body (obtained from the `tcx.thir_body` query) is stolen +once no longer used. This is inconvenient for authors of rustc drivers who want to access the THIR. + +This option disables the stealing. This has no observable effect on compiler behavior, only on +memory usage. diff --git a/src/etc/test-float-parse/src/lib.rs b/src/etc/test-float-parse/src/lib.rs index 0bd4878f9a6..f590149523b 100644 --- a/src/etc/test-float-parse/src/lib.rs +++ b/src/etc/test-float-parse/src/lib.rs @@ -119,6 +119,7 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> { // Register normal generators for all floats. + #[cfg(not(bootstrap))] #[cfg(target_has_reliable_f16)] register_float::<f16>(&mut tests, cfg); register_float::<f32>(&mut tests, cfg); diff --git a/src/etc/test-float-parse/src/traits.rs b/src/etc/test-float-parse/src/traits.rs index 65a8721bfa5..16484f8fe2c 100644 --- a/src/etc/test-float-parse/src/traits.rs +++ b/src/etc/test-float-parse/src/traits.rs @@ -170,6 +170,7 @@ macro_rules! impl_float { impl_float!(f32, u32; f64, u64); +#[cfg(not(bootstrap))] #[cfg(target_has_reliable_f16)] impl_float!(f16, u16); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7e8e087c3a2..e8cf25b1906 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1606,7 +1606,7 @@ fn first_non_private<'tcx>( && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id) && let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind { - for res in &path.res { + for res in path.res.present_items() { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { continue; } @@ -3014,7 +3014,7 @@ fn clean_use_statement<'tcx>( ) -> Vec<Item> { let mut items = Vec::new(); let hir::UsePath { segments, ref res, span } = *path; - for &res in res { + for res in res.present_items() { let path = hir::Path { segments, res, span }; items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names)); } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 254549e72c6..5b52e785b8f 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -440,7 +440,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { hir::ItemKind::GlobalAsm { .. } => {} hir::ItemKind::Use(_, hir::UseKind::ListStem) => {} hir::ItemKind::Use(path, kind) => { - for &res in &path.res { + for res in path.res.present_items() { // Struct and variant constructors and proc macro stubs always show up alongside // their definitions, we've already processed them so just discard these. if should_ignore_res(res) { diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index f69e5bee4bb..3a76c61489e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -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.13", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] } # UI test dependencies if_chain = "1.0" diff --git a/src/tools/clippy/book/src/development/trait_checking.md b/src/tools/clippy/book/src/development/trait_checking.md index cc4eb966f59..6d01496eebe 100644 --- a/src/tools/clippy/book/src/development/trait_checking.md +++ b/src/tools/clippy/book/src/development/trait_checking.md @@ -17,7 +17,7 @@ providing the `LateContext` (`cx`), our expression at hand, and the symbol of the trait in question: ```rust -use clippy_utils::is_trait_method; +use clippy_utils::ty::implements_trait; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_span::symbol::sym; @@ -25,7 +25,7 @@ use rustc_span::symbol::sym; impl LateLintPass<'_> for CheckIteratorTraitLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]) + implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[]) }); if implements_iterator { // [...] diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 9809e32de8a..7c850b4b023 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -1026,7 +1026,7 @@ The order of associated items in traits. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference. -**Default Value:** `target_pointer_width * 2` +**Default Value:** `target_pointer_width` --- **Affected lints:** diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 4ce8d001c2f..87158cec42b 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -828,7 +828,7 @@ define_Conf! { trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(), /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by /// reference. - #[default_text = "target_pointer_width * 2"] + #[default_text = "target_pointer_width"] #[lints(trivially_copy_pass_by_ref)] trivial_copy_size_limit: Option<u64> = None, /// The maximum complexity a type can have diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 13d6b1285dc..c1b6b370706 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,5 +1,6 @@ use crate::utils::{ - ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output, + ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads, + walk_dir_no_dot_or_target, }; use itertools::Itertools; use rustc_lexer::{TokenKind, tokenize}; @@ -9,7 +10,6 @@ use std::io::{self, Read}; use std::ops::ControlFlow; use std::path::PathBuf; use std::process::{self, Command, Stdio}; -use walkdir::WalkDir; pub enum Error { Io(io::Error), @@ -260,7 +260,7 @@ fn fmt_syms(update_mode: UpdateMode) { ); } -fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { +fn run_rustfmt(update_mode: UpdateMode) { let mut rustfmt_path = String::from_utf8(run_with_output( "rustup which rustfmt", Command::new("rustup").args(["which", "rustfmt"]), @@ -268,42 +268,19 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { .expect("invalid rustfmt path"); rustfmt_path.truncate(rustfmt_path.trim_end().len()); - let mut cargo_path = String::from_utf8(run_with_output( - "rustup which cargo", - Command::new("rustup").args(["which", "cargo"]), - )) - .expect("invalid cargo path"); - cargo_path.truncate(cargo_path.trim_end().len()); - - // Start all format jobs first before waiting on the results. - let mut children = Vec::with_capacity(16); - for &path in &[ - ".", - "clippy_config", - "clippy_dev", - "clippy_lints", - "clippy_lints_internal", - "clippy_utils", - "rustc_tools_util", - "lintcheck", - ] { - let mut cmd = Command::new(&cargo_path); - cmd.current_dir(clippy.path.join(path)) - .args(["fmt"]) - .env("RUSTFMT", &rustfmt_path) - .stdout(Stdio::null()) - .stdin(Stdio::null()) - .stderr(Stdio::piped()); - if update_mode.is_check() { - cmd.arg("--check"); - } - match cmd.spawn() { - Ok(x) => children.push(("cargo fmt", x)), - Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()), - } - } + let args: Vec<_> = walk_dir_no_dot_or_target() + .filter_map(|e| { + let e = expect_action(e, ErrAction::Read, "."); + e.path() + .as_os_str() + .as_encoded_bytes() + .ends_with(b".rs") + .then(|| e.into_path().into_os_string()) + }) + .collect(); - run_with_args_split( + let mut children: Vec<_> = split_args_for_threads( + 32, || { let mut cmd = Command::new(&rustfmt_path); if update_mode.is_check() { @@ -312,66 +289,44 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) { cmd.stdout(Stdio::null()) .stdin(Stdio::null()) .stderr(Stdio::piped()) - .args(["--config", "show_parse_errors=false"]); + .args(["--unstable-features", "--skip-children"]); cmd }, - |cmd| match cmd.spawn() { - Ok(x) => children.push(("rustfmt", x)), - Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()), - }, - WalkDir::new("tests") - .into_iter() - .filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt")) - .filter_map(|e| { - let e = e.expect("error reading `tests`"); - e.path() - .as_os_str() - .as_encoded_bytes() - .ends_with(b".rs") - .then(|| e.into_path().into_os_string()) - }), - ); + args.iter(), + ) + .map(|mut cmd| expect_action(cmd.spawn(), ErrAction::Run, "rustfmt")) + .collect(); - for (name, child) in &mut children { - match child.wait() { - Ok(status) => match (update_mode, status.exit_ok()) { - (UpdateMode::Check | UpdateMode::Change, Ok(())) => {}, - (UpdateMode::Check, Err(_)) => { - let mut s = String::new(); - if let Some(mut stderr) = child.stderr.take() - && stderr.read_to_string(&mut s).is_ok() - { - eprintln!("{s}"); - } - eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); - process::exit(1); - }, - (UpdateMode::Change, Err(e)) => { - let mut s = String::new(); - if let Some(mut stderr) = child.stderr.take() - && stderr.read_to_string(&mut s).is_ok() - { - eprintln!("{s}"); - } - panic_action(&e, ErrAction::Run, name.as_ref()); - }, + for child in &mut children { + let status = expect_action(child.wait(), ErrAction::Run, "rustfmt"); + match (update_mode, status.exit_ok()) { + (UpdateMode::Check | UpdateMode::Change, Ok(())) => {}, + (UpdateMode::Check, Err(_)) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update."); + process::exit(1); + }, + (UpdateMode::Change, e) => { + let mut s = String::new(); + if let Some(mut stderr) = child.stderr.take() + && stderr.read_to_string(&mut s).is_ok() + { + eprintln!("{s}"); + } + expect_action(e, ErrAction::Run, "rustfmt"); }, - Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()), } } } // the "main" function of cargo dev fmt -pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) { - if clippy.has_intellij_hook { - eprintln!( - "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\ - Not formatting because that would format the local repo as well!\n\ - Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`." - ); - return; - } - run_rustfmt(clippy, update_mode); +pub fn run(update_mode: UpdateMode) { + run_rustfmt(update_mode); fmt_syms(update_mode); if let Err(e) = fmt_conf(update_mode.is_check()) { e.display(); diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index ebcd8611d78..26aa269fb63 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -26,7 +26,7 @@ fn main() { allow_staged, allow_no_vcs, } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs), - DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)), + DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)), DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)), DevCommand::NewLint { pass, diff --git a/src/tools/clippy/clippy_dev/src/rename_lint.rs b/src/tools/clippy/clippy_dev/src/rename_lint.rs index be8b27c7a9e..d62597428e2 100644 --- a/src/tools/clippy/clippy_dev/src/rename_lint.rs +++ b/src/tools/clippy/clippy_dev/src/rename_lint.rs @@ -1,13 +1,12 @@ use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints}; use crate::utils::{ - FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, - try_rename_dir, try_rename_file, + ErrAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, + delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; use rustc_lexer::TokenKind; use std::ffi::OsString; use std::fs; use std::path::Path; -use walkdir::WalkDir; /// Runs the `rename_lint` command. /// @@ -133,17 +132,10 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b } let mut update_fn = file_update_fn(old_name, new_name, mod_edit); - for file in WalkDir::new(".").into_iter().filter_entry(|e| { - // Skip traversing some of the larger directories. - e.path() - .as_os_str() - .as_encoded_bytes() - .get(2..) - .is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes()) - }) { - let file = file.expect("error reading clippy directory"); - if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { - updater.update_file(file.path(), &mut update_fn); + for e in walk_dir_no_dot_or_target() { + let e = expect_action(e, ErrAction::Read, "."); + if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") { + updater.update_file(e.path(), &mut update_fn); } } generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 25ba2c72049..320462a2c96 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,5 +1,5 @@ use crate::utils::{ - ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn, + ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn, }; use itertools::Itertools; use std::collections::HashSet; @@ -201,10 +201,7 @@ pub fn find_lint_decls() -> Vec<Lint> { /// Reads the source files from the given root directory fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> { WalkDir::new(src_root).into_iter().filter_map(move |e| { - let e = match e { - Ok(e) => e, - Err(ref e) => panic_action(e, ErrAction::Read, src_root), - }; + let e = expect_action(e, ErrAction::Read, src_root); let path = e.path().as_os_str().as_encoded_bytes(); if let Some(path) = path.strip_suffix(b".rs") && let Some(path) = path.get("clippy_lints/src/".len()..) diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs index 255e36afe69..c4808b7048b 100644 --- a/src/tools/clippy/clippy_dev/src/utils.rs +++ b/src/tools/clippy/clippy_dev/src/utils.rs @@ -1,14 +1,16 @@ use core::fmt::{self, Display}; +use core::num::NonZero; use core::ops::Range; use core::slice; use core::str::FromStr; use rustc_lexer::{self as lexer, FrontmatterAllowed}; -use std::env; use std::ffi::OsStr; use std::fs::{self, OpenOptions}; use std::io::{self, Read as _, Seek as _, SeekFrom, Write}; use std::path::{Path, PathBuf}; use std::process::{self, Command, ExitStatus, Stdio}; +use std::{env, thread}; +use walkdir::WalkDir; #[cfg(not(windows))] static CARGO_CLIPPY_EXE: &str = "cargo-clippy"; @@ -45,6 +47,14 @@ pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! { panic!("error {} `{}`: {}", action.as_str(), path.display(), *err) } +#[track_caller] +pub fn expect_action<T>(res: Result<T, impl Display>, action: ErrAction, path: impl AsRef<Path>) -> T { + match res { + Ok(x) => x, + Err(ref e) => panic_action(e, action, path.as_ref()), + } +} + /// Wrapper around `std::fs::File` which panics with a path on failure. pub struct File<'a> { pub inner: fs::File, @@ -55,9 +65,9 @@ impl<'a> File<'a> { #[track_caller] pub fn open(path: &'a (impl AsRef<Path> + ?Sized), options: &mut OpenOptions) -> Self { let path = path.as_ref(); - match options.open(path) { - Ok(inner) => Self { inner, path }, - Err(e) => panic_action(&e, ErrAction::Open, path), + Self { + inner: expect_action(options.open(path), ErrAction::Open, path), + path, } } @@ -84,10 +94,7 @@ impl<'a> File<'a> { /// Read the entire contents of a file to the given buffer. #[track_caller] pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String { - match self.inner.read_to_string(dst) { - Ok(_) => {}, - Err(e) => panic_action(&e, ErrAction::Read, self.path), - } + expect_action(self.inner.read_to_string(dst), ErrAction::Read, self.path); dst } @@ -107,9 +114,7 @@ impl<'a> File<'a> { }, Err(e) => Err(e), }; - if let Err(e) = res { - panic_action(&e, ErrAction::Write, self.path); - } + expect_action(res, ErrAction::Write, self.path); } } @@ -660,47 +665,91 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool { } pub fn write_file(path: &Path, contents: &str) { - fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path)); + expect_action(fs::write(path, contents), ErrAction::Write, path); } #[must_use] pub fn run_with_output(path: &(impl AsRef<Path> + ?Sized), cmd: &mut Command) -> Vec<u8> { fn f(path: &Path, cmd: &mut Command) -> Vec<u8> { - match cmd - .stdin(Stdio::null()) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .output() - { - Ok(x) => match x.status.exit_ok() { - Ok(()) => x.stdout, - Err(ref e) => panic_action(e, ErrAction::Run, path), - }, - Err(ref e) => panic_action(e, ErrAction::Run, path), - } + let output = expect_action( + cmd.stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .output(), + ErrAction::Run, + path, + ); + expect_action(output.status.exit_ok(), ErrAction::Run, path); + output.stdout } f(path.as_ref(), cmd) } -pub fn run_with_args_split( - mut make_cmd: impl FnMut() -> Command, - mut run_cmd: impl FnMut(&mut Command), - args: impl Iterator<Item: AsRef<OsStr>>, -) { - let mut cmd = make_cmd(); - let mut len = 0; - for arg in args { - len += arg.as_ref().len(); - cmd.arg(arg); - // Very conservative limit - if len > 10000 { - run_cmd(&mut cmd); - cmd = make_cmd(); - len = 0; +/// Splits an argument list across multiple `Command` invocations. +/// +/// The argument list will be split into a number of batches based on +/// `thread::available_parallelism`, with `min_batch_size` setting a lower bound on the size of each +/// batch. +/// +/// If the size of the arguments would exceed the system limit additional batches will be created. +pub fn split_args_for_threads( + min_batch_size: usize, + make_cmd: impl FnMut() -> Command, + args: impl ExactSizeIterator<Item: AsRef<OsStr>>, +) -> impl Iterator<Item = Command> { + struct Iter<F, I> { + make_cmd: F, + args: I, + min_batch_size: usize, + batch_size: usize, + thread_count: usize, + } + impl<F, I> Iterator for Iter<F, I> + where + F: FnMut() -> Command, + I: ExactSizeIterator<Item: AsRef<OsStr>>, + { + type Item = Command; + fn next(&mut self) -> Option<Self::Item> { + if self.thread_count > 1 { + self.thread_count -= 1; + } + let mut cmd = (self.make_cmd)(); + let mut cmd_len = 0usize; + for arg in self.args.by_ref().take(self.batch_size) { + cmd.arg(arg.as_ref()); + // `+ 8` to account for the `argv` pointer on unix. + // Windows is complicated since the arguments are first converted to UTF-16ish, + // but this needs to account for the space between arguments and whatever additional + // is needed to escape within an argument. + cmd_len += arg.as_ref().len() + 8; + cmd_len += 8; + + // Windows has a command length limit of 32767. For unix systems this is more + // complicated since the limit includes environment variables and room needs to be + // left to edit them once the program starts, but the total size comes from + // `getconf ARG_MAX`. + // + // For simplicity we use 30000 here under a few assumptions. + // * Individual arguments aren't super long (the final argument is still added) + // * `ARG_MAX` is set to a reasonable amount. Basically every system will be configured way above + // what windows supports, but POSIX only requires `4096`. + if cmd_len > 30000 { + self.batch_size = self.args.len().div_ceil(self.thread_count).max(self.min_batch_size); + break; + } + } + (cmd_len != 0).then_some(cmd) } } - if len != 0 { - run_cmd(&mut cmd); + let thread_count = thread::available_parallelism().map_or(1, NonZero::get); + let batch_size = args.len().div_ceil(thread_count).max(min_batch_size); + Iter { + make_cmd, + args, + min_batch_size, + batch_size, + thread_count, } } @@ -720,3 +769,12 @@ pub fn delete_dir_if_exists(path: &Path) { Err(ref e) => panic_action(e, ErrAction::Delete, path), } } + +/// Walks all items excluding top-level dot files/directories and any target directories. +pub fn walk_dir_no_dot_or_target() -> impl Iterator<Item = ::walkdir::Result<::walkdir::DirEntry>> { + WalkDir::new(".").into_iter().filter_entry(|e| { + e.path() + .file_name() + .is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.')) + }) +} diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 06376c57119..152516baf73 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node}; +use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext, sym}; @@ -60,6 +60,8 @@ impl LateLintPass<'_> for DbgMacro { if cur_syntax_ctxt != self.prev_ctxt && let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) && !macro_call.span.in_external_macro(cx.sess().source_map()) && + // avoids exprs generated by the desugaring of coroutines + !is_coroutine_desugar(expr) && self.checked_dbg_call_site.insert(macro_call.span) && // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id)) @@ -73,50 +75,51 @@ impl LateLintPass<'_> for DbgMacro { "the `dbg!` macro is intended as a debugging tool", |diag| { let mut applicability = Applicability::MachineApplicable; - - let (sugg_span, suggestion) = match expr.peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - // dbg!(1) - ExprKind::Match(val, ..) => ( - macro_call.span, - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string(), - ), - // dbg!(2, 3) - ExprKind::Tup( - [ - Expr { - kind: ExprKind::Match(first, ..), - .. - }, - .., - Expr { - kind: ExprKind::Match(last, ..), - .. - }, - ], - ) => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - (macro_call.span, format!("({snippet})")) - }, - _ => unreachable!(), - }; + let (sugg_span, suggestion) = + match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = + cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. + }, + .., + Expr { + kind: ExprKind::Match(last, ..), + .. + }, + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) + }, + _ => unreachable!(), + }; diag.span_suggestion( sugg_span, @@ -134,6 +137,35 @@ impl LateLintPass<'_> for DbgMacro { } } +fn is_coroutine_desugar(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Closure(Closure { + kind: ClosureKind::Coroutine(CoroutineKind::Desugared(..)) | ClosureKind::CoroutineClosure(..), + .. + }) + ) +} + +fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Block(block, _) = expr.kind + && let [ + Stmt { + kind: + StmtKind::Let(LetStmt { + source: LocalSource::AsyncFn, + .. + }), + .. + }, + ] = block.stmts + { + return block.expr; + } + + None +} + fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index a22a2ee66d2..cde9528cd87 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -305,7 +305,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { RefOp::Method { mutbl, is_ufcs } if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) // Allow explicit deref in method chains. e.g. `foo.deref().bar()` - && (is_ufcs || !in_postfix_position(cx, expr)) => + && (is_ufcs || !is_in_method_chain(cx, expr)) => { let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr))); self.state = Some(( @@ -728,7 +728,13 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { } } -fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { +fn is_in_method_chain<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + if let ExprKind::MethodCall(_, recv, _, _) = e.kind + && matches!(recv.kind, ExprKind::MethodCall(..)) + { + return true; + } + if let Some(parent) = get_parent_expr(cx, e) && parent.span.eq_ctxt(e.span) { @@ -986,6 +992,15 @@ fn report<'tcx>( ); }, State::DerefedBorrow(state) => { + // Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context, + // as this may make rustc trigger its `dangerous_implicit_autorefs` lint. + if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind + && let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind + && cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr() + { + return; + } + let mut app = Applicability::MachineApplicable; let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index d0b2f0c8407..821bb25d2ce 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -106,8 +106,8 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind { - for res in &path.res { - self.check_res_emit(cx, res, item.span); + if let Some(res) = path.res.type_ns { + self.check_res_emit(cx, &res, item.span); } } } diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs index 8aeb835fe39..cb9d68224db 100644 --- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -2,11 +2,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_lint::LateContext; -use rustc_span::{BytePos, Span}; -use std::cmp::Ordering; +use rustc_span::BytePos; use std::ops::Range; -use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS}; +use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS, Fragments}; fn map_container_to_text(c: &super::Container) -> &'static str { match c { @@ -19,29 +18,27 @@ fn map_container_to_text(c: &super::Container) -> &'static str { pub(super) fn check( cx: &LateContext<'_>, doc: &str, - range: Range<usize>, - mut span: Span, + cooked_range: Range<usize>, + fragments: &Fragments<'_>, containers: &[super::Container], ) { - if doc[range.clone()].contains('\t') { - // We don't do tab stops correctly. - return; - } - - // Blockquote - let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); + // Get blockquotes + let ccount = doc[cooked_range.clone()].chars().filter(|c| *c == '>').count(); let blockquote_level = containers .iter() .filter(|c| matches!(c, super::Container::Blockquote)) .count(); - if ccount < blockquote_level { + + if ccount < blockquote_level + && let Some(mut span) = fragments.span(cx, cooked_range.clone()) + { span_lint_and_then( cx, DOC_LAZY_CONTINUATION, span, "doc quote line without `>` marker", |diag| { - let mut doc_start_range = &doc[range]; + let mut doc_start_range = &doc[cooked_range]; let mut suggested = String::new(); for c in containers { let text = map_container_to_text(c); @@ -78,7 +75,7 @@ pub(super) fn check( } // List - let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count(); + let leading_spaces = doc[cooked_range.clone()].chars().filter(|c| *c == ' ').count(); let list_indentation = containers .iter() .map(|c| { @@ -89,36 +86,41 @@ pub(super) fn check( } }) .sum(); - match leading_spaces.cmp(&list_indentation) { - Ordering::Less => span_lint_and_then( - cx, - DOC_LAZY_CONTINUATION, - span, - "doc list item without indentation", - |diag| { - // simpler suggestion style for indentation - let indent = list_indentation - leading_spaces; - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "indent this line", - std::iter::repeat_n(" ", indent).join(""), - Applicability::MaybeIncorrect, - ); - diag.help("if this is supposed to be its own paragraph, add a blank line"); - }, - ), - Ordering::Greater => { - let sugg = std::iter::repeat_n(" ", list_indentation).join(""); - span_lint_and_sugg( + + if leading_spaces != list_indentation + && let Some(span) = fragments.span(cx, cooked_range.clone()) + { + if leading_spaces < list_indentation { + span_lint_and_then( cx, - DOC_OVERINDENTED_LIST_ITEMS, + DOC_LAZY_CONTINUATION, span, - "doc list item overindented", - format!("try using `{sugg}` ({list_indentation} spaces)"), - sugg, - Applicability::MaybeIncorrect, + "doc list item without indentation", + |diag| { + // simpler suggestion style for indentation + let indent = list_indentation - leading_spaces; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "indent this line", + std::iter::repeat_n(" ", indent).join(""), + Applicability::MaybeIncorrect, + ); + diag.help("if this is supposed to be its own paragraph, add a blank line"); + }, ); - }, - Ordering::Equal => {}, + + return; + } + + let sugg = std::iter::repeat_n(" ", list_indentation).join(""); + span_lint_and_sugg( + cx, + DOC_OVERINDENTED_LIST_ITEMS, + span, + "doc list item overindented", + format!("try using `{sugg}` ({list_indentation} spaces)"), + sugg, + Applicability::MaybeIncorrect, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs index 7a1c7c675d2..69c3b9150c3 100644 --- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs +++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs @@ -6,13 +6,15 @@ use rustc_lint::LateContext; use rustc_span::{BytePos, Pos, Span}; use url::Url; -use crate::doc::DOC_MARKDOWN; +use crate::doc::{DOC_MARKDOWN, Fragments}; +use std::ops::Range; pub fn check( cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, - span: Span, + fragments: &Fragments<'_>, + fragment_range: Range<usize>, code_level: isize, blockquote_level: isize, ) { @@ -64,20 +66,31 @@ pub fn check( close_parens += 1; } + // We'll use this offset to calculate the span to lint. + let fragment_offset = word.as_ptr() as usize - text.as_ptr() as usize; + // Adjust for the current word - let offset = word.as_ptr() as usize - text.as_ptr() as usize; - let span = Span::new( - span.lo() + BytePos::from_usize(offset), - span.lo() + BytePos::from_usize(offset + word.len()), - span.ctxt(), - span.parent(), + check_word( + cx, + word, + fragments, + &fragment_range, + fragment_offset, + code_level, + blockquote_level, ); - - check_word(cx, word, span, code_level, blockquote_level); } } -fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) { +fn check_word( + cx: &LateContext<'_>, + word: &str, + fragments: &Fragments<'_>, + range: &Range<usize>, + fragment_offset: usize, + code_level: isize, + blockquote_level: isize, +) { /// Checks if a string is upper-camel-case, i.e., starts with an uppercase and /// contains at least two uppercase letters (`Clippy` is ok) and one lower-case /// letter (`NASA` is ok). @@ -117,6 +130,16 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b // try to get around the fact that `foo::bar` parses as a valid URL && !url.cannot_be_a_base() { + let Some(fragment_span) = fragments.span(cx, range.clone()) else { + return; + }; + let span = Span::new( + fragment_span.lo() + BytePos::from_usize(fragment_offset), + fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()), + fragment_span.ctxt(), + fragment_span.parent(), + ); + span_lint_and_sugg( cx, DOC_MARKDOWN, @@ -137,6 +160,17 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b } if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") { + let Some(fragment_span) = fragments.span(cx, range.clone()) else { + return; + }; + + let span = Span::new( + fragment_span.lo() + BytePos::from_usize(fragment_offset), + fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()), + fragment_span.ctxt(), + fragment_span.parent(), + ); + span_lint_and_then( cx, DOC_MARKDOWN, diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 87da380e954..c46dd09d60c 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -3,7 +3,6 @@ use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then}; -use clippy_utils::source::snippet_opt; use clippy_utils::{is_entrypoint_fn, is_trait_impl_item}; use pulldown_cmark::Event::{ Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, @@ -730,7 +729,10 @@ struct Fragments<'a> { } impl Fragments<'_> { - fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> { + /// get the span for the markdown range. Note that this function is not cheap, use it with + /// caution. + #[must_use] + fn span(&self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> { source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments) } } @@ -1068,9 +1070,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize ); } else { for (text, range, assoc_code_level) in text_to_check { - if let Some(span) = fragments.span(cx, range) { - markdown::check(cx, valid_idents, &text, span, assoc_code_level, blockquote_level); - } + markdown::check(cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level); } } text_to_check = Vec::new(); @@ -1081,26 +1081,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize | TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (), SoftBreak | HardBreak => { if !containers.is_empty() - && let Some((next_event, next_range)) = events.peek() - && let Some(next_span) = fragments.span(cx, next_range.clone()) - && let Some(span) = fragments.span(cx, range.clone()) && !in_footnote_definition + // Tabs aren't handled correctly vvvv + && !doc[range.clone()].contains('\t') + && let Some((next_event, next_range)) = events.peek() && !matches!(next_event, End(_)) { lazy_continuation::check( cx, doc, range.end..next_range.start, - Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()), + &fragments, &containers[..], ); } - if let Some(span) = fragments.span(cx, range.clone()) + + if event == HardBreak + && !doc[range.clone()].trim().starts_with('\\') + && let Some(span) = fragments.span(cx, range.clone()) && !span.from_expansion() - && let Some(snippet) = snippet_opt(cx, span) - && !snippet.trim().starts_with('\\') - && event == HardBreak { + { collected_breaks.push(span); } }, diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 01b49403cac..b3c63f022d3 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -51,7 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { // so lint on the `use` statement directly. if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind && !item.span.in_external_macro(cx.sess().source_map()) - && let Some(def_id) = path.res[0].opt_def_id() + // use `present_items` because it could be in either type_ns or value_ns + && let Some(res) = path.res.present_items().next() + && let Some(def_id) = res.opt_def_id() && self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS) { let module = if is_integer_module(cx, def_id) { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index 35737f3eafe..f99989ec6ba 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -83,6 +83,13 @@ pub(super) fn check<'tcx>( )[..], ); } + + // If the return type requires adjustments, we need to add a `.map` after the iterator + let inner_ret_adjust = cx.typeck_results().expr_adjustments(inner_ret); + if !inner_ret_adjust.is_empty() { + snippet.push_str(".map(|v| v as _)"); + } + // Extends to `last_stmt` to include semicolon in case of `return None;` let lint_span = span.to(last_stmt.span).to(last_ret.span); span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 9b6f97b9a2e..81f14b7b2b0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -3,7 +3,7 @@ use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::visitors::is_local_used; -use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; +use clippy_utils::{higher, is_refutable, path_to_local_id, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, Pat, PatKind}; @@ -28,7 +28,7 @@ pub(super) fn check<'tcx>( && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind && path_to_local_id(let_expr, pat_hir_id) // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` - && let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind + && let PatKind::TupleStruct(ref qpath, [inner_pat], _) = let_pat.kind && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) && let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id) @@ -37,6 +37,7 @@ pub(super) fn check<'tcx>( // Ensure expr in `if let` is not used afterwards && !is_local_used(cx, if_then, pat_hir_id) && msrv.meets(cx, msrvs::ITER_FLATTEN) + && !is_refutable(cx, inner_pat) { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index bd04827a1f0..845edb9cae1 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -1,68 +1,65 @@ use super::WHILE_LET_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet, snippet_indent, snippet_opt}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop; +use clippy_utils::{higher, peel_blocks}; +use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind, Path, QPath, StmtKind, Ty}; use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { - let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { - ([stmt, stmts @ ..], expr) => { - if let StmtKind::Let(&LetStmt { + let (init, let_info) = match (loop_block.stmts, loop_block.expr) { + ([stmt, ..], _) => match stmt.kind { + StmtKind::Let(LetStmt { init: Some(e), els: None, + pat, + ty, .. - }) - | StmtKind::Semi(e) - | StmtKind::Expr(e) = stmt.kind - { - (e, !stmts.is_empty() || expr.is_some()) - } else { - return; - } + }) => (*e, Some((*pat, *ty))), + StmtKind::Semi(e) | StmtKind::Expr(e) => (e, None), + _ => return, }, - ([], Some(e)) => (e, false), + ([], Some(e)) => (e, None), _ => return, }; + let has_trailing_exprs = loop_block.stmts.len() + usize::from(loop_block.expr.is_some()) > 1; if let Some(if_let) = higher::IfLet::hir(cx, init) && let Some(else_expr) = if_let.if_else && is_simple_break_expr(else_expr) { - could_be_while_let(cx, expr, if_let.let_pat, if_let.let_expr, has_trailing_exprs); + could_be_while_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + has_trailing_exprs, + let_info, + if_let.if_then, + ); } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind && arm1.guard.is_none() && arm2.guard.is_none() && is_simple_break_expr(arm2.body) { - could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs); + could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs, let_info, arm1.body); } } -/// Returns `true` if expr contains a single break expression without a label or eub-expression. +/// Returns `true` if expr contains a single break expression without a label or sub-expression, +/// possibly embedded in blocks. fn is_simple_break_expr(e: &Expr<'_>) -> bool { - matches!(peel_blocks(e).kind, ExprKind::Break(dest, None) if dest.label.is_none()) -} - -/// Removes any blocks containing only a single expression. -fn peel_blocks<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { if let ExprKind::Block(b, _) = e.kind { match (b.stmts, b.expr) { - ([s], None) => { - if let StmtKind::Expr(e) | StmtKind::Semi(e) = s.kind { - peel_blocks(e) - } else { - e - } - }, - ([], Some(e)) => peel_blocks(e), - _ => e, + ([s], None) => matches!(s.kind, StmtKind::Expr(e) | StmtKind::Semi(e) if is_simple_break_expr(e)), + ([], Some(e)) => is_simple_break_expr(e), + _ => false, } } else { - e + matches!(e.kind, ExprKind::Break(dest, None) if dest.label.is_none()) } } @@ -72,6 +69,8 @@ fn could_be_while_let<'tcx>( let_pat: &'tcx Pat<'_>, let_expr: &'tcx Expr<'_>, has_trailing_exprs: bool, + let_info: Option<(&Pat<'_>, Option<&Ty<'_>>)>, + inner_expr: &Expr<'_>, ) { if has_trailing_exprs && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr)) @@ -86,7 +85,24 @@ fn could_be_while_let<'tcx>( // 1) it was ugly with big bodies; // 2) it was not indented properly; // 3) it wasn’t very smart (see #675). - let mut applicability = Applicability::HasPlaceholders; + let inner_content = if let Some((pat, ty)) = let_info + // Prevent trivial reassignments such as `let x = x;` or `let _ = …;`, but + // keep them if the type has been explicitly specified. + && (!is_trivial_assignment(pat, peel_blocks(inner_expr)) || ty.is_some()) + && let Some(pat_str) = snippet_opt(cx, pat.span) + && let Some(init_str) = snippet_opt(cx, peel_blocks(inner_expr).span) + { + let ty_str = ty + .map(|ty| format!(": {}", snippet(cx, ty.span, "_"))) + .unwrap_or_default(); + format!( + "\n{indent} let {pat_str}{ty_str} = {init_str};\n{indent} ..\n{indent}", + indent = snippet_indent(cx, expr.span).unwrap_or_default(), + ) + } else { + " .. ".into() + }; + span_lint_and_sugg( cx, WHILE_LET_LOOP, @@ -94,10 +110,21 @@ fn could_be_while_let<'tcx>( "this loop could be written as a `while let` loop", "try", format!( - "while let {} = {} {{ .. }}", - snippet_with_applicability(cx, let_pat.span, "..", &mut applicability), - snippet_with_applicability(cx, let_expr.span, "..", &mut applicability), + "while let {} = {} {{{inner_content}}}", + snippet(cx, let_pat.span, ".."), + snippet(cx, let_expr.span, ".."), ), - applicability, + Applicability::HasPlaceholders, ); } + +fn is_trivial_assignment(pat: &Pat<'_>, init: &Expr<'_>) -> bool { + match (pat.kind, init.kind) { + (PatKind::Wild, _) => true, + ( + PatKind::Binding(BindingMode::NONE, _, pat_ident, None), + ExprKind::Path(QPath::Resolved(None, Path { segments: [init], .. })), + ) => pat_ident.name == init.ident.name, + _ => false, + } +} diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 98ad1f6a160..c1a26c5a9c7 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -100,10 +100,7 @@ impl LateLintPass<'_> for MacroUseImports { && let hir_id = item.hir_id() && let attrs = cx.tcx.hir_attrs(hir_id) && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)) - && let Some(id) = path.res.iter().find_map(|res| match res { - Res::Def(DefKind::Mod, id) => Some(id), - _ => None, - }) + && let Some(Res::Def(DefKind::Mod, id)) = path.res.type_ns && !id.is_local() { for kid in cx.tcx.module_children(id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index 40aad03960c..4a61c223d2c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,18 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::{Span, sym}; +use rustc_middle::ty; +use rustc_span::{BytePos, Span, sym}; use super::MANUAL_IS_VARIANT_AND; -pub(super) fn check<'tcx>( +pub(super) fn check( cx: &LateContext<'_>, - expr: &'tcx rustc_hir::Expr<'_>, - map_recv: &'tcx rustc_hir::Expr<'_>, - map_arg: &'tcx rustc_hir::Expr<'_>, + expr: &Expr<'_>, + map_recv: &Expr<'_>, + map_arg: &Expr<'_>, map_span: Span, msrv: Msrv, ) { @@ -57,3 +61,57 @@ pub(super) fn check<'tcx>( Applicability::MachineApplicable, ); } + +fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) { + if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo())) + && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3))) + { + span_lint_and_sugg( + cx, + MANUAL_IS_VARIANT_AND, + parent.span, + format!( + "called `.map() {}= {}()`", + if op == BinOpKind::Eq { '=' } else { '!' }, + if is_option { "Some" } else { "Ok" }, + ), + "use", + if is_option && op == BinOpKind::Ne { + format!("{before_map_snippet}is_none_or{after_map_snippet}",) + } else { + format!( + "{}{before_map_snippet}{}{after_map_snippet}", + if op == BinOpKind::Eq { "" } else { "!" }, + if is_option { "is_some_and" } else { "is_ok_and" }, + ) + }, + Applicability::MachineApplicable, + ); + } +} + +pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::Binary(op, left, right) = parent_expr.kind + && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) + && op.span.eq_ctxt(expr.span) + { + // Check `left` and `right` expression in any order, and for `Option` and `Result` + for (expr1, expr2) in [(left, right), (right, left)] { + for item in [sym::Option, sym::Result] { + if let ExprKind::Call(call, ..) = expr1.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res + && let ty = cx.typeck_results().expr_ty(expr1) + && let ty::Adt(adt, args) = ty.kind() + && cx.tcx.is_diagnostic_item(item, adt.did()) + && args.type_at(0).is_bool() + && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item) + { + return emit_lint(cx, op.node, parent_expr, span, item == sym::Option); + } + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index d2d59f0013c..bc159206985 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -4439,7 +4439,7 @@ declare_clippy_lint! { /// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so. /// /// ### Why is this bad? - /// `contains()` is more concise and idiomatic, sometimes more fast. + /// `contains()` is more concise and idiomatic, while also being faster in some cases. /// /// ### Example /// ```no_run @@ -5203,6 +5203,7 @@ impl Methods { unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, self.msrv); map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span); + manual_is_variant_and::check_map(cx, expr); match method_call(recv) { Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index 38cb4d51ca0..7bdd999bbba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -11,8 +11,7 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 768bbebccd4..fdccf1fb33d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -44,7 +44,7 @@ pub fn check<'tcx>( return; } // At this point, we know the call is of a `to_owned`-like function. The functions - // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary + // `check_addr_of_expr` and `check_into_iter_call_arg` determine whether the call is unnecessary // based on its context, that is, whether it is a referent in an `AddrOf` expression, an // argument in a `into_iter` call, or an argument in the call of some other function. if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index 00ea9bba0d1..99f01c8001a 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -131,8 +131,9 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { // If however the identifier is different, this means it is an alias (`use foo::bar as baz`). In // this case, we need to emit the warning for `baz`. if let Some(imported_item_path) = usenode - && let Some(Res::Def(_, imported_item_defid)) = imported_item_path.res.first() - && cx.tcx.item_name(*imported_item_defid).as_str() == str + // use `present_items` because it could be in any of type_ns, value_ns, macro_ns + && let Some(Res::Def(_, imported_item_defid)) = imported_item_path.res.value_ns + && cx.tcx.item_name(imported_item_defid).as_str() == str { return; } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index a1e621cc9f6..eeea6dfd5f4 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -72,7 +72,8 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind { - for &res in &path.res { + // use `present_items` because it could be in any of type_ns, value_ns, macro_ns + for res in path.res.present_items() { if let Res::Def(_, id) = res && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 2fd1049f42e..2f1ab3d2652 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -79,25 +79,19 @@ fn check_arguments<'tcx>( name: &str, fn_kind: &str, ) { - match type_definition.kind() { - ty::FnDef(..) | ty::FnPtr(..) => { - let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); - for (argument, parameter) in iter::zip(arguments, parameters) { - match parameter.kind() { - ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) => { - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind { - span_lint( - cx, - UNNECESSARY_MUT_PASSED, - argument.span, - format!("the {fn_kind} `{name}` doesn't need a mutable reference"), - ); - } - }, - _ => (), - } + if let ty::FnDef(..) | ty::FnPtr(..) = type_definition.kind() { + let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); + for (argument, parameter) in iter::zip(arguments, parameters) { + if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind + { + span_lint( + cx, + UNNECESSARY_MUT_PASSED, + argument.span, + format!("the {fn_kind} `{name}` doesn't need a mutable reference"), + ); } - }, - _ => (), + } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 7dd96f1f037..6a7c8436bad 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { && let body = cx.tcx.hir_body(body) // 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. - && let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind + && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..)) { let mut ret_collector = RetCollector::default(); ret_collector.visit_expr(body.value); @@ -99,11 +99,21 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { ) }; + 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 = snippet_with_applicability(cx, body.value.span, "..", &mut applicability); + let sugg = format!( "for {} in {} {}", - snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability), - snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, body.value.span, "..", &mut applicability), + body_param_sugg, + for_each_rev_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| { @@ -116,6 +126,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { } } +/// Check if the block is a desugared `_ = expr` statement. +fn is_let_desugar(block: &Block<'_>) -> bool { + matches!( + block, + Block { + stmts: [Stmt { + kind: StmtKind::Let(_), + .. + },], + .. + } + ) +} + /// This type plays two roles. /// 1. Collect spans of `return` in the closure body. /// 2. Detect use of `return` in `Loop` in the closure body. diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 4be42267b14..9c6141d8222 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::Msrv; +use clippy_utils::qualify_min_const_fn::is_stable_const_fn; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method}; +use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir as hir; @@ -19,6 +21,7 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, assignee: &'tcx hir::Expr<'_>, e: &'tcx hir::Expr<'_>, + msrv: Msrv, ) { if let hir::ExprKind::Binary(op, l, r) = &e.kind { let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { @@ -40,6 +43,15 @@ pub(super) fn check<'tcx>( return; } } + + // Skip if the trait is not stable in const contexts + if is_in_const_context(cx) + && let Some(binop_id) = cx.tcx.associated_item_def_ids(trait_id).first() + && !is_stable_const_fn(cx, *binop_id, msrv) + { + return; + } + span_lint_and_then( cx, ASSIGN_OP_PATTERN, diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index d32c062cf56..2f4e8e99588 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -919,7 +919,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); }, ExprKind::Assign(lhs, rhs, _) => { - assign_op_pattern::check(cx, e, lhs, rhs); + assign_op_pattern::check(cx, e, lhs, rhs, self.msrv); self_assignment::check(cx, e, lhs, rhs); }, ExprKind::Unary(op, arg) => { diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index 691d7b904ef..b79461663d7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -34,14 +34,10 @@ pub(super) fn check<'tcx>( } fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) else { - return false; - }; - let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else { - return false; - }; - - if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne { + if let Node::Expr(parent_expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind + && let BinOpKind::Eq | BinOpKind::Ne = op.node + { let ecx = ConstEvalCtxt::new(cx); matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0))) } else { @@ -56,35 +52,28 @@ struct OperandInfo { } fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> { - match ConstEvalCtxt::new(cx).eval(operand) { - Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() { + match ConstEvalCtxt::new(cx).eval(operand)? { + Constant::Int(v) => match *cx.typeck_results().expr_ty(expr).kind() { ty::Int(ity) => { let value = sext(cx.tcx, v, ity); - return Some(OperandInfo { + Some(OperandInfo { string_representation: Some(value.to_string()), is_negative: value < 0, is_integral: true, - }); - }, - ty::Uint(_) => { - return Some(OperandInfo { - string_representation: None, - is_negative: false, - is_integral: true, - }); + }) }, - _ => {}, + ty::Uint(_) => Some(OperandInfo { + string_representation: None, + is_negative: false, + is_integral: true, + }), + _ => None, }, // FIXME(f16_f128): add when casting is available on all platforms - Some(Constant::F32(f)) => { - return Some(floating_point_operand_info(&f)); - }, - Some(Constant::F64(f)) => { - return Some(floating_point_operand_info(&f)); - }, - _ => {}, + Constant::F32(f) => Some(floating_point_operand_info(&f)), + Constant::F64(f) => Some(floating_point_operand_info(&f)), + _ => None, } - None } fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo { diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 8962f36db1e..449d3da7639 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -152,7 +152,6 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { expr.span, "`panic_any` should not be present in production code", ); - return; } } } diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 5d30b66def2..dadf49b64e5 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,5 +1,3 @@ -use std::{cmp, iter}; - use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; @@ -20,6 +18,7 @@ use rustc_middle::ty::{self, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -33,10 +32,8 @@ declare_clippy_lint! { /// registers. /// /// ### Known problems - /// This lint is target register size dependent, it is - /// limited to 32-bit to try and reduce portability problems between 32 and - /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit - /// will be different. + /// This lint is target dependent, some cases will lint on 64-bit targets but + /// not 32-bit or lower targets. /// /// The configuration option `trivial_copy_size_limit` can be set to override /// this limit for a project. @@ -112,16 +109,9 @@ pub struct PassByRefOrValue { impl PassByRefOrValue { pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| { - let bit_width = u64::from(tcx.sess.target.pointer_width); - // Cap the calculated bit width at 32-bits to reduce - // portability problems between 32 and 64-bit targets - let bit_width = cmp::min(bit_width, 32); - #[expect(clippy::integer_division)] - let byte_width = bit_width / 8; - // Use a limit of 2 times the register byte width - byte_width * 2 - }); + let ref_min_size = conf + .trivial_copy_size_limit + .unwrap_or_else(|| u64::from(tcx.sess.target.pointer_width / 8)); Self { ref_min_size, diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 7b381fac5f1..3828aff4164 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::{Item, ItemKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) - && is_not_macro_export(item) + && !is_ignorable_export(item) && !item.span.in_external_macro(cx.sess().source_map()) { let span = item @@ -86,18 +86,17 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } -fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { - if let ItemKind::Use(path, _) = item.kind { - if path - .res - .iter() - .all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) - { - return false; +// We ignore macro exports. And `ListStem` uses, which aren't interesting. +fn is_ignorable_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { + if let ItemKind::Use(path, kind) = item.kind { + let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(MacroKind::Bang), _))) + || kind == UseKind::ListStem; + if ignore { + return true; } } else if let ItemKind::Macro(..) = item.kind { - return false; + return true; } - true + false } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 6bc5af268ff..e0c93153a77 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -435,7 +435,9 @@ fn check_final_expr<'tcx>( ExprKind::If(_, then, else_clause_opt) => { check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); if let Some(else_clause) = else_clause_opt { - check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans); + // The `RetReplacement` won't be used there as `else_clause` will be either a block or + // a `if` expression. + check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); } }, // a match expr, check all arms diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index 67ceac92dbc..39f4130afcf 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate}; use rustc_session::declare_lint_pass; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -36,21 +36,26 @@ declare_clippy_lint! { declare_lint_pass!(UnitReturnExpectingOrd => [UNIT_RETURN_EXPECTING_ORD]); -fn get_trait_predicates_for_trait_id<'tcx>( +// For each +fn get_trait_predicates_for_trait_ids<'tcx>( cx: &LateContext<'tcx>, generics: GenericPredicates<'tcx>, - trait_id: Option<DefId>, -) -> Vec<TraitPredicate<'tcx>> { - let mut preds = Vec::new(); + trait_ids: &[Option<DefId>], // At least 2 ids +) -> [Vec<TraitPredicate<'tcx>>; 3] { + debug_assert!(trait_ids.len() >= 2); + let mut preds = [Vec::new(), Vec::new(), Vec::new()]; for (pred, _) in generics.predicates { - if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() - && let trait_pred = cx + if let ClauseKind::Trait(poly_trait_pred) = pred.kind().skip_binder() { + let trait_pred = cx .tcx - .instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred)) - && let Some(trait_def_id) = trait_id - && trait_def_id == trait_pred.trait_ref.def_id - { - preds.push(trait_pred); + .instantiate_bound_regions_with_erased(pred.kind().rebind(poly_trait_pred)); + for (i, tid) in trait_ids.iter().enumerate() { + if let Some(tid) = tid + && *tid == trait_pred.trait_ref.def_id + { + preds[i].push(trait_pred); + } + } } } preds @@ -74,15 +79,24 @@ fn get_projection_pred<'tcx>( }) } -fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> { +fn get_args_to_check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + args_len: usize, + fn_mut_trait: DefId, + ord_trait: Option<DefId>, + partial_ord_trait: Option<DefId>, +) -> Vec<(usize, Symbol)> { let mut args_to_check = Vec::new(); if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity(); let generics = cx.tcx.predicates_of(def_id); - let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait()); - let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord)); - let partial_ord_preds = - get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait()); + let [fn_mut_preds, ord_preds, partial_ord_preds] = + get_trait_predicates_for_trait_ids(cx, generics, &[Some(fn_mut_trait), ord_trait, partial_ord_trait]); + if fn_mut_preds.is_empty() { + return vec![]; + } + // Trying to call instantiate_bound_regions_with_erased on fn_sig.inputs() gives the following error // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for // `&[rustc_middle::ty::Ty<'_>]` @@ -102,12 +116,18 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve .iter() .any(|ord| Some(ord.self_ty()) == return_ty_pred.term.as_type()) { - args_to_check.push((i, "Ord".to_string())); + args_to_check.push((i, sym::Ord)); + if args_to_check.len() == args_len - 1 { + break; + } } else if partial_ord_preds .iter() .any(|pord| pord.self_ty() == return_ty_pred.term.expect_type()) { - args_to_check.push((i, "PartialOrd".to_string())); + args_to_check.push((i, sym::PartialOrd)); + if args_to_check.len() == args_len - 1 { + break; + } } } } @@ -142,38 +162,50 @@ fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Option<(Spa impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind { - let arg_indices = get_args_to_check(cx, expr); + if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind + && args.iter().any(|arg| { + matches!( + arg.peel_blocks().peel_borrows().peel_drop_temps().kind, + ExprKind::Path(_) | ExprKind::Closure(_) + ) + }) + && let Some(fn_mut_trait) = cx.tcx.lang_items().fn_mut_trait() + { + let ord_trait = cx.tcx.get_diagnostic_item(sym::Ord); + let partial_ord_trait = cx.tcx.lang_items().partial_ord_trait(); + if (ord_trait, partial_ord_trait) == (None, None) { + return; + } + let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>(); + let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait); for (i, trait_name) in arg_indices { - if i < args.len() { - match check_arg(cx, args[i]) { - Some((span, None)) => { - span_lint( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + match check_arg(cx, args[i]) { + Some((span, None)) => { + span_lint( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - ); - }, - Some((span, Some(last_semi))) => { - span_lint_and_help( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + ), + ); + }, + Some((span, Some(last_semi))) => { + span_lint_and_help( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - Some(last_semi), - "probably caused by this trailing semicolon", - ); - }, - None => {}, - } + ), + Some(last_semi), + "probably caused by this trailing semicolon", + ); + }, + None => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs index 14ac65cf4df..610cec7b8c8 100644 --- a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs +++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { // Ignore imports that already use Underscore && ident.name != kw::Underscore // Only check traits - && let Some(Res::Def(DefKind::Trait, _)) = path.res.first() + && let Some(Res::Def(DefKind::Trait, _)) = path.res.type_ns && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id) // Only check this import if it is visible to its module only (no pub, pub(crate), ...) && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id) diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 45a5dbabeb4..467811c586b 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -169,8 +169,8 @@ impl LateLintPass<'_> for WildcardImports { format!("{import_source_snippet}::{imports_string}") }; - // Glob imports always have a single resolution. - let (lint, message) = if let Res::Def(DefKind::Enum, _) = use_path.res[0] { + // Glob imports always have a single resolution. Enums are in the value namespace. + let (lint, message) = if let Some(Res::Def(DefKind::Enum, _)) = use_path.res.value_ns { (ENUM_GLOB_USE, "usage of wildcard import for enum variants") } else { (WILDCARD_IMPORTS, "usage of wildcard import") diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index c9083f654c4..efbacbd72db 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: <!-- begin autogenerated nightly --> ``` -nightly-2025-05-21 +nightly-2025-05-31 ``` <!-- end autogenerated nightly --> diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index a0503a699e6..6c186ab4a6f 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -437,10 +437,10 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && both(lt.as_ref(), rt.as_ref(), |l, r| eq_ty(l, r)) }, (Enum(li, lg, le), Enum(ri, rg, re)) => { - eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant) + eq_id(*li, *ri) && eq_generics(lg, rg) && over(&le.variants, &re.variants, eq_variant) }, (Struct(li, lg, lv), Struct(ri, rg, rv)) | (Union(li, lg, lv), Union(ri, rg, rv)) => { - eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv) + eq_id(*li, *ri) && eq_generics(lg, rg) && eq_variant_data(lv, rv) }, ( Trait(box ast::Trait { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 2020f3d6b5b..f6ef638e618 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1565,10 +1565,10 @@ pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_ /// Returns `true` if a pattern is refutable. // TODO: should be implemented using rustc/mir_build/thir machinery pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { - fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool { - matches!( + fn is_qpath_refutable(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool { + !matches!( cx.qpath_res(qpath, id), - Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _) + Res::Def(DefKind::Struct, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Struct, _), _) ) } @@ -1585,16 +1585,18 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { kind: PatExprKind::Path(qpath), hir_id, .. - }) => is_enum_variant(cx, qpath, *hir_id), + }) => is_qpath_refutable(cx, qpath, *hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set are_refutable(cx, pats) }, PatKind::Tuple(pats, _) => are_refutable(cx, pats), PatKind::Struct(ref qpath, fields, _) => { - is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat)) + is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat)) + }, + PatKind::TupleStruct(ref qpath, pats, _) => { + is_qpath_refutable(cx, qpath, pat.hir_id) || are_refutable(cx, pats) }, - PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats), PatKind::Slice(head, middle, tail) => { match &cx.typeck_results().node_type(pat.hir_id).kind() { rustc_ty::Slice(..) => { diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 9d7f3086b05..f37a609497e 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -306,10 +306,13 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n let item = tcx.hir_item(item_id); if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind { if ident.name == name { - path.res - .iter() - .find(|res| ns.matches(res.ns())) - .and_then(Res::opt_def_id) + let opt_def_id = |ns: Option<Res>| ns.and_then(|res| res.opt_def_id()); + match ns { + PathNS::Type => opt_def_id(path.res.type_ns), + PathNS::Value => opt_def_id(path.res.value_ns), + PathNS::Macro => opt_def_id(path.res.macro_ns), + PathNS::Arbitrary => unreachable!(), + } } else { None } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index bb04520c6b7..5b4ec12cbec 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -393,7 +393,8 @@ fn check_terminator<'tcx>( } } -fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { +/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. +pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { cx.tcx.is_const_fn(def_id) && cx .tcx diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index f4dfc8f4b5a..6974e6512e2 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -940,7 +940,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // note: unable to trigger `Subslice` kind in tests ProjectionKind::Subslice | // Doesn't have surface syntax. Only occurs in patterns. - ProjectionKind::OpaqueCast => (), + ProjectionKind::OpaqueCast | // Only occurs in closure captures. ProjectionKind::UnwrapUnsafeBinder => (), ProjectionKind::Deref => { diff --git a/src/tools/clippy/lintcheck/src/input.rs b/src/tools/clippy/lintcheck/src/input.rs index 83eb0a577d6..408a2e087af 100644 --- a/src/tools/clippy/lintcheck/src/input.rs +++ b/src/tools/clippy/lintcheck/src/input.rs @@ -8,7 +8,7 @@ use std::time::Duration; use serde::Deserialize; use walkdir::{DirEntry, WalkDir}; -use crate::{Crate, LINTCHECK_DOWNLOADS, LINTCHECK_SOURCES}; +use crate::{Crate, lintcheck_sources, target_dir}; const DEFAULT_DOCS_LINK: &str = "https://docs.rs/{krate}/{version}/src/{krate_}/{file}.html#{line}"; const DEFAULT_GITHUB_LINK: &str = "{url}/blob/{hash}/src/{file}#L{line}"; @@ -201,8 +201,10 @@ impl CrateWithSource { let file_link = &self.file_link; match &self.source { CrateSource::CratesIo { version } => { - let extract_dir = PathBuf::from(LINTCHECK_SOURCES); - let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS); + let extract_dir = PathBuf::from(lintcheck_sources()); + // Keep constant downloads path to avoid repeating work and + // filling up disk space unnecessarily. + let krate_download_dir = PathBuf::from("target/lintcheck/downloads/"); // url to download the crate from crates.io let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download"); @@ -211,7 +213,7 @@ impl CrateWithSource { let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz")); // don't download/extract if we already have done so - if !krate_file_path.is_file() { + if !krate_file_path.is_file() || !extract_dir.join(format!("{name}-{version}")).exists() { // create a file path to download and write the crate data into let mut krate_dest = fs::File::create(&krate_file_path).unwrap(); let mut krate_req = get(&url).unwrap().into_reader(); @@ -236,7 +238,7 @@ impl CrateWithSource { }, CrateSource::Git { url, commit } => { let repo_path = { - let mut repo_path = PathBuf::from(LINTCHECK_SOURCES); + let mut repo_path = PathBuf::from(lintcheck_sources()); // add a -git suffix in case we have the same crate from crates.io and a git repo repo_path.push(format!("{name}-git")); repo_path @@ -286,7 +288,7 @@ impl CrateWithSource { // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory // as a result of this filter. - let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); + let dest_crate_root = PathBuf::from(lintcheck_sources()).join(name); if dest_crate_root.exists() { println!("Deleting existing directory at `{}`", dest_crate_root.display()); fs::remove_dir_all(&dest_crate_root).unwrap(); @@ -326,15 +328,16 @@ impl CrateWithSource { /// /// This function panics if creating one of the dirs fails. fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { - fs::create_dir("target/lintcheck/").unwrap_or_else(|err| { + fs::create_dir(format!("{}/lintcheck/", target_dir())).unwrap_or_else(|err| { assert_eq!( err.kind(), ErrorKind::AlreadyExists, "cannot create lintcheck target dir" ); }); - fs::create_dir(krate_download_dir).unwrap_or_else(|err| { - assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir"); + fs::create_dir_all(krate_download_dir).unwrap_or_else(|err| { + // We are allowed to reuse download dirs + assert_ne!(err.kind(), ErrorKind::AlreadyExists); }); fs::create_dir(extract_dir).unwrap_or_else(|err| { assert_eq!( diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index d4bf6cd48a1..84183831432 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -43,8 +43,14 @@ use input::read_crates; use output::{ClippyCheckOutput, ClippyWarning, RustcIce}; use rayon::prelude::*; -const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads"; -const LINTCHECK_SOURCES: &str = "target/lintcheck/sources"; +#[must_use] +pub fn target_dir() -> String { + env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned()) +} + +fn lintcheck_sources() -> String { + format!("{}/lintcheck/sources", target_dir()) +} /// Represents the actual source code of a crate that we ran "cargo clippy" on #[derive(Debug)] @@ -307,7 +313,8 @@ fn main() { fn lintcheck(config: LintcheckConfig) { let clippy_ver = build_clippy(config.perf); let clippy_driver_path = fs::canonicalize(format!( - "target/{}/clippy-driver{EXE_SUFFIX}", + "{}/{}/clippy-driver{EXE_SUFFIX}", + target_dir(), if config.perf { "release" } else { "debug" } )) .unwrap(); @@ -315,7 +322,8 @@ fn lintcheck(config: LintcheckConfig) { // assert that clippy is found assert!( clippy_driver_path.is_file(), - "target/{}/clippy-driver binary not found! {}", + "{}/{}/clippy-driver binary not found! {}", + target_dir(), if config.perf { "release" } else { "debug" }, clippy_driver_path.display() ); @@ -386,7 +394,7 @@ fn lintcheck(config: LintcheckConfig) { .unwrap(); let server = config.recursive.then(|| { - let _: io::Result<()> = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive"); + let _: io::Result<()> = fs::remove_dir_all(format!("{}/lintcheck/shared_target_dir/recursive", target_dir())); LintcheckServer::spawn(recursive_options) }); @@ -488,7 +496,7 @@ fn clippy_project_root() -> &'static Path { #[must_use] fn shared_target_dir(qualifier: &str) -> PathBuf { clippy_project_root() - .join("target/lintcheck/shared_target_dir") + .join(format!("{}/lintcheck/shared_target_dir", target_dir())) .join(qualifier) } diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs index dcc1ec339ef..d7fe0915121 100644 --- a/src/tools/clippy/lintcheck/src/output.rs +++ b/src/tools/clippy/lintcheck/src/output.rs @@ -162,9 +162,9 @@ pub fn summarize_and_print_changes( fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { // count lint type occurrences let mut counter: HashMap<&String, usize> = HashMap::new(); - warnings - .iter() - .for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1); + for wrn in warnings { + *counter.entry(&wrn.name).or_insert(0) += 1; + } // collect into a tupled list for sorting let mut stats: Vec<(&&String, &usize)> = counter.iter().collect(); diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 0b9ebe554f5..b6817d9a146 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-05-21" +channel = "nightly-2025-05-31" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml index 0dc6adce7bf..0ed58a2dfc1 100644 --- a/src/tools/clippy/rustfmt.toml +++ b/src/tools/clippy/rustfmt.toml @@ -6,4 +6,8 @@ edition = "2024" error_on_line_overflow = true imports_granularity = "Module" style_edition = "2024" -ignore = ["tests/ui/crashes/ice-10912.rs"] +ignore = [ + "tests/ui/crashes/ice-9405.rs", + "tests/ui/crashes/ice-10912.rs", + "tests/ui/non_expressive_names_error_recovery.rs", +] diff --git a/src/tools/clippy/tests/headers.rs b/src/tools/clippy/tests/headers.rs deleted file mode 100644 index d1f986ef526..00000000000 --- a/src/tools/clippy/tests/headers.rs +++ /dev/null @@ -1,34 +0,0 @@ -use regex::Regex; -use std::fs; -use walkdir::WalkDir; - -#[test] -fn old_test_headers() { - let old_headers = Regex::new( - r"^//( ?\[\w+\])? ?((check|build|run|ignore|aux|only|needs|rustc|unset|no|normalize|run|compile)-|edition|incremental|revisions).*", - ) - .unwrap(); - let mut failed = false; - - for entry in WalkDir::new("tests") { - let entry = entry.unwrap(); - let is_hidden_file = entry - .file_name() - .to_str() - .expect("non-UTF-8 file name") - .starts_with('.'); - if is_hidden_file || !entry.file_type().is_file() { - continue; - } - - let file = fs::read_to_string(entry.path()).unwrap_or_else(|err| panic!("{}: {err}", entry.path().display())); - - if let Some(header) = old_headers.find(&file) { - println!("Found header `{}` in {}", header.as_str(), entry.path().display()); - - failed = true; - } - } - - assert!(!failed, "use `//@foo` style test headers instead"); -} diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed index 429c20f95e9..3bc6885d7c3 100644 --- a/src/tools/clippy/tests/ui/assign_ops.fixed +++ b/src/tools/clippy/tests/ui/assign_ops.fixed @@ -1,5 +1,6 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] +#![feature(const_trait_impl, const_ops)] use core::num::Wrapping; use std::ops::{Mul, MulAssign}; @@ -73,3 +74,33 @@ impl MulAssign<i64> for Wrap { *self = *self * rhs } } + +mod issue14871 { + + use std::ops::{Add, AddAssign}; + + pub trait Number: Copy + Add<Self, Output = Self> + AddAssign { + const ZERO: Self; + const ONE: Self; + } + + #[const_trait] + pub trait NumberConstants { + fn constant(value: usize) -> Self; + } + + impl<T> const NumberConstants for T + where + T: Number + ~const core::ops::Add, + { + fn constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res = res + Self::ONE; + count += 1; + } + res + } + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs index 480ff07f150..f1f8f9daff9 100644 --- a/src/tools/clippy/tests/ui/assign_ops.rs +++ b/src/tools/clippy/tests/ui/assign_ops.rs @@ -1,5 +1,6 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] +#![feature(const_trait_impl, const_ops)] use core::num::Wrapping; use std::ops::{Mul, MulAssign}; @@ -73,3 +74,33 @@ impl MulAssign<i64> for Wrap { *self = *self * rhs } } + +mod issue14871 { + + use std::ops::{Add, AddAssign}; + + pub trait Number: Copy + Add<Self, Output = Self> + AddAssign { + const ZERO: Self; + const ONE: Self; + } + + #[const_trait] + pub trait NumberConstants { + fn constant(value: usize) -> Self; + } + + impl<T> const NumberConstants for T + where + T: Number + ~const core::ops::Add, + { + fn constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res = res + Self::ONE; + count += 1; + } + res + } + } +} diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr index 881a333fbe4..c5e698b3ee1 100644 --- a/src/tools/clippy/tests/ui/assign_ops.stderr +++ b/src/tools/clippy/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:9:5 + --> tests/ui/assign_ops.rs:10:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,67 +8,67 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:11:5 + --> tests/ui/assign_ops.rs:12:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:13:5 + --> tests/ui/assign_ops.rs:14:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:15:5 + --> tests/ui/assign_ops.rs:16:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:17:5 + --> tests/ui/assign_ops.rs:18:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:19:5 + --> tests/ui/assign_ops.rs:20:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:21:5 + --> tests/ui/assign_ops.rs:22:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:23:5 + --> tests/ui/assign_ops.rs:24:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:30:5 + --> tests/ui/assign_ops.rs:31:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:35:5 + --> tests/ui/assign_ops.rs:36:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:38:5 + --> tests/ui/assign_ops.rs:39:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:51:5 + --> tests/ui/assign_ops.rs:52:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed index 3b9dee81898..5993c2faf0d 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed @@ -123,3 +123,19 @@ mod issue12131 { //~^ dbg_macro } } + +mod issue14914 { + use std::future::Future; + + fn takes_async_fn<F, Fut>(_f: F) + where + F: FnOnce(i32) -> Fut, + Fut: Future<Output = i32>, + { + } + + fn should_not_panic() { + takes_async_fn(async |val| val); + //~^ dbg_macro + } +} diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs index 1dbbc6fe984..58d7e106e23 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs @@ -123,3 +123,19 @@ mod issue12131 { //~^ dbg_macro } } + +mod issue14914 { + use std::future::Future; + + fn takes_async_fn<F, Fut>(_f: F) + where + F: FnOnce(i32) -> Fut, + Fut: Future<Output = i32>, + { + } + + fn should_not_panic() { + takes_async_fn(async |val| dbg!(val)); + //~^ dbg_macro + } +} diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr index f1412023cc8..5a65b38a85c 100644 --- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr +++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.stderr @@ -230,5 +230,17 @@ LL - print!("{}", dbg!(s)); LL + print!("{}", s); | -error: aborting due to 19 previous errors +error: the `dbg!` macro is intended as a debugging tool + --> tests/ui/dbg_macro/dbg_macro.rs:138:36 + | +LL | takes_async_fn(async |val| dbg!(val)); + | ^^^^^^^^^ + | +help: remove the invocation before committing it to a version control system + | +LL - takes_async_fn(async |val| dbg!(val)); +LL + takes_async_fn(async |val| val); + | + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed index fb0f40b34a4..e0136584f3d 100644 --- a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.fixed @@ -1,11 +1,37 @@ -// This test checks that words starting with capital letters and ending with "ified" don't -// trigger the lint. - #![deny(clippy::doc_markdown)] +#![allow(clippy::doc_lazy_continuation)] + +mod issue13097 { + // This test checks that words starting with capital letters and ending with "ified" don't + // trigger the lint. + pub enum OutputFormat { + /// `HumaNified` + //~^ ERROR: item in documentation is missing backticks + Plain, + // Should not warn! + /// JSONified console output + Json, + } +} +#[rustfmt::skip] pub enum OutputFormat { - /// `HumaNified` - //~^ ERROR: item in documentation is missing backticks + /** + * `HumaNified` + //~^ ERROR: item in documentation is missing backticks + * Before \u{08888} `HumaNified` \{u08888} After + //~^ ERROR: item in documentation is missing backticks + * meow meow \[`meow_meow`\] meow meow? + //~^ ERROR: item in documentation is missing backticks + * \u{08888} `meow_meow` \[meow meow] meow? + //~^ ERROR: item in documentation is missing backticks + * Above + * \u{08888} + * \[hi\](<https://example.com>) `HumaNified` \[example](<https://example.com>) + //~^ ERROR: item in documentation is missing backticks + * \u{08888} + * Below + */ Plain, // Should not warn! /// JSONified console output diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs index 8c1e1a3cd6c..2e89fe6c56b 100644 --- a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs +++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.rs @@ -1,11 +1,37 @@ -// This test checks that words starting with capital letters and ending with "ified" don't -// trigger the lint. - #![deny(clippy::doc_markdown)] +#![allow(clippy::doc_lazy_continuation)] + +mod issue13097 { + // This test checks that words starting with capital letters and ending with "ified" don't + // trigger the lint. + pub enum OutputFormat { + /// HumaNified + //~^ ERROR: item in documentation is missing backticks + Plain, + // Should not warn! + /// JSONified console output + Json, + } +} +#[rustfmt::skip] pub enum OutputFormat { - /// HumaNified - //~^ ERROR: item in documentation is missing backticks + /** + * HumaNified + //~^ ERROR: item in documentation is missing backticks + * Before \u{08888} HumaNified \{u08888} After + //~^ ERROR: item in documentation is missing backticks + * meow meow \[meow_meow\] meow meow? + //~^ ERROR: item in documentation is missing backticks + * \u{08888} meow_meow \[meow meow] meow? + //~^ ERROR: item in documentation is missing backticks + * Above + * \u{08888} + * \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>) + //~^ ERROR: item in documentation is missing backticks + * \u{08888} + * Below + */ Plain, // Should not warn! /// JSONified console output diff --git a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr index 65b8f2ed80b..cea788301d4 100644 --- a/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_markdown-issue_13097.stderr @@ -1,19 +1,79 @@ error: item in documentation is missing backticks - --> tests/ui/doc/doc_markdown-issue_13097.rs:7:9 + --> tests/ui/doc/doc_markdown-issue_13097.rs:8:13 | -LL | /// HumaNified - | ^^^^^^^^^^ +LL | /// HumaNified + | ^^^^^^^^^^ | note: the lint level is defined here - --> tests/ui/doc/doc_markdown-issue_13097.rs:4:9 + --> tests/ui/doc/doc_markdown-issue_13097.rs:1:9 | LL | #![deny(clippy::doc_markdown)] | ^^^^^^^^^^^^^^^^^^^^ help: try | -LL - /// HumaNified -LL + /// `HumaNified` +LL - /// HumaNified +LL + /// `HumaNified` | -error: aborting due to 1 previous error +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:20:8 + | +LL | * HumaNified + | ^^^^^^^^^^ + | +help: try + | +LL - * HumaNified +LL + * `HumaNified` + | + +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:22:25 + | +LL | * Before \u{08888} HumaNified \{u08888} After + | ^^^^^^^^^^ + | +help: try + | +LL - * Before \u{08888} HumaNified \{u08888} After +LL + * Before \u{08888} `HumaNified` \{u08888} After + | + +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:24:20 + | +LL | * meow meow \[meow_meow\] meow meow? + | ^^^^^^^^^ + | +help: try + | +LL - * meow meow \[meow_meow\] meow meow? +LL + * meow meow \[`meow_meow`\] meow meow? + | + +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:26:18 + | +LL | * \u{08888} meow_meow \[meow meow] meow? + | ^^^^^^^^^ + | +help: try + | +LL - * \u{08888} meow_meow \[meow meow] meow? +LL + * \u{08888} `meow_meow` \[meow meow] meow? + | + +error: item in documentation is missing backticks + --> tests/ui/doc/doc_markdown-issue_13097.rs:30:38 + | +LL | * \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>) + | ^^^^^^^^^^ + | +help: try + | +LL - * \[hi\](<https://example.com>) HumaNified \[example](<https://example.com>) +LL + * \[hi\](<https://example.com>) `HumaNified` \[example](<https://example.com>) + | + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed index 619329a6ade..52c4d1b1f30 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed @@ -81,12 +81,10 @@ fn main() { let b: String = concat(just_return(a)); //~^ explicit_deref_methods - let b: &str = &**a; - //~^ explicit_deref_methods + let b: &str = a.deref().deref(); let opt_a = Some(a.clone()); - let b = &*opt_a.unwrap(); - //~^ explicit_deref_methods + let b = opt_a.unwrap().deref(); Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs index 9f2d513283c..706d6cb2b79 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs @@ -82,11 +82,9 @@ fn main() { //~^ explicit_deref_methods let b: &str = a.deref().deref(); - //~^ explicit_deref_methods let opt_a = Some(a.clone()); let b = opt_a.unwrap().deref(); - //~^ explicit_deref_methods Aaa::deref(&Aaa); Aaa::deref_mut(&mut Aaa); diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr index a81e2f60317..5036884366c 100644 --- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr +++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr @@ -56,40 +56,28 @@ LL | let b: String = concat(just_return(a).deref()); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `just_return(a)` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:84:19 - | -LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try: `&**a` - -error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:88:13 - | -LL | let b = opt_a.unwrap().deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*opt_a.unwrap()` - -error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:123:31 + --> tests/ui/explicit_deref_methods.rs:121:31 | LL | let b: &str = expr_deref!(a.deref()); | ^^^^^^^^^ help: try: `&*a` error: explicit `deref` method call - --> tests/ui/explicit_deref_methods.rs:141:14 + --> tests/ui/explicit_deref_methods.rs:139:14 | LL | let _ = &Deref::deref(&"foo"); | ^^^^^^^^^^^^^^^^^^^^ help: try: `*&"foo"` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:143:14 + --> tests/ui/explicit_deref_methods.rs:141:14 | LL | let _ = &DerefMut::deref_mut(&mut x); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut **&mut x` error: explicit `deref_mut` method call - --> tests/ui/explicit_deref_methods.rs:144:14 + --> tests/ui/explicit_deref_methods.rs:142:14 | LL | let _ = &DerefMut::deref_mut((&mut &mut x).deref_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut ***(&mut &mut x)` -error: aborting due to 15 previous errors +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.fixed b/src/tools/clippy/tests/ui/manual_find_fixable.fixed index 5e6849a4dfb..01b3ebacbeb 100644 --- a/src/tools/clippy/tests/ui/manual_find_fixable.fixed +++ b/src/tools/clippy/tests/ui/manual_find_fixable.fixed @@ -179,3 +179,13 @@ fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> { } fn main() {} + +mod issue14826 { + fn adjust_fixable(needle: &str) -> Option<&'static str> { + ["foo", "bar"].iter().find(|&candidate| candidate.eq_ignore_ascii_case(needle)).map(|v| v as _) + } + + fn adjust_unfixable(needle: &str) -> Option<*const str> { + ["foo", "bar"].iter().find(|&&candidate| candidate.eq_ignore_ascii_case(needle)).copied().map(|v| v as _) + } +} diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.rs b/src/tools/clippy/tests/ui/manual_find_fixable.rs index 08a7dd2c6ee..ce62a4beba1 100644 --- a/src/tools/clippy/tests/ui/manual_find_fixable.rs +++ b/src/tools/clippy/tests/ui/manual_find_fixable.rs @@ -251,3 +251,25 @@ fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> { } fn main() {} + +mod issue14826 { + fn adjust_fixable(needle: &str) -> Option<&'static str> { + for candidate in &["foo", "bar"] { + //~^ manual_find + if candidate.eq_ignore_ascii_case(needle) { + return Some(candidate); + } + } + None + } + + fn adjust_unfixable(needle: &str) -> Option<*const str> { + for &candidate in &["foo", "bar"] { + //~^ manual_find + if candidate.eq_ignore_ascii_case(needle) { + return Some(candidate); + } + } + None + } +} diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.stderr b/src/tools/clippy/tests/ui/manual_find_fixable.stderr index afa453c5a87..020635d90bb 100644 --- a/src/tools/clippy/tests/ui/manual_find_fixable.stderr +++ b/src/tools/clippy/tests/ui/manual_find_fixable.stderr @@ -139,5 +139,27 @@ LL | | return Some(x); LL | | None | |____________^ help: replace with an iterator: `arr.into_iter().find(|&x| x < 1)` -error: aborting due to 12 previous errors +error: manual implementation of `Iterator::find` + --> tests/ui/manual_find_fixable.rs:257:9 + | +LL | / for candidate in &["foo", "bar"] { +LL | | +LL | | if candidate.eq_ignore_ascii_case(needle) { +LL | | return Some(candidate); +... | +LL | | None + | |____________^ help: replace with an iterator: `["foo", "bar"].iter().find(|&candidate| candidate.eq_ignore_ascii_case(needle)).map(|v| v as _)` + +error: manual implementation of `Iterator::find` + --> tests/ui/manual_find_fixable.rs:267:9 + | +LL | / for &candidate in &["foo", "bar"] { +LL | | +LL | | if candidate.eq_ignore_ascii_case(needle) { +LL | | return Some(candidate); +... | +LL | | None + | |____________^ help: replace with an iterator: `["foo", "bar"].iter().find(|&&candidate| candidate.eq_ignore_ascii_case(needle)).copied().map(|v| v as _)` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/manual_flatten.rs b/src/tools/clippy/tests/ui/manual_flatten.rs index 97f35c36e24..f1a0053ef38 100644 --- a/src/tools/clippy/tests/ui/manual_flatten.rs +++ b/src/tools/clippy/tests/ui/manual_flatten.rs @@ -123,6 +123,13 @@ fn main() { println!("{}", n); } + // Using nested `Some` pattern should not trigger the lint + for n in vec![Some((1, Some(2)))] { + if let Some((_, Some(n))) = n { + println!("{}", n); + } + } + run_unformatted_tests(); } diff --git a/src/tools/clippy/tests/ui/manual_flatten.stderr b/src/tools/clippy/tests/ui/manual_flatten.stderr index 5ab25658017..9a846fe17f3 100644 --- a/src/tools/clippy/tests/ui/manual_flatten.stderr +++ b/src/tools/clippy/tests/ui/manual_flatten.stderr @@ -178,7 +178,7 @@ LL | | } | |_________^ error: unnecessary `if let` since only the `Some` variant of the iterator element is used - --> tests/ui/manual_flatten.rs:132:5 + --> tests/ui/manual_flatten.rs:139:5 | LL | / for n in vec![ LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ | help: remove the `if let` statement in the for loop and then... - --> tests/ui/manual_flatten.rs:139:9 + --> tests/ui/manual_flatten.rs:146:9 | LL | / if let Some(n) = n { LL | | println!("{:?}", n); diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed index c9c184561dd..18a72188ab5 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed @@ -4,6 +4,44 @@ #[macro_use] extern crate option_helpers; +struct Foo<T>(T); + +impl<T> Foo<T> { + fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> { + Some(f(self.0)) + } +} + +fn foo() -> Option<bool> { + Some(true) +} + +macro_rules! some_true { + () => { + Some(true) + }; +} +macro_rules! some_false { + () => { + Some(false) + }; +} + +macro_rules! mac { + (some $e:expr) => { + Some($e) + }; + (some_map $e:expr) => { + Some($e).map(|x| x % 2 == 0) + }; + (map $e:expr) => { + $e.map(|x| x % 2 == 0) + }; + (eq $a:expr, $b:expr) => { + $a == $b + }; +} + #[rustfmt::skip] fn option_methods() { let opt = Some(1); @@ -21,6 +59,15 @@ fn option_methods() { let _ = opt .is_some_and(|x| x > 1); + let _ = Some(2).is_some_and(|x| x % 2 == 0); + //~^ manual_is_variant_and + let _ = Some(2).is_none_or(|x| x % 2 == 0); + //~^ manual_is_variant_and + let _ = Some(2).is_some_and(|x| x % 2 == 0); + //~^ manual_is_variant_and + let _ = Some(2).is_none_or(|x| x % 2 == 0); + //~^ manual_is_variant_and + // won't fix because the return type of the closure is not `bool` let _ = opt.map(|x| x + 1).unwrap_or_default(); @@ -28,6 +75,14 @@ fn option_methods() { let _ = opt2.is_some_and(char::is_alphanumeric); // should lint //~^ manual_is_variant_and let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint + + // Should not lint. + let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true); + let _ = Some(2).map(|x| x % 2 == 0) != foo(); + let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true)); + let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true); + let _ = mac!(some_map 2) == Some(true); + let _ = mac!(map Some(2)) == Some(true); } #[rustfmt::skip] @@ -41,6 +96,13 @@ fn result_methods() { }); let _ = res.is_ok_and(|x| x > 1); + let _ = Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0); + //~^ manual_is_variant_and + let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0); + //~^ manual_is_variant_and + let _ = !Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0); + //~^ manual_is_variant_and + // won't fix because the return type of the closure is not `bool` let _ = res.map(|x| x + 1).unwrap_or_default(); diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs index 52c7b56804c..a92f7c04369 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.rs +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs @@ -4,6 +4,44 @@ #[macro_use] extern crate option_helpers; +struct Foo<T>(T); + +impl<T> Foo<T> { + fn map<F: FnMut(T) -> bool>(self, mut f: F) -> Option<bool> { + Some(f(self.0)) + } +} + +fn foo() -> Option<bool> { + Some(true) +} + +macro_rules! some_true { + () => { + Some(true) + }; +} +macro_rules! some_false { + () => { + Some(false) + }; +} + +macro_rules! mac { + (some $e:expr) => { + Some($e) + }; + (some_map $e:expr) => { + Some($e).map(|x| x % 2 == 0) + }; + (map $e:expr) => { + $e.map(|x| x % 2 == 0) + }; + (eq $a:expr, $b:expr) => { + $a == $b + }; +} + #[rustfmt::skip] fn option_methods() { let opt = Some(1); @@ -27,6 +65,15 @@ fn option_methods() { //~^ manual_is_variant_and .unwrap_or_default(); + let _ = Some(2).map(|x| x % 2 == 0) == Some(true); + //~^ manual_is_variant_and + let _ = Some(2).map(|x| x % 2 == 0) != Some(true); + //~^ manual_is_variant_and + let _ = Some(2).map(|x| x % 2 == 0) == some_true!(); + //~^ manual_is_variant_and + let _ = Some(2).map(|x| x % 2 == 0) != some_false!(); + //~^ manual_is_variant_and + // won't fix because the return type of the closure is not `bool` let _ = opt.map(|x| x + 1).unwrap_or_default(); @@ -34,6 +81,14 @@ fn option_methods() { let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint //~^ manual_is_variant_and let _ = opt_map!(opt2, |x| x == 'a').unwrap_or_default(); // should not lint + + // Should not lint. + let _ = Foo::<u32>(0).map(|x| x % 2 == 0) == Some(true); + let _ = Some(2).map(|x| x % 2 == 0) != foo(); + let _ = mac!(eq Some(2).map(|x| x % 2 == 0), Some(true)); + let _ = mac!(some 2).map(|x| x % 2 == 0) == Some(true); + let _ = mac!(some_map 2) == Some(true); + let _ = mac!(map Some(2)) == Some(true); } #[rustfmt::skip] @@ -50,6 +105,13 @@ fn result_methods() { //~^ manual_is_variant_and .unwrap_or_default(); + let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true); + //~^ manual_is_variant_and + let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true); + //~^ manual_is_variant_and + let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true); + //~^ manual_is_variant_and + // won't fix because the return type of the closure is not `bool` let _ = res.map(|x| x + 1).unwrap_or_default(); diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr index a4fa500580d..1fb437a8bc7 100644 --- a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr +++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr @@ -1,5 +1,5 @@ error: called `map(<f>).unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:13:17 + --> tests/ui/manual_is_variant_and.rs:51:17 | LL | let _ = opt.map(|x| x > 1) | _________________^ @@ -11,7 +11,7 @@ LL | | .unwrap_or_default(); = help: to override `-D warnings` add `#[allow(clippy::manual_is_variant_and)]` error: called `map(<f>).unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:18:17 + --> tests/ui/manual_is_variant_and.rs:56:17 | LL | let _ = opt.map(|x| { | _________________^ @@ -30,13 +30,13 @@ LL ~ }); | error: called `map(<f>).unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:23:17 + --> tests/ui/manual_is_variant_and.rs:61:17 | LL | let _ = opt.map(|x| x > 1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)` error: called `map(<f>).unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:26:10 + --> tests/ui/manual_is_variant_and.rs:64:10 | LL | .map(|x| x > 1) | __________^ @@ -44,14 +44,38 @@ LL | | LL | | .unwrap_or_default(); | |____________________________^ help: use: `is_some_and(|x| x > 1)` +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:68:13 + | +LL | let _ = Some(2).map(|x| x % 2 == 0) == Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:70:13 + | +LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` + +error: called `.map() == Some()` + --> tests/ui/manual_is_variant_and.rs:72:13 + | +LL | let _ = Some(2).map(|x| x % 2 == 0) == some_true!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)` + +error: called `.map() != Some()` + --> tests/ui/manual_is_variant_and.rs:74:13 + | +LL | let _ = Some(2).map(|x| x % 2 == 0) != some_false!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` + error: called `map(<f>).unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:34:18 + --> tests/ui/manual_is_variant_and.rs:81:18 | LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)` error: called `map(<f>).unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:44:17 + --> tests/ui/manual_is_variant_and.rs:99:17 | LL | let _ = res.map(|x| { | _________________^ @@ -70,7 +94,7 @@ LL ~ }); | error: called `map(<f>).unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:49:17 + --> tests/ui/manual_is_variant_and.rs:104:17 | LL | let _ = res.map(|x| x > 1) | _________________^ @@ -78,11 +102,29 @@ LL | | LL | | .unwrap_or_default(); | |____________________________^ help: use: `is_ok_and(|x| x > 1)` +error: called `.map() == Ok()` + --> tests/ui/manual_is_variant_and.rs:108:13 + | +LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) == Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:110:13 + | +LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)` + +error: called `.map() != Ok()` + --> tests/ui/manual_is_variant_and.rs:112:13 + | +LL | let _ = Ok::<usize, ()>(2).map(|x| x % 2 == 0) != Ok(true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::<usize, ()>(2).is_ok_and(|x| x % 2 == 0)` + error: called `map(<f>).unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:57:18 + --> tests/ui/manual_is_variant_and.rs:119:18 | LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` -error: aborting due to 8 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed index d7d344452c5..54cad2e393f 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.fixed +++ b/src/tools/clippy/tests/ui/needless_borrow.fixed @@ -107,9 +107,6 @@ fn main() { let x = (1, 2); let _ = x.0; //~^ needless_borrow - let x = &x as *const (i32, i32); - let _ = unsafe { (*x).0 }; - //~^ needless_borrow // Issue #8367 trait Foo { @@ -289,3 +286,15 @@ fn issue_12268() { // compiler } + +fn issue_14743<T>(slice: &[T]) { + let _ = slice.len(); + //~^ needless_borrow + + let slice = slice as *const [T]; + let _ = unsafe { (&*slice).len() }; + + // Check that rustc would actually warn if Clippy had suggested removing the reference + #[expect(dangerous_implicit_autorefs)] + let _ = unsafe { (*slice).len() }; +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs index 1f05b90b472..b698c6bfc96 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.rs +++ b/src/tools/clippy/tests/ui/needless_borrow.rs @@ -107,9 +107,6 @@ fn main() { let x = (1, 2); let _ = (&x).0; //~^ needless_borrow - let x = &x as *const (i32, i32); - let _ = unsafe { (&*x).0 }; - //~^ needless_borrow // Issue #8367 trait Foo { @@ -289,3 +286,15 @@ fn issue_12268() { // compiler } + +fn issue_14743<T>(slice: &[T]) { + let _ = (&slice).len(); + //~^ needless_borrow + + let slice = slice as *const [T]; + let _ = unsafe { (&*slice).len() }; + + // Check that rustc would actually warn if Clippy had suggested removing the reference + #[expect(dangerous_implicit_autorefs)] + let _ = unsafe { (*slice).len() }; +} diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr index b036b1e47d1..172d36bd73a 100644 --- a/src/tools/clippy/tests/ui/needless_borrow.stderr +++ b/src/tools/clippy/tests/ui/needless_borrow.stderr @@ -103,71 +103,71 @@ error: this expression borrows a value the compiler would automatically borrow LL | let _ = (&x).0; | ^^^^ help: change this to: `x` -error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:111:22 - | -LL | let _ = unsafe { (&*x).0 }; - | ^^^^^ help: change this to: `(*x)` - error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:122:5 + --> tests/ui/needless_borrow.rs:119:5 | LL | (&&()).foo(); | ^^^^^^ help: change this to: `(&())` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:132:5 + --> tests/ui/needless_borrow.rs:129:5 | LL | (&&5).foo(); | ^^^^^ help: change this to: `(&5)` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:159:23 + --> tests/ui/needless_borrow.rs:156:23 | LL | let x: (&str,) = (&"",); | ^^^ help: change this to: `""` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:202:13 + --> tests/ui/needless_borrow.rs:199:13 | LL | (&self.f)() | ^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:212:13 + --> tests/ui/needless_borrow.rs:209:13 | LL | (&mut self.f)() | ^^^^^^^^^^^^^ help: change this to: `(self.f)` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:250:22 + --> tests/ui/needless_borrow.rs:247:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:258:22 + --> tests/ui/needless_borrow.rs:255:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:263:22 + --> tests/ui/needless_borrow.rs:260:22 | LL | let _ = &mut (&mut x.u).x; | ^^^^^^^^^^ help: change this to: `x.u` error: this expression borrows a value the compiler would automatically borrow - --> tests/ui/needless_borrow.rs:265:22 + --> tests/ui/needless_borrow.rs:262:22 | LL | let _ = &mut (&mut { x.u }).x; | ^^^^^^^^^^^^^^ help: change this to: `{ x.u }` error: this expression creates a reference which is immediately dereferenced by the compiler - --> tests/ui/needless_borrow.rs:287:23 + --> tests/ui/needless_borrow.rs:284:23 | LL | option.unwrap_or((&x.0,)); | ^^^^ help: change this to: `x.0` +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_borrow.rs:291:13 + | +LL | let _ = (&slice).len(); + | ^^^^^^^^ help: change this to: `slice` + error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed index fa23e18318f..a73aff55639 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed @@ -128,3 +128,18 @@ fn should_not_lint() { } fn main() {} + +mod issue14734 { + fn let_desugar(rows: &[u8]) { + let mut v = vec![]; + for x in rows.iter() { _ = v.push(x) } + //~^ needless_for_each + } + + fn do_something(_: &u8, _: u8) {} + + fn single_expr(rows: &[u8]) { + for x in rows.iter() { do_something(x, 1u8); } + //~^ needless_for_each + } +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs index 2c7e68a6f51..d92f055d3f4 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs @@ -128,3 +128,18 @@ fn should_not_lint() { } fn main() {} + +mod issue14734 { + fn let_desugar(rows: &[u8]) { + let mut v = vec![]; + rows.iter().for_each(|x| _ = v.push(x)); + //~^ needless_for_each + } + + fn do_something(_: &u8, _: u8) {} + + fn single_expr(rows: &[u8]) { + rows.iter().for_each(|x| do_something(x, 1u8)); + //~^ needless_for_each + } +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr index 013a3fa3e36..f8014456097 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr @@ -136,5 +136,17 @@ LL + acc += elem; LL + } | -error: aborting due to 8 previous errors +error: needless use of `for_each` + --> tests/ui/needless_for_each_fixable.rs:135:9 + | +LL | rows.iter().for_each(|x| _ = v.push(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { _ = v.push(x) }` + +error: needless use of `for_each` + --> tests/ui/needless_for_each_fixable.rs:142:9 + | +LL | rows.iter().for_each(|x| do_something(x, 1u8)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { do_something(x, 1u8); }` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index 17d3862cd86..d571b97f519 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -452,3 +452,68 @@ pub unsafe fn issue_12157() -> *const i32 { (unsafe { todo() } as *const i32) //~^ needless_return } + +mod else_ifs { + fn test1(a: i32) -> u32 { + if a == 0 { + 1 + //~^ needless_return + } else if a < 10 { + 2 + //~^ needless_return + } else { + 3 + //~^ needless_return + } + } + + fn test2(a: i32) -> u32 { + if a == 0 { + 1 + //~^ needless_return + } else if a < 10 { + 2 + } else { + 3 + //~^ needless_return + } + } + + fn test3(a: i32) -> u32 { + if a == 0 { + 1 + //~^ needless_return + } else if a < 10 { + 2 + } else { + 3 + //~^ needless_return + } + } + + #[allow(clippy::match_single_binding, clippy::redundant_pattern)] + fn test4(a: i32) -> u32 { + if a == 0 { + 1 + //~^ needless_return + } else if if if a > 0x1_1 { + return 2; + } else { + return 5; + } { + true + } else { + true + } { + 0xDEADC0DE + } else if match a { + b @ _ => { + return 1; + }, + } { + 0xDEADBEEF + } else { + 1 + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 1c6e7ffa1ee..2e4348ea338 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -461,3 +461,68 @@ pub unsafe fn issue_12157() -> *const i32 { return unsafe { todo() } as *const i32; //~^ needless_return } + +mod else_ifs { + fn test1(a: i32) -> u32 { + if a == 0 { + return 1; + //~^ needless_return + } else if a < 10 { + return 2; + //~^ needless_return + } else { + return 3; + //~^ needless_return + } + } + + fn test2(a: i32) -> u32 { + if a == 0 { + return 1; + //~^ needless_return + } else if a < 10 { + 2 + } else { + return 3; + //~^ needless_return + } + } + + fn test3(a: i32) -> u32 { + if a == 0 { + return 1; + //~^ needless_return + } else if a < 10 { + 2 + } else { + return 3; + //~^ needless_return + } + } + + #[allow(clippy::match_single_binding, clippy::redundant_pattern)] + fn test4(a: i32) -> u32 { + if a == 0 { + return 1; + //~^ needless_return + } else if if if a > 0x1_1 { + return 2; + } else { + return 5; + } { + true + } else { + true + } { + 0xDEADC0DE + } else if match a { + b @ _ => { + return 1; + }, + } { + 0xDEADBEEF + } else { + 1 + } + } +} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index 26dd265379b..206bd8ee5af 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -685,5 +685,101 @@ LL - return unsafe { todo() } as *const i32; LL + (unsafe { todo() } as *const i32) | -error: aborting due to 55 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:468:13 + | +LL | return 1; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 1; +LL + 1 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:471:13 + | +LL | return 2; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 2; +LL + 2 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:474:13 + | +LL | return 3; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 3; +LL + 3 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:481:13 + | +LL | return 1; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 1; +LL + 1 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:486:13 + | +LL | return 3; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 3; +LL + 3 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:493:13 + | +LL | return 1; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 1; +LL + 1 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:498:13 + | +LL | return 3; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 3; +LL + 3 + | + +error: unneeded `return` statement + --> tests/ui/needless_return.rs:506:13 + | +LL | return 1; + | ^^^^^^^^ + | +help: remove `return` + | +LL - return 1; +LL + 1 + | + +error: aborting due to 63 previous errors diff --git a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.fixed index c96a53ba2cd..c96a53ba2cd 100644 --- a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.fixed +++ b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.fixed diff --git a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.rs index a3a35eb26d1..a3a35eb26d1 100644 --- a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs +++ b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.rs diff --git a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.stderr index 4998b9bd2cc..28d9a42a9a1 100644 --- a/src/tools/clippy/tests/ui/skip_rustfmt/non_expressive_names_error_recovery.stderr +++ b/src/tools/clippy/tests/ui/non_expressive_names_error_recovery.stderr @@ -1,5 +1,5 @@ error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `)` - --> tests/ui/skip_rustfmt/non_expressive_names_error_recovery.rs:6:19 + --> tests/ui/non_expressive_names_error_recovery.rs:6:19 | LL | fn aa(a: Aa<String) { | ^ expected one of 7 possible tokens diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 507bc2b29d8..60dc1c101b6 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -443,3 +443,13 @@ fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> { //~^^^ question_mark Some(format!("{a}")) } + +fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> { + const N: i32 = 0; + + let Some((x, N)) = x else { + return None; + }; + + None +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index 64b51b849ed..99d0122a98f 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -539,3 +539,13 @@ fn issue_14615(a: MutexGuard<Option<u32>>) -> Option<String> { //~^^^ question_mark Some(format!("{a}")) } + +fn const_in_pattern(x: Option<(i32, i32)>) -> Option<()> { + const N: i32 = 0; + + let Some((x, N)) = x else { + return None; + }; + + None +} diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed new file mode 100644 index 00000000000..af7d82130f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.fixed @@ -0,0 +1,179 @@ +//@normalize-stderr-test: "\(\d+ byte\)" -> "(N byte)" +//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)" +#![deny(clippy::trivially_copy_pass_by_ref)] +#![allow( + clippy::disallowed_names, + clippy::extra_unused_lifetimes, + clippy::needless_lifetimes, + clippy::needless_pass_by_ref_mut, + clippy::redundant_field_names, + clippy::uninlined_format_args +)] + +#[derive(Copy, Clone)] +struct Foo(u32); + +#[derive(Copy, Clone)] +struct Bar([u8; 24]); + +#[derive(Copy, Clone)] +pub struct Color { + pub r: u8, + pub g: u8, + pub b: u8, + pub a: u8, +} + +struct FooRef<'a> { + foo: &'a Foo, +} + +type Baz = u32; + +fn good(a: &mut u32, b: u32, c: &Bar) {} + +fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 { + &foo.0 +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 { + &foo.0 +} + +fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef { + FooRef { foo } +} + +#[allow(clippy::needless_lifetimes)] +fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> { + FooRef { foo } +} + +fn bad(x: u32, y: Foo, z: Baz) {} +//~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by +//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by +//~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + +impl Foo { + fn good(self, a: &mut u32, b: u32, c: &Bar) {} + + fn good2(&mut self) {} + + fn bad(self, x: u32, y: Foo, z: Baz) {} + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + + fn bad2(x: u32, y: Foo, z: Baz) {} + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + + fn bad_issue7518(self, other: Self) {} + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if +} + +impl AsRef<u32> for Foo { + fn as_ref(&self) -> &u32 { + &self.0 + } +} + +impl Bar { + fn good(&self, a: &mut u32, b: u32, c: &Bar) {} + + fn bad2(x: u32, y: Foo, z: Baz) {} + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if + //~| ERROR: this argument (4 byte) is passed by reference, but would be more efficient if +} + +trait MyTrait { + fn trait_method(&self, foo: Foo); + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if +} + +pub trait MyTrait2 { + fn trait_method2(&self, color: &Color); +} + +trait MyTrait3 { + #[expect(clippy::trivially_copy_pass_by_ref)] + fn trait_method(&self, foo: &Foo); +} + +// Trait impls should not warn +impl MyTrait3 for Foo { + fn trait_method(&self, foo: &Foo) { + unimplemented!() + } +} + +mod issue3992 { + pub trait A { + #[allow(clippy::trivially_copy_pass_by_ref)] + fn a(b: &u16) {} + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn c(d: &u16) {} +} + +mod issue5876 { + // Don't lint here as it is always inlined + #[inline(always)] + fn foo_always(x: &i32) { + println!("{}", x); + } + + #[inline(never)] + fn foo_never(x: i32) { + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + println!("{}", x); + } + + #[inline] + fn foo(x: i32) { + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + println!("{}", x); + } +} + +fn ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> { + Some(x) +} + +fn ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> { + Some(x) +} + +fn with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 { + if true { x } else { y } +} + +async fn async_implicit(x: &u32) -> &u32 { + x +} + +async fn async_explicit<'a>(x: &'a u32) -> &'a u32 { + x +} + +fn unrelated_lifetimes<'a, 'b>(_x: u32, y: &'b u32) -> &'b u32 { + //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by + y +} + +fn return_ptr(x: &u32) -> *const u32 { + x +} + +fn return_field_ptr(x: &(u32, u32)) -> *const u32 { + &x.0 +} + +fn return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 { + core::ptr::addr_of!(x.0) +} diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs index 37bc6f89a20..00e11a1ea28 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs @@ -1,14 +1,15 @@ //@normalize-stderr-test: "\(\d+ byte\)" -> "(N byte)" -//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: 8 byte)" +//@normalize-stderr-test: "\(limit: \d+ byte\)" -> "(limit: N byte)" #![deny(clippy::trivially_copy_pass_by_ref)] #![allow( clippy::disallowed_names, + clippy::extra_unused_lifetimes, clippy::needless_lifetimes, + clippy::needless_pass_by_ref_mut, clippy::redundant_field_names, - clippy::uninlined_format_args, - clippy::needless_pass_by_ref_mut + clippy::uninlined_format_args )] -//@no-rustfix + #[derive(Copy, Clone)] struct Foo(u32); @@ -90,21 +91,26 @@ impl Bar { } trait MyTrait { - fn trait_method(&self, _foo: &Foo); + fn trait_method(&self, foo: &Foo); //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if } pub trait MyTrait2 { - fn trait_method2(&self, _color: &Color); + fn trait_method2(&self, color: &Color); +} + +trait MyTrait3 { + #[expect(clippy::trivially_copy_pass_by_ref)] + fn trait_method(&self, foo: &Foo); } -impl MyTrait for Foo { - fn trait_method(&self, _foo: &Foo) { +// Trait impls should not warn +impl MyTrait3 for Foo { + fn trait_method(&self, foo: &Foo) { unimplemented!() } } -#[allow(unused_variables)] mod issue3992 { pub trait A { #[allow(clippy::trivially_copy_pass_by_ref)] @@ -135,57 +141,39 @@ mod issue5876 { } } -fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> { +fn ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> { Some(x) } -#[allow(clippy::needless_lifetimes)] -fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> { +fn ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> { Some(x) } -fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 { +fn with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 { if true { x } else { y } } -async fn _async_implicit(x: &u32) -> &u32 { +async fn async_implicit(x: &u32) -> &u32 { x } -#[allow(clippy::needless_lifetimes)] -async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 { +async fn async_explicit<'a>(x: &'a u32) -> &'a u32 { x } -fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { +fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { //~^ ERROR: this argument (4 byte) is passed by reference, but would be more efficient if passed by y } -fn _return_ptr(x: &u32) -> *const u32 { +fn return_ptr(x: &u32) -> *const u32 { x } -fn _return_field_ptr(x: &(u32, u32)) -> *const u32 { +fn return_field_ptr(x: &(u32, u32)) -> *const u32 { &x.0 } -fn _return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 { +fn return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 { core::ptr::addr_of!(x.0) } - -fn main() { - let (mut foo, bar) = (Foo(0), Bar([0; 24])); - let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0); - good(&mut a, b, &c); - good_return_implicit_lt_ref(&y); - good_return_explicit_lt_ref(&y); - bad(&x, &y, &z); - foo.good(&mut a, b, &c); - foo.good2(); - foo.bad(&x, &y, &z); - Foo::bad2(&x, &y, &z); - bar.good(&mut a, b, &c); - Bar::bad2(&x, &y, &z); - foo.as_ref(); -} diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr index e813fecf653..f101ac5ccd6 100644 --- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr +++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr @@ -1,5 +1,5 @@ -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:52:11 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:53:11 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` @@ -10,107 +10,107 @@ note: the lint level is defined here LL | #![deny(clippy::trivially_copy_pass_by_ref)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:52:20 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:53:20 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:52:29 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:53:29 | LL | fn bad(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:62:12 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:63:12 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^^ help: consider passing by value instead: `self` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:62:22 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:63:22 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:62:31 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:63:31 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:62:40 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:63:40 | LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:68:16 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:69:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:68:25 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:69:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:68:34 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:69:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:73:35 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:74:35 | LL | fn bad_issue7518(self, other: &Self) {} | ^^^^^ help: consider passing by value instead: `Self` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:86:16 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:87:16 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `u32` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:86:25 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:87:25 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:86:34 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:87:34 | LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {} | ^^^^ help: consider passing by value instead: `Baz` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:93:34 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:94:33 | -LL | fn trait_method(&self, _foo: &Foo); - | ^^^^ help: consider passing by value instead: `Foo` +LL | fn trait_method(&self, foo: &Foo); + | ^^^^ help: consider passing by value instead: `Foo` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:126:21 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:132:21 | LL | fn foo_never(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:132:15 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:138:15 | LL | fn foo(x: &i32) { | ^^^^ help: consider passing by value instead: `i32` -error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: 8 byte) - --> tests/ui/trivially_copy_pass_by_ref.rs:160:37 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte) + --> tests/ui/trivially_copy_pass_by_ref.rs:164:36 | -LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { - | ^^^^^^^ help: consider passing by value instead: `u32` +LL | fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 { + | ^^^^^^^ help: consider passing by value instead: `u32` error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs index d591ab984cf..95062c9f46c 100644 --- a/src/tools/clippy/tests/ui/while_let_loop.rs +++ b/src/tools/clippy/tests/ui/while_let_loop.rs @@ -154,3 +154,89 @@ fn issue_5715(mut m: core::cell::RefCell<Option<u32>>) { m = core::cell::RefCell::new(Some(x + 1)); } } + +mod issue_362 { + pub fn merge_sorted<T>(xs: Vec<T>, ys: Vec<T>) -> Vec<T> + where + T: PartialOrd, + { + let total_len = xs.len() + ys.len(); + let mut res = Vec::with_capacity(total_len); + let mut ix = xs.into_iter().peekable(); + let mut iy = ys.into_iter().peekable(); + loop { + //~^ while_let_loop + let lt = match (ix.peek(), iy.peek()) { + (Some(x), Some(y)) => x < y, + _ => break, + }; + res.push(if lt { &mut ix } else { &mut iy }.next().unwrap()); + } + res.extend(ix); + res.extend(iy); + res + } +} + +fn let_assign() { + loop { + //~^ while_let_loop + let x = if let Some(y) = Some(3) { + y + } else { + break; + }; + if x == 3 { + break; + } + } + + loop { + //~^ while_let_loop + let x: u32 = if let Some(y) = Some(3) { + y + } else { + break; + }; + if x == 3 { + break; + } + } + + loop { + //~^ while_let_loop + let x = if let Some(x) = Some(3) { + x + } else { + break; + }; + if x == 3 { + break; + } + } + + loop { + //~^ while_let_loop + let x: u32 = if let Some(x) = Some(3) { + x + } else { + break; + }; + if x == 3 { + break; + } + } + + loop { + //~^ while_let_loop + let x = if let Some(x) = Some(2) { + let t = 1; + t + x + } else { + break; + }; + if x == 3 { + break; + } + } +} diff --git a/src/tools/clippy/tests/ui/while_let_loop.stderr b/src/tools/clippy/tests/ui/while_let_loop.stderr index bd482857e67..ed42628a53e 100644 --- a/src/tools/clippy/tests/ui/while_let_loop.stderr +++ b/src/tools/clippy/tests/ui/while_let_loop.stderr @@ -57,7 +57,125 @@ LL | | let (e, l) = match "".split_whitespace().next() { ... | LL | | let _ = (e, l); LL | | } - | |_____^ help: try: `while let Some(word) = "".split_whitespace().next() { .. }` + | |_____^ + | +help: try + | +LL ~ while let Some(word) = "".split_whitespace().next() { +LL + let (e, l) = (word.is_empty(), word.len()); +LL + .. +LL + } + | + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:167:9 + | +LL | / loop { +LL | | +LL | | let lt = match (ix.peek(), iy.peek()) { +LL | | (Some(x), Some(y)) => x < y, +... | +LL | | res.push(if lt { &mut ix } else { &mut iy }.next().unwrap()); +LL | | } + | |_________^ + | +help: try + | +LL ~ while let (Some(x), Some(y)) = (ix.peek(), iy.peek()) { +LL + let lt = x < y; +LL + .. +LL + } + | + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:182:5 + | +LL | / loop { +LL | | +LL | | let x = if let Some(y) = Some(3) { +LL | | y +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ while let Some(y) = Some(3) { +LL + let x = y; +LL + .. +LL + } + | + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:194:5 + | +LL | / loop { +LL | | +LL | | let x: u32 = if let Some(y) = Some(3) { +LL | | y +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ while let Some(y) = Some(3) { +LL + let x: u32 = y; +LL + .. +LL + } + | + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:206:5 + | +LL | / loop { +LL | | +LL | | let x = if let Some(x) = Some(3) { +LL | | x +... | +LL | | } + | |_____^ help: try: `while let Some(x) = Some(3) { .. }` + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:218:5 + | +LL | / loop { +LL | | +LL | | let x: u32 = if let Some(x) = Some(3) { +LL | | x +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ while let Some(x) = Some(3) { +LL + let x: u32 = x; +LL + .. +LL + } + | + +error: this loop could be written as a `while let` loop + --> tests/ui/while_let_loop.rs:230:5 + | +LL | / loop { +LL | | +LL | | let x = if let Some(x) = Some(2) { +LL | | let t = 1; +... | +LL | | } + | |_____^ + | +help: try + | +LL ~ while let Some(x) = Some(2) { +LL + let x = { +LL + let t = 1; +LL + t + x +LL + }; +LL + .. +LL + } + | -error: aborting due to 5 previous errors +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 389f22c6a2c..16557a4bebb 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -45,6 +45,7 @@ contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIB users_on_vacation = [ "matthiaskrgr", "Manishearth", + "blyxyas", ] [assign.owners] diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index 19dc1ec0b0c..865b9523c39 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -50,7 +50,7 @@ Otherwise, have a great day =^.^= <div class="container"> {# #} <div class="page-header"> {# #} - <h1>Clippy Lints</h1> {# #} + <h1>Clippy Lints <span id="lint-count" class="badge"></span></h1> {# #} </div> {# #} <noscript> {# #} diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index c942a6a05a1..fec883938d6 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -71,6 +71,7 @@ window.searchState = { } else { window.location.hash = ''; } + updateLintCount(); }, }; @@ -598,3 +599,14 @@ generateSearch(); parseURLFilters(); scrollToLintByURL(); filters.filterLints(); +updateLintCount(); + +function updateLintCount() { + const allLints = filters.getAllLints(); + const totalLints = allLints.length; + + const countElement = document.getElementById("lint-count"); + if (countElement) { + countElement.innerText = `Total number: ${totalLints}`; + } +} diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 17d0f3f5ff6..aaa3fc85a6c 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -8,8 +8,19 @@ pub mod thread; mod vector_clock; pub mod weak_memory; +// cfg(bootstrap) +macro_rules! cfg_select_dispatch { + ($($tokens:tt)*) => { + #[cfg(bootstrap)] + cfg_match! { $($tokens)* } + + #[cfg(not(bootstrap))] + cfg_select! { $($tokens)* } + }; +} + // Import either the real genmc adapter or a dummy module. -cfg_select! { +cfg_select_dispatch! { feature = "genmc" => { mod genmc; pub use self::genmc::{GenmcCtx, GenmcConfig}; diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 8802216448f..51ec19af52a 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,5 +1,6 @@ +#![cfg_attr(bootstrap, feature(cfg_match))] +#![cfg_attr(not(bootstrap), feature(cfg_select))] #![feature(rustc_private)] -#![feature(cfg_select)] #![feature(float_gamma)] #![feature(float_erf)] #![feature(map_try_insert)] diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 347930c52f2..31cb269059c 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -89,8 +89,19 @@ impl UnixFileDescription for FileHandle { communicate_allowed: bool, op: FlockOp, ) -> InterpResult<'tcx, io::Result<()>> { + // cfg(bootstrap) + macro_rules! cfg_select_dispatch { + ($($tokens:tt)*) => { + #[cfg(bootstrap)] + cfg_match! { $($tokens)* } + + #[cfg(not(bootstrap))] + cfg_select! { $($tokens)* } + }; + } + assert!(communicate_allowed, "isolation should have prevented even opening a file"); - cfg_select! { + cfg_select_dispatch! { all(target_family = "unix", not(target_os = "solaris")) => { use std::os::fd::AsRawFd; diff --git a/tests/crashes/140255.rs b/tests/crashes/140255.rs deleted file mode 100644 index 6b0ec1718b0..00000000000 --- a/tests/crashes/140255.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ known-bug: #140255 -#[unsafe(macro_use::VAR2)] -fn dead_code() {} diff --git a/tests/crashes/140484.rs b/tests/crashes/140484.rs deleted file mode 100644 index 92ec1984398..00000000000 --- a/tests/crashes/140484.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ known-bug: #140484 -//@edition:2024 -#![feature(async_drop)] -use std::future::AsyncDrop; -struct a; -impl Drop for a { - fn b() {} -} -impl AsyncDrop for a { - type c; -} -async fn bar() { - a; -} diff --git a/tests/crashes/140500.rs b/tests/crashes/140500.rs deleted file mode 100644 index ee5b93ab821..00000000000 --- a/tests/crashes/140500.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ known-bug: #140500 - -#![feature(async_drop)] -use std::future::AsyncDrop; -struct a; -impl Drop for a { - fn b() {} -} -impl AsyncDrop for a { - fn c(d: impl Sized) {} -} -async fn bar() { - a; -} diff --git a/tests/ui/allocator/auxiliary/helper.rs b/tests/ui/allocator/auxiliary/helper.rs index c638546a947..4267b901ca0 100644 --- a/tests/ui/allocator/auxiliary/helper.rs +++ b/tests/ui/allocator/auxiliary/helper.rs @@ -6,6 +6,6 @@ extern crate alloc; use alloc::fmt; -pub fn work_with(p: &fmt::Debug) { +pub fn work_with(p: &dyn fmt::Debug) { drop(p); } diff --git a/tests/ui/async-await/async-drop/type-parameter.rs b/tests/ui/async-await/async-drop/type-parameter.rs new file mode 100644 index 00000000000..dde5f9f8e64 --- /dev/null +++ b/tests/ui/async-await/async-drop/type-parameter.rs @@ -0,0 +1,16 @@ +//@ edition: 2024 +// ex-ice: #140500 +#![crate_type = "lib"] +#![feature(async_drop)] +#![expect(incomplete_features)] +use std::future::AsyncDrop; +struct A; +impl Drop for A { + fn drop(&mut self) {} +} +impl AsyncDrop for A { + fn drop(_wrong: impl Sized) {} //~ ERROR: method `drop` has a `self: Pin<&mut Self>` declaration in the trait, but not in the impl +} +async fn bar() { + A; +} diff --git a/tests/ui/async-await/async-drop/type-parameter.stderr b/tests/ui/async-await/async-drop/type-parameter.stderr new file mode 100644 index 00000000000..841576b839e --- /dev/null +++ b/tests/ui/async-await/async-drop/type-parameter.stderr @@ -0,0 +1,11 @@ +error[E0186]: method `drop` has a `self: Pin<&mut Self>` declaration in the trait, but not in the impl + --> $DIR/type-parameter.rs:12:5 + | +LL | fn drop(_wrong: impl Sized) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `self: Pin<&mut Self>` in impl + | + = note: `drop` from trait: `fn(Pin<&mut Self>) -> impl Future<Output = ()>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0186`. diff --git a/tests/ui/async-await/async-drop/unexpected-sort.rs b/tests/ui/async-await/async-drop/unexpected-sort.rs new file mode 100644 index 00000000000..659e21eb241 --- /dev/null +++ b/tests/ui/async-await/async-drop/unexpected-sort.rs @@ -0,0 +1,18 @@ +// Ex-ice: #140484 +//@ edition: 2024 +#![crate_type = "lib"] +#![allow(incomplete_features)] +#![allow(non_camel_case_types)] +#![feature(async_drop)] +use std::future::AsyncDrop; +struct a; +impl Drop for a { //~ ERROR: not all trait items implemented, missing: `drop` + fn b() {} //~ ERROR: method `b` is not a member of trait `Drop` +} +impl AsyncDrop for a { //~ ERROR: not all trait items implemented, missing: `drop` + type c = (); + //~^ ERROR: type `c` is not a member of trait `AsyncDrop` +} +async fn bar() { + a; +} diff --git a/tests/ui/async-await/async-drop/unexpected-sort.stderr b/tests/ui/async-await/async-drop/unexpected-sort.stderr new file mode 100644 index 00000000000..a6e4f9fd573 --- /dev/null +++ b/tests/ui/async-await/async-drop/unexpected-sort.stderr @@ -0,0 +1,32 @@ +error[E0407]: method `b` is not a member of trait `Drop` + --> $DIR/unexpected-sort.rs:10:5 + | +LL | fn b() {} + | ^^^^^^^^^ not a member of trait `Drop` + +error[E0437]: type `c` is not a member of trait `AsyncDrop` + --> $DIR/unexpected-sort.rs:13:5 + | +LL | type c = (); + | ^^^^^^^^^^^^ not a member of trait `AsyncDrop` + +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/unexpected-sort.rs:9:1 + | +LL | impl Drop for a { + | ^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `fn drop(&mut self) { todo!() }` + +error[E0046]: not all trait items implemented, missing: `drop` + --> $DIR/unexpected-sort.rs:12:1 + | +LL | impl AsyncDrop for a { + | ^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation + | + = help: implement the missing item: `async fn drop(self: Pin<&mut Self>) { todo!() }` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0046, E0407, E0437. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/attributes/auxiliary/external-macro-use.rs b/tests/ui/attributes/auxiliary/external-macro-use.rs new file mode 100644 index 00000000000..beeb7fe689c --- /dev/null +++ b/tests/ui/attributes/auxiliary/external-macro-use.rs @@ -0,0 +1,7 @@ +extern crate proc_macro; +use proc_macro::*; + +#[proc_macro_attribute] +pub fn a(_: TokenStream, input: TokenStream) -> TokenStream { + input +} diff --git a/tests/ui/attributes/illegal-macro-use.rs b/tests/ui/attributes/illegal-macro-use.rs new file mode 100644 index 00000000000..5a567107a6e --- /dev/null +++ b/tests/ui/attributes/illegal-macro-use.rs @@ -0,0 +1,15 @@ +// issue#140255 + +#[macro_use::a] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `macro_use` +fn f0() {} + +#[macro_use::a::b] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `macro_use` +fn f1() {} + +#[macro_escape::a] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `macro_escape` +fn f2() {} + +#[macro_escape::a::b] //~ ERROR failed to resolve: use of unresolved module or unlinked crate `macro_escape` +fn f3() {} + +fn main() {} diff --git a/tests/ui/attributes/illegal-macro-use.stderr b/tests/ui/attributes/illegal-macro-use.stderr new file mode 100644 index 00000000000..fa6bb83d588 --- /dev/null +++ b/tests/ui/attributes/illegal-macro-use.stderr @@ -0,0 +1,27 @@ +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `macro_escape` + --> $DIR/illegal-macro-use.rs:12:3 + | +LL | #[macro_escape::a::b] + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `macro_escape` + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `macro_escape` + --> $DIR/illegal-macro-use.rs:9:3 + | +LL | #[macro_escape::a] + | ^^^^^^^^^^^^ use of unresolved module or unlinked crate `macro_escape` + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `macro_use` + --> $DIR/illegal-macro-use.rs:6:3 + | +LL | #[macro_use::a::b] + | ^^^^^^^^^ use of unresolved module or unlinked crate `macro_use` + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `macro_use` + --> $DIR/illegal-macro-use.rs:3:3 + | +LL | #[macro_use::a] + | ^^^^^^^^^ use of unresolved module or unlinked crate `macro_use` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0433`. diff --git a/tests/ui/attributes/use-extern-crate-named-macro-use.rs b/tests/ui/attributes/use-extern-crate-named-macro-use.rs new file mode 100644 index 00000000000..5b322ccfaf7 --- /dev/null +++ b/tests/ui/attributes/use-extern-crate-named-macro-use.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@ proc-macro: external-macro-use.rs + +// issue#140255 + +extern crate external_macro_use as macro_use; + +#[macro_use::a] +fn f() {} + +fn main() {} diff --git a/tests/ui/coercion/retslot-cast.rs b/tests/ui/coercion/retslot-cast.rs index ae500cb15df..a87ac35a8e1 100644 --- a/tests/ui/coercion/retslot-cast.rs +++ b/tests/ui/coercion/retslot-cast.rs @@ -1,7 +1,7 @@ #![allow(warnings)] -pub fn fail(x: Option<&(Iterator<Item=()>+Send)>) - -> Option<&Iterator<Item=()>> { +pub fn fail(x: Option<&(dyn Iterator<Item=()>+Send)>) + -> Option<&dyn Iterator<Item=()>> { // This call used to trigger an LLVM assertion because the return // slot had type "Option<&Iterator>"* instead of // "Option<&(Iterator+Send)>"* -- but this now yields a @@ -13,8 +13,8 @@ pub fn fail(x: Option<&(Iterator<Item=()>+Send)>) inner(x) //~ ERROR mismatched types } -pub fn inner(x: Option<&(Iterator<Item=()>+Send)>) - -> Option<&(Iterator<Item=()>+Send)> { +pub fn inner(x: Option<&(dyn Iterator<Item=()>+Send)>) + -> Option<&(dyn Iterator<Item=()>+Send)> { x } diff --git a/tests/ui/coercion/retslot-cast.stderr b/tests/ui/coercion/retslot-cast.stderr index dac21a7f25b..a5242c13edd 100644 --- a/tests/ui/coercion/retslot-cast.stderr +++ b/tests/ui/coercion/retslot-cast.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types --> $DIR/retslot-cast.rs:13:5 | -LL | -> Option<&Iterator<Item=()>> { - | -------------------------- expected `Option<&dyn Iterator<Item = ()>>` because of return type +LL | -> Option<&dyn Iterator<Item=()>> { + | ------------------------------ expected `Option<&dyn Iterator<Item = ()>>` because of return type ... LL | inner(x) | ^^^^^^^^ expected trait `Iterator<Item = ()>`, found trait `Iterator<Item = ()> + Send` diff --git a/tests/ui/coroutine/auxiliary/xcrate.rs b/tests/ui/coroutine/auxiliary/xcrate.rs index 52f188135bd..524eaafc752 100644 --- a/tests/ui/coroutine/auxiliary/xcrate.rs +++ b/tests/ui/coroutine/auxiliary/xcrate.rs @@ -12,7 +12,7 @@ pub fn foo() -> impl Coroutine<(), Yield = (), Return = ()> { } } -pub fn bar<T: 'static>(t: T) -> Box<Coroutine<(), Yield = T, Return = ()> + Unpin> { +pub fn bar<T: 'static>(t: T) -> Box<dyn Coroutine<(), Yield = T, Return = ()> + Unpin> { Box::new( #[coroutine] || { diff --git a/tests/ui/deprecation/deprecation-lint.rs b/tests/ui/deprecation/deprecation-lint.rs index dc11a4d56a2..5eda38732c8 100644 --- a/tests/ui/deprecation/deprecation-lint.rs +++ b/tests/ui/deprecation/deprecation-lint.rs @@ -71,7 +71,7 @@ mod cross_crate { <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated method `deprecation_lint::Trait::trait_deprecated_text`: text } - fn test_method_object(foo: &Trait) { + fn test_method_object(foo: &dyn Trait) { foo.trait_deprecated(); //~ ERROR use of deprecated method `deprecation_lint::Trait::trait_deprecated` foo.trait_deprecated_text(); //~ ERROR use of deprecated method `deprecation_lint::Trait::trait_deprecated_text`: text } @@ -299,7 +299,7 @@ mod this_crate { <Foo as Trait>::trait_deprecated_text(&foo); //~ ERROR use of deprecated method `this_crate::Trait::trait_deprecated_text`: text } - fn test_method_object(foo: &Trait) { + fn test_method_object(foo: &dyn Trait) { foo.trait_deprecated(); //~ ERROR use of deprecated method `this_crate::Trait::trait_deprecated` foo.trait_deprecated_text(); //~ ERROR use of deprecated method `this_crate::Trait::trait_deprecated_text`: text } diff --git a/tests/ui/destructuring-assignment/bad-let-in-destructure.rs b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs new file mode 100644 index 00000000000..70a0403fc38 --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs @@ -0,0 +1,13 @@ +// Regression test for <https://github.com/rust-lang/rust/issues/141844>. + +fn main() { + // The following expression gets desugared into something like: + // ``` + // let (lhs,) = x; (let x = 1) = lhs; + // ``` + // This used to ICE since we haven't yet declared the type for `x` when + // checking the first desugared statement, whose RHS resolved to `x` since + // in the AST, the `let` expression was visited first. + (let x = 1,) = x; + //~^ ERROR expected expression, found `let` statement +} diff --git a/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr new file mode 100644 index 00000000000..622d714ba2f --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr @@ -0,0 +1,10 @@ +error: expected expression, found `let` statement + --> $DIR/bad-let-in-destructure.rs:11:4 + | +LL | (let x = 1,) = x; + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: aborting due to 1 previous error + diff --git a/tests/ui/did_you_mean/E0178.rs b/tests/ui/did_you_mean/E0178.rs index 095df640c38..cdaddac35a3 100644 --- a/tests/ui/did_you_mean/E0178.rs +++ b/tests/ui/did_you_mean/E0178.rs @@ -1,3 +1,4 @@ +//@ edition:2015 #![allow(bare_trait_objects)] trait Foo {} diff --git a/tests/ui/did_you_mean/E0178.stderr b/tests/ui/did_you_mean/E0178.stderr index 36e4dbdf7c4..7b69f860381 100644 --- a/tests/ui/did_you_mean/E0178.stderr +++ b/tests/ui/did_you_mean/E0178.stderr @@ -1,5 +1,5 @@ error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/E0178.rs:6:8 + --> $DIR/E0178.rs:7:8 | LL | w: &'a Foo + Copy, | ^^^^^^^ @@ -10,7 +10,7 @@ LL | w: &'a (Foo + Copy), | + + error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/E0178.rs:7:8 + --> $DIR/E0178.rs:8:8 | LL | x: &'a Foo + 'a, | ^^^^^^^ @@ -21,7 +21,7 @@ LL | x: &'a (Foo + 'a), | + + error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/E0178.rs:8:8 + --> $DIR/E0178.rs:9:8 | LL | y: &'a mut Foo + 'a, | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | y: &'a mut (Foo + 'a), | + + error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/E0178.rs:9:8 + --> $DIR/E0178.rs:10:8 | LL | z: fn() -> Foo + 'a, | ^^^^^^^^^^^----- diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs index b7470864a30..eff431f4665 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.rs @@ -1,3 +1,4 @@ +//@ edition: 2015 #![allow(bare_trait_objects)] fn main() { diff --git a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr index 57dbc79a0fd..b051d181ad8 100644 --- a/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr +++ b/tests/ui/did_you_mean/trait-object-reference-without-parens-suggestion.stderr @@ -1,5 +1,5 @@ error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:12 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:5:12 | LL | let _: &Copy + 'static; | ^^^^^ @@ -10,7 +10,7 @@ LL | let _: &(Copy + 'static); | + + error[E0178]: expected a path on the left-hand side of `+` - --> $DIR/trait-object-reference-without-parens-suggestion.rs:6:12 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:7:12 | LL | let _: &'static Copy + 'static; | ^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | let _: &'static (Copy + 'static); | + + error[E0038]: the trait `Copy` is not dyn compatible - --> $DIR/trait-object-reference-without-parens-suggestion.rs:4:13 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:5:13 | LL | let _: &Copy + 'static; | ^^^^ `Copy` is not dyn compatible @@ -31,7 +31,7 @@ LL | let _: &Copy + 'static; for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility> error[E0038]: the trait `Copy` is not dyn compatible - --> $DIR/trait-object-reference-without-parens-suggestion.rs:6:21 + --> $DIR/trait-object-reference-without-parens-suggestion.rs:7:21 | LL | let _: &'static Copy + 'static; | ^^^^ `Copy` is not dyn compatible diff --git a/tests/ui/dyn-drop/dyn-drop.rs b/tests/ui/dyn-drop/dyn-drop.rs index e1668a3f188..f336949d2cb 100644 --- a/tests/ui/dyn-drop/dyn-drop.rs +++ b/tests/ui/dyn-drop/dyn-drop.rs @@ -1,8 +1,7 @@ #![deny(dyn_drop)] -#![allow(bare_trait_objects)] fn foo(_: Box<dyn Drop>) {} //~ ERROR fn bar(_: &dyn Drop) {} //~ERROR -fn baz(_: *mut Drop) {} //~ ERROR +fn baz(_: *mut dyn Drop) {} //~ ERROR struct Foo { _x: Box<dyn Drop> //~ ERROR } diff --git a/tests/ui/dyn-drop/dyn-drop.stderr b/tests/ui/dyn-drop/dyn-drop.stderr index 1b1dbc4d12d..8210d8a4c48 100644 --- a/tests/ui/dyn-drop/dyn-drop.stderr +++ b/tests/ui/dyn-drop/dyn-drop.stderr @@ -1,5 +1,5 @@ error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/dyn-drop.rs:3:19 + --> $DIR/dyn-drop.rs:2:19 | LL | fn foo(_: Box<dyn Drop>) {} | ^^^^ @@ -11,25 +11,25 @@ LL | #![deny(dyn_drop)] | ^^^^^^^^ error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/dyn-drop.rs:4:16 + --> $DIR/dyn-drop.rs:3:16 | LL | fn bar(_: &dyn Drop) {} | ^^^^ error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/dyn-drop.rs:5:16 + --> $DIR/dyn-drop.rs:4:20 | -LL | fn baz(_: *mut Drop) {} - | ^^^^ +LL | fn baz(_: *mut dyn Drop) {} + | ^^^^ error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/dyn-drop.rs:7:15 + --> $DIR/dyn-drop.rs:6:15 | LL | _x: Box<dyn Drop> | ^^^^ error: types that do not implement `Drop` can still have drop glue, consider instead using `std::mem::needs_drop` to detect whether a type is trivially dropped - --> $DIR/dyn-drop.rs:14:16 + --> $DIR/dyn-drop.rs:13:16 | LL | type T = dyn Drop; | ^^^^ diff --git a/tests/ui/editions/async-block-2015.rs b/tests/ui/editions/async-block-2015.rs index a079b4aad91..21549f059de 100644 --- a/tests/ui/editions/async-block-2015.rs +++ b/tests/ui/editions/async-block-2015.rs @@ -1,3 +1,4 @@ +//@ edition:2015 async fn foo() { //~^ ERROR `async fn` is not permitted in Rust 2015 //~| NOTE to use `async fn`, switch to Rust 2018 or later diff --git a/tests/ui/editions/async-block-2015.stderr b/tests/ui/editions/async-block-2015.stderr index 574bcacc1cf..139df1758cc 100644 --- a/tests/ui/editions/async-block-2015.stderr +++ b/tests/ui/editions/async-block-2015.stderr @@ -1,5 +1,5 @@ error[E0670]: `async fn` is not permitted in Rust 2015 - --> $DIR/async-block-2015.rs:1:1 + --> $DIR/async-block-2015.rs:2:1 | LL | async fn foo() { | ^^^^^ to use `async fn`, switch to Rust 2018 or later @@ -8,7 +8,7 @@ LL | async fn foo() { = note: for more on editions, read https://doc.rust-lang.org/edition-guide error: expected identifier, found keyword `let` - --> $DIR/async-block-2015.rs:11:9 + --> $DIR/async-block-2015.rs:12:9 | LL | let y = async { | ----- `async` blocks are only allowed in Rust 2018 or later @@ -19,7 +19,7 @@ LL | let x = 42; = note: for more on editions, read https://doc.rust-lang.org/edition-guide error: expected identifier, found `42` - --> $DIR/async-block-2015.rs:19:9 + --> $DIR/async-block-2015.rs:20:9 | LL | let z = async { | ----- `async` blocks are only allowed in Rust 2018 or later @@ -30,7 +30,7 @@ LL | 42 = note: for more on editions, read https://doc.rust-lang.org/edition-guide error[E0422]: cannot find struct, variant or union type `async` in this scope - --> $DIR/async-block-2015.rs:7:13 + --> $DIR/async-block-2015.rs:8:13 | LL | let x = async {}; | ^^^^^ `async` blocks are only allowed in Rust 2018 or later diff --git a/tests/ui/ergonomic-clones/async/edition-2015.rs b/tests/ui/ergonomic-clones/async/edition-2015.rs index d3b2071b9f9..78831249429 100644 --- a/tests/ui/ergonomic-clones/async/edition-2015.rs +++ b/tests/ui/ergonomic-clones/async/edition-2015.rs @@ -1,3 +1,4 @@ +//@ edition:2015 #![feature(ergonomic_clones)] #![allow(incomplete_features)] diff --git a/tests/ui/ergonomic-clones/async/edition-2015.stderr b/tests/ui/ergonomic-clones/async/edition-2015.stderr index b218e6b242e..8de90f4bfd3 100644 --- a/tests/ui/ergonomic-clones/async/edition-2015.stderr +++ b/tests/ui/ergonomic-clones/async/edition-2015.stderr @@ -1,5 +1,5 @@ error: `async use` blocks are only allowed in Rust 2018 or later - --> $DIR/edition-2015.rs:5:5 + --> $DIR/edition-2015.rs:6:5 | LL | async use {}; | ^^^^^^^^^ diff --git a/tests/ui/error-codes/E0657.rs b/tests/ui/error-codes/E0657.rs index 212c1d9e581..f046788153d 100644 --- a/tests/ui/error-codes/E0657.rs +++ b/tests/ui/error-codes/E0657.rs @@ -7,7 +7,7 @@ impl<'a> Lt<'a> for () {} impl<T> Id<T> for T {} fn free_fn_capture_hrtb_in_impl_trait() - -> Box<for<'a> Id<impl Lt<'a>>> + -> Box<dyn for<'a> Id<impl Lt<'a>>> //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type { Box::new(()) @@ -16,7 +16,7 @@ fn free_fn_capture_hrtb_in_impl_trait() struct Foo; impl Foo { fn impl_fn_capture_hrtb_in_impl_trait() - -> Box<for<'a> Id<impl Lt<'a>>> + -> Box<dyn for<'a> Id<impl Lt<'a>>> //~^ ERROR `impl Trait` cannot capture higher-ranked lifetime from `dyn` type { Box::new(()) diff --git a/tests/ui/error-codes/E0657.stderr b/tests/ui/error-codes/E0657.stderr index c539007cdcf..c9dfc9eb906 100644 --- a/tests/ui/error-codes/E0657.stderr +++ b/tests/ui/error-codes/E0657.stderr @@ -1,26 +1,26 @@ error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type - --> $DIR/E0657.rs:10:31 + --> $DIR/E0657.rs:10:35 | -LL | -> Box<for<'a> Id<impl Lt<'a>>> - | ^^ +LL | -> Box<dyn for<'a> Id<impl Lt<'a>>> + | ^^ | note: lifetime declared here - --> $DIR/E0657.rs:10:16 + --> $DIR/E0657.rs:10:20 | -LL | -> Box<for<'a> Id<impl Lt<'a>>> - | ^^ +LL | -> Box<dyn for<'a> Id<impl Lt<'a>>> + | ^^ error[E0657]: `impl Trait` cannot capture higher-ranked lifetime from `dyn` type - --> $DIR/E0657.rs:19:35 + --> $DIR/E0657.rs:19:39 | -LL | -> Box<for<'a> Id<impl Lt<'a>>> - | ^^ +LL | -> Box<dyn for<'a> Id<impl Lt<'a>>> + | ^^ | note: lifetime declared here - --> $DIR/E0657.rs:19:20 + --> $DIR/E0657.rs:19:24 | -LL | -> Box<for<'a> Id<impl Lt<'a>>> - | ^^ +LL | -> Box<dyn for<'a> Id<impl Lt<'a>>> + | ^^ error: aborting due to 2 previous errors diff --git a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.rs b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.rs index 69a4c13530b..d6bb6466227 100644 --- a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.rs +++ b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.rs @@ -1,3 +1,4 @@ +//@ edition:2015 trait MyIterator : Iterator {} fn main() { diff --git a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr index 24f00cfa6be..7f3022c2923 100644 --- a/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr +++ b/tests/ui/errors/dynless-turbofish-e0191-issue-91997.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/dynless-turbofish-e0191-issue-91997.rs:4:13 + --> $DIR/dynless-turbofish-e0191-issue-91997.rs:5:13 | LL | let _ = MyIterator::next; | ^^^^^^^^^^ @@ -13,7 +13,7 @@ LL | let _ = <dyn MyIterator>::next; | ++++ + error[E0191]: the value of the associated type `Item` in `Iterator` must be specified - --> $DIR/dynless-turbofish-e0191-issue-91997.rs:4:13 + --> $DIR/dynless-turbofish-e0191-issue-91997.rs:5:13 | LL | let _ = MyIterator::next; | ^^^^^^^^^^ help: specify the associated type: `MyIterator::<Item = Type>` diff --git a/tests/ui/expr/scope.rs b/tests/ui/expr/scope.rs index 3a1c8b87da8..b059e43de9e 100644 --- a/tests/ui/expr/scope.rs +++ b/tests/ui/expr/scope.rs @@ -1,3 +1,4 @@ +//@ edition:2015 //@ run-pass // Regression test for issue #762 diff --git a/tests/ui/imports/import-glob-crate.rs b/tests/ui/imports/import-glob-crate.rs index 0a2ca6ef2c3..ee067931876 100644 --- a/tests/ui/imports/import-glob-crate.rs +++ b/tests/ui/imports/import-glob-crate.rs @@ -1,3 +1,4 @@ +//@ edition: 2015 //@ run-pass use std::mem::*; diff --git a/tests/ui/intrinsics/non-integer-atomic.rs b/tests/ui/intrinsics/non-integer-atomic.rs index dd129e55945..5464bf747fa 100644 --- a/tests/ui/intrinsics/non-integer-atomic.rs +++ b/tests/ui/intrinsics/non-integer-atomic.rs @@ -8,7 +8,7 @@ use std::intrinsics::{self, AtomicOrdering}; #[derive(Copy, Clone)] pub struct Foo(i64); -pub type Bar = &'static Fn(); +pub type Bar = &'static dyn Fn(); pub type Quux = [u8; 100]; pub unsafe fn test_bool_load(p: &mut bool, v: bool) { diff --git a/tests/ui/macros/auxiliary/borrowck-error-in-macro.rs b/tests/ui/macros/auxiliary/borrowck-error-in-macro.rs new file mode 100644 index 00000000000..2d5f2bda2ed --- /dev/null +++ b/tests/ui/macros/auxiliary/borrowck-error-in-macro.rs @@ -0,0 +1,10 @@ +#[macro_export] +macro_rules! ice { + () => { + fn main() { + let d = &mut 0; + let c = || *d += 1; + c(); + } + }; +} diff --git a/tests/ui/macros/borrowck-error-in-macro.rs b/tests/ui/macros/borrowck-error-in-macro.rs new file mode 100644 index 00000000000..fe75188efd2 --- /dev/null +++ b/tests/ui/macros/borrowck-error-in-macro.rs @@ -0,0 +1,8 @@ +//@ aux-build: borrowck-error-in-macro.rs +//@ error-pattern: a call in this macro requires a mutable binding due to mutable borrow of `d` +//FIXME: remove error-pattern (see #141896) + +extern crate borrowck_error_in_macro as a; + +a::ice! {} +//~^ ERROR cannot borrow value as mutable, as it is not declared as mutable diff --git a/tests/ui/macros/borrowck-error-in-macro.stderr b/tests/ui/macros/borrowck-error-in-macro.stderr new file mode 100644 index 00000000000..ec0302ee287 --- /dev/null +++ b/tests/ui/macros/borrowck-error-in-macro.stderr @@ -0,0 +1,19 @@ +error[E0596]: cannot borrow value as mutable, as it is not declared as mutable + --> $DIR/borrowck-error-in-macro.rs:7:1 + | +LL | a::ice! {} + | ^^^^^^^^^^ + | | + | cannot borrow as mutable + | a call in this macro requires a mutable binding due to mutable borrow of `d` + | + = note: this error originates in the macro `a::ice` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider changing this to be mutable + --> $DIR/auxiliary/borrowck-error-in-macro.rs:6:17 + | +LL | let mut c = || *d += 1; + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/resolve/extern-crate-lint-issue-141785.rs b/tests/ui/resolve/extern-crate-lint-issue-141785.rs new file mode 100644 index 00000000000..8d044d666df --- /dev/null +++ b/tests/ui/resolve/extern-crate-lint-issue-141785.rs @@ -0,0 +1,11 @@ +//@ check-pass +//@ edition:2018 + +#![no_implicit_prelude] +#![warn(unused_extern_crates)] + +extern crate std; +fn main() { + let r = 1u16..10; + std::println!("{:?}", r.is_empty()); +} |
