diff options
879 files changed, 15457 insertions, 8556 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1497d2d463a..e973d7f9a2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4630,7 +4630,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" dependencies = [ "annotate-snippets 0.9.2", "anyhow", diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index f4de4e06d1b..01593d34c97 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -527,15 +527,10 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> { let count = (niche_variants.end().index() as u128 - niche_variants.start().index() as u128) + 1; - // Find the field with the largest niche - let (field_index, niche, (niche_start, niche_scalar)) = variants[largest_variant_index] - .iter() - .enumerate() - .filter_map(|(j, field)| Some((j, field.largest_niche?))) - .max_by_key(|(_, niche)| niche.available(dl)) - .and_then(|(j, niche)| Some((j, niche, niche.reserve(dl, count)?)))?; - let niche_offset = - niche.offset + variant_layouts[largest_variant_index].fields.offset(field_index); + // Use the largest niche in the largest variant. + let niche = variant_layouts[largest_variant_index].largest_niche?; + let (niche_start, niche_scalar) = niche.reserve(dl, count)?; + let niche_offset = niche.offset; let niche_size = niche.value.size(dl); let size = variant_layouts[largest_variant_index].size.align_to(align.abi); diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 5668d8992c8..7bd2507d1ad 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -833,6 +833,28 @@ pub enum Integer { } impl Integer { + pub fn int_ty_str(self) -> &'static str { + use Integer::*; + match self { + I8 => "i8", + I16 => "i16", + I32 => "i32", + I64 => "i64", + I128 => "i128", + } + } + + pub fn uint_ty_str(self) -> &'static str { + use Integer::*; + match self { + I8 => "u8", + I16 => "u16", + I32 => "u32", + I64 => "u64", + I128 => "u128", + } + } + #[inline] pub fn size(self) -> Size { use Integer::*; diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f433f2eca1a..ac65b7b22bc 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1293,7 +1293,7 @@ impl Expr { ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), ExprKind::Unary(..) => ExprPrecedence::Unary, ExprKind::Lit(_) | ExprKind::IncludedBytes(..) => ExprPrecedence::Lit, - ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, + ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Let(..) => ExprPrecedence::Let, ExprKind::If(..) => ExprPrecedence::If, ExprKind::While(..) => ExprPrecedence::While, @@ -1317,17 +1317,18 @@ impl Expr { ExprKind::Break(..) => ExprPrecedence::Break, ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, - ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf, - ExprKind::MacCall(..) => ExprPrecedence::Mac, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, ExprKind::Paren(..) => ExprPrecedence::Paren, ExprKind::Try(..) => ExprPrecedence::Try, ExprKind::Yield(..) => ExprPrecedence::Yield, ExprKind::Yeet(..) => ExprPrecedence::Yeet, - ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs, ExprKind::Become(..) => ExprPrecedence::Become, + ExprKind::InlineAsm(..) + | ExprKind::Type(..) + | ExprKind::OffsetOf(..) + | ExprKind::FormatArgs(..) + | ExprKind::MacCall(..) => ExprPrecedence::Mac, ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Err, } } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 8436c760d16..d8dad4550c0 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -265,10 +265,7 @@ pub enum ExprPrecedence { Field, Index, Try, - InlineAsm, - OffsetOf, Mac, - FormatArgs, Array, Repeat, @@ -333,17 +330,14 @@ impl ExprPrecedence { | ExprPrecedence::ConstBlock | ExprPrecedence::Field | ExprPrecedence::ForLoop - | ExprPrecedence::FormatArgs | ExprPrecedence::Gen | ExprPrecedence::If | ExprPrecedence::Index - | ExprPrecedence::InlineAsm | ExprPrecedence::Lit | ExprPrecedence::Loop | ExprPrecedence::Mac | ExprPrecedence::Match | ExprPrecedence::MethodCall - | ExprPrecedence::OffsetOf | ExprPrecedence::Paren | ExprPrecedence::Path | ExprPrecedence::PostfixMatch diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index edb25e12864..ee4b2f95cb1 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -207,7 +207,7 @@ borrowck_simd_intrinsic_arg_const = *[other] {$arg}th } argument of `{$intrinsic}` is required to be a `const` item -borrowck_suggest_create_freash_reborrow = +borrowck_suggest_create_fresh_reborrow = consider reborrowing the `Pin` instead of moving it borrowck_suggest_iterate_over_slice = diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 4a50b0f0704..b6c6960d4ca 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -415,7 +415,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> { span: Span, }, #[suggestion( - borrowck_suggest_create_freash_reborrow, + borrowck_suggest_create_fresh_reborrow, applicability = "maybe-incorrect", code = ".as_mut()", style = "verbose" diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 4af4b39cc5b..8839829e2f5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -785,8 +785,10 @@ fn codegen_stmt<'tcx>( } Rvalue::Repeat(ref operand, times) => { let operand = codegen_operand(fx, operand); - let times = - fx.monomorphize(times).eval_target_usize(fx.tcx, ParamEnv::reveal_all()); + let times = fx + .monomorphize(times) + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen"); if operand.layout().size.bytes() == 0 { // Do nothing for ZST's } else if fx.clif_type(operand.layout().ty) == Some(types::I8) { @@ -944,7 +946,10 @@ fn codegen_stmt<'tcx>( fn codegen_array_len<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, place: CPlace<'tcx>) -> Value { match *place.layout().ty.kind() { ty::Array(_elem_ty, len) => { - let len = fx.monomorphize(len).eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64; + let len = fx + .monomorphize(len) + .try_to_target_usize(fx.tcx) + .expect("expected monomorphic const in codegen") as i64; fx.bcx.ins().iconst(fx.pointer_type, len) } ty::Slice(_elem_ty) => place.to_ptr_unsized().1, diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index b9000a3874f..22ca2070715 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -309,8 +309,6 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { } impl<'tcx> LayoutOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { RevealAllLayoutCx(self.tcx).handle_layout_err(err, span, ty) @@ -318,8 +316,6 @@ impl<'tcx> LayoutOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { } impl<'tcx> FnAbiOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, @@ -450,8 +446,6 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { @@ -466,8 +460,6 @@ impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { } impl<'tcx> FnAbiOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs index 7baf0a3868d..8a55a23128d 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/types.rs @@ -44,7 +44,7 @@ impl DebugContext { type_dbg, ty, *elem_ty, - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), + len.try_to_target_usize(tcx).expect("expected monomorphic const in codegen"), ), // ty::Slice(_) | ty::Str // ty::Dynamic diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs index a5621aec244..b96abb14b2e 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -131,9 +131,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let idx = generic_args[2] .expect_const() - .eval(fx.tcx, ty::ParamEnv::reveal_all(), span) - .unwrap() - .1 + .try_to_valtree() + .expect("expected monomorphic const in codegen") .unwrap_branch(); assert_eq!(x.layout(), y.layout()); diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index e09cd16e89a..8cfe93b4d9c 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -24,10 +24,10 @@ pub(crate) fn unsized_info<'tcx>( let (source, target) = fx.tcx.struct_lockstep_tails_for_codegen(source, target, ParamEnv::reveal_all()); match (&source.kind(), &target.kind()) { - (&ty::Array(_, len), &ty::Slice(_)) => fx - .bcx - .ins() - .iconst(fx.pointer_type, len.eval_target_usize(fx.tcx, ParamEnv::reveal_all()) as i64), + (&ty::Array(_, len), &ty::Slice(_)) => fx.bcx.ins().iconst( + fx.pointer_type, + len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64, + ), (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) if src_dyn_kind == target_dyn_kind => { diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 0a99e7213be..ed78d4ef19f 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -1,7 +1,7 @@ #[cfg(feature = "master")] use gccjit::FnAttribute; use gccjit::{ToLValue, ToRValue, Type}; -use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods}; +use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeCodegenMethods}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::bug; use rustc_middle::ty::layout::LayoutOf; diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 7c135289958..13a00f7e08d 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -5,8 +5,8 @@ use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{ - AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, - InlineAsmOperandRef, + AsmBuilderMethods, AsmCodegenMethods, BaseTypeCodegenMethods, BuilderMethods, + GlobalAsmOperandRef, InlineAsmOperandRef, }; use rustc_middle::bug; use rustc_middle::ty::Instance; @@ -770,7 +770,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl } } -impl<'gcc, 'tcx> AsmMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn codegen_global_asm( &self, template: &[InlineAsmTemplatePiece], diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 4940a7fa205..c0443faf24a 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -6,7 +6,7 @@ use std::time::Instant; use gccjit::{CType, FunctionType, GlobalKind}; use rustc_codegen_ssa::base::maybe_create_entry_wrapper; use rustc_codegen_ssa::mono_item::MonoItemExt; -use rustc_codegen_ssa::traits::DebugInfoMethods; +use rustc_codegen_ssa::traits::DebugInfoCodegenMethods; use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; use rustc_middle::dep_graph; use rustc_middle::mir::mono::Linkage; diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 31d778823e0..9282d8699eb 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -14,8 +14,8 @@ use rustc_codegen_ssa::common::{ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{ - BackendTypes, BaseTypeMethods, BuilderMethods, ConstMethods, HasCodegen, LayoutTypeMethods, - OverflowOp, StaticBuilderMethods, + BackendTypes, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, + LayoutTypeCodegenMethods, OverflowOp, StaticBuilderMethods, }; use rustc_codegen_ssa::MemFlags; use rustc_data_structures::fx::FxHashSet; @@ -23,7 +23,6 @@ use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, - TyAndLayout, }; use rustc_middle::ty::{Instance, ParamEnv, Ty, TyCtxt}; use rustc_span::def_id::DefId; @@ -460,10 +459,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } } -impl<'gcc, 'tcx> HasCodegen<'tcx> for Builder<'_, 'gcc, 'tcx> { - type CodegenCx = CodegenCx<'gcc, 'tcx>; -} - impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.cx.tcx() @@ -477,8 +472,6 @@ impl HasDataLayout for Builder<'_, '_, '_> { } impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { self.cx.handle_layout_err(err, span, ty) @@ -486,8 +479,6 @@ impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { } impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, @@ -531,6 +522,8 @@ fn set_rvalue_location<'a, 'gcc, 'tcx>( } impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { + type CodegenCx = CodegenCx<'gcc, 'tcx>; + fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Builder<'a, 'gcc, 'tcx> { Builder::with_cx(cx, block) } diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dca6b6494f9..b7ed34858b5 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -1,5 +1,7 @@ use gccjit::{LValue, RValue, ToRValue, Type}; -use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, MiscMethods, StaticMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, ConstCodegenMethods, MiscCodegenMethods, StaticCodegenMethods, +}; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::mir::Mutability; use rustc_middle::ty::layout::LayoutOf; @@ -55,7 +57,7 @@ pub fn type_is_pointer(typ: Type<'_>) -> bool { typ.get_pointee().is_some() } -impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> { if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) } } @@ -78,22 +80,14 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_undef(typ) } - fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { - self.gcc_int(typ, int) - } - - fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> { - self.gcc_uint(typ, int) - } - - fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> { - self.gcc_uint_big(typ, num) - } - fn const_bool(&self, val: bool) -> RValue<'gcc> { self.const_uint(self.type_i1(), val as u64) } + fn const_i8(&self, i: i8) -> RValue<'gcc> { + self.const_int(self.type_i8(), i as i64) + } + fn const_i16(&self, i: i16) -> RValue<'gcc> { self.const_int(self.type_i16(), i as i64) } @@ -102,8 +96,12 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_int(self.type_i32(), i as i64) } - fn const_i8(&self, i: i8) -> RValue<'gcc> { - self.const_int(self.type_i8(), i as i64) + fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { + self.gcc_int(typ, int) + } + + fn const_u8(&self, i: u8) -> RValue<'gcc> { + self.const_uint(self.type_u8(), i as u64) } fn const_u32(&self, i: u32) -> RValue<'gcc> { @@ -128,8 +126,12 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.const_uint(self.usize_type, i) } - fn const_u8(&self, i: u8) -> RValue<'gcc> { - self.const_uint(self.type_u8(), i as u64) + fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> { + self.gcc_uint(typ, int) + } + + fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> { + self.gcc_uint_big(typ, num) } fn const_real(&self, typ: Type<'gcc>, val: f64) -> RValue<'gcc> { diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index e5673cddc4a..68b9df946d0 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -1,7 +1,9 @@ #[cfg(feature = "master")] use gccjit::{FnAttribute, VarAttribute, Visibility}; use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type}; -use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, StaticMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods, +}; use rustc_hir::def::DefKind; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ @@ -37,7 +39,7 @@ fn set_global_alignment<'gcc, 'tcx>( gv.set_alignment(align.bytes() as i32); } -impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { // TODO(antoyo): implement a proper rvalue comparison in libgccjit instead of doing the // following: diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index e330102fbd8..8e1a5b61285 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -5,20 +5,19 @@ use gccjit::{ }; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::errors as ssa_errors; -use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, MiscMethods}; +use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods}; use rustc_data_structures::base_n::{ToBaseN, ALPHANUMERIC_ONLY}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::mono::CodegenUnit; use rustc_middle::span_bug; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOf, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, - LayoutOfHelpers, TyAndLayout, + LayoutOfHelpers, }; use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_session::Session; use rustc_span::source_map::respan; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi}; @@ -426,7 +425,7 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { type DIVariable = (); // TODO(antoyo) } -impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn vtables( &self, ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>), RValue<'gcc>>> { @@ -572,8 +571,6 @@ impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> { } impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { @@ -585,8 +582,6 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { } impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index f2ae9f9c8a6..1d859656b3e 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -2,7 +2,7 @@ use std::ops::Range; use gccjit::{Location, RValue}; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; -use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods}; +use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; use rustc_data_structures::sync::Lrc; use rustc_index::bit_set::BitSet; use rustc_index::{Idx, IndexVec}; @@ -206,7 +206,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } -impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn create_vtable_debuginfo( &self, _ty: Ty<'tcx>, diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index a2b158ee0a7..46818045f0b 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -1,7 +1,7 @@ #[cfg(feature = "master")] use gccjit::{FnAttribute, ToRValue}; use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type}; -use rustc_codegen_ssa::traits::BaseTypeMethods; +use rustc_codegen_ssa::traits::BaseTypeCodegenMethods; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::abi::call::FnAbi; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 92d5c1cbbb8..29f4db6738b 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -4,7 +4,7 @@ use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; -use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeMethods, BuilderMethods, OverflowOp}; +use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp}; use rustc_middle::ty::{ParamEnv, Ty}; use rustc_target::abi::call::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode}; use rustc_target::abi::Endian; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 9352a67e362..4fd033255fe 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -13,10 +13,10 @@ use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::{ - ArgAbiMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods, + ArgAbiBuilderMethods, BuilderMethods, ConstCodegenMethods, IntrinsicCallBuilderMethods, }; #[cfg(feature = "master")] -use rustc_codegen_ssa::traits::{BaseTypeMethods, MiscMethods}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, MiscCodegenMethods}; use rustc_codegen_ssa::MemFlags; use rustc_middle::bug; use rustc_middle::ty::layout::LayoutOf; @@ -94,7 +94,7 @@ fn get_simple_intrinsic<'gcc, 'tcx>( Some(cx.context.get_builtin_function(gcc_name)) } -impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { +impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn codegen_intrinsic_call( &mut self, instance: Instance<'tcx>, @@ -448,7 +448,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } } -impl<'a, 'gcc, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { +impl<'a, 'gcc, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn store_fn_arg( &mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 2eabc1430db..28f6a0821fb 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -10,7 +10,7 @@ use rustc_codegen_ssa::errors::ExpectedPointerMutability; use rustc_codegen_ssa::errors::InvalidMonomorphization; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; -use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; #[cfg(feature = "master")] use rustc_hir as hir; use rustc_middle::mir::BinOp; diff --git a/compiler/rustc_codegen_gcc/src/mono_item.rs b/compiler/rustc_codegen_gcc/src/mono_item.rs index e6b22d51871..8a8b748750c 100644 --- a/compiler/rustc_codegen_gcc/src/mono_item.rs +++ b/compiler/rustc_codegen_gcc/src/mono_item.rs @@ -1,6 +1,6 @@ #[cfg(feature = "master")] use gccjit::{FnAttribute, VarAttribute}; -use rustc_codegen_ssa::traits::PreDefineMethods; +use rustc_codegen_ssa::traits::PreDefineCodegenMethods; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_middle::bug; @@ -13,7 +13,7 @@ use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; use crate::{attributes, base}; -impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { #[cfg_attr(not(feature = "master"), allow(unused_variables))] fn predefine_static( &self, diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index e20234c5ce7..4ea5544721d 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -5,7 +5,9 @@ use std::convert::TryInto; use gccjit::CType; use gccjit::{RValue, Struct, Type}; use rustc_codegen_ssa::common::TypeKind; -use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, DerivedTypeCodegenMethods, TypeMembershipCodegenMethods, +}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, ty}; use rustc_target::abi::{AddressSpace, Align, Integer, Size}; @@ -121,7 +123,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } -impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn type_i8(&self) -> Type<'gcc> { self.i8_type } @@ -381,4 +383,4 @@ pub fn struct_fields<'gcc, 'tcx>( (result, packed) } -impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {} +impl<'gcc, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> {} diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index b7b1be5369c..cb45bbde2c2 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -1,7 +1,9 @@ use std::fmt::Write; use gccjit::{Struct, Type}; -use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods}; +use rustc_codegen_ssa::traits::{ + BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods, +}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -330,7 +332,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } } -impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { +impl<'gcc, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> { layout.gcc_type(self) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index dea574a53cd..26718792f5f 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -285,7 +285,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } } -impl<'ll, 'tcx> ArgAbiMethods<'tcx> for Builder<'_, 'll, 'tcx> { +impl<'ll, 'tcx> ArgAbiBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn store_fn_arg( &mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, @@ -465,9 +465,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { cx.type_array(cx.type_i8(), self.ret.layout.size.bytes()), ); attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]); - if cx.sess().opts.optimize != config::OptLevel::No - && llvm_util::get_version() >= (18, 0, 0) - { + if cx.sess().opts.optimize != config::OptLevel::No { attributes::apply_to_llfn( llfn, llvm::AttributePlace::Argument(i), diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 1d91c3fb17d..430ba735243 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -356,7 +356,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } -impl<'tcx> AsmMethods<'tcx> for CodegenCx<'_, 'tcx> { +impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { fn codegen_global_asm( &self, template: &[InlineAsmTemplatePiece], @@ -520,24 +520,16 @@ pub(crate) fn inline_asm_call<'ll>( /// If the register is an xmm/ymm/zmm register then return its index. fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> { + use X86InlineAsmReg::*; match reg { - InlineAsmReg::X86(reg) - if reg as u32 >= X86InlineAsmReg::xmm0 as u32 - && reg as u32 <= X86InlineAsmReg::xmm15 as u32 => - { - Some(reg as u32 - X86InlineAsmReg::xmm0 as u32) + InlineAsmReg::X86(reg) if reg as u32 >= xmm0 as u32 && reg as u32 <= xmm15 as u32 => { + Some(reg as u32 - xmm0 as u32) } - InlineAsmReg::X86(reg) - if reg as u32 >= X86InlineAsmReg::ymm0 as u32 - && reg as u32 <= X86InlineAsmReg::ymm15 as u32 => - { - Some(reg as u32 - X86InlineAsmReg::ymm0 as u32) + InlineAsmReg::X86(reg) if reg as u32 >= ymm0 as u32 && reg as u32 <= ymm15 as u32 => { + Some(reg as u32 - ymm0 as u32) } - InlineAsmReg::X86(reg) - if reg as u32 >= X86InlineAsmReg::zmm0 as u32 - && reg as u32 <= X86InlineAsmReg::zmm31 as u32 => - { - Some(reg as u32 - X86InlineAsmReg::zmm0 as u32) + InlineAsmReg::X86(reg) if reg as u32 >= zmm0 as u32 && reg as u32 <= zmm31 as u32 => { + Some(reg as u32 - zmm0 as u32) } _ => None, } @@ -545,50 +537,56 @@ fn xmm_reg_index(reg: InlineAsmReg) -> Option<u32> { /// If the register is an AArch64 integer register then return its index. fn a64_reg_index(reg: InlineAsmReg) -> Option<u32> { - match reg { - InlineAsmReg::AArch64(AArch64InlineAsmReg::x0) => Some(0), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x1) => Some(1), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x2) => Some(2), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x3) => Some(3), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x4) => Some(4), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x5) => Some(5), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x6) => Some(6), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x7) => Some(7), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x8) => Some(8), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x9) => Some(9), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x10) => Some(10), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x11) => Some(11), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x12) => Some(12), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x13) => Some(13), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x14) => Some(14), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x15) => Some(15), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x16) => Some(16), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x17) => Some(17), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x18) => Some(18), - // x19 is reserved - InlineAsmReg::AArch64(AArch64InlineAsmReg::x20) => Some(20), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x21) => Some(21), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x22) => Some(22), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x23) => Some(23), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x24) => Some(24), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x25) => Some(25), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x26) => Some(26), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x27) => Some(27), - InlineAsmReg::AArch64(AArch64InlineAsmReg::x28) => Some(28), - // x29 is reserved - InlineAsmReg::AArch64(AArch64InlineAsmReg::x30) => Some(30), - _ => None, - } + use AArch64InlineAsmReg::*; + // Unlike `a64_vreg_index`, we can't subtract `x0` to get the u32 because + // `x19` and `x29` are missing and the integer constants for the + // `x0`..`x30` enum variants don't all match the register number. E.g. the + // integer constant for `x18` is 18, but the constant for `x20` is 19. + Some(match reg { + InlineAsmReg::AArch64(r) => match r { + x0 => 0, + x1 => 1, + x2 => 2, + x3 => 3, + x4 => 4, + x5 => 5, + x6 => 6, + x7 => 7, + x8 => 8, + x9 => 9, + x10 => 10, + x11 => 11, + x12 => 12, + x13 => 13, + x14 => 14, + x15 => 15, + x16 => 16, + x17 => 17, + x18 => 18, + // x19 is reserved + x20 => 20, + x21 => 21, + x22 => 22, + x23 => 23, + x24 => 24, + x25 => 25, + x26 => 26, + x27 => 27, + x28 => 28, + // x29 is reserved + x30 => 30, + _ => return None, + }, + _ => return None, + }) } /// If the register is an AArch64 vector register then return its index. fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> { + use AArch64InlineAsmReg::*; match reg { - InlineAsmReg::AArch64(reg) - if reg as u32 >= AArch64InlineAsmReg::v0 as u32 - && reg as u32 <= AArch64InlineAsmReg::v31 as u32 => - { - Some(reg as u32 - AArch64InlineAsmReg::v0 as u32) + InlineAsmReg::AArch64(reg) if reg as u32 >= v0 as u32 && reg as u32 <= v31 as u32 => { + Some(reg as u32 - v0 as u32) } _ => None, } @@ -596,6 +594,7 @@ fn a64_vreg_index(reg: InlineAsmReg) -> Option<u32> { /// Converts a register class to an LLVM constraint code. fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> String { + use InlineAsmRegClass::*; match reg { // For vector registers LLVM wants the register name to match the type size. InlineAsmRegOrRegClass::Reg(reg) => { @@ -652,75 +651,66 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> // The constraints can be retrieved from // https://llvm.org/docs/LangRef.html#supported-constraint-code-list InlineAsmRegOrRegClass::RegClass(reg) => match reg { - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => "t", - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => "x", - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "w", - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + AArch64(AArch64InlineAsmRegClass::reg) => "r", + AArch64(AArch64InlineAsmRegClass::vreg) => "w", + AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", + AArch64(AArch64InlineAsmRegClass::preg) => unreachable!("clobber-only"), + Arm(ArmInlineAsmRegClass::reg) => "r", + Arm(ArmInlineAsmRegClass::sreg) + | Arm(ArmInlineAsmRegClass::dreg_low16) + | Arm(ArmInlineAsmRegClass::qreg_low8) => "t", + Arm(ArmInlineAsmRegClass::sreg_low16) + | Arm(ArmInlineAsmRegClass::dreg_low8) + | Arm(ArmInlineAsmRegClass::qreg_low4) => "x", + Arm(ArmInlineAsmRegClass::dreg) | Arm(ArmInlineAsmRegClass::qreg) => "w", + Hexagon(HexagonInlineAsmRegClass::reg) => "r", + LoongArch(LoongArchInlineAsmRegClass::reg) => "r", + LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + Mips(MipsInlineAsmRegClass::reg) => "r", + Mips(MipsInlineAsmRegClass::freg) => "f", + Nvptx(NvptxInlineAsmRegClass::reg16) => "h", + Nvptx(NvptxInlineAsmRegClass::reg32) => "r", + Nvptx(NvptxInlineAsmRegClass::reg64) => "l", + PowerPC(PowerPCInlineAsmRegClass::reg) => "r", + PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", + PowerPC(PowerPCInlineAsmRegClass::freg) => "f", + PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", - InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", - InlineAsmRegClass::X86( + RiscV(RiscVInlineAsmRegClass::reg) => "r", + RiscV(RiscVInlineAsmRegClass::freg) => "f", + RiscV(RiscVInlineAsmRegClass::vreg) => unreachable!("clobber-only"), + X86(X86InlineAsmRegClass::reg) => "r", + X86(X86InlineAsmRegClass::reg_abcd) => "Q", + X86(X86InlineAsmRegClass::reg_byte) => "q", + X86(X86InlineAsmRegClass::xmm_reg) | X86(X86InlineAsmRegClass::ymm_reg) => "x", + X86(X86InlineAsmRegClass::zmm_reg) => "v", + X86(X86InlineAsmRegClass::kreg) => "^Yk", + X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), - InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { - bug!("LLVM backend does not support SPIR-V") - } - InlineAsmRegClass::Err => unreachable!(), + Wasm(WasmInlineAsmRegClass::local) => "r", + Bpf(BpfInlineAsmRegClass::reg) => "r", + Bpf(BpfInlineAsmRegClass::wreg) => "w", + Avr(AvrInlineAsmRegClass::reg) => "r", + Avr(AvrInlineAsmRegClass::reg_upper) => "d", + Avr(AvrInlineAsmRegClass::reg_pair) => "r", + Avr(AvrInlineAsmRegClass::reg_iw) => "w", + Avr(AvrInlineAsmRegClass::reg_ptr) => "e", + S390x(S390xInlineAsmRegClass::reg) => "r", + S390x(S390xInlineAsmRegClass::reg_addr) => "a", + S390x(S390xInlineAsmRegClass::freg) => "f", + Msp430(Msp430InlineAsmRegClass::reg) => "r", + M68k(M68kInlineAsmRegClass::reg) => "r", + M68k(M68kInlineAsmRegClass::reg_addr) => "a", + M68k(M68kInlineAsmRegClass::reg_data) => "d", + CSKY(CSKYInlineAsmRegClass::reg) => "r", + CSKY(CSKYInlineAsmRegClass::freg) => "f", + SpirV(SpirVInlineAsmRegClass::reg) => bug!("LLVM backend does not support SPIR-V"), + Err => unreachable!(), } .to_string(), } @@ -732,44 +722,41 @@ fn modifier_to_llvm( reg: InlineAsmRegClass, modifier: Option<char>, ) -> Option<char> { + use InlineAsmRegClass::*; // The modifiers can be retrieved from // https://llvm.org/docs/LangRef.html#asm-template-argument-modifiers match reg { - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier, - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) - | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { - if modifier == Some('v') { None } else { modifier } - } - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { - unreachable!("clobber-only") + AArch64(AArch64InlineAsmRegClass::reg) => modifier, + AArch64(AArch64InlineAsmRegClass::vreg) | AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + if modifier == Some('v') { + None + } else { + modifier + } } - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => None, - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => None, - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => Some('P'), - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { + AArch64(AArch64InlineAsmRegClass::preg) => unreachable!("clobber-only"), + Arm(ArmInlineAsmRegClass::reg) => None, + Arm(ArmInlineAsmRegClass::sreg) | Arm(ArmInlineAsmRegClass::sreg_low16) => None, + Arm(ArmInlineAsmRegClass::dreg) + | Arm(ArmInlineAsmRegClass::dreg_low16) + | Arm(ArmInlineAsmRegClass::dreg_low8) => Some('P'), + Arm(ArmInlineAsmRegClass::qreg) + | Arm(ArmInlineAsmRegClass::qreg_low8) + | Arm(ArmInlineAsmRegClass::qreg_low4) => { if modifier.is_none() { Some('q') } else { modifier } } - InlineAsmRegClass::Hexagon(_) => None, - InlineAsmRegClass::LoongArch(_) => None, - InlineAsmRegClass::Mips(_) => None, - InlineAsmRegClass::Nvptx(_) => None, - InlineAsmRegClass::PowerPC(_) => None, - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) - | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { + Hexagon(_) => None, + LoongArch(_) => None, + Mips(_) => None, + Nvptx(_) => None, + PowerPC(_) => None, + RiscV(RiscVInlineAsmRegClass::reg) | RiscV(RiscVInlineAsmRegClass::freg) => None, + RiscV(RiscVInlineAsmRegClass::vreg) => unreachable!("clobber-only"), + X86(X86InlineAsmRegClass::reg) | X86(X86InlineAsmRegClass::reg_abcd) => match modifier { None if arch == InlineAsmArch::X86_64 => Some('q'), None => Some('k'), Some('l') => Some('b'), @@ -779,10 +766,10 @@ fn modifier_to_llvm( Some('r') => Some('q'), _ => unreachable!(), }, - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None, - InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg) - | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) { + X86(X86InlineAsmRegClass::reg_byte) => None, + X86(reg @ X86InlineAsmRegClass::xmm_reg) + | X86(reg @ X86InlineAsmRegClass::ymm_reg) + | X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) { (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), (X86InlineAsmRegClass::zmm_reg, None) => Some('g'), @@ -791,116 +778,97 @@ fn modifier_to_llvm( (_, Some('z')) => Some('g'), _ => unreachable!(), }, - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, - InlineAsmRegClass::X86( + X86(X86InlineAsmRegClass::kreg) => None, + X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, - ) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None, - InlineAsmRegClass::Bpf(_) => None, - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) - | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) - | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => match modifier { + ) => unreachable!("clobber-only"), + Wasm(WasmInlineAsmRegClass::local) => None, + Bpf(_) => None, + Avr(AvrInlineAsmRegClass::reg_pair) + | Avr(AvrInlineAsmRegClass::reg_iw) + | Avr(AvrInlineAsmRegClass::reg_ptr) => match modifier { Some('h') => Some('B'), Some('l') => Some('A'), _ => None, }, - InlineAsmRegClass::Avr(_) => None, - InlineAsmRegClass::S390x(_) => None, - InlineAsmRegClass::Msp430(_) => None, - InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { - bug!("LLVM backend does not support SPIR-V") - } - InlineAsmRegClass::M68k(_) => None, - InlineAsmRegClass::CSKY(_) => None, - InlineAsmRegClass::Err => unreachable!(), + Avr(_) => None, + S390x(_) => None, + Msp430(_) => None, + SpirV(SpirVInlineAsmRegClass::reg) => bug!("LLVM backend does not support SPIR-V"), + M68k(_) => None, + CSKY(_) => None, + Err => unreachable!(), } } /// Type to use for outputs that are discarded. It doesn't really matter what /// the type is, as long as it is valid for the constraint code. fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'ll Type { + use InlineAsmRegClass::*; match reg { - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) - | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(), + AArch64(AArch64InlineAsmRegClass::vreg) | AArch64(AArch64InlineAsmRegClass::vreg_low16) => { cx.type_vector(cx.type_i64(), 2) } - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + AArch64(AArch64InlineAsmRegClass::preg) => unreachable!("clobber-only"), + Arm(ArmInlineAsmRegClass::reg) => cx.type_i32(), + Arm(ArmInlineAsmRegClass::sreg) | Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(), + Arm(ArmInlineAsmRegClass::dreg) + | Arm(ArmInlineAsmRegClass::dreg_low16) + | Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(), + Arm(ArmInlineAsmRegClass::qreg) + | Arm(ArmInlineAsmRegClass::qreg_low8) + | Arm(ArmInlineAsmRegClass::qreg_low4) => cx.type_vector(cx.type_i64(), 2), + Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), + LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), + LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), + Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), + Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), + Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), + Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), + Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), + PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(), + PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), + PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), + PowerPC(PowerPCInlineAsmRegClass::cr) | PowerPC(PowerPCInlineAsmRegClass::xer) => { unreachable!("clobber-only") } - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(), - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(), - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { - cx.type_vector(cx.type_i64(), 2) - } - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => cx.type_f32(), - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), - InlineAsmRegClass::X86( + RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), + RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), + RiscV(RiscVInlineAsmRegClass::vreg) => unreachable!("clobber-only"), + X86(X86InlineAsmRegClass::reg) | X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), + X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), + X86(X86InlineAsmRegClass::xmm_reg) + | X86(X86InlineAsmRegClass::ymm_reg) + | X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), + X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + X86( X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, - ) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(), - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => cx.type_i8(), - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(), - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(), - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(), - InlineAsmRegClass::S390x( - S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr, - ) => cx.type_i32(), - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), - InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(), - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => cx.type_i32(), - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => cx.type_i32(), - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => cx.type_f32(), - InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { - bug!("LLVM backend does not support SPIR-V") - } - InlineAsmRegClass::Err => unreachable!(), + ) => unreachable!("clobber-only"), + Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), + Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), + Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), + Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(), + Avr(AvrInlineAsmRegClass::reg_upper) => cx.type_i8(), + Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(), + Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(), + Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(), + S390x(S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr) => cx.type_i32(), + S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(), + Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(), + M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(), + M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(), + M68k(M68kInlineAsmRegClass::reg_data) => cx.type_i32(), + CSKY(CSKYInlineAsmRegClass::reg) => cx.type_i32(), + CSKY(CSKYInlineAsmRegClass::freg) => cx.type_f32(), + SpirV(SpirVInlineAsmRegClass::reg) => bug!("LLVM backend does not support SPIR-V"), + Err => unreachable!(), } } @@ -940,9 +908,10 @@ fn llvm_fixup_input<'ll, 'tcx>( layout: &TyAndLayout<'tcx>, instance: Instance<'_>, ) -> &'ll Value { + use InlineAsmRegClass::*; let dl = &bx.tcx.data_layout; match (reg, layout.abi) { - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + (AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I8, _) = s.primitive() { let vec_ty = bx.cx.type_vector(bx.cx.type_i8(), 8); bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0)) @@ -950,7 +919,7 @@ fn llvm_fixup_input<'ll, 'tcx>( value } } - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) if s.primitive() != Primitive::Float(Float::F128) => { let elem_ty = llvm_asm_scalar_type(bx.cx, s); @@ -963,26 +932,25 @@ fn llvm_fixup_input<'ll, 'tcx>( } bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0)) } - ( - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), - Abi::Vector { element, count }, - ) if layout.size.bytes() == 8 => { + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Vector { element, count }) + if layout.size.bytes() == 8 => + { let elem_ty = llvm_asm_scalar_type(bx.cx, element); let vec_ty = bx.cx.type_vector(elem_ty, count); let indices: Vec<_> = (0..count * 2).map(|x| bx.const_i32(x as i32)).collect(); bx.shuffle_vector(value, bx.const_undef(vec_ty), bx.const_vector(&indices)) } - (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + (X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F64) => { bx.bitcast(value, bx.cx.type_i64()) } ( - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => bx.bitcast(value, bx.cx.type_vector(bx.cx.type_f64(), 8)), ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -994,7 +962,7 @@ fn llvm_fixup_input<'ll, 'tcx>( bx.bitcast(value, bx.type_vector(bx.type_i32(), 4)) } ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1009,7 +977,7 @@ fn llvm_fixup_input<'ll, 'tcx>( bx.bitcast(value, bx.type_vector(bx.type_i16(), 8)) } ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1018,10 +986,7 @@ fn llvm_fixup_input<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_i16(), count)) } - ( - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), - Abi::Scalar(s), - ) => { + (Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I32, _) = s.primitive() { bx.bitcast(value, bx.cx.type_f32()) } else { @@ -1029,7 +994,7 @@ fn llvm_fixup_input<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16, @@ -1043,7 +1008,7 @@ fn llvm_fixup_input<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16 @@ -1055,7 +1020,7 @@ fn llvm_fixup_input<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_i16(), count)) } - (InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { + (Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => bx.zext(value, bx.cx.type_i32()), @@ -1064,7 +1029,7 @@ fn llvm_fixup_input<'ll, 'tcx>( _ => value, } } - (InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) + (RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F16) && !any_target_feature_enabled(bx, instance, &[sym::zfhmin, sym::zfh]) => { @@ -1086,15 +1051,16 @@ fn llvm_fixup_output<'ll, 'tcx>( layout: &TyAndLayout<'tcx>, instance: Instance<'_>, ) -> &'ll Value { + use InlineAsmRegClass::*; match (reg, layout.abi) { - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + (AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I8, _) = s.primitive() { bx.extract_element(value, bx.const_i32(0)) } else { value } } - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) if s.primitive() != Primitive::Float(Float::F128) => { value = bx.extract_element(value, bx.const_i32(0)); @@ -1103,26 +1069,25 @@ fn llvm_fixup_output<'ll, 'tcx>( } value } - ( - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), - Abi::Vector { element, count }, - ) if layout.size.bytes() == 8 => { + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Vector { element, count }) + if layout.size.bytes() == 8 => + { let elem_ty = llvm_asm_scalar_type(bx.cx, element); let vec_ty = bx.cx.type_vector(elem_ty, count * 2); let indices: Vec<_> = (0..count).map(|x| bx.const_i32(x as i32)).collect(); bx.shuffle_vector(value, bx.const_undef(vec_ty), bx.const_vector(&indices)) } - (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + (X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F64) => { bx.bitcast(value, bx.cx.type_f64()) } ( - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => bx.bitcast(value, layout.llvm_type(bx.cx)), ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1134,7 +1099,7 @@ fn llvm_fixup_output<'ll, 'tcx>( bx.bitcast(value, bx.type_f128()) } ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1145,7 +1110,7 @@ fn llvm_fixup_output<'ll, 'tcx>( bx.extract_element(value, bx.const_usize(0)) } ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1154,10 +1119,7 @@ fn llvm_fixup_output<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_f16(), count)) } - ( - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), - Abi::Scalar(s), - ) => { + (Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I32, _) = s.primitive() { bx.bitcast(value, bx.cx.type_i32()) } else { @@ -1165,7 +1127,7 @@ fn llvm_fixup_output<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16, @@ -1179,7 +1141,7 @@ fn llvm_fixup_output<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16 @@ -1191,7 +1153,7 @@ fn llvm_fixup_output<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { bx.bitcast(value, bx.type_vector(bx.type_f16(), count)) } - (InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { + (Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8, _) => bx.trunc(value, bx.cx.type_i8()), @@ -1201,7 +1163,7 @@ fn llvm_fixup_output<'ll, 'tcx>( _ => value, } } - (InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) + (RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F16) && !any_target_feature_enabled(bx, instance, &[sym::zfhmin, sym::zfh]) => { @@ -1220,39 +1182,39 @@ fn llvm_fixup_output_type<'ll, 'tcx>( layout: &TyAndLayout<'tcx>, instance: Instance<'_>, ) -> &'ll Type { + use InlineAsmRegClass::*; match (reg, layout.abi) { - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { + (AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I8, _) = s.primitive() { cx.type_vector(cx.type_i8(), 8) } else { layout.llvm_type(cx) } } - (InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) if s.primitive() != Primitive::Float(Float::F128) => { let elem_ty = llvm_asm_scalar_type(cx, s); let count = 16 / layout.size.bytes(); cx.type_vector(elem_ty, count) } - ( - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), - Abi::Vector { element, count }, - ) if layout.size.bytes() == 8 => { + (AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Vector { element, count }) + if layout.size.bytes() == 8 => + { let elem_ty = llvm_asm_scalar_type(cx, element); cx.type_vector(elem_ty, count * 2) } - (InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) + (X86(X86InlineAsmRegClass::reg_abcd), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F64) => { cx.type_i64() } ( - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), + X86(X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::zmm_reg), Abi::Vector { .. }, ) if layout.size.bytes() == 64 => cx.type_vector(cx.type_f64(), 8), ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1264,7 +1226,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( cx.type_vector(cx.type_i32(), 4) } ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1272,7 +1234,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( Abi::Scalar(s), ) if s.primitive() == Primitive::Float(Float::F16) => cx.type_vector(cx.type_i16(), 8), ( - InlineAsmRegClass::X86( + X86( X86InlineAsmRegClass::xmm_reg | X86InlineAsmRegClass::ymm_reg | X86InlineAsmRegClass::zmm_reg, @@ -1281,10 +1243,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { cx.type_vector(cx.type_i16(), count) } - ( - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), - Abi::Scalar(s), - ) => { + (Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16), Abi::Scalar(s)) => { if let Primitive::Int(Integer::I32, _) = s.primitive() { cx.type_f32() } else { @@ -1292,7 +1251,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16, @@ -1306,7 +1265,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( } } ( - InlineAsmRegClass::Arm( + Arm( ArmInlineAsmRegClass::dreg | ArmInlineAsmRegClass::dreg_low8 | ArmInlineAsmRegClass::dreg_low16 @@ -1318,7 +1277,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( ) if element.primitive() == Primitive::Float(Float::F16) => { cx.type_vector(cx.type_i16(), count) } - (InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { + (Mips(MipsInlineAsmRegClass::reg), Abi::Scalar(s)) => { match s.primitive() { // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => cx.type_i32(), @@ -1327,7 +1286,7 @@ fn llvm_fixup_output_type<'ll, 'tcx>( _ => layout.llvm_type(cx), } } - (InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) + (RiscV(RiscVInlineAsmRegClass::freg), Abi::Scalar(s)) if s.primitive() == Primitive::Float(Float::F16) && !any_target_feature_enabled(cx, instance, &[sym::zfhmin, sym::zfh]) => { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 9d4497d73a8..6df63eec513 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -403,8 +403,9 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { to_add.push(AttributeKind::Naked.create_attr(cx.llcx)); // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions. - // And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules. - // Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768 + // And it is a module-level attribute, so the alternative is pulling naked functions into + // new LLVM modules. Otherwise LLVM's "naked" functions come with endbr prefixes per + // https://github.com/rust-lang/rust/issues/98768 to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx)); if llvm_util::get_version() < (19, 0, 0) { // Prior to LLVM 19, branch-target-enforcement was disabled by setting the attribute to @@ -454,7 +455,8 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( flags |= AllocKindFlags::Zeroed; } to_add.push(llvm::CreateAllocKindAttr(cx.llcx, flags)); - // apply to return place instead of function (unlike all other attributes applied in this function) + // apply to return place instead of function (unlike all other attributes applied in this + // function) let no_alias = AttributeKind::NoAlias.create_attr(cx.llcx); attributes::apply_to_llfn(llfn, AttributePlace::ReturnValue, &[no_alias]); } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 2ebe0be53aa..66479ad7f34 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -156,15 +156,15 @@ fn get_bitcode_slice_from_object_data<'a>( obj: &'a [u8], cgcx: &CodegenContext<LlvmCodegenBackend>, ) -> Result<&'a [u8], LtoBitcodeFromRlib> { - // We're about to assume the data here is an object file with sections, but if it's raw LLVM IR that - // won't work. Fortunately, if that's what we have we can just return the object directly, so we sniff - // the relevant magic strings here and return. + // We're about to assume the data here is an object file with sections, but if it's raw LLVM IR + // that won't work. Fortunately, if that's what we have we can just return the object directly, + // so we sniff the relevant magic strings here and return. if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") { return Ok(obj); } - // We drop the "__LLVM," prefix here because on Apple platforms there's a notion of "segment name" - // which in the public API for sections gets treated as part of the section name, but internally - // in MachOObjectFile.cpp gets treated separately. + // We drop the "__LLVM," prefix here because on Apple platforms there's a notion of "segment + // name" which in the public API for sections gets treated as part of the section name, but + // internally in MachOObjectFile.cpp gets treated separately. let section_name = bitcode_section_name(cgcx).trim_start_matches("__LLVM,"); let mut len = 0; let data = unsafe { diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 681ac75c877..76529e0c83b 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -30,7 +30,7 @@ impl OwnedTargetMachine { data_sections: bool, unique_section_names: bool, trap_unreachable: bool, - singletree: bool, + singlethread: bool, verbose_asm: bool, emit_stack_size_section: bool, relax_elf_relocations: bool, @@ -62,7 +62,7 @@ impl OwnedTargetMachine { data_sections, unique_section_names, trap_unreachable, - singletree, + singlethread, verbose_asm, emit_stack_size_section, relax_elf_relocations, @@ -86,15 +86,17 @@ impl Deref for OwnedTargetMachine { type Target = llvm::TargetMachine; fn deref(&self) -> &Self::Target { - // SAFETY: constructing ensures we have a valid pointer created by llvm::LLVMRustCreateTargetMachine + // SAFETY: constructing ensures we have a valid pointer created by + // llvm::LLVMRustCreateTargetMachine. unsafe { self.tm_unique.as_ref() } } } impl Drop for OwnedTargetMachine { fn drop(&mut self) { - // SAFETY: constructing ensures we have a valid pointer created by llvm::LLVMRustCreateTargetMachine - // OwnedTargetMachine is not copyable so there is no double free or use after free + // SAFETY: constructing ensures we have a valid pointer created by + // llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no + // double free or use after free. unsafe { llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_mut()); } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index b1b692cc027..d2c4ea8171b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -38,7 +38,7 @@ use crate::errors::{ CopyBitcode, FromLlvmDiag, FromLlvmOptimizationDiag, LlvmError, UnknownCompression, WithLlvmError, WriteBytecode, }; -use crate::llvm::diagnostic::OptimizationDiagnosticKind; +use crate::llvm::diagnostic::OptimizationDiagnosticKind::*; use crate::llvm::{self, DiagnosticInfo, PassManager}; use crate::type_::Type; use crate::{base, common, llvm_util, LlvmCodegenBackend, ModuleLlvm}; @@ -157,7 +157,8 @@ fn to_pass_builder_opt_level(cfg: config::OptLevel) -> llvm::PassBuilderOptLevel fn to_llvm_relocation_model(relocation_model: RelocModel) -> llvm::RelocModel { match relocation_model { RelocModel::Static => llvm::RelocModel::Static, - // LLVM doesn't have a PIE relocation model, it represents PIE as PIC with an extra attribute. + // LLVM doesn't have a PIE relocation model, it represents PIE as PIC with an extra + // attribute. RelocModel::Pic | RelocModel::Pie => llvm::RelocModel::PIC, RelocModel::DynamicNoPic => llvm::RelocModel::DynamicNoPic, RelocModel::Ropi => llvm::RelocModel::ROPI, @@ -188,8 +189,8 @@ pub(crate) fn target_machine_factory( let use_softfp = if sess.target.arch == "arm" && sess.target.abi == "eabihf" { sess.opts.cg.soft_float } else { - // `validate_commandline_args_with_session_available` has already warned about this being ignored. - // Let's make sure LLVM doesn't suddenly start using this flag on more targets. + // `validate_commandline_args_with_session_available` has already warned about this being + // ignored. Let's make sure LLVM doesn't suddenly start using this flag on more targets. false }; @@ -446,13 +447,12 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void column: opt.column, pass_name: &opt.pass_name, kind: match opt.kind { - OptimizationDiagnosticKind::OptimizationRemark => "success", - OptimizationDiagnosticKind::OptimizationMissed - | OptimizationDiagnosticKind::OptimizationFailure => "missed", - OptimizationDiagnosticKind::OptimizationAnalysis - | OptimizationDiagnosticKind::OptimizationAnalysisFPCommute - | OptimizationDiagnosticKind::OptimizationAnalysisAliasing => "analysis", - OptimizationDiagnosticKind::OptimizationRemarkOther => "other", + OptimizationRemark => "success", + OptimizationMissed | OptimizationFailure => "missed", + OptimizationAnalysis + | OptimizationAnalysisFPCommute + | OptimizationAnalysisAliasing => "analysis", + OptimizationRemarkOther => "other", }, message: &opt.message, }); @@ -945,11 +945,12 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data: } fn target_is_apple(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool { - cgcx.opts.target_triple.triple().contains("-ios") - || cgcx.opts.target_triple.triple().contains("-darwin") - || cgcx.opts.target_triple.triple().contains("-tvos") - || cgcx.opts.target_triple.triple().contains("-watchos") - || cgcx.opts.target_triple.triple().contains("-visionos") + let triple = cgcx.opts.target_triple.triple(); + triple.contains("-ios") + || triple.contains("-darwin") + || triple.contains("-tvos") + || triple.contains("-watchos") + || triple.contains("-visionos") } fn target_is_aix(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 40783825cae..6ffe90997f5 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -26,13 +26,13 @@ use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; +use crate::attributes; use crate::common::Funclet; use crate::context::CodegenCx; use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -use crate::{attributes, llvm_util}; // All Builders must have an llfn associated with them #[must_use] @@ -93,8 +93,6 @@ impl HasTargetSpec for Builder<'_, '_, '_> { } impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { self.cx.handle_layout_err(err, span, ty) @@ -102,8 +100,6 @@ impl<'tcx> LayoutOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { } impl<'tcx> FnAbiOfHelpers<'tcx> for Builder<'_, '_, 'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, @@ -124,11 +120,7 @@ impl<'ll, 'tcx> Deref for Builder<'_, 'll, 'tcx> { } } -impl<'ll, 'tcx> HasCodegen<'tcx> for Builder<'_, 'll, 'tcx> { - type CodegenCx = CodegenCx<'ll, 'tcx>; -} - -macro_rules! builder_methods_for_value_instructions { +macro_rules! math_builder_methods { ($($name:ident($($arg:ident),*) => $llvm_capi:ident),+ $(,)?) => { $(fn $name(&mut self, $($arg: &'ll Value),*) -> &'ll Value { unsafe { @@ -138,7 +130,21 @@ macro_rules! builder_methods_for_value_instructions { } } +macro_rules! set_math_builder_methods { + ($($name:ident($($arg:ident),*) => ($llvm_capi:ident, $llvm_set_math:ident)),+ $(,)?) => { + $(fn $name(&mut self, $($arg: &'ll Value),*) -> &'ll Value { + unsafe { + let instr = llvm::$llvm_capi(self.llbuilder, $($arg,)* UNNAMED); + llvm::$llvm_set_math(instr); + instr + } + })+ + } +} + impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { + type CodegenCx = CodegenCx<'ll, 'tcx>; + fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Self { let bx = Builder::with_cx(cx); unsafe { @@ -273,7 +279,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } } - builder_methods_for_value_instructions! { + math_builder_methods! { add(a, b) => LLVMBuildAdd, fadd(a, b) => LLVMBuildFAdd, sub(a, b) => LLVMBuildSub, @@ -305,84 +311,17 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unchecked_umul(x, y) => LLVMBuildNUWMul, } - fn fadd_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetFastMath(instr); - instr - } - } - - fn fsub_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetFastMath(instr); - instr - } - } - - fn fmul_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetFastMath(instr); - instr - } - } - - fn fdiv_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetFastMath(instr); - instr - } - } - - fn frem_fast(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetFastMath(instr); - instr - } - } - - fn fadd_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetAlgebraicMath(instr); - instr - } - } - - fn fsub_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetAlgebraicMath(instr); - instr - } - } - - fn fmul_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetAlgebraicMath(instr); - instr - } - } - - fn fdiv_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetAlgebraicMath(instr); - instr - } - } - - fn frem_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { - unsafe { - let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetAlgebraicMath(instr); - instr - } + set_math_builder_methods! { + fadd_fast(x, y) => (LLVMBuildFAdd, LLVMRustSetFastMath), + fsub_fast(x, y) => (LLVMBuildFSub, LLVMRustSetFastMath), + fmul_fast(x, y) => (LLVMBuildFMul, LLVMRustSetFastMath), + fdiv_fast(x, y) => (LLVMBuildFDiv, LLVMRustSetFastMath), + frem_fast(x, y) => (LLVMBuildFRem, LLVMRustSetFastMath), + fadd_algebraic(x, y) => (LLVMBuildFAdd, LLVMRustSetAlgebraicMath), + fsub_algebraic(x, y) => (LLVMBuildFSub, LLVMRustSetAlgebraicMath), + fmul_algebraic(x, y) => (LLVMBuildFMul, LLVMRustSetAlgebraicMath), + fdiv_algebraic(x, y) => (LLVMBuildFDiv, LLVMRustSetAlgebraicMath), + frem_algebraic(x, y) => (LLVMBuildFRem, LLVMRustSetAlgebraicMath), } fn checked_binop( @@ -465,6 +404,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { val } } + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: abi::Scalar) -> Self::Value { if scalar.is_bool() { return self.trunc(val, self.cx().type_i1()); @@ -733,11 +673,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // for performance. LLVM doesn't seem to care about this, and will happily treat // `!nontemporal` stores as-if they were normal stores (for reordering optimizations // etc) even on x86, despite later lowering them to MOVNT which do *not* behave like - // regular stores but require special fences. - // So we keep a list of architectures where `!nontemporal` is known to be truly just - // a hint, and use regular stores everywhere else. - // (In the future, we could alternatively ensure that an sfence gets emitted after a sequence of movnt - // before any kind of synchronizing operation. But it's not clear how to do that with LLVM.) + // regular stores but require special fences. So we keep a list of architectures + // where `!nontemporal` is known to be truly just a hint, and use regular stores + // everywhere else. (In the future, we could alternatively ensure that an sfence + // gets emitted after a sequence of movnt before any kind of synchronizing + // operation. But it's not clear how to do that with LLVM.) // For more context, see <https://github.com/rust-lang/rust/issues/114582> and // <https://github.com/llvm/llvm-project/issues/64521>. const WELL_BEHAVED_NONTEMPORAL_ARCHS: &[&str] = @@ -1166,6 +1106,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { (val, success) } } + fn atomic_rmw( &mut self, op: rustc_codegen_ssa::common::AtomicRmwBinOp, @@ -1317,15 +1258,9 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn apply_attrs_to_cleanup_callsite(&mut self, llret: &'ll Value) { - if llvm_util::get_version() < (17, 0, 2) { - // Work around https://github.com/llvm/llvm-project/issues/66984. - let noinline = llvm::AttributeKind::NoInline.create_attr(self.llcx); - attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[noinline]); - } else { - // Cleanup is always the cold path. - let cold_inline = llvm::AttributeKind::Cold.create_attr(self.llcx); - attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[cold_inline]); - } + // Cleanup is always the cold path. + let cold_inline = llvm::AttributeKind::Cold.create_attr(self.llcx); + attributes::apply_to_callsite(llret, llvm::AttributePlace::Function, &[cold_inline]); } } @@ -1767,8 +1702,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { ) { debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes); - assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); - let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) }; let llty = self.cx.type_func( &[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()], @@ -1802,7 +1735,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { "mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp ); - assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) }; @@ -1844,7 +1776,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { "mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})", fn_name, hash, cond_loc, mcdc_temp, bool_value ); - assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later"); let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) }; let llty = self.cx.type_func( &[ diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 949fd1bc124..206a7069792 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -15,11 +15,6 @@ use crate::value::Value; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. -/// -/// # Parameters -/// -/// - `cx`: the crate context -/// - `instance`: the instance to be instantiated pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value { let tcx = cx.tcx(); @@ -106,62 +101,42 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t let is_generic = instance.args.non_erasable_generics(tcx, instance.def_id()).next().is_some(); - if is_generic { - // This is a monomorphization. Its expected visibility depends - // on whether we are in share-generics mode. - - if cx.tcx.sess.opts.share_generics() { - // We are in share_generics mode. - + let is_hidden = if is_generic { + // This is a monomorphization of a generic function. + if !cx.tcx.sess.opts.share_generics() { + // When not sharing generics, all instances are in the same + // crate and have hidden visibility. + true + } else { if let Some(instance_def_id) = instance_def_id.as_local() { - // This is a definition from the current crate. If the - // definition is unreachable for downstream crates or - // the current crate does not re-export generics, the - // definition of the instance will have been declared - // as `hidden`. - if cx.tcx.is_unreachable_local_definition(instance_def_id) + // This is a monomorphization of a generic function + // defined in the current crate. It is hidden if: + // - the definition is unreachable for downstream + // crates, or + // - the current crate does not re-export generics + // (because the crate is a C library or executable) + cx.tcx.is_unreachable_local_definition(instance_def_id) || !cx.tcx.local_crate_exports_generics() - { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } } else { // This is a monomorphization of a generic function - // defined in an upstream crate. - if instance.upstream_monomorphization(tcx).is_some() { - // This is instantiated in another crate. It cannot - // be `hidden`. - } else { - // This is a local instantiation of an upstream definition. - // If the current crate does not re-export it - // (because it is a C library or an executable), it - // will have been declared `hidden`. - if !cx.tcx.local_crate_exports_generics() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } + // defined in an upstream crate. It is hidden if: + // - it is instantiated in this crate, and + // - the current crate does not re-export generics + instance.upstream_monomorphization(tcx).is_none() + && !cx.tcx.local_crate_exports_generics() } - } else { - // When not sharing generics, all instances are in the same - // crate and have hidden visibility - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } } else { - // This is a non-generic function - if cx.tcx.is_codegened_item(instance_def_id) { - // This is a function that is instantiated in the local crate - - if instance_def_id.is_local() { - // This is function that is defined in the local crate. - // If it is not reachable, it is hidden. - if !cx.tcx.is_reachable_non_generic(instance_def_id) { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a function from an upstream crate that has - // been instantiated here. These are always hidden. - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } + // This is a non-generic function. It is hidden if: + // - it is instantiated in the local crate, and + // - it is defined an upstream crate (non-local), or + // - it is not reachable + cx.tcx.is_codegened_item(instance_def_id) + && (!instance_def_id.is_local() + || !cx.tcx.is_reachable_non_generic(instance_def_id)) + }; + if is_hidden { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } // MinGW: For backward compatibility we rely on the linker to decide whether it diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index ef6560ecbe5..508c2d1a820 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -113,7 +113,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } -impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn const_null(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMConstNull(t) } } @@ -126,25 +126,14 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMGetPoison(t) } } - fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value { - unsafe { llvm::LLVMConstInt(t, i as u64, True) } - } - - fn const_uint(&self, t: &'ll Type, i: u64) -> &'ll Value { - unsafe { llvm::LLVMConstInt(t, i, False) } - } - - fn const_uint_big(&self, t: &'ll Type, u: u128) -> &'ll Value { - unsafe { - let words = [u as u64, (u >> 64) as u64]; - llvm::LLVMConstIntOfArbitraryPrecision(t, 2, words.as_ptr()) - } - } - fn const_bool(&self, val: bool) -> &'ll Value { self.const_uint(self.type_i1(), val as u64) } + fn const_i8(&self, i: i8) -> &'ll Value { + self.const_int(self.type_i8(), i as i64) + } + fn const_i16(&self, i: i16) -> &'ll Value { self.const_int(self.type_i16(), i as i64) } @@ -153,8 +142,12 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_int(self.type_i32(), i as i64) } - fn const_i8(&self, i: i8) -> &'ll Value { - self.const_int(self.type_i8(), i as i64) + fn const_int(&self, t: &'ll Type, i: i64) -> &'ll Value { + unsafe { llvm::LLVMConstInt(t, i as u64, True) } + } + + fn const_u8(&self, i: u8) -> &'ll Value { + self.const_uint(self.type_i8(), i as u64) } fn const_u32(&self, i: u32) -> &'ll Value { @@ -179,8 +172,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { self.const_uint(self.isize_ty, i) } - fn const_u8(&self, i: u8) -> &'ll Value { - self.const_uint(self.type_i8(), i as u64) + fn const_uint(&self, t: &'ll Type, i: u64) -> &'ll Value { + unsafe { llvm::LLVMConstInt(t, i, False) } + } + + fn const_uint_big(&self, t: &'ll Type, u: u128) -> &'ll Value { + unsafe { + let words = [u as u64, (u >> 64) as u64]; + llvm::LLVMConstIntOfArbitraryPrecision(t, 2, words.as_ptr()) + } } fn const_real(&self, t: &'ll Type, val: f64) -> &'ll Value { diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index d60122fccee..33d3b5d4474 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -73,8 +73,8 @@ pub(crate) fn const_alloc_to_llvm<'ll>( // Generating partially-uninit consts is limited to small numbers of chunks, // to avoid the cost of generating large complex const expressions. - // For example, `[(u32, u8); 1024 * 1024]` contains uninit padding in each element, - // and would result in `{ [5 x i8] zeroinitializer, [3 x i8] undef, ...repeat 1M times... }`. + // For example, `[(u32, u8); 1024 * 1024]` contains uninit padding in each element, and + // would result in `{ [5 x i8] zeroinitializer, [3 x i8] undef, ...repeat 1M times... }`. let max = cx.sess().opts.unstable_opts.uninit_const_chunk_threshold; let allow_uninit_chunks = chunks.clone().take(max.saturating_add(1)).count() <= max; @@ -249,8 +249,8 @@ impl<'ll> CodegenCx<'ll, '_> { trace!(?instance); let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() }; - // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out - // the llvm type from the actual evaluated initializer. + // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure + // out the llvm type from the actual evaluated initializer. let llty = if nested { self.type_i8() } else { @@ -262,7 +262,7 @@ impl<'ll> CodegenCx<'ll, '_> { } #[instrument(level = "debug", skip(self, llty))] - pub(crate) fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value { + fn get_static_inner(&self, def_id: DefId, llty: &'ll Type) -> &'ll Value { let instance = Instance::mono(self.tcx, def_id); if let Some(&g) = self.instances.borrow().get(&instance) { trace!("used cached value"); @@ -320,15 +320,16 @@ impl<'ll> CodegenCx<'ll, '_> { } if !def_id.is_local() { - let needs_dll_storage_attr = self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) && + let needs_dll_storage_attr = self.use_dll_storage_attrs + && !self.tcx.is_foreign_item(def_id) // Local definitions can never be imported, so we must not apply // the DLLImport annotation. - !dso_local && + && !dso_local // ThinLTO can't handle this workaround in all cases, so we don't // emit the attrs. Instead we make them unnecessary by disallowing // dynamic linking when linker plugin based LTO is enabled. - !self.tcx.sess.opts.cg.linker_plugin_lto.enabled() && - self.tcx.sess.lto() != Lto::Thin; + && !self.tcx.sess.opts.cg.linker_plugin_lto.enabled() + && self.tcx.sess.lto() != Lto::Thin; // If this assertion triggers, there's something wrong with commandline // argument validation. @@ -442,58 +443,6 @@ impl<'ll> CodegenCx<'ll, '_> { if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { llvm::set_thread_local_mode(g, self.tls_model); - - // Do not allow LLVM to change the alignment of a TLS on macOS. - // - // By default a global's alignment can be freely increased. - // This allows LLVM to generate more performant instructions - // e.g., using load-aligned into a SIMD register. - // - // However, on macOS 10.10 or below, the dynamic linker does not - // respect any alignment given on the TLS (radar 24221680). - // This will violate the alignment assumption, and causing segfault at runtime. - // - // This bug is very easy to trigger. In `println!` and `panic!`, - // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, - // which the values would be `mem::replace`d on initialization. - // The implementation of `mem::replace` will use SIMD - // whenever the size is 32 bytes or higher. LLVM notices SIMD is used - // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, - // which macOS's dyld disregarded and causing crashes - // (see issues #51794, #51758, #50867, #48866 and #44056). - // - // To workaround the bug, we trick LLVM into not increasing - // the global's alignment by explicitly assigning a section to it - // (equivalent to automatically generating a `#[link_section]` attribute). - // See the comment in the `GlobalValue::canIncreaseAlignment()` function - // of `lib/IR/Globals.cpp` for why this works. - // - // When the alignment is not increased, the optimized `mem::replace` - // will use load-unaligned instructions instead, and thus avoiding the crash. - // - // We could remove this hack whenever we decide to drop macOS 10.10 support. - if self.tcx.sess.target.is_like_osx { - // The `inspect` method is okay here because we checked for provenance, and - // because we are doing this access to inspect the final interpreter state - // (not as part of the interpreter execution). - // - // FIXME: This check requires that the (arbitrary) value of undefined bytes - // happens to be zero. Instead, we should only check the value of defined bytes - // and set all undefined bytes to zero if this allocation is headed for the - // BSS. - let all_bytes_are_zero = alloc.provenance().ptrs().is_empty() - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) - .iter() - .all(|&byte| byte == 0); - - let sect_name = if all_bytes_are_zero { - c"__DATA,__thread_bss" - } else { - c"__DATA,__thread_data" - }; - llvm::LLVMSetSection(g, sect_name.as_ptr()); - } } // Wasm statics with custom link sections get special treatment as they @@ -551,8 +500,8 @@ impl<'ll> CodegenCx<'ll, '_> { // `#[used(compiler)]` is explicitly requested. This is to avoid similar breakage // on other targets, in particular MachO targets have *their* static constructor // lists broken if `llvm.compiler.used` is emitted rather than `llvm.used`. However, - // that check happens when assigning the `CodegenFnAttrFlags` in `rustc_hir_analysis`, - // so we don't need to take care of it here. + // that check happens when assigning the `CodegenFnAttrFlags` in + // `rustc_hir_analysis`, so we don't need to take care of it here. self.add_compiler_used_global(g); } if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) { @@ -565,7 +514,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } -impl<'ll> StaticMethods for CodegenCx<'ll, '_> { +impl<'ll> StaticCodegenMethods for CodegenCx<'ll, '_> { fn static_addr_of(&self, cv: &'ll Value, align: Align, kind: Option<&str>) -> &'ll Value { if let Some(&gv) = self.const_globals.borrow().get(&cv) { unsafe { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 73c2c15717f..1d5580fdd07 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -15,7 +15,6 @@ use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry; use rustc_middle::mir::mono::CodegenUnit; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, LayoutError, LayoutOfHelpers, - TyAndLayout, }; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -25,7 +24,6 @@ use rustc_session::config::{ use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi::call::FnAbi; use rustc_target::abi::{HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; @@ -37,8 +35,8 @@ use crate::type_::Type; use crate::value::Value; use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; -/// There is one `CodegenCx` per compilation unit. Each one has its own LLVM -/// `llvm::Context` so that several compilation units may be optimized in parallel. +/// There is one `CodegenCx` per codegen unit. Each one has its own LLVM +/// `llvm::Context` so that several codegen units may be processed in parallel. /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. pub(crate) struct CodegenCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, @@ -122,14 +120,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (18, 0, 0) { - if sess.target.arch == "x86" || sess.target.arch == "x86_64" { - // LLVM 18 adjusts i128 to be 128-bit aligned on x86 variants. - // Earlier LLVMs leave this as default alignment, so remove it. - // See https://reviews.llvm.org/D86310 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - } if llvm_version < (19, 0, 0) { if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { @@ -241,7 +231,8 @@ pub(crate) unsafe fn create_module<'ll>( } } - // Enable LTO unit splitting if specified or if CFI is enabled. (See https://reviews.llvm.org/D53891.) + // Enable LTO unit splitting if specified or if CFI is enabled. (See + // https://reviews.llvm.org/D53891.) if sess.is_split_lto_unit_enabled() || sess.is_sanitizer_cfi_enabled() { let enable_split_lto_unit = c"EnableSplitLTOUnit".as_ptr(); unsafe { @@ -598,7 +589,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } } -impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn vtables( &self, ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), &'ll Value>> @@ -1158,8 +1149,6 @@ impl<'tcx, 'll> HasParamEnv<'tcx> for CodegenCx<'ll, 'tcx> { } impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { @@ -1171,8 +1160,6 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { } impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { - type FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>; - #[inline] fn handle_fn_abi_err( &self, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index d7a4f105f3c..77821ca89bc 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -121,7 +121,8 @@ mod mcdc { num_conditions: u16, } - // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at [19](https://github.com/llvm/llvm-project/pull/81257) + // ConditionId in llvm is `unsigned int` at 18 while `int16_t` at + // [19](https://github.com/llvm/llvm-project/pull/81257). type LLVMConditionId = i16; /// Must match the layout of `LLVMRustMCDCBranchParameters`. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b5acfabfde2..a9f65ee8a93 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -1,5 +1,5 @@ use itertools::Itertools as _; -use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 69babc7c9cf..c9d2a1c9b88 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -2,8 +2,8 @@ use std::cell::RefCell; use libc::c_uint; use rustc_codegen_ssa::traits::{ - BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, MiscMethods, - StaticMethods, + BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, + MiscCodegenMethods, StaticCodegenMethods, }; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_llvm::RustString; @@ -48,11 +48,10 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> { self.function_coverage_map.replace(FxIndexMap::default()) } - /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap. - /// In order to handle nested decisions, several condition bitmaps can be - /// allocated for a function body. - /// These values are named `mcdc.addr.{i}` and are a 32-bit integers. - /// They respectively hold the condition bitmaps for decisions with a depth of `i`. + /// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is + /// called condition bitmap. In order to handle nested decisions, several condition bitmaps can + /// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit + /// integers. They respectively hold the condition bitmaps for decisions with a depth of `i`. fn try_get_mcdc_condition_bitmap( &self, instance: &Instance<'tcx>, @@ -157,8 +156,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { ), CoverageKind::CounterIncrement { id } => { func_coverage.mark_counter_id_seen(id); - // We need to explicitly drop the `RefMut` before calling into `instrprof_increment`, - // as that needs an exclusive borrow. + // We need to explicitly drop the `RefMut` before calling into + // `instrprof_increment`, as that needs an exclusive borrow. drop(coverage_map); // The number of counters passed to `llvm.instrprof.increment` might diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index dc228e94811..f93d3e40b20 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -44,7 +44,8 @@ pub(crate) fn get_or_insert_gdb_debug_scripts_section_global<'ll>( // Add the pretty printers for the standard library first. section_contents.extend_from_slice(b"\x01gdb_load_rust_pretty_printers.py\0"); - // Next, add the pretty printers that were specified via the `#[debugger_visualizer]` attribute. + // Next, add the pretty printers that were specified via the `#[debugger_visualizer]` + // attribute. let visualizers = collect_debugger_visualizers_transitive( cx.tcx, DebuggerVisualizerType::GdbPrettyPrinter, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index d231b103964..57e396415cc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -125,7 +125,9 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( let (size, align) = cx.size_and_align_of(array_type); - let upper_bound = len.eval_target_usize(cx.tcx, ty::ParamEnv::reveal_all()) as c_longlong; + let upper_bound = len + .try_to_target_usize(cx.tcx) + .expect("expected monomorphic const in codegen") as c_longlong; let subrange = unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; @@ -216,8 +218,9 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( // need to make sure that we don't break existing debuginfo consumers // by doing that (at least not without a warning period). let layout_type = if ptr_type.is_box() { - // The assertion at the start of this function ensures we have a ZST allocator. - // We'll make debuginfo "skip" all ZST allocators, not just the default allocator. + // The assertion at the start of this function ensures we have a ZST + // allocator. We'll make debuginfo "skip" all ZST allocators, not just the + // default allocator. Ty::new_mut_ptr(cx.tcx, pointee_type) } else { ptr_type @@ -280,8 +283,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { - // It's possible to create a self-referential - // type in Rust by using 'impl trait': + // It's possible to create a self-referential type in Rust by using 'impl trait': // // fn foo() -> impl Copy { foo } // @@ -573,14 +575,14 @@ pub(crate) fn file_metadata<'ll>(cx: &CodegenCx<'ll, '_>, source_file: &SourceFi { // If the compiler's working directory (which also is the DW_AT_comp_dir of // the compilation unit) is a prefix of the path we are about to emit, then - // only emit the part relative to the working directory. - // Because of path remapping we sometimes see strange things here: `abs_path` - // might actually look like a relative path - // (e.g. `<crate-name-and-version>/src/lib.rs`), so if we emit it without - // taking the working directory into account, downstream tooling will - // interpret it as `<working-directory>/<crate-name-and-version>/src/lib.rs`, - // which makes no sense. Usually in such cases the working directory will also - // be remapped to `<crate-name-and-version>` or some other prefix of the path + // only emit the part relative to the working directory. Because of path + // remapping we sometimes see strange things here: `abs_path` might + // actually look like a relative path (e.g. + // `<crate-name-and-version>/src/lib.rs`), so if we emit it without taking + // the working directory into account, downstream tooling will interpret it + // as `<working-directory>/<crate-name-and-version>/src/lib.rs`, which + // makes no sense. Usually in such cases the working directory will also be + // remapped to `<crate-name-and-version>` or some other prefix of the path // we are remapping, so we end up with // `<crate-name-and-version>/<crate-name-and-version>/src/lib.rs`. // By moving the working directory portion into the `directory` part of the diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 181022087f3..8a132f89aa3 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use libc::c_uint; use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; -use rustc_codegen_ssa::traits::ConstMethods; +use rustc_codegen_ssa::traits::ConstCodegenMethods; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index 238fbad4dfd..0b3140cc91f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use libc::c_uint; use rustc_codegen_ssa::debuginfo::type_names::compute_debuginfo_type_name; use rustc_codegen_ssa::debuginfo::{tag_base_type, wants_c_like_enum_debuginfo}; -use rustc_codegen_ssa::traits::ConstMethods; +use rustc_codegen_ssa::traits::ConstCodegenMethods; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self}; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 842212ac05d..920c9e06be4 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -286,7 +286,7 @@ impl CodegenCx<'_, '_> { } } -impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn create_function_debug_context( &self, instance: Instance<'tcx>, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs index 83d7a82dadc..9674b1eb848 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -13,8 +13,7 @@ pub(crate) fn mangled_name_of_instance<'a, 'tcx>( cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>, ) -> ty::SymbolName<'tcx> { - let tcx = cx.tcx; - tcx.symbol_name(instance) + cx.tcx.symbol_name(instance) } pub(crate) fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope { diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index 4e4500b6373..b0b29ca1280 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -12,7 +12,7 @@ //! * When in doubt, define. use itertools::Itertools; -use rustc_codegen_ssa::traits::TypeMembershipMethods; +use rustc_codegen_ssa::traits::TypeMembershipCodegenMethods; use rustc_data_structures::fx::FxIndexSet; use rustc_middle::ty::{Instance, Ty}; use rustc_sanitizers::{cfi, kcfi}; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 15c519dfcb4..307fb9c35b7 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -148,7 +148,7 @@ fn get_simple_intrinsic<'ll>( Some(cx.get_intrinsic(llvm_name)) } -impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { +impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, @@ -404,7 +404,8 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { let llvm_name = &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); - // llvm expects shift to be the same type as the values, but rust always uses `u32` + // llvm expects shift to be the same type as the values, but rust + // always uses `u32`. let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); self.call_intrinsic(llvm_name, &[val, val, raw_shift]) @@ -573,8 +574,8 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { span, ) { Ok(llval) => llval, - // If there was an error, just skip this invocation... we'll abort compilation anyway, - // but we can keep codegen'ing to find more errors. + // If there was an error, just skip this invocation... we'll abort compilation + // anyway, but we can keep codegen'ing to find more errors. Err(()) => return Ok(()), } } @@ -1847,7 +1848,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>( require!( matches!( *pointer_ty.kind(), - ty::RawPtr(p_ty, p_mutbl) if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut() + ty::RawPtr(p_ty, p_mutbl) + if p_ty == values_elem && p_ty.kind() == values_elem.kind() && p_mutbl.is_mut() ), InvalidMonomorphization::ExpectedElementType { span, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e84ab0aa538..a588f11b623 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -220,17 +220,18 @@ pub enum IntPredicate { impl IntPredicate { pub fn from_generic(intpre: rustc_codegen_ssa::common::IntPredicate) -> Self { + use rustc_codegen_ssa::common::IntPredicate as Common; match intpre { - rustc_codegen_ssa::common::IntPredicate::IntEQ => IntPredicate::IntEQ, - rustc_codegen_ssa::common::IntPredicate::IntNE => IntPredicate::IntNE, - rustc_codegen_ssa::common::IntPredicate::IntUGT => IntPredicate::IntUGT, - rustc_codegen_ssa::common::IntPredicate::IntUGE => IntPredicate::IntUGE, - rustc_codegen_ssa::common::IntPredicate::IntULT => IntPredicate::IntULT, - rustc_codegen_ssa::common::IntPredicate::IntULE => IntPredicate::IntULE, - rustc_codegen_ssa::common::IntPredicate::IntSGT => IntPredicate::IntSGT, - rustc_codegen_ssa::common::IntPredicate::IntSGE => IntPredicate::IntSGE, - rustc_codegen_ssa::common::IntPredicate::IntSLT => IntPredicate::IntSLT, - rustc_codegen_ssa::common::IntPredicate::IntSLE => IntPredicate::IntSLE, + Common::IntEQ => Self::IntEQ, + Common::IntNE => Self::IntNE, + Common::IntUGT => Self::IntUGT, + Common::IntUGE => Self::IntUGE, + Common::IntULT => Self::IntULT, + Common::IntULE => Self::IntULE, + Common::IntSGT => Self::IntSGT, + Common::IntSGE => Self::IntSGE, + Common::IntSLT => Self::IntSLT, + Common::IntSLE => Self::IntSLE, } } } @@ -259,27 +260,24 @@ pub enum RealPredicate { impl RealPredicate { pub fn from_generic(realp: rustc_codegen_ssa::common::RealPredicate) -> Self { + use rustc_codegen_ssa::common::RealPredicate as Common; match realp { - rustc_codegen_ssa::common::RealPredicate::RealPredicateFalse => { - RealPredicate::RealPredicateFalse - } - rustc_codegen_ssa::common::RealPredicate::RealOEQ => RealPredicate::RealOEQ, - rustc_codegen_ssa::common::RealPredicate::RealOGT => RealPredicate::RealOGT, - rustc_codegen_ssa::common::RealPredicate::RealOGE => RealPredicate::RealOGE, - rustc_codegen_ssa::common::RealPredicate::RealOLT => RealPredicate::RealOLT, - rustc_codegen_ssa::common::RealPredicate::RealOLE => RealPredicate::RealOLE, - rustc_codegen_ssa::common::RealPredicate::RealONE => RealPredicate::RealONE, - rustc_codegen_ssa::common::RealPredicate::RealORD => RealPredicate::RealORD, - rustc_codegen_ssa::common::RealPredicate::RealUNO => RealPredicate::RealUNO, - rustc_codegen_ssa::common::RealPredicate::RealUEQ => RealPredicate::RealUEQ, - rustc_codegen_ssa::common::RealPredicate::RealUGT => RealPredicate::RealUGT, - rustc_codegen_ssa::common::RealPredicate::RealUGE => RealPredicate::RealUGE, - rustc_codegen_ssa::common::RealPredicate::RealULT => RealPredicate::RealULT, - rustc_codegen_ssa::common::RealPredicate::RealULE => RealPredicate::RealULE, - rustc_codegen_ssa::common::RealPredicate::RealUNE => RealPredicate::RealUNE, - rustc_codegen_ssa::common::RealPredicate::RealPredicateTrue => { - RealPredicate::RealPredicateTrue - } + Common::RealPredicateFalse => Self::RealPredicateFalse, + Common::RealOEQ => Self::RealOEQ, + Common::RealOGT => Self::RealOGT, + Common::RealOGE => Self::RealOGE, + Common::RealOLT => Self::RealOLT, + Common::RealOLE => Self::RealOLE, + Common::RealONE => Self::RealONE, + Common::RealORD => Self::RealORD, + Common::RealUNO => Self::RealUNO, + Common::RealUEQ => Self::RealUEQ, + Common::RealUGT => Self::RealUGT, + Common::RealUGE => Self::RealUGE, + Common::RealULT => Self::RealULT, + Common::RealULE => Self::RealULE, + Common::RealUNE => Self::RealUNE, + Common::RealPredicateTrue => Self::RealPredicateTrue, } } } @@ -311,26 +309,27 @@ pub enum TypeKind { impl TypeKind { pub fn to_generic(self) -> rustc_codegen_ssa::common::TypeKind { + use rustc_codegen_ssa::common::TypeKind as Common; match self { - TypeKind::Void => rustc_codegen_ssa::common::TypeKind::Void, - TypeKind::Half => rustc_codegen_ssa::common::TypeKind::Half, - TypeKind::Float => rustc_codegen_ssa::common::TypeKind::Float, - TypeKind::Double => rustc_codegen_ssa::common::TypeKind::Double, - TypeKind::X86_FP80 => rustc_codegen_ssa::common::TypeKind::X86_FP80, - TypeKind::FP128 => rustc_codegen_ssa::common::TypeKind::FP128, - TypeKind::PPC_FP128 => rustc_codegen_ssa::common::TypeKind::PPC_FP128, - TypeKind::Label => rustc_codegen_ssa::common::TypeKind::Label, - TypeKind::Integer => rustc_codegen_ssa::common::TypeKind::Integer, - TypeKind::Function => rustc_codegen_ssa::common::TypeKind::Function, - TypeKind::Struct => rustc_codegen_ssa::common::TypeKind::Struct, - TypeKind::Array => rustc_codegen_ssa::common::TypeKind::Array, - TypeKind::Pointer => rustc_codegen_ssa::common::TypeKind::Pointer, - TypeKind::Vector => rustc_codegen_ssa::common::TypeKind::Vector, - TypeKind::Metadata => rustc_codegen_ssa::common::TypeKind::Metadata, - TypeKind::Token => rustc_codegen_ssa::common::TypeKind::Token, - TypeKind::ScalableVector => rustc_codegen_ssa::common::TypeKind::ScalableVector, - TypeKind::BFloat => rustc_codegen_ssa::common::TypeKind::BFloat, - TypeKind::X86_AMX => rustc_codegen_ssa::common::TypeKind::X86_AMX, + Self::Void => Common::Void, + Self::Half => Common::Half, + Self::Float => Common::Float, + Self::Double => Common::Double, + Self::X86_FP80 => Common::X86_FP80, + Self::FP128 => Common::FP128, + Self::PPC_FP128 => Common::PPC_FP128, + Self::Label => Common::Label, + Self::Integer => Common::Integer, + Self::Function => Common::Function, + Self::Struct => Common::Struct, + Self::Array => Common::Array, + Self::Pointer => Common::Pointer, + Self::Vector => Common::Vector, + Self::Metadata => Common::Metadata, + Self::Token => Common::Token, + Self::ScalableVector => Common::ScalableVector, + Self::BFloat => Common::BFloat, + Self::X86_AMX => Common::X86_AMX, } } } @@ -354,18 +353,19 @@ pub enum AtomicRmwBinOp { impl AtomicRmwBinOp { pub fn from_generic(op: rustc_codegen_ssa::common::AtomicRmwBinOp) -> Self { + use rustc_codegen_ssa::common::AtomicRmwBinOp as Common; match op { - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXchg => AtomicRmwBinOp::AtomicXchg, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicAdd => AtomicRmwBinOp::AtomicAdd, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicSub => AtomicRmwBinOp::AtomicSub, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicAnd => AtomicRmwBinOp::AtomicAnd, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicNand => AtomicRmwBinOp::AtomicNand, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicOr => AtomicRmwBinOp::AtomicOr, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicXor => AtomicRmwBinOp::AtomicXor, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicMax => AtomicRmwBinOp::AtomicMax, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicMin => AtomicRmwBinOp::AtomicMin, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicUMax => AtomicRmwBinOp::AtomicUMax, - rustc_codegen_ssa::common::AtomicRmwBinOp::AtomicUMin => AtomicRmwBinOp::AtomicUMin, + Common::AtomicXchg => Self::AtomicXchg, + Common::AtomicAdd => Self::AtomicAdd, + Common::AtomicSub => Self::AtomicSub, + Common::AtomicAnd => Self::AtomicAnd, + Common::AtomicNand => Self::AtomicNand, + Common::AtomicOr => Self::AtomicOr, + Common::AtomicXor => Self::AtomicXor, + Common::AtomicMax => Self::AtomicMax, + Common::AtomicMin => Self::AtomicMin, + Common::AtomicUMax => Self::AtomicUMax, + Common::AtomicUMin => Self::AtomicUMin, } } } @@ -387,17 +387,14 @@ pub enum AtomicOrdering { impl AtomicOrdering { pub fn from_generic(ao: rustc_codegen_ssa::common::AtomicOrdering) -> Self { + use rustc_codegen_ssa::common::AtomicOrdering as Common; match ao { - rustc_codegen_ssa::common::AtomicOrdering::Unordered => AtomicOrdering::Unordered, - rustc_codegen_ssa::common::AtomicOrdering::Relaxed => AtomicOrdering::Monotonic, - rustc_codegen_ssa::common::AtomicOrdering::Acquire => AtomicOrdering::Acquire, - rustc_codegen_ssa::common::AtomicOrdering::Release => AtomicOrdering::Release, - rustc_codegen_ssa::common::AtomicOrdering::AcquireRelease => { - AtomicOrdering::AcquireRelease - } - rustc_codegen_ssa::common::AtomicOrdering::SequentiallyConsistent => { - AtomicOrdering::SequentiallyConsistent - } + Common::Unordered => Self::Unordered, + Common::Relaxed => Self::Monotonic, + Common::Acquire => Self::Acquire, + Common::Release => Self::Release, + Common::AcquireRelease => Self::AcquireRelease, + Common::SequentiallyConsistent => Self::SequentiallyConsistent, } } } @@ -563,13 +560,11 @@ pub enum ArchiveKind { K_AIXBIG, } -// LLVMRustThinLTOData unsafe extern "C" { + // LLVMRustThinLTOData pub type ThinLTOData; -} -// LLVMRustThinLTOBuffer -unsafe extern "C" { + // LLVMRustThinLTOBuffer pub type ThinLTOBuffer; } @@ -633,26 +628,12 @@ struct InvariantOpaque<'a> { // Opaque pointer types unsafe extern "C" { pub type Module; -} -unsafe extern "C" { pub type Context; -} -unsafe extern "C" { pub type Type; -} -unsafe extern "C" { pub type Value; -} -unsafe extern "C" { pub type ConstantInt; -} -unsafe extern "C" { pub type Attribute; -} -unsafe extern "C" { pub type Metadata; -} -unsafe extern "C" { pub type BasicBlock; } #[repr(C)] @@ -661,11 +642,7 @@ pub struct Builder<'a>(InvariantOpaque<'a>); pub struct PassManager<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type Pass; -} -unsafe extern "C" { pub type TargetMachine; -} -unsafe extern "C" { pub type Archive; } #[repr(C)] @@ -674,11 +651,7 @@ pub struct ArchiveIterator<'a>(InvariantOpaque<'a>); pub struct ArchiveChild<'a>(InvariantOpaque<'a>); unsafe extern "C" { pub type Twine; -} -unsafe extern "C" { pub type DiagnosticInfo; -} -unsafe extern "C" { pub type SMDiagnostic; } #[repr(C)] @@ -2177,7 +2150,8 @@ unsafe extern "C" { pub fn LLVMRustGetHostCPUName(len: *mut usize) -> *const c_char; - // This function makes copies of pointed to data, so the data's lifetime may end after this function returns + // This function makes copies of pointed to data, so the data's lifetime may end after this + // function returns. pub fn LLVMRustCreateTargetMachine( Triple: *const c_char, CPU: *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 29afe6f6bfc..fd8db4ad1d5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -217,10 +217,10 @@ impl<'a> IntoIterator for LLVMFeature<'a> { // where `{ARCH}` is the architecture name. Look for instances of `SubtargetFeature`. // // Check the current rustc fork of LLVM in the repo at https://github.com/rust-lang/llvm-project/. -// The commit in use can be found via the `llvm-project` submodule in https://github.com/rust-lang/rust/tree/master/src -// Though note that Rust can also be build with an external precompiled version of LLVM -// which might lead to failures if the oldest tested / supported LLVM version -// doesn't yet support the relevant intrinsics +// The commit in use can be found via the `llvm-project` submodule in +// https://github.com/rust-lang/rust/tree/master/src Though note that Rust can also be build with +// an external precompiled version of LLVM which might lead to failures if the oldest tested / +// supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFeature<'a>> { let arch = if sess.target.arch == "x86_64" { "x86" @@ -258,28 +258,14 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea ("aarch64", "fhm") => Some(LLVMFeature::new("fp16fml")), ("aarch64", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version - ("aarch64", "faminmax") if get_version().0 < 18 => None, - ("aarch64", "fp8") if get_version().0 < 18 => None, - ("aarch64", "fp8dot2") if get_version().0 < 18 => None, - ("aarch64", "fp8dot4") if get_version().0 < 18 => None, - ("aarch64", "fp8fma") if get_version().0 < 18 => None, ("aarch64", "fpmr") if get_version().0 != 18 => None, - ("aarch64", "lut") if get_version().0 < 18 => None, - ("aarch64", "sme-f8f16") if get_version().0 < 18 => None, - ("aarch64", "sme-f8f32") if get_version().0 < 18 => None, - ("aarch64", "sme-fa64") if get_version().0 < 18 => None, - ("aarch64", "sme-lutv2") if get_version().0 < 18 => None, - ("aarch64", "ssve-fp8dot2") if get_version().0 < 18 => None, - ("aarch64", "ssve-fp8dot4") if get_version().0 < 18 => None, - ("aarch64", "ssve-fp8fma") if get_version().0 < 18 => None, - ("aarch64", "v9.5a") if get_version().0 < 18 => None, - // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single feature called - // `fast-unaligned-access`. In LLVM 19, it was split back out. + // In LLVM 18, `unaligned-scalar-mem` was merged with `unaligned-vector-mem` into a single + // feature called `fast-unaligned-access`. In LLVM 19, it was split back out. ("riscv32" | "riscv64", "unaligned-scalar-mem") if get_version().0 == 18 => { Some(LLVMFeature::new("fast-unaligned-access")) } - // For LLVM 18, enable the evex512 target feature if a avx512 target feature is enabled. - ("x86", s) if get_version().0 >= 18 && s.starts_with("avx512") => { + // Enable the evex512 target feature if an avx512 target feature is enabled. + ("x86", s) if s.starts_with("avx512") => { Some(LLVMFeature::with_dependency(s, TargetFeatureFoldStrength::EnableOnly("evex512"))) } (_, s) => Some(LLVMFeature::new(s)), @@ -420,7 +406,8 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach .supported_target_features() .iter() .filter_map(|(feature, _gate, _implied)| { - // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings. + // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these + // strings. let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name; let desc = match llvm_target_features.binary_search_by_key(&llvm_feature, |(f, _d)| f).ok() { @@ -587,7 +574,6 @@ pub(crate) fn global_llvm_features( // -Ctarget-features if !only_base_features { let supported_features = sess.target.supported_target_features(); - let (llvm_major, _, _) = get_version(); let mut featsmap = FxHashMap::default(); // insert implied features @@ -664,12 +650,6 @@ pub(crate) fn global_llvm_features( return None; } - // if the target-feature is "backchain" and LLVM version is greater than 18 - // then we also need to add "+backchain" to the target-features attribute. - // otherwise, we will only add the naked `backchain` attribute to the attribute-group. - if feature == "backchain" && llvm_major < 18 { - return None; - } // ... otherwise though we run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index f1ef359594b..02e1995620b 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -14,7 +14,7 @@ use crate::errors::SymbolAlreadyDefined; use crate::type_of::LayoutLlvmExt; use crate::{base, llvm}; -impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { +impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { fn predefine_static( &self, def_id: DefId, @@ -24,8 +24,8 @@ impl<'tcx> PreDefineMethods<'tcx> for CodegenCx<'_, 'tcx> { ) { let instance = Instance::mono(self.tcx, def_id); let DefKind::Static { nested, .. } = self.tcx.def_kind(def_id) else { bug!() }; - // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure out - // the llvm type from the actual evaluated initializer. + // Nested statics do not have a type, so pick a dummy type and let `codegen_static` figure + // out the llvm type from the actual evaluated initializer. let ty = if nested { self.tcx.types.unit } else { diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index ec1e2cb8094..2c2b9030b7c 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -141,7 +141,7 @@ impl<'ll> CodegenCx<'ll, '_> { } } -impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> BaseTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn type_i8(&self) -> &'ll Type { unsafe { llvm::LLVMInt8TypeInContext(self.llcx) } } @@ -245,7 +245,7 @@ impl Type { } } -impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> LayoutTypeCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn backend_type(&self, layout: TyAndLayout<'tcx>) -> &'ll Type { layout.llvm_type(self) } @@ -280,7 +280,7 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } -impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> { +impl<'ll, 'tcx> TypeMembershipCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn add_type_metadata(&self, function: &'ll Value, typeid: String) { let typeid_metadata = self.typeid_metadata(typeid).unwrap(); let v = [self.const_usize(0), typeid_metadata]; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 94e77c5bd70..781cee81180 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,6 +1,6 @@ use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; -use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods, ConstMethods}; +use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::Ty; use rustc_target::abi::{Align, Endian, HasDataLayout, Size}; diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 76a94de5433..a665f5c9306 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -157,7 +157,7 @@ pub trait ArchiveBuilderBuilder { } } -pub fn create_mingw_dll_import_lib( +fn create_mingw_dll_import_lib( sess: &Session, lib_name: &str, import_name_and_ordinal_vector: Vec<(String, Option<u16>)>, diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 95c4af2e59e..b3c5b86ccf4 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -8,7 +8,7 @@ use std::{fmt, io, mem}; use rustc_target::spec::LldFlavor; #[derive(Clone)] -pub struct Command { +pub(crate) struct Command { program: Program, args: Vec<OsString>, env: Vec<(OsString, OsString)>, @@ -23,15 +23,15 @@ enum Program { } impl Command { - pub fn new<P: AsRef<OsStr>>(program: P) -> Command { + pub(crate) fn new<P: AsRef<OsStr>>(program: P) -> Command { Command::_new(Program::Normal(program.as_ref().to_owned())) } - pub fn bat_script<P: AsRef<OsStr>>(program: P) -> Command { + pub(crate) fn bat_script<P: AsRef<OsStr>>(program: P) -> Command { Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) } - pub fn lld<P: AsRef<OsStr>>(program: P, flavor: LldFlavor) -> Command { + pub(crate) fn lld<P: AsRef<OsStr>>(program: P, flavor: LldFlavor) -> Command { Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) } @@ -39,12 +39,12 @@ impl Command { Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() } } - pub fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command { + pub(crate) fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command { self._arg(arg.as_ref()); self } - pub fn args<I>(&mut self, args: I) -> &mut Command + pub(crate) fn args<I>(&mut self, args: I) -> &mut Command where I: IntoIterator<Item: AsRef<OsStr>>, { @@ -58,7 +58,7 @@ impl Command { self.args.push(arg.to_owned()); } - pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Command + pub(crate) fn env<K, V>(&mut self, key: K, value: V) -> &mut Command where K: AsRef<OsStr>, V: AsRef<OsStr>, @@ -71,7 +71,7 @@ impl Command { self.env.push((key.to_owned(), value.to_owned())); } - pub fn env_remove<K>(&mut self, key: K) -> &mut Command + pub(crate) fn env_remove<K>(&mut self, key: K) -> &mut Command where K: AsRef<OsStr>, { @@ -83,11 +83,11 @@ impl Command { self.env_remove.push(key.to_owned()); } - pub fn output(&mut self) -> io::Result<Output> { + pub(crate) fn output(&mut self) -> io::Result<Output> { self.command().output() } - pub fn command(&self) -> process::Command { + pub(crate) fn command(&self) -> process::Command { let mut ret = match self.program { Program::Normal(ref p) => process::Command::new(p), Program::CmdBatScript(ref p) => { @@ -111,17 +111,17 @@ impl Command { // extensions - pub fn get_args(&self) -> &[OsString] { + pub(crate) fn get_args(&self) -> &[OsString] { &self.args } - pub fn take_args(&mut self) -> Vec<OsString> { + pub(crate) fn take_args(&mut self) -> Vec<OsString> { mem::take(&mut self.args) } /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, /// or `false` if we should attempt to spawn and see what the OS says. - pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { + pub(crate) fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { // We mostly only care about Windows in this method, on Unix the limits // can be gargantuan anyway so we're pretty unlikely to hit them if cfg!(unix) { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index bda22255705..06fc164893f 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -30,7 +30,7 @@ use crate::errors; /// and prevent inspection of linker output in case of errors, which we occasionally do. /// This should be acceptable because other messages from rustc are in English anyway, /// and may also be desirable to improve searchability of the linker diagnostics. -pub fn disable_localization(linker: &mut Command) { +pub(crate) fn disable_localization(linker: &mut Command) { // No harm in setting both env vars simultaneously. // Unix-style linkers. linker.env("LC_ALL", "C"); @@ -41,7 +41,7 @@ pub fn disable_localization(linker: &mut Command) { /// The third parameter is for env vars, used on windows to set up the /// path for MSVC to find its DLLs, and gcc to find its bundled /// toolchain -pub fn get_linker<'a>( +pub(crate) fn get_linker<'a>( sess: &'a Session, linker: &Path, flavor: LinkerFlavor, @@ -215,28 +215,36 @@ fn link_or_cc_args<L: Linker + ?Sized>( macro_rules! generate_arg_methods { ($($ty:ty)*) => { $( impl $ty { - pub fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + #[allow(unused)] + pub(crate) fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { verbatim_args(self, args) } - pub fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + #[allow(unused)] + pub(crate) fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { verbatim_args(self, iter::once(arg)) } - pub fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self { + #[allow(unused)] + pub(crate) fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self { link_args(self, args) } - pub fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + #[allow(unused)] + pub(crate) fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { link_args(self, iter::once(arg)) } - pub fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + #[allow(unused)] + pub(crate) fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { cc_args(self, args) } - pub fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + #[allow(unused)] + pub(crate) fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { cc_args(self, iter::once(arg)) } - pub fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { + #[allow(unused)] + pub(crate) fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self { link_or_cc_args(self, args) } - pub fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { + #[allow(unused)] + pub(crate) fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self { link_or_cc_args(self, iter::once(arg)) } } @@ -263,7 +271,7 @@ generate_arg_methods! { /// represents the meaning of each option being passed down. This trait is then /// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an /// MSVC linker (e.g., `link.exe`) is being used. -pub trait Linker { +pub(crate) trait Linker { fn cmd(&mut self) -> &mut Command; fn is_cc(&self) -> bool { false @@ -314,12 +322,12 @@ pub trait Linker { } impl dyn Linker + '_ { - pub fn take_cmd(&mut self) -> Command { + pub(crate) fn take_cmd(&mut self) -> Command { mem::replace(self.cmd(), Command::new("")) } } -pub struct GccLinker<'a> { +struct GccLinker<'a> { cmd: Command, sess: &'a Session, target_cpu: &'a str, @@ -849,7 +857,7 @@ impl<'a> Linker for GccLinker<'a> { } } -pub struct MsvcLinker<'a> { +struct MsvcLinker<'a> { cmd: Command, sess: &'a Session, } @@ -1103,7 +1111,7 @@ impl<'a> Linker for MsvcLinker<'a> { } } -pub struct EmLinker<'a> { +struct EmLinker<'a> { cmd: Command, sess: &'a Session, } @@ -1220,7 +1228,7 @@ impl<'a> Linker for EmLinker<'a> { } } -pub struct WasmLd<'a> { +struct WasmLd<'a> { cmd: Command, sess: &'a Session, } @@ -1404,7 +1412,7 @@ impl<'a> WasmLd<'a> { } /// Linker shepherd script for L4Re (Fiasco) -pub struct L4Bender<'a> { +struct L4Bender<'a> { cmd: Command, sess: &'a Session, hinted_static: bool, @@ -1510,7 +1518,7 @@ impl<'a> Linker for L4Bender<'a> { } impl<'a> L4Bender<'a> { - pub fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> { + fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> { L4Bender { cmd, sess, hinted_static: false } } @@ -1523,14 +1531,14 @@ impl<'a> L4Bender<'a> { } /// Linker for AIX. -pub struct AixLinker<'a> { +struct AixLinker<'a> { cmd: Command, sess: &'a Session, hinted_static: Option<bool>, } impl<'a> AixLinker<'a> { - pub fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> { + fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> { AixLinker { cmd, sess, hinted_static: None } } @@ -1758,7 +1766,7 @@ pub(crate) fn linked_symbols( /// Much simplified and explicit CLI for the NVPTX linker. The linker operates /// with bitcode and uses LLVM backend to generate a PTX assembly. -pub struct PtxLinker<'a> { +struct PtxLinker<'a> { cmd: Command, sess: &'a Session, } @@ -1824,7 +1832,7 @@ impl<'a> Linker for PtxLinker<'a> { } /// The `self-contained` LLVM bitcode linker -pub struct LlbcLinker<'a> { +struct LlbcLinker<'a> { cmd: Command, sess: &'a Session, } @@ -1895,7 +1903,7 @@ impl<'a> Linker for LlbcLinker<'a> { fn linker_plugin_lto(&mut self) {} } -pub struct BpfLinker<'a> { +struct BpfLinker<'a> { cmd: Command, sess: &'a Session, } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 6215616e510..ff87f7f1ea4 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -32,7 +32,7 @@ use rustc_target::spec::{ef_avr_arch, RelocModel, Target}; /// <dd>The metadata can be found in the `.rustc` section of the shared library.</dd> /// </dl> #[derive(Debug)] -pub struct DefaultMetadataLoader; +pub(crate) struct DefaultMetadataLoader; static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata"; @@ -416,7 +416,7 @@ fn macho_is_arm64e(target: &Target) -> bool { target.llvm_target.starts_with("arm64e") } -pub enum MetadataPosition { +pub(crate) enum MetadataPosition { First, Last, } @@ -452,7 +452,7 @@ pub enum MetadataPosition { /// * ELF - All other targets are similar to Windows in that there's a /// `SHF_EXCLUDE` flag we can set on sections in an object file to get /// automatically removed from the final output. -pub fn create_wrapper_file( +pub(crate) fn create_wrapper_file( sess: &Session, section_name: String, data: &[u8], diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index d11ed54eb20..2b3a2e3a369 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -1,9 +1,9 @@ pub mod archive; -pub mod command; +pub(crate) mod command; pub mod link; -pub mod linker; +pub(crate) mod linker; pub mod lto; pub mod metadata; -pub mod rpath; +pub(crate) mod rpath; pub mod symbol_export; pub mod write; diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 42f8c3114ff..56a808df6b0 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -6,14 +6,14 @@ use rustc_data_structures::fx::FxHashSet; use rustc_fs_util::try_canonicalize; use tracing::debug; -pub struct RPathConfig<'a> { +pub(super) struct RPathConfig<'a> { pub libs: &'a [&'a Path], pub out_filename: PathBuf, pub is_like_osx: bool, pub linker_is_gnu: bool, } -pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> { +pub(super) fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> { debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index d2f11d48140..257e2dfac00 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -18,7 +18,7 @@ use tracing::debug; use crate::base::allocator_kind_for_codegen; -pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { +fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(tcx.crate_types()) } @@ -484,7 +484,7 @@ fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) !tcx.reachable_set(()).contains(&def_id) } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { providers.reachable_non_generics = reachable_non_generics_provider; providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; providers.exported_symbols = exported_symbols_provider_local; @@ -525,7 +525,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel } /// This is the symbol name of the given instance instantiated in a specific crate. -pub fn symbol_name_for_instance_in_crate<'tcx>( +pub(crate) fn symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, instantiating_crate: CrateNum, @@ -582,7 +582,7 @@ pub fn symbol_name_for_instance_in_crate<'tcx>( /// This is the symbol name of the given instance as seen by the linker. /// /// On 32-bit Windows symbols are decorated according to their calling conventions. -pub fn linking_symbol_name_for_instance_in_crate<'tcx>( +pub(crate) fn linking_symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, instantiating_crate: CrateNum, @@ -661,7 +661,7 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( format!("{prefix}{undecorated}{suffix}{args_in_bytes}") } -pub fn exporting_symbol_name_for_instance_in_crate<'tcx>( +pub(crate) fn exporting_symbol_name_for_instance_in_crate<'tcx>( tcx: TyCtxt<'tcx>, symbol: ExportedSymbol<'tcx>, cnum: CrateNum, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index feb27c148a1..c170cd41ec4 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -16,7 +16,7 @@ use rustc_errors::emitter::Emitter; use rustc_errors::translation::Translate; use rustc_errors::{ Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan, - Style, + Style, Suggestions, }; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -335,7 +335,7 @@ pub type TargetMachineFactoryFn<B> = Arc< + Sync, >; -pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>; +type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>; /// Additional resources used by optimize_and_codegen (not module specific) #[derive(Clone)] @@ -437,9 +437,9 @@ fn generate_lto_work<B: ExtraBackendMethods>( } } -pub struct CompiledModules { - pub modules: Vec<CompiledModule>, - pub allocator_module: Option<CompiledModule>, +struct CompiledModules { + modules: Vec<CompiledModule>, + allocator_module: Option<CompiledModule>, } fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { @@ -462,7 +462,7 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { } } -pub fn start_async_codegen<B: ExtraBackendMethods>( +pub(crate) fn start_async_codegen<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>, target_cpu: String, @@ -836,13 +836,13 @@ pub enum FatLtoInput<B: WriteBackendMethods> { } /// Actual LTO type we end up choosing based on multiple factors. -pub enum ComputedLtoType { +pub(crate) enum ComputedLtoType { No, Thin, Fat, } -pub fn compute_per_cgu_lto_type( +pub(crate) fn compute_per_cgu_lto_type( sess_lto: &Lto, opts: &config::Options, sess_crate_types: &[CrateType], @@ -1087,7 +1087,7 @@ struct Diagnostic { // A cut-down version of `rustc_errors::Subdiag` that impls `Send`. It's // missing the following fields from `rustc_errors::Subdiag`. // - `span`: it doesn't impl `Send`. -pub struct Subdiagnostic { +pub(crate) struct Subdiagnostic { level: Level, messages: Vec<(DiagMessage, Style)>, } @@ -1779,7 +1779,7 @@ fn start_executing_work<B: ExtraBackendMethods>( /// `FatalError` is explicitly not `Send`. #[must_use] -pub struct WorkerFatalError; +pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext<B>, @@ -1867,7 +1867,7 @@ pub struct SharedEmitterMain { } impl SharedEmitter { - pub fn new() -> (SharedEmitter, SharedEmitterMain) { + fn new() -> (SharedEmitter, SharedEmitterMain) { let (sender, receiver) = channel(); (SharedEmitter { sender }, SharedEmitterMain { receiver }) @@ -1883,7 +1883,7 @@ impl SharedEmitter { drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source))); } - pub fn fatal(&self, msg: &str) { + fn fatal(&self, msg: &str) { drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); } } @@ -1903,7 +1903,7 @@ impl Emitter for SharedEmitter { // Check that we aren't missing anything interesting when converting to // the cut-down local `DiagInner`. assert_eq!(diag.span, MultiSpan::new()); - assert_eq!(diag.suggestions, Ok(vec![])); + assert_eq!(diag.suggestions, Suggestions::Enabled(vec![])); assert_eq!(diag.sort_span, rustc_span::DUMMY_SP); assert_eq!(diag.is_lint, None); // No sensible check for `diag.emitted_at`. @@ -1930,7 +1930,7 @@ impl Emitter for SharedEmitter { } impl SharedEmitterMain { - pub fn check(&self, sess: &Session, blocking: bool) { + fn check(&self, sess: &Session, blocking: bool) { loop { let message = if blocking { match self.receiver.recv() { @@ -2087,17 +2087,17 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { ) } - pub fn codegen_finished(&self, tcx: TyCtxt<'_>) { + pub(crate) fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::<B>))); } - pub fn check_for_errors(&self, sess: &Session) { + pub(crate) fn check_for_errors(&self, sess: &Session) { self.shared_emitter_main.check(sess, false); } - pub fn wait_for_signal_to_codegen_item(&self) { + pub(crate) fn wait_for_signal_to_codegen_item(&self) { match self.codegen_worker_receive.recv() { Ok(CguMessage) => { // Ok to proceed. @@ -2110,7 +2110,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { } } -pub fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>( +pub(crate) fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, module: ModuleCodegen<B::Module>, @@ -2120,7 +2120,7 @@ pub fn submit_codegened_module_to_llvm<B: ExtraBackendMethods>( drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost }))); } -pub fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>( +pub(crate) fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, module: CachedModuleCodegen, @@ -2129,7 +2129,7 @@ pub fn submit_post_lto_module_to_llvm<B: ExtraBackendMethods>( drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone::<B> { llvm_work_item, cost: 0 }))); } -pub fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( +pub(crate) fn submit_pre_lto_module_to_llvm<B: ExtraBackendMethods>( _backend: &B, tcx: TyCtxt<'_>, tx_to_llvm_workers: &Sender<Box<dyn Any + Send>>, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index f1e7f87f567..3d1007a4673 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -44,47 +44,23 @@ use crate::{ errors, meth, mir, CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, }; -pub fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { - match op { - BinOp::Eq => IntPredicate::IntEQ, - BinOp::Ne => IntPredicate::IntNE, - BinOp::Lt => { - if signed { - IntPredicate::IntSLT - } else { - IntPredicate::IntULT - } - } - BinOp::Le => { - if signed { - IntPredicate::IntSLE - } else { - IntPredicate::IntULE - } - } - BinOp::Gt => { - if signed { - IntPredicate::IntSGT - } else { - IntPredicate::IntUGT - } - } - BinOp::Ge => { - if signed { - IntPredicate::IntSGE - } else { - IntPredicate::IntUGE - } - } - op => bug!( - "comparison_op_to_icmp_predicate: expected comparison operator, \ - found {:?}", - op - ), +pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { + match (op, signed) { + (BinOp::Eq, _) => IntPredicate::IntEQ, + (BinOp::Ne, _) => IntPredicate::IntNE, + (BinOp::Lt, true) => IntPredicate::IntSLT, + (BinOp::Lt, false) => IntPredicate::IntULT, + (BinOp::Le, true) => IntPredicate::IntSLE, + (BinOp::Le, false) => IntPredicate::IntULE, + (BinOp::Gt, true) => IntPredicate::IntSGT, + (BinOp::Gt, false) => IntPredicate::IntUGT, + (BinOp::Ge, true) => IntPredicate::IntSGE, + (BinOp::Ge, false) => IntPredicate::IntUGE, + op => bug!("bin_op_to_icmp_predicate: expected comparison operator, found {:?}", op), } } -pub fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate { +pub(crate) fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate { match op { BinOp::Eq => RealPredicate::RealOEQ, BinOp::Ne => RealPredicate::RealUNE, @@ -92,13 +68,7 @@ pub fn bin_op_to_fcmp_predicate(op: BinOp) -> RealPredicate { BinOp::Le => RealPredicate::RealOLE, BinOp::Gt => RealPredicate::RealOGT, BinOp::Ge => RealPredicate::RealOGE, - op => { - bug!( - "comparison_op_to_fcmp_predicate: expected comparison operator, \ - found {:?}", - op - ); - } + op => bug!("bin_op_to_fcmp_predicate: expected comparison operator, found {:?}", op), } } @@ -135,7 +105,7 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// /// The `old_info` argument is a bit odd. It is intended for use in an upcast, /// where the new vtable for an object will be derived from the old one. -pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, source: Ty<'tcx>, target: Ty<'tcx>, @@ -145,16 +115,17 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let (source, target) = cx.tcx().struct_lockstep_tails_for_codegen(source, target, bx.param_env()); match (source.kind(), target.kind()) { - (&ty::Array(_, len), &ty::Slice(_)) => { - cx.const_usize(len.eval_target_usize(cx.tcx(), ty::ParamEnv::reveal_all())) - } + (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize( + len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"), + ), (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) if src_dyn_kind == target_dyn_kind => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. + // A NOP cast that doesn't actually change anything, should be allowed even with + // invalid vtables. return old_info; } @@ -182,7 +153,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } /// Coerces `src` to `dst_ty`. `src_ty` must be a pointer. -pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, src: Bx::Value, src_ty: Ty<'tcx>, @@ -227,7 +198,7 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } /// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type. -pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, src: Bx::Value, src_ty_and_layout: TyAndLayout<'tcx>, @@ -250,7 +221,7 @@ pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// Coerces `src`, which is a reference to a value of type `src_ty`, /// to a value of type `dst_ty`, and stores the result in `dst`. -pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, src: PlaceRef<'tcx, Bx::Value>, dst: PlaceRef<'tcx, Bx::Value>, @@ -305,7 +276,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// /// If `is_unchecked` is true, this does no masking, and adds sufficient `assume` /// calls or operation flags to preserve as much freedom to optimize as possible. -pub fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, lhs: Bx::Value, mut rhs: Bx::Value, @@ -369,11 +340,11 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { /// Returns `true` if this session's target requires the new exception /// handling LLVM IR instructions (catchpad / cleanuppad / ... instead /// of landingpad) -pub fn wants_new_eh_instructions(sess: &Session) -> bool { +pub(crate) fn wants_new_eh_instructions(sess: &Session) -> bool { wants_wasm_eh(sess) || wants_msvc_seh(sess) } -pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, instance: Instance<'tcx>, ) { @@ -454,7 +425,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let isize_ty = cx.type_isize(); let ptr_ty = cx.type_ptr(); - let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); + let (arg_argc, arg_argv) = get_argc_argv(&mut bx); let (start_fn, start_ty, args, instance) = if let EntryFnType::Main { sigpipe } = entry_type { @@ -497,33 +468,30 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } /// Obtain the `argc` and `argv` values to pass to the rust start function. -fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( - cx: &'a Bx::CodegenCx, - bx: &mut Bx, -) -> (Bx::Value, Bx::Value) { - if cx.sess().target.os.contains("uefi") { +fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> (Bx::Value, Bx::Value) { + if bx.cx().sess().target.os.contains("uefi") { // Params for UEFI let param_handle = bx.get_param(0); let param_system_table = bx.get_param(1); let ptr_size = bx.tcx().data_layout.pointer_size; let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let arg_argc = bx.const_int(cx.type_isize(), 2); + let arg_argc = bx.const_int(bx.cx().type_isize(), 2); let arg_argv = bx.alloca(2 * ptr_size, ptr_align); bx.store(param_handle, arg_argv, ptr_align); let arg_argv_el1 = bx.inbounds_ptradd(arg_argv, bx.const_usize(ptr_size.bytes())); bx.store(param_system_table, arg_argv_el1, ptr_align); (arg_argc, arg_argv) - } else if cx.sess().target.main_needs_argc_argv { + } else if bx.cx().sess().target.main_needs_argc_argv { // Params from native `main()` used as args for rust start function let param_argc = bx.get_param(0); let param_argv = bx.get_param(1); - let arg_argc = bx.intcast(param_argc, cx.type_isize(), true); + let arg_argc = bx.intcast(param_argc, bx.cx().type_isize(), true); let arg_argv = param_argv; (arg_argc, arg_argv) } else { // The Rust start function doesn't need `argc` and `argv`, so just pass zeros. - let arg_argc = bx.const_int(cx.type_int(), 0); - let arg_argv = bx.const_null(cx.type_ptr()); + let arg_argc = bx.const_int(bx.cx().type_int(), 0); + let arg_argv = bx.const_null(bx.cx().type_ptr()); (arg_argc, arg_argv) } } @@ -985,7 +953,8 @@ impl CrateInfo { false } CrateType::Staticlib | CrateType::Rlib => { - // We don't invoke the linker for these, so we don't need to collect the NatVis for them. + // We don't invoke the linker for these, so we don't need to collect the NatVis for + // them. false } }); @@ -999,7 +968,7 @@ impl CrateInfo { } } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { providers.backend_optimization_level = |tcx, cratenum| { let for_speed = match tcx.sess.opts.optimize { // If globally no optimisation is done, #[optimize] has no effect. diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 9149c602296..137e481f08c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -317,9 +317,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { "extern mutable statics are not allowed with `#[linkage]`", ); diag.note( - "marking the extern static mutable would allow changing which symbol \ - the static references rather than make the target of the symbol \ - mutable", + "marking the extern static mutable would allow changing which \ + symbol the static references rather than make the target of the \ + symbol mutable", ); diag.emit(); } @@ -711,18 +711,19 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { if let Some(MetaItemLit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list { - // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, - // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined - // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information - // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. + // According to the table at + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the + // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined + // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import + // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t. // - // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this: - // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies - // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library - // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import - // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet - // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment - // about LINK.EXE failing.) + // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for + // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that + // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import + // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an + // import library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I + // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL -- + // see earlier comment about LINK.EXE failing.) if *ordinal <= u16::MAX as u128 { Some(ordinal.get() as u16) } else { @@ -755,6 +756,6 @@ fn check_link_name_xor_ordinal( } } -pub fn provide(providers: &mut Providers) { +pub(crate) fn provide(providers: &mut Providers) { *providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..*providers }; } diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index bfb1d217eae..582a2a87e48 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -118,7 +118,7 @@ mod temp_stable_hash_impls { } } -pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &Bx, span: Option<Span>, li: LangItem, @@ -129,7 +129,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance), instance) } -pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( +pub(crate) fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, llty: Bx::Type, mask_llty: Bx::Type, @@ -200,7 +200,8 @@ pub fn i686_decorated_name( let mut decorated_name = String::with_capacity(name.len() + 6); if disable_name_mangling { - // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be disabled. + // LLVM uses a binary 1 ('\x01') prefix to a name to indicate that mangling needs to be + // disabled. decorated_name.push('\x01'); } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs index 0918660e6be..bfd1b94c790 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs @@ -54,7 +54,7 @@ pub fn tag_base_type<'tcx>(tcx: TyCtxt<'tcx>, enum_type_and_layout: TyAndLayout< }) } -pub fn tag_base_type_opt<'tcx>( +fn tag_base_type_opt<'tcx>( tcx: TyCtxt<'tcx>, enum_type_and_layout: TyAndLayout<'tcx>, ) -> Option<Ty<'tcx>> { @@ -76,9 +76,9 @@ pub fn tag_base_type_opt<'tcx>( Primitive::Float(f) => Integer::from_size(f.size()).unwrap(), // FIXME(erikdesjardins): handle non-default addrspace ptr sizes Primitive::Pointer(_) => { - // If the niche is the NULL value of a reference, then `discr_enum_ty` will be - // a RawPtr. CodeView doesn't know what to do with enums whose base type is a - // pointer so we fix this up to just be `usize`. + // If the niche is the NULL value of a reference, then `discr_enum_ty` will + // be a RawPtr. CodeView doesn't know what to do with enums whose base type + // is a pointer so we fix this up to just be `usize`. // DWARF might be able to deal with this but with an integer type we are on // the safe side there too. tcx.data_layout.ptr_sized_integer() diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index e34fbfe3f76..369ab387bea 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -95,7 +95,8 @@ fn push_debuginfo_type_name<'tcx>( } Err(e) => { // Computing the layout can still fail here, e.g. if the target architecture - // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. + // cannot represent the type. See + // https://github.com/rust-lang/rust/issues/94961. tcx.dcx().emit_fatal(e.into_diagnostic()); } } @@ -187,7 +188,8 @@ fn push_debuginfo_type_name<'tcx>( _ => write!( output, ",{}>", - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) + len.try_to_target_usize(tcx) + .expect("expected monomorphic const in codegen") ) .unwrap(), } @@ -199,7 +201,8 @@ fn push_debuginfo_type_name<'tcx>( _ => write!( output, "; {}]", - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) + len.try_to_target_usize(tcx) + .expect("expected monomorphic const in codegen") ) .unwrap(), } @@ -575,33 +578,20 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: & } fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str { + use CoroutineDesugaring::*; + use CoroutineKind::*; + use CoroutineSource::*; match coroutine_kind { - Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Block)) => { - "gen_block" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Closure)) => { - "gen_closure" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn)) => "gen_fn", - Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Block)) => { - "async_block" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Closure)) => { - "async_closure" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn)) => { - "async_fn" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Block)) => { - "async_gen_block" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Closure)) => { - "async_gen_closure" - } - Some(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, CoroutineSource::Fn)) => { - "async_gen_fn" - } - Some(CoroutineKind::Coroutine(_)) => "coroutine", + Some(Desugared(Gen, Block)) => "gen_block", + Some(Desugared(Gen, Closure)) => "gen_closure", + Some(Desugared(Gen, Fn)) => "gen_fn", + Some(Desugared(Async, Block)) => "async_block", + Some(Desugared(Async, Closure)) => "async_closure", + Some(Desugared(Async, Fn)) => "async_fn", + Some(Desugared(AsyncGen, Block)) => "async_gen_block", + Some(Desugared(AsyncGen, Closure)) => "async_gen_closure", + Some(Desugared(AsyncGen, Fn)) => "async_gen_fn", + Some(Coroutine(_)) => "coroutine", None => "closure", } } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 573a8cf7cbe..58877379e26 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -21,7 +21,7 @@ use crate::fluent_generated as fluent; #[derive(Diagnostic)] #[diag(codegen_ssa_incorrect_cgu_reuse_type)] -pub struct IncorrectCguReuseType<'a> { +pub(crate) struct IncorrectCguReuseType<'a> { #[primary_span] pub span: Span, pub cgu_user_name: &'a str, @@ -32,14 +32,14 @@ pub struct IncorrectCguReuseType<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_cgu_not_recorded)] -pub struct CguNotRecorded<'a> { +pub(crate) struct CguNotRecorded<'a> { pub cgu_user_name: &'a str, pub cgu_name: &'a str, } #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] -pub struct UnknownReuseKind { +pub(crate) struct UnknownReuseKind { #[primary_span] pub span: Span, pub kind: Symbol, @@ -47,14 +47,14 @@ pub struct UnknownReuseKind { #[derive(Diagnostic)] #[diag(codegen_ssa_missing_query_depgraph)] -pub struct MissingQueryDepGraph { +pub(crate) struct MissingQueryDepGraph { #[primary_span] pub span: Span, } #[derive(Diagnostic)] #[diag(codegen_ssa_malformed_cgu_name)] -pub struct MalformedCguName { +pub(crate) struct MalformedCguName { #[primary_span] pub span: Span, pub user_path: String, @@ -63,7 +63,7 @@ pub struct MalformedCguName { #[derive(Diagnostic)] #[diag(codegen_ssa_no_module_named)] -pub struct NoModuleNamed<'a> { +pub(crate) struct NoModuleNamed<'a> { #[primary_span] pub span: Span, pub user_path: &'a str, @@ -73,7 +73,7 @@ pub struct NoModuleNamed<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_field_associated_value_expected)] -pub struct FieldAssociatedValueExpected { +pub(crate) struct FieldAssociatedValueExpected { #[primary_span] pub span: Span, pub name: Symbol, @@ -81,7 +81,7 @@ pub struct FieldAssociatedValueExpected { #[derive(Diagnostic)] #[diag(codegen_ssa_no_field)] -pub struct NoField { +pub(crate) struct NoField { #[primary_span] pub span: Span, pub name: Symbol, @@ -89,49 +89,49 @@ pub struct NoField { #[derive(Diagnostic)] #[diag(codegen_ssa_lib_def_write_failure)] -pub struct LibDefWriteFailure { +pub(crate) struct LibDefWriteFailure { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_version_script_write_failure)] -pub struct VersionScriptWriteFailure { +pub(crate) struct VersionScriptWriteFailure { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_symbol_file_write_failure)] -pub struct SymbolFileWriteFailure { +pub(crate) struct SymbolFileWriteFailure { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_ld64_unimplemented_modifier)] -pub struct Ld64UnimplementedModifier; +pub(crate) struct Ld64UnimplementedModifier; #[derive(Diagnostic)] #[diag(codegen_ssa_linker_unsupported_modifier)] -pub struct LinkerUnsupportedModifier; +pub(crate) struct LinkerUnsupportedModifier; #[derive(Diagnostic)] #[diag(codegen_ssa_L4Bender_exporting_symbols_unimplemented)] -pub struct L4BenderExportingSymbolsUnimplemented; +pub(crate) struct L4BenderExportingSymbolsUnimplemented; #[derive(Diagnostic)] #[diag(codegen_ssa_no_natvis_directory)] -pub struct NoNatvisDirectory { +pub(crate) struct NoNatvisDirectory { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_no_saved_object_file)] -pub struct NoSavedObjectFile<'a> { +pub(crate) struct NoSavedObjectFile<'a> { pub cgu_name: &'a str, } #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] -pub struct CopyPathBuf { +pub(crate) struct CopyPathBuf { pub source_file: PathBuf, pub output_path: PathBuf, pub error: Error, @@ -180,20 +180,20 @@ pub struct IgnoringOutput { #[derive(Diagnostic)] #[diag(codegen_ssa_create_temp_dir)] -pub struct CreateTempDir { +pub(crate) struct CreateTempDir { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_add_native_library)] -pub struct AddNativeLibrary { +pub(crate) struct AddNativeLibrary { pub library_path: PathBuf, pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_multiple_external_func_decl)] -pub struct MultipleExternalFuncDecl<'a> { +pub(crate) struct MultipleExternalFuncDecl<'a> { #[primary_span] pub span: Span, pub function: Symbol, @@ -215,7 +215,7 @@ pub enum LinkRlibError { IncompatibleDependencyFormats { ty1: String, ty2: String, list1: String, list2: String }, } -pub struct ThorinErrorWrapper(pub thorin::Error); +pub(crate) struct ThorinErrorWrapper(pub thorin::Error); impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { @@ -343,7 +343,7 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper { } } -pub struct LinkingFailed<'a> { +pub(crate) struct LinkingFailed<'a> { pub linker_path: &'a PathBuf, pub exit_status: ExitStatus, pub command: &'a Command, @@ -376,28 +376,28 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { #[derive(Diagnostic)] #[diag(codegen_ssa_link_exe_unexpected_error)] -pub struct LinkExeUnexpectedError; +pub(crate) struct LinkExeUnexpectedError; #[derive(Diagnostic)] #[diag(codegen_ssa_repair_vs_build_tools)] -pub struct RepairVSBuildTools; +pub(crate) struct RepairVSBuildTools; #[derive(Diagnostic)] #[diag(codegen_ssa_missing_cpp_build_tool_component)] -pub struct MissingCppBuildToolComponent; +pub(crate) struct MissingCppBuildToolComponent; #[derive(Diagnostic)] #[diag(codegen_ssa_select_cpp_build_tool_workload)] -pub struct SelectCppBuildToolWorkload; +pub(crate) struct SelectCppBuildToolWorkload; #[derive(Diagnostic)] #[diag(codegen_ssa_visual_studio_not_installed)] -pub struct VisualStudioNotInstalled; +pub(crate) struct VisualStudioNotInstalled; #[derive(Diagnostic)] #[diag(codegen_ssa_linker_not_found)] #[note] -pub struct LinkerNotFound { +pub(crate) struct LinkerNotFound { pub linker_path: PathBuf, pub error: Error, } @@ -406,7 +406,7 @@ pub struct LinkerNotFound { #[diag(codegen_ssa_unable_to_exe_linker)] #[note] #[note(codegen_ssa_command_note)] -pub struct UnableToExeLinker { +pub(crate) struct UnableToExeLinker { pub linker_path: PathBuf, pub error: Error, pub command_formatted: String, @@ -414,38 +414,38 @@ pub struct UnableToExeLinker { #[derive(Diagnostic)] #[diag(codegen_ssa_msvc_missing_linker)] -pub struct MsvcMissingLinker; +pub(crate) struct MsvcMissingLinker; #[derive(Diagnostic)] #[diag(codegen_ssa_self_contained_linker_missing)] -pub struct SelfContainedLinkerMissing; +pub(crate) struct SelfContainedLinkerMissing; #[derive(Diagnostic)] #[diag(codegen_ssa_check_installed_visual_studio)] -pub struct CheckInstalledVisualStudio; +pub(crate) struct CheckInstalledVisualStudio; #[derive(Diagnostic)] #[diag(codegen_ssa_insufficient_vs_code_product)] -pub struct InsufficientVSCodeProduct; +pub(crate) struct InsufficientVSCodeProduct; #[derive(Diagnostic)] #[diag(codegen_ssa_processing_dymutil_failed)] #[note] -pub struct ProcessingDymutilFailed { +pub(crate) struct ProcessingDymutilFailed { pub status: ExitStatus, pub output: String, } #[derive(Diagnostic)] #[diag(codegen_ssa_unable_to_run_dsymutil)] -pub struct UnableToRunDsymutil { +pub(crate) struct UnableToRunDsymutil { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_stripping_debug_info_failed)] #[note] -pub struct StrippingDebugInfoFailed<'a> { +pub(crate) struct StrippingDebugInfoFailed<'a> { pub util: &'a str, pub status: ExitStatus, pub output: String, @@ -453,58 +453,59 @@ pub struct StrippingDebugInfoFailed<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_unable_to_run)] -pub struct UnableToRun<'a> { +pub(crate) struct UnableToRun<'a> { pub util: &'a str, pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_linker_file_stem)] -pub struct LinkerFileStem; +pub(crate) struct LinkerFileStem; #[derive(Diagnostic)] #[diag(codegen_ssa_static_library_native_artifacts)] -pub struct StaticLibraryNativeArtifacts; +pub(crate) struct StaticLibraryNativeArtifacts; #[derive(Diagnostic)] #[diag(codegen_ssa_static_library_native_artifacts_to_file)] -pub struct StaticLibraryNativeArtifactsToFile<'a> { +pub(crate) struct StaticLibraryNativeArtifactsToFile<'a> { pub path: &'a Path, } #[derive(Diagnostic)] #[diag(codegen_ssa_link_script_unavailable)] -pub struct LinkScriptUnavailable; +pub(crate) struct LinkScriptUnavailable; #[derive(Diagnostic)] #[diag(codegen_ssa_link_script_write_failure)] -pub struct LinkScriptWriteFailure { +pub(crate) struct LinkScriptWriteFailure { pub path: PathBuf, pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_failed_to_write)] -pub struct FailedToWrite { +pub(crate) struct FailedToWrite { pub path: PathBuf, pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_unable_to_write_debugger_visualizer)] -pub struct UnableToWriteDebuggerVisualizer { +pub(crate) struct UnableToWriteDebuggerVisualizer { pub path: PathBuf, pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_rlib_archive_build_failure)] -pub struct RlibArchiveBuildFailure { +pub(crate) struct RlibArchiveBuildFailure { pub path: PathBuf, pub error: Error, } #[derive(Diagnostic)] +// Public for rustc_codegen_llvm::back::archive pub enum ExtractBundledLibsError<'a> { #[diag(codegen_ssa_extract_bundled_libs_open_file)] OpenFile { rlib: &'a Path, error: Box<dyn std::error::Error> }, @@ -533,26 +534,26 @@ pub enum ExtractBundledLibsError<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_unsupported_arch)] -pub struct UnsupportedArch<'a> { +pub(crate) struct UnsupportedArch<'a> { pub arch: &'a str, pub os: &'a str, } #[derive(Diagnostic)] -pub enum AppleSdkRootError<'a> { +pub(crate) enum AppleSdkRootError<'a> { #[diag(codegen_ssa_apple_sdk_error_sdk_path)] SdkPath { sdk_name: &'a str, error: Error }, } #[derive(Diagnostic)] #[diag(codegen_ssa_read_file)] -pub struct ReadFileError { +pub(crate) struct ReadFileError { pub message: std::io::Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_unsupported_link_self_contained)] -pub struct UnsupportedLinkSelfContained; +pub(crate) struct UnsupportedLinkSelfContained; #[derive(Diagnostic)] #[diag(codegen_ssa_archive_build_failure)] @@ -571,7 +572,7 @@ pub struct UnknownArchiveKind<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_expected_used_symbol)] -pub struct ExpectedUsedSymbol { +pub(crate) struct ExpectedUsedSymbol { #[primary_span] pub span: Span, } @@ -579,45 +580,45 @@ pub struct ExpectedUsedSymbol { #[derive(Diagnostic)] #[diag(codegen_ssa_multiple_main_functions)] #[help] -pub struct MultipleMainFunctions { +pub(crate) struct MultipleMainFunctions { #[primary_span] pub span: Span, } #[derive(Diagnostic)] #[diag(codegen_ssa_metadata_object_file_write)] -pub struct MetadataObjectFileWrite { +pub(crate) struct MetadataObjectFileWrite { pub error: Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_invalid_windows_subsystem)] -pub struct InvalidWindowsSubsystem { +pub(crate) struct InvalidWindowsSubsystem { pub subsystem: Symbol, } #[derive(Diagnostic)] #[diag(codegen_ssa_shuffle_indices_evaluation)] -pub struct ShuffleIndicesEvaluation { +pub(crate) struct ShuffleIndicesEvaluation { #[primary_span] pub span: Span, } #[derive(Diagnostic)] #[diag(codegen_ssa_missing_memory_ordering)] -pub struct MissingMemoryOrdering; +pub(crate) struct MissingMemoryOrdering; #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_atomic_ordering)] -pub struct UnknownAtomicOrdering; +pub(crate) struct UnknownAtomicOrdering; #[derive(Diagnostic)] #[diag(codegen_ssa_atomic_compare_exchange)] -pub struct AtomicCompareExchange; +pub(crate) struct AtomicCompareExchange; #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_atomic_operation)] -pub struct UnknownAtomicOperation; +pub(crate) struct UnknownAtomicOperation; #[derive(Diagnostic)] pub enum InvalidMonomorphization<'tcx> { @@ -986,7 +987,7 @@ impl IntoDiagArg for ExpectedPointerMutability { #[derive(Diagnostic)] #[diag(codegen_ssa_invalid_no_sanitize)] #[note] -pub struct InvalidNoSanitize { +pub(crate) struct InvalidNoSanitize { #[primary_span] pub span: Span, } @@ -994,7 +995,7 @@ pub struct InvalidNoSanitize { #[derive(Diagnostic)] #[diag(codegen_ssa_invalid_link_ordinal_nargs)] #[note] -pub struct InvalidLinkOrdinalNargs { +pub(crate) struct InvalidLinkOrdinalNargs { #[primary_span] pub span: Span, } @@ -1002,14 +1003,14 @@ pub struct InvalidLinkOrdinalNargs { #[derive(Diagnostic)] #[diag(codegen_ssa_illegal_link_ordinal_format)] #[note] -pub struct InvalidLinkOrdinalFormat { +pub(crate) struct InvalidLinkOrdinalFormat { #[primary_span] pub span: Span, } #[derive(Diagnostic)] #[diag(codegen_ssa_target_feature_safe_trait)] -pub struct TargetFeatureSafeTrait { +pub(crate) struct TargetFeatureSafeTrait { #[primary_span] #[label] pub span: Span, @@ -1050,7 +1051,7 @@ pub(crate) struct ErrorCallingDllTool<'a> { #[derive(Diagnostic)] #[diag(codegen_ssa_error_creating_remark_dir)] -pub struct ErrorCreatingRemarkDir { +pub(crate) struct ErrorCreatingRemarkDir { pub error: std::io::Error, } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index c89bfca6687..26d4d0acac5 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -11,6 +11,7 @@ #![feature(negative_impls)] #![feature(rustdoc_internals)] #![feature(strict_provenance)] +#![feature(trait_alias)] #![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end @@ -128,7 +129,7 @@ impl CompiledModule { } } -pub struct CachedModuleCodegen { +pub(crate) struct CachedModuleCodegen { pub name: String, pub source: WorkProduct, } diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index c9602d9cdae..ecc3b2b24f1 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -8,10 +8,10 @@ use tracing::{debug, instrument}; use crate::traits::*; #[derive(Copy, Clone, Debug)] -pub struct VirtualIndex(u64); +pub(crate) struct VirtualIndex(u64); impl<'a, 'tcx> VirtualIndex { - pub fn from_index(index: usize) -> Self { + pub(crate) fn from_index(index: usize) -> Self { VirtualIndex(index as u64) } @@ -51,7 +51,7 @@ impl<'a, 'tcx> VirtualIndex { } } - pub fn get_optional_fn<Bx: BuilderMethods<'a, 'tcx>>( + pub(crate) fn get_optional_fn<Bx: BuilderMethods<'a, 'tcx>>( self, bx: &mut Bx, llvtable: Bx::Value, @@ -61,7 +61,7 @@ impl<'a, 'tcx> VirtualIndex { self.get_fn_inner(bx, llvtable, ty, fn_abi, false) } - pub fn get_fn<Bx: BuilderMethods<'a, 'tcx>>( + pub(crate) fn get_fn<Bx: BuilderMethods<'a, 'tcx>>( self, bx: &mut Bx, llvtable: Bx::Value, @@ -71,7 +71,7 @@ impl<'a, 'tcx> VirtualIndex { self.get_fn_inner(bx, llvtable, ty, fn_abi, true) } - pub fn get_usize<Bx: BuilderMethods<'a, 'tcx>>( + pub(crate) fn get_usize<Bx: BuilderMethods<'a, 'tcx>>( self, bx: &mut Bx, llvtable: Bx::Value, @@ -115,7 +115,7 @@ fn expect_dyn_trait_in_self(ty: Ty<'_>) -> ty::PolyExistentialTraitRef<'_> { /// making an object `Foo<dyn Trait>` from a value of type `Foo<T>`, then /// `trait_ref` would map `T: Trait`. #[instrument(level = "debug", skip(cx))] -pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( +pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx: &Cx, ty: Ty<'tcx>, trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 386e1f91e7f..f13c46e8bef 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -69,13 +69,13 @@ enum LocalKind { SSA(DefLocation), } -struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { - fx: &'mir FunctionCx<'a, 'tcx, Bx>, - dominators: &'mir Dominators<mir::BasicBlock>, +struct LocalAnalyzer<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> { + fx: &'a FunctionCx<'b, 'tcx, Bx>, + dominators: &'a Dominators<mir::BasicBlock>, locals: IndexVec<mir::Local, LocalKind>, } -impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> { +impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx> { fn define(&mut self, local: mir::Local, location: DefLocation) { let kind = &mut self.locals[local]; match *kind { @@ -152,9 +152,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, } } -impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx> - for LocalAnalyzer<'mir, 'a, 'tcx, Bx> -{ +impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer<'a, 'b, 'tcx, Bx> { fn visit_assign( &mut self, place: &mir::Place<'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 8f96c462240..f23ae7abafd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -996,7 +996,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // To get a `*mut RcBox<Self>`, we just keep unwrapping newtypes until // we get a value of a built-in pointer type. // - // This is also relevant for `Pin<&mut Self>`, where we need to peel the `Pin`. + // This is also relevant for `Pin<&mut Self>`, where we need to peel the + // `Pin`. while !op.layout.ty.is_unsafe_ptr() && !op.layout.ty.is_ref() { let (idx, _) = op.layout.non_1zst_field(bx).expect( "not exactly one non-1-ZST field in a `DispatchFromDyn` type", @@ -1004,9 +1005,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { op = op.extract_field(bx, idx); } - // now that we have `*dyn Trait` or `&dyn Trait`, split it up into its + // Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its // data pointer and vtable. Look up the method in the vtable, and pass - // the data pointer as the first argument + // the data pointer as the first argument. llfn = Some(meth::VirtualIndex::from_index(idx).get_fn( bx, meta, @@ -1212,10 +1213,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mergeable_succ, ) } -} -impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_block(&mut self, mut bb: mir::BasicBlock) { + pub(crate) fn codegen_block(&mut self, mut bb: mir::BasicBlock) { let llbb = match self.try_llbb(bb) { Some(llbb) => llbb, None => return, @@ -1255,7 +1254,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) { + pub(crate) fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) { let llbb = match self.try_llbb(bb) { Some(llbb) => llbb, None => return, @@ -1440,8 +1439,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (mut llval, align, by_ref) = match op.val { Immediate(_) | Pair(..) => match arg.mode { PassMode::Indirect { attrs, .. } => { - // Indirect argument may have higher alignment requirements than the type's alignment. - // This can happen, e.g. when passing types with <4 byte alignment on the stack on x86. + // Indirect argument may have higher alignment requirements than the type's + // alignment. This can happen, e.g. when passing types with <4 byte alignment + // on the stack on x86. let required_align = match attrs.pointee_align { Some(pointee_align) => cmp::max(pointee_align, arg.layout.align.abi), None => arg.layout.align.abi, @@ -1740,7 +1740,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } /// Like `llbb`, but may fail if the basic block should be skipped. - pub fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option<Bx::BasicBlock> { + pub(crate) fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option<Bx::BasicBlock> { match self.cached_llbbs[bb] { CachedLlbb::None => { let llbb = Bx::append_block(self.cx, self.llfn, &format!("{bb:?}")); diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 8254fb3d866..15f45b226f5 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -10,7 +10,7 @@ use crate::mir::operand::OperandRef; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn eval_mir_constant_to_operand( + pub(crate) fn eval_mir_constant_to_operand( &self, bx: &mut Bx, constant: &mir::ConstOperand<'tcx>, @@ -32,27 +32,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// that the given `constant` is an `Const::Unevaluated` and must be convertible to /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip. /// - /// Note that this function is cursed, since usually MIR consts should not be evaluated to valtrees! - pub fn eval_unevaluated_mir_constant_to_valtree( + /// Note that this function is cursed, since usually MIR consts should not be evaluated to + /// valtrees! + fn eval_unevaluated_mir_constant_to_valtree( &self, constant: &mir::ConstOperand<'tcx>, ) -> Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled> { let uv = match self.monomorphize(constant.const_) { mir::Const::Unevaluated(uv, _) => uv.shrink(), mir::Const::Ty(_, c) => match c.kind() { - // A constant that came from a const generic but was then used as an argument to old-style - // simd_shuffle (passing as argument instead of as a generic param). + // A constant that came from a const generic but was then used as an argument to + // old-style simd_shuffle (passing as argument instead of as a generic param). rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)), other => span_bug!(constant.span, "{other:#?}"), }, // We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate - // a constant and write that value back into `Operand`s. This could happen, but is unlikely. - // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care - // around intrinsics. For an issue to happen here, it would require a macro expanding to a - // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but - // the user pass through arbitrary expressions. - // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real - // const generic, and get rid of this entire function. + // a constant and write that value back into `Operand`s. This could happen, but is + // unlikely. Also: all users of `simd_shuffle` are on unstable and already need to take + // a lot of care around intrinsics. For an issue to happen here, it would require a + // macro expanding to a `simd_shuffle` call without wrapping the constant argument in a + // `const {}` block, but the user pass through arbitrary expressions. + // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a + // real const generic, and get rid of this entire function. other => span_bug!(constant.span, "{other:#?}"), }; let uv = self.monomorphize(uv); diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index 67f1ef5d944..52e749f4fb7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -5,7 +5,7 @@ use super::FunctionCx; use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_coverage(&self, bx: &mut Bx, kind: &CoverageKind, scope: SourceScope) { + pub(crate) fn codegen_coverage(&self, bx: &mut Bx, kind: &CoverageKind, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { self.monomorphize(inlined) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index ab08ef72a69..5b36a11aa25 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -24,6 +24,7 @@ pub struct FunctionDebugContext<'tcx, S, L> { /// Maps from an inlined function to its debug info declaration. pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>, } + #[derive(Copy, Clone)] pub enum VariableKind { ArgumentVariable(usize /*index*/), @@ -243,7 +244,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// Apply debuginfo and/or name, after creating the `alloca` for a local, /// or initializing the local with an operand (whichever applies). - pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { + pub(crate) fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) { let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full; let vars = match &self.per_local_var_debug_info { @@ -426,7 +427,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn debug_introduce_locals(&self, bx: &mut Bx) { + pub(crate) fn debug_introduce_locals(&self, bx: &mut Bx) { if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() { for local in self.locals.indices() { self.debug_introduce_local(bx, local); @@ -435,7 +436,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } /// Partition all `VarDebugInfo` in `self.mir`, by their base `Local`. - pub fn compute_per_local_var_debug_info( + pub(crate) fn compute_per_local_var_debug_info( &self, bx: &mut Bx, ) -> Option<IndexVec<mir::Local, Vec<PerLocalVarDebugInfo<'tcx, Bx::DIVariable>>>> { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index de94d87bcea..61e9b9bd774 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -15,8 +15,8 @@ use crate::traits::*; mod analyze; mod block; -pub mod constant; -pub mod coverageinfo; +mod constant; +mod coverageinfo; pub mod debuginfo; mod intrinsic; mod locals; diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 1891de8c0eb..ee32b4f23c9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -27,32 +27,32 @@ pub enum OperandValue<V> { /// which indicates that it refers to an unsized rvalue. /// /// An `OperandValue` *must* be this variant for any type for which - /// [`LayoutTypeMethods::is_backend_ref`] returns `true`. + /// [`LayoutTypeCodegenMethods::is_backend_ref`] returns `true`. /// (That basically amounts to "isn't one of the other variants".) /// /// This holds a [`PlaceValue`] (like a [`PlaceRef`] does) with a pointer /// to the location holding the value. The type behind that pointer is the - /// one returned by [`LayoutTypeMethods::backend_type`]. + /// one returned by [`LayoutTypeCodegenMethods::backend_type`]. Ref(PlaceValue<V>), /// A single LLVM immediate value. /// /// An `OperandValue` *must* be this variant for any type for which - /// [`LayoutTypeMethods::is_backend_immediate`] returns `true`. + /// [`LayoutTypeCodegenMethods::is_backend_immediate`] returns `true`. /// The backend value in this variant must be the *immediate* backend type, - /// as returned by [`LayoutTypeMethods::immediate_backend_type`]. + /// as returned by [`LayoutTypeCodegenMethods::immediate_backend_type`]. Immediate(V), /// A pair of immediate LLVM values. Used by fat pointers too. /// /// An `OperandValue` *must* be this variant for any type for which - /// [`LayoutTypeMethods::is_backend_scalar_pair`] returns `true`. + /// [`LayoutTypeCodegenMethods::is_backend_scalar_pair`] returns `true`. /// The backend values in this variant must be the *immediate* backend types, - /// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`] + /// as returned by [`LayoutTypeCodegenMethods::scalar_pair_element_backend_type`] /// with `immediate: true`. Pair(V, V), /// A value taking no bytes, and which therefore needs no LLVM value at all. /// /// If you ever need a `V` to pass to something, get a fresh poison value - /// from [`ConstMethods::const_poison`]. + /// from [`ConstCodegenMethods::const_poison`]. /// /// An `OperandValue` *must* be this variant for any type for which /// `is_zst` on its `Layout` returns `true`. Note however that @@ -64,7 +64,7 @@ impl<V: CodegenObject> OperandValue<V> { /// If this is ZeroSized/Immediate/Pair, return an array of the 0/1/2 values. /// If this is Ref, return the place. #[inline] - pub fn immediates_or_place(self) -> Either<ArrayVec<V, 2>, PlaceValue<V>> { + pub(crate) fn immediates_or_place(self) -> Either<ArrayVec<V, 2>, PlaceValue<V>> { match self { OperandValue::ZeroSized => Either::Left(ArrayVec::new()), OperandValue::Immediate(a) => Either::Left(ArrayVec::from_iter([a])), @@ -75,7 +75,7 @@ impl<V: CodegenObject> OperandValue<V> { /// Given an array of 0/1/2 immediate values, return ZeroSized/Immediate/Pair. #[inline] - pub fn from_immediates(immediates: ArrayVec<V, 2>) -> Self { + pub(crate) fn from_immediates(immediates: ArrayVec<V, 2>) -> Self { let mut it = immediates.into_iter(); let Some(a) = it.next() else { return OperandValue::ZeroSized; @@ -90,7 +90,7 @@ impl<V: CodegenObject> OperandValue<V> { /// optional metadata as backend values. /// /// If you're making a place, use [`Self::deref`] instead. - pub fn pointer_parts(self) -> (V, Option<V>) { + pub(crate) fn pointer_parts(self) -> (V, Option<V>) { match self { OperandValue::Immediate(llptr) => (llptr, None), OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)), @@ -105,12 +105,12 @@ impl<V: CodegenObject> OperandValue<V> { /// alignment, then maybe you want [`OperandRef::deref`] instead. /// /// This is the inverse of [`PlaceValue::address`]. - pub fn deref(self, align: Align) -> PlaceValue<V> { + pub(crate) fn deref(self, align: Align) -> PlaceValue<V> { let (llval, llextra) = self.pointer_parts(); PlaceValue { llval, llextra, align } } - pub(crate) fn is_expected_variant_for_type<'tcx, Cx: LayoutTypeMethods<'tcx>>( + pub(crate) fn is_expected_variant_for_type<'tcx, Cx: LayoutTypeCodegenMethods<'tcx>>( &self, cx: &Cx, ty: TyAndLayout<'tcx>, @@ -153,7 +153,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandRef { val: OperandValue::ZeroSized, layout } } - pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + pub(crate) fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>( bx: &mut Bx, val: mir::ConstValue<'tcx>, ty: Ty<'tcx>, @@ -280,7 +280,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { /// /// If you don't need the type, see [`OperandValue::pointer_parts`] /// or [`OperandValue::deref`]. - pub fn deref<Cx: LayoutTypeMethods<'tcx>>(self, cx: &Cx) -> PlaceRef<'tcx, V> { + pub fn deref<Cx: CodegenMethods<'tcx>>(self, cx: &Cx) -> PlaceRef<'tcx, V> { if self.layout.ty.is_box() { // Derefer should have removed all Box derefs bug!("dereferencing {:?} in codegen", self.layout.ty); @@ -334,7 +334,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { OperandRef { val, layout } } - pub fn extract_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( + pub(crate) fn extract_field<Bx: BuilderMethods<'a, 'tcx, Value = V>>( &self, bx: &mut Bx, i: usize, @@ -478,8 +478,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> { debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); match self { OperandValue::ZeroSized => { - // Avoid generating stores of zero-sized values, because the only way to have a zero-sized - // value is through `undef`/`poison`, and the store itself is useless. + // Avoid generating stores of zero-sized values, because the only way to have a + // zero-sized value is through `undef`/`poison`, and the store itself is useless. } OperandValue::Ref(val) => { assert!(dest.layout.is_sized(), "cannot directly store unsized values"); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 0fad4d169ed..eba150c6eac 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -133,7 +133,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { Self::alloca(bx, ptr_layout) } - pub fn len<Cx: ConstMethods<'tcx, Value = V>>(&self, cx: &Cx) -> V { + pub fn len<Cx: ConstCodegenMethods<'tcx, Value = V>>(&self, cx: &Cx) -> V { if let FieldsShape::Array { count, .. } = self.layout.fields { if self.layout.is_unsized() { assert_eq!(count, 0); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 91fd9905f63..6bf75293fce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -19,7 +19,7 @@ use crate::{base, MemFlags}; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "trace", skip(self, bx))] - pub fn codegen_rvalue( + pub(crate) fn codegen_rvalue( &mut self, bx: &mut Bx, dest: PlaceRef<'tcx, Bx::Value>, @@ -114,7 +114,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let count = self .monomorphize(count) - .eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); + .try_to_target_usize(bx.tcx()) + .expect("expected monomorphic const in codegen"); bx.write_operand_repeatedly(cg_elem, count, dest); } @@ -419,7 +420,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn codegen_rvalue_unsized( + pub(crate) fn codegen_rvalue_unsized( &mut self, bx: &mut Bx, indirect_dest: PlaceRef<'tcx, Bx::Value>, @@ -440,7 +441,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn codegen_rvalue_operand( + pub(crate) fn codegen_rvalue_operand( &mut self, bx: &mut Bx, rvalue: &mir::Rvalue<'tcx>, @@ -803,7 +804,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if let Some(index) = place.as_local() { if let LocalRef::Operand(op) = self.locals[index] { if let ty::Array(_, n) = op.layout.ty.kind() { - let n = n.eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all()); + let n = n + .try_to_target_usize(bx.tcx()) + .expect("expected monomorphic const in codegen"); return bx.cx().const_usize(n); } } @@ -836,7 +839,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandRef { val, layout: self.cx.layout_of(mk_ptr_ty(self.cx.tcx(), ty)) } } - pub fn codegen_scalar_binop( + fn codegen_scalar_binop( &mut self, bx: &mut Bx, op: mir::BinOp, @@ -981,7 +984,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn codegen_fat_ptr_binop( + fn codegen_fat_ptr_binop( &mut self, bx: &mut Bx, op: mir::BinOp, @@ -1023,7 +1026,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub fn codegen_scalar_checked_binop( + fn codegen_scalar_checked_binop( &mut self, bx: &mut Bx, op: mir::BinOp, @@ -1047,10 +1050,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { OperandValue::Pair(val, of) } -} -impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { + pub(crate) fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool { match *rvalue { mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => { let operand_ty = operand.ty(self.mir, self.cx.tcx()); diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index 73283cafb49..6338d16c897 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -7,7 +7,7 @@ use crate::traits::*; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { #[instrument(level = "debug", skip(self, bx))] - pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) { + pub(crate) fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) { self.set_debug_loc(bx, statement.source_info); match statement.kind { mir::StatementKind::Assign(box (ref place, ref rvalue)) => { diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 933904f9845..3f3ae21035d 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -45,11 +45,13 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // The info in this case is the length of the str, so the size is that // times the unit size. ( - // All slice sizes must fit into `isize`, so this multiplication cannot (signed) wrap. + // All slice sizes must fit into `isize`, so this multiplication cannot (signed) + // wrap. // NOTE: ideally, we want the effects of both `unchecked_smul` and `unchecked_umul` // (resulting in `mul nsw nuw` in LLVM IR), since we know that the multiplication - // cannot signed wrap, and that both operands are non-negative. But at the time of writing, - // the `LLVM-C` binding can't do this, and it doesn't seem to enable any further optimizations. + // cannot signed wrap, and that both operands are non-negative. But at the time of + // writing, the `LLVM-C` binding can't do this, and it doesn't seem to enable any + // further optimizations. bx.unchecked_smul(info.unwrap(), bx.const_usize(unit.size.bytes())), bx.const_usize(unit.align.abi.bytes()), ) @@ -67,9 +69,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let (fn_abi, llfn, _instance) = common::build_langcall(bx, None, LangItem::PanicNounwind); - // Generate the call. - // Cannot use `do_call` since we don't have a MIR terminator so we can't create a `TerminationCodegenHelper`. - // (But we are in good company, this code is duplicated plenty of times.) + // Generate the call. Cannot use `do_call` since we don't have a MIR terminator so we + // can't create a `TerminationCodegenHelper`. (But we are in good company, this code is + // duplicated plenty of times.) let fn_ty = bx.fn_decl_backend_type(fn_abi); bx.call( @@ -148,9 +150,14 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // The full formula for the size would be: // let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align); // let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align); - // However, `unsized_size` is a multiple of `unsized_align`. - // Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`: - // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(unsized_align).align_to(full_align); + // However, `unsized_size` is a multiple of `unsized_align`. Therefore, we can + // equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`: + // + // let full_size = + // (unsized_offset_unadjusted + unsized_size) + // .align_to(unsized_align) + // .align_to(full_align); + // // Furthermore, `align >= unsized_align`, and therefore we only need to do: // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index cf8f7fa25d8..0349d5817b4 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -15,7 +15,7 @@ use rustc_span::Span; use crate::errors; -pub fn from_target_feature( +pub(crate) fn from_target_feature( tcx: TyCtxt<'_>, attr: &ast::Attribute, supported_target_features: &UnordMap<String, Option<Symbol>>, @@ -146,7 +146,7 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> { /// Checks the function annotated with `#[target_feature]` is not a safe /// trait method implementation, reporting an error if it is. -pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { +pub(crate) fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) { if let DefKind::AssocFn = tcx.def_kind(id) { let parent_id = tcx.local_parent(id); if let DefKind::Trait | DefKind::Impl { of_trait: true } = tcx.def_kind(parent_id) { diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 162141a106b..f4853da1156 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -60,7 +60,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes { ); } -pub trait AsmMethods<'tcx> { +pub trait AsmCodegenMethods<'tcx> { fn codegen_global_asm( &self, template: &[InlineAsmTemplatePiece], diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index c5e2d55be83..e45af1cd153 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -8,13 +8,11 @@ use rustc_errors::ErrorGuaranteed; use rustc_metadata::creader::MetadataLoaderDyn; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, LayoutOf, TyAndLayout}; -use rustc_middle::ty::{Ty, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::config::{self, OutputFilenames, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; -use rustc_target::abi::call::FnAbi; use super::write::WriteBackendMethods; use super::CodegenObject; @@ -36,34 +34,21 @@ pub trait BackendTypes { type DIVariable: Copy; } -pub trait Backend<'tcx>: - Sized - + BackendTypes - + HasTyCtxt<'tcx> - + LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> - + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> -{ -} - -impl<'tcx, T> Backend<'tcx> for T where - Self: BackendTypes - + HasTyCtxt<'tcx> - + LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> - + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> -{ -} - pub trait CodegenBackend { /// Locale resources for diagnostic messages - a string the content of the Fluent resource. /// Called before `init` so that all other functions are able to emit translatable diagnostics. fn locale_resource(&self) -> &'static str; fn init(&self, _sess: &Session) {} + fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {} + fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> { vec![] } + fn print_passes(&self) {} + fn print_version(&self) {} /// The metadata loader used to load rlib and dylib metadata. @@ -75,6 +60,7 @@ pub trait CodegenBackend { } fn provide(&self, _providers: &mut Providers) {} + fn codegen_crate<'tcx>( &self, tcx: TyCtxt<'tcx>, @@ -120,6 +106,7 @@ pub trait ExtraBackendMethods: kind: AllocatorKind, alloc_error_handler_kind: AllocatorKind, ) -> Self::Module; + /// This generates the codegen unit and returns it along with /// a `u64` giving an estimate of the unit's processing cost. fn compile_codegen_unit( @@ -127,6 +114,7 @@ pub trait ExtraBackendMethods: tcx: TyCtxt<'_>, cgu_name: Symbol, ) -> (ModuleCodegen<Self::Module>, u64); + fn target_machine_factory( &self, sess: &Session, diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 6cf84a012f0..5af0457bebd 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,23 +1,23 @@ use std::assert_matches::assert_matches; +use std::ops::Deref; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; -use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; +use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{Instance, Ty}; use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Abi, Align, Scalar, Size, WrappingRange}; -use rustc_target::spec::HasTargetSpec; use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; -use super::consts::ConstMethods; +use super::consts::ConstCodegenMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; -use super::intrinsic::IntrinsicCallMethods; -use super::misc::MiscMethods; -use super::type_::{ArgAbiMethods, BaseTypeMethods, LayoutTypeMethods}; -use super::{HasCodegen, StaticBuilderMethods}; +use super::intrinsic::IntrinsicCallBuilderMethods; +use super::misc::MiscCodegenMethods; +use super::type_::{ArgAbiBuilderMethods, BaseTypeCodegenMethods, LayoutTypeCodegenMethods}; +use super::{CodegenMethods, StaticBuilderMethods}; use crate::common::{ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; @@ -33,17 +33,33 @@ pub enum OverflowOp { } pub trait BuilderMethods<'a, 'tcx>: - HasCodegen<'tcx> + Sized + + LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> + + Deref<Target = Self::CodegenCx> + CoverageInfoBuilderMethods<'tcx> + DebugInfoBuilderMethods - + ArgAbiMethods<'tcx> + + ArgAbiBuilderMethods<'tcx> + AbiBuilderMethods<'tcx> - + IntrinsicCallMethods<'tcx> + + IntrinsicCallBuilderMethods<'tcx> + AsmBuilderMethods<'tcx> + StaticBuilderMethods - + HasParamEnv<'tcx> - + HasTargetSpec { + // `BackendTypes` is a supertrait of both `CodegenMethods` and + // `BuilderMethods`. This bound ensures all impls agree on the associated + // types within. + type CodegenCx: CodegenMethods< + 'tcx, + Value = Self::Value, + Function = Self::Function, + BasicBlock = Self::BasicBlock, + Type = Self::Type, + Funclet = Self::Funclet, + DIScope = Self::DIScope, + DILocation = Self::DILocation, + DIVariable = Self::DIVariable, + >; + fn build(cx: &'a Self::CodegenCx, llbb: Self::BasicBlock) -> Self; fn cx(&self) -> &Self::CodegenCx; diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index c15f1fa8e56..9af463a691a 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -3,7 +3,7 @@ use rustc_target::abi; use super::BackendTypes; -pub trait ConstMethods<'tcx>: BackendTypes { +pub trait ConstCodegenMethods<'tcx>: BackendTypes { // Constant constructors fn const_null(&self, t: Self::Type) -> Self::Value; /// Generate an uninitialized value (matching uninitialized memory in MIR). @@ -14,18 +14,20 @@ pub trait ConstMethods<'tcx>: BackendTypes { /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a /// poison value. fn const_poison(&self, t: Self::Type) -> Self::Value; - fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; - fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value; - fn const_uint_big(&self, t: Self::Type, u: u128) -> Self::Value; + fn const_bool(&self, val: bool) -> Self::Value; + + fn const_i8(&self, i: i8) -> Self::Value; fn const_i16(&self, i: i16) -> Self::Value; fn const_i32(&self, i: i32) -> Self::Value; - fn const_i8(&self, i: i8) -> Self::Value; + fn const_int(&self, t: Self::Type, i: i64) -> Self::Value; + fn const_u8(&self, i: u8) -> Self::Value; fn const_u32(&self, i: u32) -> Self::Value; fn const_u64(&self, i: u64) -> Self::Value; fn const_u128(&self, i: u128) -> Self::Value; fn const_usize(&self, i: u64) -> Self::Value; - fn const_u8(&self, i: u8) -> Self::Value; + fn const_uint(&self, t: Self::Type, i: u64) -> Self::Value; + fn const_uint_big(&self, t: Self::Type, u: u128) -> Self::Value; fn const_real(&self, t: Self::Type, val: f64) -> Self::Value; fn const_str(&self, s: &str) -> (Self::Value, Self::Value); diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 0b1645c66ed..0b513dac503 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -1,9 +1,7 @@ use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; -use super::BackendTypes; - -pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { +pub trait CoverageInfoBuilderMethods<'tcx> { /// Performs any start-of-function codegen needed for coverage instrumentation. /// /// Can be a no-op in backends that don't support coverage instrumentation. diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index 5fbe97214fb..7526c221b05 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -9,7 +9,7 @@ use rustc_target::abi::Size; use super::BackendTypes; use crate::mir::debuginfo::{FunctionDebugContext, VariableKind}; -pub trait DebugInfoMethods<'tcx>: BackendTypes { +pub trait DebugInfoCodegenMethods<'tcx>: BackendTypes { fn create_vtable_debuginfo( &self, ty: Ty<'tcx>, diff --git a/compiler/rustc_codegen_ssa/src/traits/declare.rs b/compiler/rustc_codegen_ssa/src/traits/declare.rs index 792d2b04ed6..c1edeac31b0 100644 --- a/compiler/rustc_codegen_ssa/src/traits/declare.rs +++ b/compiler/rustc_codegen_ssa/src/traits/declare.rs @@ -2,9 +2,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, Visibility}; use rustc_middle::ty::Instance; -use super::BackendTypes; - -pub trait PreDefineMethods<'tcx>: BackendTypes { +pub trait PreDefineCodegenMethods<'tcx> { fn predefine_static( &self, def_id: DefId, diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 172004a9cc7..e721cfb7134 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -5,7 +5,7 @@ use rustc_target::abi::call::FnAbi; use super::BackendTypes; use crate::mir::operand::OperandRef; -pub trait IntrinsicCallMethods<'tcx>: BackendTypes { +pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, /// and in `library/core/src/intrinsics.rs`; if you need access to any LLVM intrinsics, /// add them to `compiler/rustc_codegen_llvm/src/context.rs`. diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 40a49b3e1b5..5b33fd7ab10 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -7,7 +7,7 @@ use rustc_session::Session; use super::BackendTypes; -pub trait MiscMethods<'tcx>: BackendTypes { +pub trait MiscCodegenMethods<'tcx>: BackendTypes { fn vtables( &self, ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>; @@ -25,6 +25,7 @@ pub trait MiscMethods<'tcx>: BackendTypes { fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx>; fn set_frame_pointer_type(&self, llfn: Self::Function); fn apply_target_cpu_attr(&self, llfn: Self::Function); - /// Declares the extern "C" main function for the entry point. Returns None if the symbol already exists. + /// Declares the extern "C" main function for the entry point. Returns None if the symbol + /// already exists. fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function>; } diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 9ac923bef88..2196bc996d9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -8,9 +8,6 @@ //! actual codegen, while the builder stores the information about the function during codegen and //! is used to produce the instructions of the backend IR. //! -//! Finally, a third `Backend` structure has to implement methods related to how codegen information -//! is passed to the backend, especially for asynchronous compilation. -//! //! The traits contain associated types that are backend-specific, such as the backend's value or //! basic blocks. @@ -30,71 +27,36 @@ mod write; use std::fmt; -use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt}; -use rustc_target::spec::HasTargetSpec; +use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; +use rustc_middle::ty::Ty; +use rustc_target::abi::call::FnAbi; pub use self::abi::AbiBuilderMethods; -pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods}; +pub use self::asm::{ + AsmBuilderMethods, AsmCodegenMethods, GlobalAsmOperandRef, InlineAsmOperandRef, +}; +pub use self::backend::{BackendTypes, CodegenBackend, ExtraBackendMethods}; pub use self::builder::{BuilderMethods, OverflowOp}; -pub use self::consts::ConstMethods; +pub use self::consts::ConstCodegenMethods; pub use self::coverageinfo::CoverageInfoBuilderMethods; -pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods}; -pub use self::declare::PreDefineMethods; -pub use self::intrinsic::IntrinsicCallMethods; -pub use self::misc::MiscMethods; -pub use self::statics::{StaticBuilderMethods, StaticMethods}; +pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoCodegenMethods}; +pub use self::declare::PreDefineCodegenMethods; +pub use self::intrinsic::IntrinsicCallBuilderMethods; +pub use self::misc::MiscCodegenMethods; +pub use self::statics::{StaticBuilderMethods, StaticCodegenMethods}; pub use self::type_::{ - ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods, - TypeMethods, + ArgAbiBuilderMethods, BaseTypeCodegenMethods, DerivedTypeCodegenMethods, + LayoutTypeCodegenMethods, TypeCodegenMethods, TypeMembershipCodegenMethods, }; pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; -pub trait CodegenObject: Copy + PartialEq + fmt::Debug {} -impl<T: Copy + PartialEq + fmt::Debug> CodegenObject for T {} - -pub trait CodegenMethods<'tcx>: - Backend<'tcx> - + TypeMethods<'tcx> - + MiscMethods<'tcx> - + ConstMethods<'tcx> - + StaticMethods - + DebugInfoMethods<'tcx> - + AsmMethods<'tcx> - + PreDefineMethods<'tcx> - + HasParamEnv<'tcx> - + HasTyCtxt<'tcx> - + HasTargetSpec -{ -} - -impl<'tcx, T> CodegenMethods<'tcx> for T where - Self: Backend<'tcx> - + TypeMethods<'tcx> - + MiscMethods<'tcx> - + ConstMethods<'tcx> - + StaticMethods - + DebugInfoMethods<'tcx> - + AsmMethods<'tcx> - + PreDefineMethods<'tcx> - + HasParamEnv<'tcx> - + HasTyCtxt<'tcx> - + HasTargetSpec -{ -} +pub trait CodegenObject = Copy + PartialEq + fmt::Debug; -pub trait HasCodegen<'tcx>: - Backend<'tcx> + std::ops::Deref<Target = <Self as HasCodegen<'tcx>>::CodegenCx> -{ - type CodegenCx: CodegenMethods<'tcx> - + BackendTypes< - Value = Self::Value, - Function = Self::Function, - BasicBlock = Self::BasicBlock, - Type = Self::Type, - Funclet = Self::Funclet, - DIScope = Self::DIScope, - DILocation = Self::DILocation, - DIVariable = Self::DIVariable, - >; -} +pub trait CodegenMethods<'tcx> = LayoutOf<'tcx, LayoutOfResult = TyAndLayout<'tcx>> + + FnAbiOf<'tcx, FnAbiOfResult = &'tcx FnAbi<'tcx, Ty<'tcx>>> + + TypeCodegenMethods<'tcx> + + ConstCodegenMethods<'tcx> + + StaticCodegenMethods + + DebugInfoCodegenMethods<'tcx> + + AsmCodegenMethods<'tcx> + + PreDefineCodegenMethods<'tcx>; diff --git a/compiler/rustc_codegen_ssa/src/traits/statics.rs b/compiler/rustc_codegen_ssa/src/traits/statics.rs index b418199e616..c10733fb0ed 100644 --- a/compiler/rustc_codegen_ssa/src/traits/statics.rs +++ b/compiler/rustc_codegen_ssa/src/traits/statics.rs @@ -3,7 +3,7 @@ use rustc_target::abi::Align; use super::BackendTypes; -pub trait StaticMethods: BackendTypes { +pub trait StaticCodegenMethods: BackendTypes { fn static_addr_of(&self, cv: Self::Value, align: Align, kind: Option<&str>) -> Self::Value; fn codegen_static(&self, def_id: DefId); diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 7c042c0c621..f6f309287fe 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -1,17 +1,15 @@ use rustc_middle::bug; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::{self, Ty}; use rustc_target::abi::call::{ArgAbi, CastTarget, FnAbi, Reg}; use rustc_target::abi::{AddressSpace, Float, Integer}; -use super::misc::MiscMethods; -use super::{Backend, HasCodegen}; +use super::misc::MiscCodegenMethods; +use super::BackendTypes; use crate::common::TypeKind; use crate::mir::place::PlaceRef; -// This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use -// `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves. -pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { +pub trait BaseTypeCodegenMethods<'tcx>: BackendTypes { fn type_i8(&self) -> Self::Type; fn type_i16(&self) -> Self::Type; fn type_i32(&self) -> Self::Type; @@ -42,7 +40,9 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> { fn val_ty(&self, v: Self::Value) -> Self::Type; } -pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { +pub trait DerivedTypeCodegenMethods<'tcx>: + BaseTypeCodegenMethods<'tcx> + MiscCodegenMethods<'tcx> + HasTyCtxt<'tcx> +{ fn type_int(&self) -> Self::Type { match &self.sess().target.c_int_width[..] { "16" => self.type_i16(), @@ -100,9 +100,12 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { } } -impl<'tcx, T> DerivedTypeMethods<'tcx> for T where Self: BaseTypeMethods<'tcx> + MiscMethods<'tcx> {} +impl<'tcx, T> DerivedTypeCodegenMethods<'tcx> for T where + Self: BaseTypeCodegenMethods<'tcx> + MiscCodegenMethods<'tcx> + HasTyCtxt<'tcx> +{ +} -pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { +pub trait LayoutTypeCodegenMethods<'tcx>: BackendTypes { /// The backend type used for a rust type when it's in memory, /// such as when it's stack-allocated or when it's being loaded or stored. fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Self::Type; @@ -114,7 +117,7 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { /// /// For nearly all types this is the same as the [`Self::backend_type`], however /// `bool` (and other `0`-or-`1` values) are kept as `i1` in registers but as - /// [`BaseTypeMethods::type_i8`] in memory. + /// [`BaseTypeCodegenMethods::type_i8`] in memory. /// /// Converting values between the two different backend types is done using /// [`from_immediate`](super::BuilderMethods::from_immediate) and @@ -146,7 +149,7 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { // For backends that support CFI using type membership (i.e., testing whether a given pointer is // associated with a type identifier). -pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> { +pub trait TypeMembershipCodegenMethods<'tcx>: BackendTypes { fn add_type_metadata(&self, _function: Self::Function, _typeid: String) {} fn set_type_metadata(&self, _function: Self::Function, _typeid: String) {} fn typeid_metadata(&self, _typeid: String) -> Option<Self::Value> { @@ -156,7 +159,7 @@ pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> { fn set_kcfi_type_metadata(&self, _function: Self::Function, _typeid: u32) {} } -pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { +pub trait ArgAbiBuilderMethods<'tcx>: BackendTypes { fn store_fn_arg( &mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, @@ -172,12 +175,6 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> { fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type; } -pub trait TypeMethods<'tcx>: - DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> -{ -} - -impl<'tcx, T> TypeMethods<'tcx> for T where - Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx> -{ -} +pub trait TypeCodegenMethods<'tcx> = DerivedTypeCodegenMethods<'tcx> + + LayoutTypeCodegenMethods<'tcx> + + TypeMembershipCodegenMethods<'tcx>; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 9d4061d16a1..44a6b03177e 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -320,7 +320,7 @@ fn run_compiler( output_dir: odir, ice_file, file_loader, - locale_resources: DEFAULT_LOCALE_RESOURCES, + locale_resources: DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, @@ -1218,17 +1218,30 @@ pub fn handle_options(early_dcx: &EarlyDiagCtxt, args: &[String]) -> Option<geto // Parse with *all* options defined in the compiler, we don't worry about // option stability here we just want to parse as much as possible. let mut options = getopts::Options::new(); - for option in config::rustc_optgroups() { + let optgroups = config::rustc_optgroups(); + for option in &optgroups { (option.apply)(&mut options); } let matches = options.parse(args).unwrap_or_else(|e| { - let msg = match e { + let msg: Option<String> = match e { getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS .iter() .map(|&(name, ..)| ('C', name)) .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) .find(|&(_, name)| *opt == name.replace('_', "-")) .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")), + getopts::Fail::ArgumentMissing(ref opt) => { + optgroups.iter().find(|option| option.name == opt).map(|option| { + // Print the help just for the option in question. + let mut options = getopts::Options::new(); + (option.apply)(&mut options); + // getopt requires us to pass a function for joining an iterator of + // strings, even though in this case we expect exactly one string. + options.usage_with_format(|it| { + it.fold(format!("{e}\nUsage:"), |a, b| a + "\n" + &b) + }) + }) + } _ => None, }; early_dcx.early_fatal(msg.unwrap_or_else(|| e.to_string())); diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index e1f868c2522..411661824f0 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -35,6 +35,8 @@ macro raw_errln($tokens:tt) { } /// Signal handler installed for SIGSEGV +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[allow(static_mut_refs)] extern "C" fn print_stack_trace(_: libc::c_int) { const MAX_FRAMES: usize = 256; // Reserve data segment so we don't have to malloc in a signal handler, which might fail diff --git a/compiler/rustc_error_codes/src/error_codes/E0796.md b/compiler/rustc_error_codes/src/error_codes/E0796.md index 7ac429e5215..ced163e98f7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0796.md +++ b/compiler/rustc_error_codes/src/error_codes/E0796.md @@ -1,14 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler. + You have created a reference to a mutable static. Erroneous code example: -```compile_fail,edition2024,E0796 +``` static mut X: i32 = 23; - fn work() { let _val = unsafe { X }; } - let x_ref = unsafe { &mut X }; work(); // The next line has Undefined Behavior! diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index d6f0206b0de..11cad0b8f97 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -681,3 +681,4 @@ E0800: 0800, // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. // E0744, // merged into E0728 +// E0796, // unused error code. We use `static_mut_refs` lint instead. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index d71ae9d210d..556e6d46f89 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -48,7 +48,7 @@ impl Emitter for AnnotateSnippetEmitter { fn emit_diagnostic(&mut self, mut diag: DiagInner) { let fluent_args = to_fluent_args(diag.args.iter()); - let mut suggestions = diag.suggestions.unwrap_or(vec![]); + let mut suggestions = diag.suggestions.unwrap_tag(); self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 7a4d8dba179..6b756cff225 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -19,13 +19,9 @@ use crate::snippet::Style; use crate::{ CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle, + Suggestions, }; -/// Error type for `DiagInner`'s `suggestions` field, indicating that -/// `.disable_suggestions()` was called on the `DiagInner`. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] -pub struct SuggestionsDisabled; - /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of /// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic /// emission. @@ -296,7 +292,7 @@ pub struct DiagInner { pub code: Option<ErrCode>, pub span: MultiSpan, pub children: Vec<Subdiag>, - pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + pub suggestions: Suggestions, pub args: DiagArgMap, /// This is not used for highlighting or rendering any error message. Rather, it can be used @@ -325,7 +321,7 @@ impl DiagInner { code: None, span: MultiSpan::new(), children: vec![], - suggestions: Ok(vec![]), + suggestions: Suggestions::Enabled(vec![]), args: Default::default(), sort_span: DUMMY_SP, is_lint: None, @@ -409,7 +405,7 @@ impl DiagInner { &Option<ErrCode>, &MultiSpan, &[Subdiag], - &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, + &Suggestions, Vec<(&DiagArgName, &DiagArgValue)>, &Option<IsLint>, ) { @@ -823,16 +819,32 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } - /// Disallow attaching suggestions this diagnostic. + /// Disallow attaching suggestions to this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods /// (before and after the call to `disable_suggestions`) will be ignored. #[rustc_lint_diagnostics] pub fn disable_suggestions(&mut self) -> &mut Self { - self.suggestions = Err(SuggestionsDisabled); + self.suggestions = Suggestions::Disabled; self } - /// Helper for pushing to `self.suggestions`, if available (not disable). + /// Prevent new suggestions from being added to this diagnostic. + /// + /// Suggestions added before the call to `.seal_suggestions()` will be preserved + /// and new suggestions will be ignored. + #[rustc_lint_diagnostics] + pub fn seal_suggestions(&mut self) -> &mut Self { + if let Suggestions::Enabled(suggestions) = &mut self.suggestions { + let suggestions_slice = std::mem::take(suggestions).into_boxed_slice(); + self.suggestions = Suggestions::Sealed(suggestions_slice); + } + self + } + + /// Helper for pushing to `self.suggestions`. + /// + /// A new suggestion is added if suggestions are enabled for this diagnostic. + /// Otherwise, they are ignored. #[rustc_lint_diagnostics] fn push_suggestion(&mut self, suggestion: CodeSuggestion) { for subst in &suggestion.substitutions { @@ -846,7 +858,7 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { } } - if let Ok(suggestions) = &mut self.suggestions { + if let Suggestions::Enabled(suggestions) = &mut self.suggestions { suggestions.push(suggestion); } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 2b135df91a4..3ab371b8057 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -498,7 +498,7 @@ impl Emitter for HumanEmitter { fn emit_diagnostic(&mut self, mut diag: DiagInner) { let fluent_args = to_fluent_args(diag.args.iter()); - let mut suggestions = diag.suggestions.unwrap_or(vec![]); + let mut suggestions = diag.suggestions.unwrap_tag(); self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args); self.fix_multispans_in_extern_macros_and_render_macro_backtrace( diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 32e59f9ab03..6a2ecf13f7b 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -33,7 +33,8 @@ use crate::emitter::{ use crate::registry::Registry; use crate::translation::{to_fluent_args, Translate}; use crate::{ - CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, TerminalUrl, + CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions, + TerminalUrl, }; #[cfg(test)] @@ -292,7 +293,7 @@ impl Diagnostic { /// Converts from `rustc_errors::DiagInner` to `Diagnostic`. fn from_errors_diagnostic(diag: crate::DiagInner, je: &JsonEmitter) -> Diagnostic { let args = to_fluent_args(diag.args.iter()); - let sugg = diag.suggestions.iter().flatten().map(|sugg| { + let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); Diagnostic { @@ -303,7 +304,12 @@ impl Diagnostic { children: vec![], rendered: None, } - }); + }; + let sugg = match &diag.suggestions { + Suggestions::Enabled(suggestions) => suggestions.iter().map(sugg_to_diag), + Suggestions::Sealed(suggestions) => suggestions.iter().map(sugg_to_diag), + Suggestions::Disabled => [].iter().map(sugg_to_diag), + }; // generate regular command line output and store it in the json diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 13da1721a4a..59866012069 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -126,6 +126,41 @@ impl SuggestionStyle { } } +/// Represents the help messages seen on a diagnostic. +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum Suggestions { + /// Indicates that new suggestions can be added or removed from this diagnostic. + /// + /// `DiagInner`'s new_* methods initialize the `suggestions` field with + /// this variant. Also, this is the default variant for `Suggestions`. + Enabled(Vec<CodeSuggestion>), + /// Indicates that suggestions cannot be added or removed from this diagnostic. + /// + /// Gets toggled when `.seal_suggestions()` is called on the `DiagInner`. + Sealed(Box<[CodeSuggestion]>), + /// Indicates that no suggestion is available for this diagnostic. + /// + /// Gets toggled when `.disable_suggestions()` is called on the `DiagInner`. + Disabled, +} + +impl Suggestions { + /// Returns the underlying list of suggestions. + pub fn unwrap_tag(self) -> Vec<CodeSuggestion> { + match self { + Suggestions::Enabled(suggestions) => suggestions, + Suggestions::Sealed(suggestions) => suggestions.into_vec(), + Suggestions::Disabled => Vec::new(), + } + } +} + +impl Default for Suggestions { + fn default() -> Self { + Self::Enabled(vec![]) + } +} + #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] pub struct CodeSuggestion { /// Each substitute can have multiple variants due to multiple @@ -536,6 +571,8 @@ pub enum StashKey { /// Query cycle detected, stashing in favor of a better error. Cycle, UndeterminedMacroResolution, + /// Used by `Parser::maybe_recover_trailing_expr` + ExprInPat, } fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index d0c0460ddfe..40333c3953a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -204,7 +204,7 @@ declare_features! ( /// Changes `impl Trait` to capture all lifetimes in scope. (unstable, lifetime_capture_rules_2024, "1.76.0", None), /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 - (unstable, link_cfg, "1.14.0", None), + (internal, link_cfg, "1.14.0", None), /// Allows using `?Trait` trait bounds in more contexts. (internal, more_maybe_bounds, "1.82.0", None), /// Allows the `multiple_supertrait_upcastable` lint. @@ -558,6 +558,8 @@ declare_features! ( (unstable, optimize_attribute, "1.34.0", Some(54882)), /// Allows specifying nop padding on functions for dynamic patching. (unstable, patchable_function_entry, "1.81.0", Some(123115)), + /// Experimental features that make `Pin` more ergonomic. + (incomplete, pin_ergonomics, "CURRENT_RUSTC_VERSION", Some(130494)), /// Allows postfix match `expr.match { ... }` (unstable, postfix_match, "1.79.0", Some(121618)), /// Allows macro attributes on expressions, statements and non-inline modules. diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index ca8bace28f3..37e610a85a6 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -138,25 +138,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok // with a lowercase as rustc errors do. err.replace_range(0..1, &err.chars().next().unwrap().to_lowercase().to_string()); - let line_starts: Vec<usize> = std::iter::once(0) - .chain( - this.source() - .char_indices() - .filter_map(|(i, c)| Some(i + 1).filter(|_| c == '\n')), - ) - .collect(); - let line_start = line_starts - .iter() - .enumerate() - .map(|(line, idx)| (line + 1, idx)) - .filter(|(_, idx)| **idx <= pos.start) - .last() - .unwrap() - .0; - let message = annotate_snippets::Level::Error.title(&err).snippet( Snippet::source(this.source()) - .line_start(line_start) .origin(&relative_ftl_path) .fold(true) .annotation(annotate_snippets::Level::Error.span(pos.start..pos.end - 1)), diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index bd55617d84e..9b4174013a6 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -287,7 +287,10 @@ impl DefKind { #[inline] pub fn is_fn_like(self) -> bool { - matches!(self, DefKind::Fn | DefKind::AssocFn | DefKind::Closure) + matches!( + self, + DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::SyntheticCoroutineBody + ) } /// Whether `query get_codegen_attrs` should be used with this definition. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ee03f780e16..f92c6650355 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1378,7 +1378,8 @@ pub struct LetStmt<'hir> { pub hir_id: HirId, pub span: Span, /// Can be `ForLoopDesugar` if the `let` statement is part of a `for` loop - /// desugaring. Otherwise will be `Normal`. + /// desugaring, or `AssignDesugar` if it is the result of a complex + /// assignment desugaring. Otherwise will be `Normal`. pub source: LocalSource, } @@ -1726,7 +1727,7 @@ impl Expr<'_> { ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), ExprKind::Unary(..) => ExprPrecedence::Unary, ExprKind::Lit(_) => ExprPrecedence::Lit, - ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast, + ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::DropTemps(ref expr, ..) => expr.precedence(), ExprKind::If(..) => ExprPrecedence::If, ExprKind::Let(..) => ExprPrecedence::Let, @@ -1744,11 +1745,12 @@ impl Expr<'_> { ExprKind::Continue(..) => ExprPrecedence::Continue, ExprKind::Ret(..) => ExprPrecedence::Ret, ExprKind::Become(..) => ExprPrecedence::Become, - ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm, - ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf, ExprKind::Struct(..) => ExprPrecedence::Struct, ExprKind::Repeat(..) => ExprPrecedence::Repeat, ExprKind::Yield(..) => ExprPrecedence::Yield, + ExprKind::Type(..) | ExprKind::InlineAsm(..) | ExprKind::OffsetOf(..) => { + ExprPrecedence::Mac + } ExprKind::Err(_) => ExprPrecedence::Err, } } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e7398fd2226..c148dc7f53b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -45,7 +45,16 @@ impl LanguageItems { pub fn set(&mut self, item: LangItem, def_id: DefId) { self.items[item as usize] = Some(def_id); - self.reverse_items.insert(def_id, item); + let preexisting = self.reverse_items.insert(def_id, item); + + // This needs to be a bijection. + if let Some(preexisting) = preexisting { + panic!( + "For the bijection of LangItem <=> DefId to work,\ + one item DefId may only be assigned one LangItem. \ + Separate the LangItem definitions for {item:?} and {preexisting:?}." + ); + } } pub fn from_def_id(&self, def_id: DefId) -> Option<LangItem> { diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index bde94be6f51..633ccacedfc 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -467,25 +467,6 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]` .label = `#[start]` function is not allowed to be `#[track_caller]` -hir_analysis_static_mut_ref = creating a {$shared} reference to a mutable static - .label = {$shared} reference to mutable static - .note = {$shared -> - [shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - *[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior - } - .suggestion = use `addr_of!` instead to create a raw pointer - .suggestion_mut = use `addr_of_mut!` instead to create a raw pointer - -hir_analysis_static_mut_refs_lint = creating a {$shared} reference to mutable static is discouraged - .label = {$shared} reference to mutable static - .suggestion = use `addr_of!` instead to create a raw pointer - .suggestion_mut = use `addr_of_mut!` instead to create a raw pointer - .note = this will be a hard error in the 2024 edition - .why_note = {$shared -> - [shared] this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - *[mutable] this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior - } - hir_analysis_static_specialize = cannot specialize on `'static` lifetime hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index dbdad2eb41d..3a7366ef78a 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -66,7 +66,6 @@ mod check; mod compare_impl_item; pub mod dropck; mod entry; -mod errs; pub mod intrinsic; pub mod intrinsicck; mod region; diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 33e58a92e37..1a5f4659812 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -20,8 +20,6 @@ use rustc_middle::ty::TyCtxt; use rustc_span::source_map; use tracing::debug; -use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut}; - #[derive(Debug, Copy, Clone)] struct Context { /// The scope that contains any new variables declared, plus its depth in @@ -229,8 +227,6 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h let stmt_id = stmt.hir_id.local_id; debug!("resolve_stmt(stmt.id={:?})", stmt_id); - maybe_stmt_static_mut(visitor.tcx, *stmt); - // Every statement will clean up the temporaries created during // execution of that statement. Therefore each statement has an // associated destruction scope that represents the scope of the @@ -249,8 +245,6 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); - maybe_expr_static_mut(visitor.tcx, *expr); - let prev_cx = visitor.cx; visitor.enter_node_scope_with_dtor(expr.hir_id.local_id); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 5cb90e97eef..b877cacd998 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,6 +1,6 @@ use core::ops::ControlFlow; -use rustc_errors::{Applicability, StashKey}; +use rustc_errors::{Applicability, StashKey, Suggestions}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::HirId; @@ -670,7 +670,7 @@ fn infer_placeholder_type<'tcx>( // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. // We are typeck and have the real type, so remove that and suggest the actual type. - if let Ok(suggestions) = &mut err.suggestions { + if let Suggestions::Enabled(suggestions) = &mut err.suggestions { suggestions.clear(); } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 39df18ff658..e4e5f76ae32 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1523,57 +1523,6 @@ pub(crate) struct OnlyCurrentTraitsPointerSugg<'a> { } #[derive(Diagnostic)] -#[diag(hir_analysis_static_mut_ref, code = E0796)] -#[note] -pub(crate) struct StaticMutRef<'a> { - #[primary_span] - #[label] - pub span: Span, - #[subdiagnostic] - pub sugg: MutRefSugg, - pub shared: &'a str, -} - -#[derive(Subdiagnostic)] -pub(crate) enum MutRefSugg { - #[multipart_suggestion( - hir_analysis_suggestion, - style = "verbose", - applicability = "maybe-incorrect" - )] - Shared { - #[suggestion_part(code = "addr_of!(")] - lo: Span, - #[suggestion_part(code = ")")] - hi: Span, - }, - #[multipart_suggestion( - hir_analysis_suggestion_mut, - style = "verbose", - applicability = "maybe-incorrect" - )] - Mut { - #[suggestion_part(code = "addr_of_mut!(")] - lo: Span, - #[suggestion_part(code = ")")] - hi: Span, - }, -} - -// STATIC_MUT_REF lint -#[derive(LintDiagnostic)] -#[diag(hir_analysis_static_mut_refs_lint)] -#[note] -#[note(hir_analysis_why_note)] -pub(crate) struct RefOfMutStatic<'a> { - #[label] - pub span: Span, - #[subdiagnostic] - pub sugg: MutRefSugg, - pub shared: &'a str, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_not_supported_delegation)] pub(crate) struct UnsupportedDelegation<'a> { #[primary_span] diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 97402dd1109..236543007fc 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -1048,7 +1048,18 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { }, ); - err.span_suggestion(span, msg, "", Applicability::MaybeIncorrect); + if span.is_empty() { + // HACK: Avoid ICE when types with the same name with `derive`s are in the same scope: + // struct NotSM; + // #[derive(PartialEq, Eq)] + // struct NotSM<T>(T); + // With the above code, the suggestion would be to remove the generics of the first + // `NotSM`, which doesn't *have* generics, so we would suggest to remove no code with + // no code, which would trigger an `assert!` later. Ideally, we would do something a + // bit more principled. See closed PR #109082. + } else { + err.span_suggestion(span, msg, "", Applicability::MaybeIncorrect); + } } else if redundant_lifetime_args && redundant_type_or_const_args { remove_lifetime_args(err); remove_type_or_const_args(err); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index fca7babea30..26c9b8dfdc3 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -214,6 +214,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => { return self.coerce_dyn_star(a, b, predicates, region); } + ty::Adt(pin, _) + if self.tcx.features().pin_ergonomics + && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => + { + return self.coerce_pin(a, b); + } _ => {} } @@ -774,6 +780,62 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { }) } + /// Applies reborrowing for `Pin` + /// + /// We currently only support reborrowing `Pin<&mut T>` as `Pin<&mut T>`. This is accomplished + /// by inserting a call to `Pin::as_mut` during MIR building. + /// + /// In the future we might want to support other reborrowing coercions, such as: + /// - `Pin<&mut T>` as `Pin<&T>` + /// - `Pin<&T>` as `Pin<&T>` + /// - `Pin<Box<T>>` as `Pin<&T>` + /// - `Pin<Box<T>>` as `Pin<&mut T>` + #[instrument(skip(self), level = "trace")] + fn coerce_pin(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { + // We need to make sure the two types are compatible for coercion. + // Then we will build a ReborrowPin adjustment and return that as an InferOk. + + // Right now we can only reborrow if this is a `Pin<&mut T>`. + let extract_pin_mut = |ty: Ty<'tcx>| { + // Get the T out of Pin<T> + let (pin, ty) = match ty.kind() { + ty::Adt(pin, args) if self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) => { + (*pin, args[0].expect_ty()) + } + _ => { + debug!("can't reborrow {:?} as pinned", ty); + return Err(TypeError::Mismatch); + } + }; + // Make sure the T is something we understand (just `&mut U` for now) + match ty.kind() { + ty::Ref(region, ty, mutbl) => Ok((pin, *region, *ty, *mutbl)), + _ => { + debug!("can't reborrow pin of inner type {:?}", ty); + Err(TypeError::Mismatch) + } + } + }; + + let (pin, a_region, a_ty, mut_a) = extract_pin_mut(a)?; + let (_, b_region, _b_ty, mut_b) = extract_pin_mut(b)?; + + coerce_mutbls(mut_a, mut_b)?; + + // update a with b's mutability since we'll be coercing mutability + let a = Ty::new_adt( + self.tcx, + pin, + self.tcx.mk_args(&[Ty::new_ref(self.tcx, a_region, a_ty, mut_b).into()]), + ); + + // To complete the reborrow, we need to make sure we can unify the inner types, and if so we + // add the adjustments. + self.unify_and(a, b, |_inner_ty| { + vec![Adjustment { kind: Adjust::ReborrowPin(b_region, mut_b), target: b }] + }) + } + fn coerce_from_safe_fn<F, G>( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index da8c0ad3a30..3b2ddf659a1 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -780,6 +780,16 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::Borrow(ref autoref) => { self.walk_autoref(expr, &place_with_id, autoref); } + + adjustment::Adjust::ReborrowPin(_, mutbl) => { + // Reborrowing a Pin is like a combinations of a deref and a borrow, so we do + // both. + let bk = match mutbl { + ty::Mutability::Not => ty::BorrowKind::ImmBorrow, + ty::Mutability::Mut => ty::BorrowKind::MutBorrow, + }; + self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); + } } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -1284,6 +1294,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::ReborrowPin(..) | adjustment::Adjust::DynStar => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7318d02d24c..61898b06476 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1388,10 +1388,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This also occurs for an enum variant on a type alias. let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).instantiate(tcx, args)); let self_ty = self.normalize(span, self_ty); - match self.at(&self.misc(span), self.param_env).sub( + match self.at(&self.misc(span), self.param_env).eq( DefineOpaqueTypes::Yes, - self_ty, impl_ty, + self_ty, ) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 5dc341653e5..17aa43ae7c0 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -621,6 +621,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.unsatisfied_predicates.borrow_mut().clear(); } + /// When we're looking up a method by path (UFCS), we relate the receiver + /// types invariantly. When we are looking up a method by the `.` operator, + /// we relate them covariantly. + fn variance(&self) -> ty::Variance { + match self.mode { + Mode::MethodCall => ty::Covariant, + Mode::Path => ty::Invariant, + } + } + /////////////////////////////////////////////////////////////////////////// // CANDIDATE ASSEMBLY @@ -1443,7 +1453,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, impl_ty, impl_args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty) + { Ok(()) => {} Err(err) => { debug!("--> cannot relate self-types {:?}", err); @@ -1514,7 +1525,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { { return ProbeResult::NoMatch; } - _ => match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + _ => match ocx.relate( + cause, + self.param_env, + self.variance(), + self_ty, + xform_self_ty, + ) { Ok(()) => {} Err(err) => { debug!("--> cannot relate self-types {:?}", err); @@ -1560,7 +1577,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { (xform_self_ty, xform_ret_ty) = self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args); xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty); - match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) { + match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty) + { Ok(()) => {} Err(err) => { debug!("--> cannot relate self-types {:?}", err); @@ -1603,7 +1621,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } debug!("comparing return_ty {:?} with xform ret ty {:?}", return_ty, xform_ret_ty); - match ocx.sup(cause, self.param_env, return_ty, xform_ret_ty) { + match ocx.relate(cause, self.param_env, self.variance(), xform_ret_ty, return_ty) { Ok(()) => {} Err(_) => { result = ProbeResult::BadReturnType; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 45a6efc7a6a..7dd6deb4fe6 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,7 +11,6 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind}; use rustc_infer::infer; -use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; @@ -2413,17 +2412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { len: ty::Const<'tcx>, min_len: u64, ) -> (Option<Ty<'tcx>>, Ty<'tcx>) { - let len = match len.eval(self.tcx, self.param_env, span) { - Ok((_, val)) => val - .try_to_scalar() - .and_then(|scalar| scalar.try_to_scalar_int().ok()) - .map(|int| int.to_target_usize(self.tcx)), - Err(ErrorHandled::Reported(..)) => { - let guar = self.error_scrutinee_unfixed_length(span); - return (Some(Ty::new_error(self.tcx, guar)), arr_ty); - } - Err(ErrorHandled::TooGeneric(..)) => None, - }; + let len = len.try_eval_target_usize(self.tcx, self.param_env); let guar = if let Some(len) = len { // Now we know the length... diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index c2555d2bb47..2fbbc3200e1 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -803,7 +803,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { // We must deeply normalize in the new solver, since later lints // expect that types that show up in the typeck are fully // normalized. - let value = if self.should_normalize { + let mut value = if self.should_normalize { let body_id = tcx.hir().body_owner_def_id(self.body.id()); let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); let at = self.fcx.at(&cause, self.fcx.param_env); @@ -818,12 +818,27 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { value }; + // Bail if there are any non-region infer. if value.has_non_region_infer() { let guar = self.report_error(value); - new_err(tcx, guar) - } else { - tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased) + value = new_err(tcx, guar); + } + + // Erase the regions from the ty, since it's not really meaningful what + // these region values are; there's not a trivial correspondence between + // regions in the HIR and MIR, so when we turn the body into MIR, there's + // no reason to keep regions around. They will be repopulated during MIR + // borrowck, and specifically region constraints will be populated during + // MIR typeck which is run on the new body. + value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased); + + // Normalize consts in writeback, because GCE doesn't normalize eagerly. + if tcx.features().generic_const_exprs { + value = + value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env }); } + + value } } @@ -858,3 +873,17 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> { predicate } } + +struct EagerlyNormalizeConsts<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, +} +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EagerlyNormalizeConsts<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + self.tcx.try_normalize_erasing_regions(self.param_env, ct).unwrap_or(ct) + } +} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 04e2b7d45dc..347dc185043 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -311,7 +311,9 @@ pub struct Config { pub output_file: Option<OutFileName>, pub ice_file: Option<PathBuf>, pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>, - pub locale_resources: &'static [&'static str], + /// The list of fluent resources, used for lints declared with + /// [`Diagnostic`](rustc_errors::Diagnostic) and [`LintDiagnostic`](rustc_errors::LintDiagnostic). + pub locale_resources: Vec<&'static str>, pub lint_caps: FxHashMap<lint::LintId, lint::Level>, diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 4aa86944a0b..e71c5676ce4 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -395,6 +395,8 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent lint_improper_ctypes_pat_help = consider using the base type instead lint_improper_ctypes_pat_reason = pattern types have no C equivalent + +lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive lint_improper_ctypes_slice_help = consider using a raw pointer instead lint_improper_ctypes_slice_reason = slices have no C equivalent @@ -769,6 +771,13 @@ lint_single_use_lifetime = lifetime parameter `{$ident}` only used once lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` +lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged + .label = {$shared_label}reference to mutable static + .suggestion = use `&raw const` instead to create a raw pointer + .suggestion_mut = use `&raw mut` instead to create a raw pointer + .shared_note = shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + .mut_note = mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion .label = `{$self_ty}` implements `Deref<Target = dyn {$target_principal}>` which conflicts with supertrait `{$supertrait_principal}` .label2 = target type is a supertrait of `{$self_ty}` diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs index ddaa819df14..38c4e48928f 100644 --- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs @@ -2,6 +2,7 @@ use rustc_middle::bug; use rustc_session::config::ExpectedValues; use rustc_session::Session; use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol}; use crate::lints; @@ -30,7 +31,7 @@ enum EscapeQuotes { No, } -fn to_check_cfg_arg(name: Symbol, value: Option<Symbol>, quotes: EscapeQuotes) -> String { +fn to_check_cfg_arg(name: Ident, value: Option<Symbol>, quotes: EscapeQuotes) -> String { if let Some(value) = value { let value = str::escape_debug(value.as_str()).to_string(); let values = match quotes { @@ -110,6 +111,7 @@ pub(super) fn unexpected_cfg_name( } }; + let best_match = Ident::new(best_match, name_span); if let Some((value, value_span)) = value { if best_match_values.contains(&Some(value)) { lints::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { @@ -163,6 +165,8 @@ pub(super) fn unexpected_cfg_name( }; let expected_names = if !possibilities.is_empty() { let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); + let possibilities: Vec<_> = + possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); Some(lints::unexpected_cfg_name::ExpectedNames { possibilities: possibilities.into(), and_more, @@ -176,7 +180,9 @@ pub(super) fn unexpected_cfg_name( } }; - let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); + let inst = |escape_quotes| { + to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) + }; let invocation_help = if is_from_cargo { let sub = if !is_feature_cfg { Some(cargo_help_sub(sess, &inst)) } else { None }; @@ -273,7 +279,9 @@ pub(super) fn unexpected_cfg_value( || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat) && !sess.opts.unstable_opts.ui_testing); - let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes); + let inst = |escape_quotes| { + to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) + }; let invocation_help = if is_from_cargo { let help = if name == sym::feature { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5c67e21687f..d1da2809cc7 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -81,6 +81,7 @@ mod ptr_nulls; mod redundant_semicolon; mod reference_casting; mod shadowed_into_iter; +mod static_mut_refs; mod tail_expr_drop_order; mod traits; mod types; @@ -120,6 +121,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; use shadowed_into_iter::ShadowedIntoIter; pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; +use static_mut_refs::*; use tail_expr_drop_order::TailExprDropOrder; use traits::*; use types::*; @@ -246,6 +248,7 @@ late_lint_methods!( ImplTraitOvercaptures: ImplTraitOvercaptures, TailExprDropOrder: TailExprDropOrder, IfLetRescope: IfLetRescope::default(), + StaticMutRefs: StaticMutRefs, ] ] ); @@ -583,6 +586,11 @@ fn register_builtins(store: &mut LintStore) { "const_eval_mutable_ptr_in_final_value", "partially allowed now, otherwise turned into a hard error", ); + store.register_removed( + "where_clauses_object_safety", + "converted into hard error, see PR #125380 \ + <https://github.com/rust-lang/rust/pull/125380> for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a2ccb93347a..11006862d05 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2180,6 +2180,7 @@ pub(crate) struct UnexpectedCfgName { pub(crate) mod unexpected_cfg_name { use rustc_errors::DiagSymbolList; use rustc_macros::Subdiagnostic; + use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; #[derive(Subdiagnostic)] @@ -2260,7 +2261,7 @@ pub(crate) mod unexpected_cfg_name { #[derive(Subdiagnostic)] #[help_once(lint_unexpected_cfg_name_expected_names)] pub(crate) struct ExpectedNames { - pub possibilities: DiagSymbolList, + pub possibilities: DiagSymbolList<Ident>, pub and_more: usize, } @@ -3060,3 +3061,35 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { pub(crate) struct OutOfScopeMacroCalls { pub path: String, } + +#[derive(LintDiagnostic)] +#[diag(lint_static_mut_refs_lint)] +pub(crate) struct RefOfMutStatic<'a> { + #[label] + pub span: Span, + #[subdiagnostic] + pub sugg: Option<MutRefSugg>, + pub shared_label: &'a str, + #[note(lint_shared_note)] + pub shared_note: bool, + #[note(lint_mut_note)] + pub mut_note: bool, +} + +#[derive(Subdiagnostic)] +pub(crate) enum MutRefSugg { + #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")] + Shared { + #[suggestion_part(code = "&raw const ")] + span: Span, + }, + #[multipart_suggestion( + lint_suggestion_mut, + style = "verbose", + applicability = "maybe-incorrect" + )] + Mut { + #[suggestion_part(code = "&raw mut ")] + span: Span, + }, +} diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index e0ba6a912f1..c9ca1ea5e7a 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -72,6 +72,18 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { let hir::ItemKind::OpaqueTy(opaque) = &item.kind else { return; }; + + // If this is an RPITIT from a trait method with no body, then skip. + // That's because although we may have an opaque type on the function, + // it won't have a hidden type, so proving predicates about it is + // not really meaningful. + if let hir::OpaqueTyOrigin::FnReturn(method_def_id) = opaque.origin + && let hir::Node::TraitItem(trait_item) = cx.tcx.hir_node_by_def_id(method_def_id) + && !trait_item.defaultness.has_value() + { + return; + } + let def_id = item.owner_id.def_id.to_def_id(); let infcx = &cx.tcx.infer_ctxt().build(); // For every projection predicate in the opaque type's explicit bounds, diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs new file mode 100644 index 00000000000..3dd26fb9c53 --- /dev/null +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -0,0 +1,154 @@ +use rustc_hir as hir; +use rustc_hir::{Expr, Stmt}; +use rustc_middle::ty::{Mutability, TyKind}; +use rustc_session::lint::FutureIncompatibilityReason; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::edition::Edition; +use rustc_span::Span; + +use crate::lints::{MutRefSugg, RefOfMutStatic}; +use crate::{LateContext, LateLintPass, LintContext}; + +declare_lint! { + /// The `static_mut_refs` lint checks for shared or mutable references + /// of mutable static inside `unsafe` blocks and `unsafe` functions. + /// + /// ### Example + /// + /// ```rust,edition2021 + /// fn main() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// unsafe { + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// } + /// + /// unsafe fn _foo() { + /// static mut X: i32 = 23; + /// static mut Y: i32 = 24; + /// + /// let y = &X; + /// let ref x = X; + /// let (x, y) = (&X, &Y); + /// foo(&X); + /// } + /// + /// fn foo<'a>(_x: &'a i32) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Shared or mutable references of mutable static are almost always a mistake and + /// can lead to undefined behavior and various other problems in your code. + /// + /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny". + pub STATIC_MUT_REFS, + Warn, + "shared references or mutable references of mutable static is discouraged", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), + reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>", + explain_reason: false, + }; + @edition Edition2024 => Deny; +} + +declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]); + +impl<'tcx> LateLintPass<'tcx> for StaticMutRefs { + #[allow(rustc::usage_of_ty_tykind)] + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let err_span = expr.span; + match expr.kind { + hir::ExprKind::AddrOf(borrow_kind, m, ex) + if matches!(borrow_kind, hir::BorrowKind::Ref) + && let Some(err_span) = path_is_static_mut(ex, err_span) => + { + emit_static_mut_refs( + cx, + err_span, + err_span.with_hi(ex.span.lo()), + m, + !expr.span.from_expansion(), + ); + } + hir::ExprKind::MethodCall(_, e, _, _) + if let Some(err_span) = path_is_static_mut(e, expr.span) + && let typeck = cx.typeck_results() + && let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id) + && let inputs = + cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder() + && let Some(receiver) = inputs.get(0) + && let TyKind::Ref(_, _, m) = receiver.kind() => + { + emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false); + } + _ => {} + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) { + if let hir::StmtKind::Let(loc) = stmt.kind + && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind + && let hir::ByRef::Yes(m) = ba.0 + && let Some(init) = loc.init + && let Some(err_span) = path_is_static_mut(init, init.span) + { + emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false); + } + } +} + +fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> { + if err_span.from_expansion() { + err_span = expr.span; + } + + while let hir::ExprKind::Field(e, _) = expr.kind { + expr = e; + } + + if let hir::ExprKind::Path(qpath) = expr.kind + && let hir::QPath::Resolved(_, path) = qpath + && let hir::def::Res::Def(def_kind, _) = path.res + && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } = + def_kind + { + return Some(err_span); + } + None +} + +fn emit_static_mut_refs( + cx: &LateContext<'_>, + span: Span, + sugg_span: Span, + mutable: Mutability, + suggest_addr_of: bool, +) { + let (shared_label, shared_note, mut_note, sugg) = match mutable { + Mutability::Mut => { + let sugg = + if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None }; + ("mutable ", false, true, sugg) + } + Mutability::Not => { + let sugg = + if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None }; + ("shared ", true, false, sugg) + } + }; + + cx.emit_span_lint( + STATIC_MUT_REFS, + span, + RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note }, + ); +} diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 900c496e033..f9d0cd49708 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -3,9 +3,9 @@ use std::ops::ControlFlow; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; +use rustc_hir::{Expr, ExprKind}; use rustc_middle::bug; -use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; +use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; @@ -13,22 +13,23 @@ use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; use rustc_span::{source_map, Span, Symbol}; -use rustc_target::abi::{Abi, Integer, Size, TagEncoding, Variants, WrappingRange}; +use rustc_target::abi::{Abi, TagEncoding, Variants, WrappingRange}; use rustc_target::spec::abi::Abi as SpecAbi; use tracing::debug; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, - InvalidNanComparisonsSuggestion, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, - OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, - OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, - UseInclusiveRange, VariantSizeDifferencesDiag, + InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag, }; use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext}; +mod literal; + +use literal::{int_ty_range, lint_literal, uint_ty_range}; + declare_lint! { /// The `unused_comparisons` lint detects comparisons made useless by /// limits of the types involved. @@ -185,403 +186,6 @@ impl TypeLimits { } } -/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). -/// Returns `true` iff the lint was emitted. -fn lint_overflowing_range_endpoint<'tcx>( - cx: &LateContext<'tcx>, - lit: &hir::Lit, - lit_val: u128, - max: u128, - expr: &'tcx hir::Expr<'tcx>, - ty: &str, -) -> bool { - // Look past casts to support cases like `0..256 as u8` - let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) - && let ExprKind::Cast(_, _) = par_expr.kind - { - (par_expr, expr.span) - } else { - (expr, expr.span) - }; - - // We only want to handle exclusive (`..`) ranges, - // which are represented as `ExprKind::Struct`. - let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; - let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; - if !is_range_literal(struct_expr) { - return false; - }; - let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; - - // We can suggest using an inclusive range - // (`..=`) instead only if it is the `end` that is - // overflowing and only by 1. - if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { - return false; - }; - - use rustc_ast::{LitIntType, LitKind}; - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsuffixed) => "", - _ => bug!(), - }; - - let sub_sugg = if expr.span.lo() == lit_span.lo() { - let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; - UseInclusiveRange::WithoutParen { - sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), - start, - literal: lit_val - 1, - suffix, - } - } else { - UseInclusiveRange::WithParen { - eq_sugg: expr.span.shrink_to_lo(), - lit_sugg: lit_span, - literal: lit_val - 1, - suffix, - } - }; - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - struct_expr.span, - RangeEndpointOutOfRange { ty, sub: sub_sugg }, - ); - - // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, - // return `true` so the callers don't also emit a lint - true -} - -// For `isize` & `usize`, be conservative with the warnings, so that the -// warnings are consistent between 32- and 64-bit platforms. -fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { - match int_ty { - ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), - ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), - ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), - ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), - ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), - ty::IntTy::I128 => (i128::MIN, i128::MAX), - } -} - -fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { - let max = match uint_ty { - ty::UintTy::Usize => u64::MAX.into(), - ty::UintTy::U8 => u8::MAX.into(), - ty::UintTy::U16 => u16::MAX.into(), - ty::UintTy::U32 => u32::MAX.into(), - ty::UintTy::U64 => u64::MAX.into(), - ty::UintTy::U128 => u128::MAX, - }; - (0, max) -} - -fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> { - let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; - let firstch = src.chars().next()?; - - if firstch == '0' { - match src.chars().nth(1) { - Some('x' | 'b') => return Some(src), - _ => return None, - } - } - - None -} - -fn report_bin_hex_error( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - ty: attr::IntType, - size: Size, - repr_str: String, - val: u128, - negative: bool, -) { - let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { - let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; - (t.name_str(), actually.to_string()) - } - attr::IntType::UnsignedInt(t) => { - let actually = size.truncate(val); - (t.name_str(), actually.to_string()) - } - }; - let sign = - if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; - let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( - |suggestion_ty| { - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - let (sans_suffix, _) = repr_str.split_at(pos); - OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } - } else { - OverflowingBinHexSub::Help { suggestion_ty } - } - }, - ); - let sign_bit_sub = (!negative) - .then(|| { - let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { - return None; - }; - - let Some(bit_width) = int_ty.bit_width() else { - return None; // isize case - }; - - // Skip if sign bit is not set - if (val & (1 << (bit_width - 1))) == 0 { - return None; - } - - let lit_no_suffix = - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - repr_str.split_at(pos).0 - } else { - &repr_str - }; - - Some(OverflowingBinHexSignBitSub { - span: expr.span, - lit_no_suffix, - negative_val: actually.clone(), - int_ty: int_ty.name_str(), - uint_ty: int_ty.to_unsigned().name_str(), - }) - }) - .flatten(); - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - expr.span, - OverflowingBinHex { - ty: t, - lit: repr_str.clone(), - dec: val, - actually, - sign, - sub, - sign_bit_sub, - }, - ) -} - -// This function finds the next fitting type and generates a suggestion string. -// It searches for fitting types in the following way (`X < Y`): -// - `iX`: if literal fits in `uX` => `uX`, else => `iY` -// - `-iX` => `iY` -// - `uX` => `uY` -// -// No suggestion for: `isize`, `usize`. -fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { - use ty::IntTy::*; - use ty::UintTy::*; - macro_rules! find_fit { - ($ty:expr, $val:expr, $negative:expr, - $($type:ident => [$($utypes:expr),*] => [$($itypes:expr),*]),+) => { - { - let _neg = if negative { 1 } else { 0 }; - match $ty { - $($type => { - $(if !negative && val <= uint_ty_range($utypes).1 { - return Some($utypes.name_str()) - })* - $(if val <= int_ty_range($itypes).1 as u128 + _neg { - return Some($itypes.name_str()) - })* - None - },)+ - _ => None - } - } - } - } - match t.kind() { - ty::Int(i) => find_fit!(i, val, negative, - I8 => [U8] => [I16, I32, I64, I128], - I16 => [U16] => [I32, I64, I128], - I32 => [U32] => [I64, I128], - I64 => [U64] => [I128], - I128 => [U128] => []), - ty::Uint(u) => find_fit!(u, val, negative, - U8 => [U8, U16, U32, U64, U128] => [], - U16 => [U16, U32, U64, U128] => [], - U32 => [U32, U64, U128] => [], - U64 => [U64, U128] => [], - U128 => [U128] => []), - _ => None, - } -} - -fn lint_int_literal<'tcx>( - cx: &LateContext<'tcx>, - type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, - t: ty::IntTy, - v: u128, -) { - let int_type = t.normalize(cx.sess().target.pointer_width); - let (min, max) = int_ty_range(int_type); - let max = max as u128; - let negative = type_limits.negated_expr_id == Some(e.hir_id); - - // Detect literal value out of range [min, max] inclusive - // avoiding use of -min to prevent overflow/panic - if (negative && v > max + 1) || (!negative && v > max) { - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::SignedInt(ty::ast_int_ty(t)), - Integer::from_int_ty(cx, t).size(), - repr_str, - v, - negative, - ); - return; - } - - if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { - // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. - return; - } - - let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; - let lit = cx - .sess() - .source_map() - .span_to_snippet(span) - .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); - let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) - .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); - - cx.emit_span_lint( - OVERFLOWING_LITERALS, - span, - OverflowingInt { ty: t.name_str(), lit, min, max, help }, - ); - } -} - -fn lint_uint_literal<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, - t: ty::UintTy, -) { - let uint_type = t.normalize(cx.sess().target.pointer_width); - let (min, max) = uint_ty_range(uint_type); - let lit_val: u128 = match lit.node { - // _v is u8, within range by definition - ast::LitKind::Byte(_v) => return, - ast::LitKind::Int(v, _) => v.get(), - _ => bug!(), - }; - - if lit_val < min || lit_val > max { - if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { - match par_e.kind { - hir::ExprKind::Cast(..) => { - if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - par_e.span, - OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, - ); - return; - } - } - _ => {} - } - } - if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { - // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. - return; - } - if let Some(repr_str) = get_bin_hex_repr(cx, lit) { - report_bin_hex_error( - cx, - e, - attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), - Integer::from_uint_ty(cx, t).size(), - repr_str, - lit_val, - false, - ); - return; - } - cx.emit_span_lint( - OVERFLOWING_LITERALS, - e.span, - OverflowingUInt { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| lit_val.to_string()), - min, - max, - }, - ); - } -} - -fn lint_literal<'tcx>( - cx: &LateContext<'tcx>, - type_limits: &TypeLimits, - e: &'tcx hir::Expr<'tcx>, - lit: &hir::Lit, -) { - match *cx.typeck_results().node_type(e.hir_id).kind() { - ty::Int(t) => { - match lit.node { - ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { - lint_int_literal(cx, type_limits, e, lit, t, v.get()) - } - _ => bug!(), - }; - } - ty::Uint(t) => lint_uint_literal(cx, e, lit, t), - ty::Float(t) => { - let (is_infinite, sym) = match lit.node { - ast::LitKind::Float(v, _) => match t { - // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI - // issues resolved). - ty::FloatTy::F16 => (Ok(false), v), - ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), - ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), - ty::FloatTy::F128 => (Ok(false), v), - }, - _ => bug!(), - }; - if is_infinite == Ok(true) { - cx.emit_span_lint( - OVERFLOWING_LITERALS, - e.span, - OverflowingLiteral { - ty: t.name_str(), - lit: cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| sym.to_string()), - }, - ); - } - } - _ => {} - } -} - fn lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, @@ -991,6 +595,8 @@ struct CTypesVisitorState<'tcx> { /// The original type being checked, before we recursed /// to any other types it contains. base_ty: Ty<'tcx>, + /// Number of times we recursed while checking the type + recursion_depth: usize, } enum FfiResult<'tcx> { @@ -1296,12 +902,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // Protect against infinite recursion, for example // `struct S(*mut S);`. - // FIXME: A recursion limit is necessary as well, for irregular - // recursive types. if !acc.cache.insert(ty) { return FfiSafe; } + // Additional recursion check for more complex types like + // `struct A<T> { v: *const A<A<T>>, ... }` for which the + // cache check above won't be enough (fixes #130310) + if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) { + return FfiUnsafe { + ty: acc.base_ty, + reason: fluent::lint_improper_ctypes_recursion_limit_reached, + help: None, + }; + } + + acc.recursion_depth += 1; + match *ty.kind() { ty::Adt(def, args) => { if let Some(boxed) = ty.boxed_ty() @@ -1644,7 +1261,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { return; } - let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty }; + let mut acc = + CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 }; match self.check_type_for_ffi(&mut acc, ty) { FfiResult::FfiSafe => {} FfiResult::FfiPhantom(ty) => { diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs new file mode 100644 index 00000000000..67404be24b5 --- /dev/null +++ b/compiler/rustc_lint/src/types/literal.rs @@ -0,0 +1,386 @@ +use hir::{is_range_literal, ExprKind, Node}; +use rustc_middle::ty::layout::IntegerExt; +use rustc_middle::ty::Ty; +use rustc_middle::{bug, ty}; +use rustc_target::abi::{Integer, Size}; +use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; + +use crate::context::LintContext; +use crate::lints::{ + OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, + OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, + RangeEndpointOutOfRange, UseInclusiveRange, +}; +use crate::types::{TypeLimits, OVERFLOWING_LITERALS}; +use crate::LateContext; + +/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). +/// Returns `true` iff the lint was emitted. +fn lint_overflowing_range_endpoint<'tcx>( + cx: &LateContext<'tcx>, + lit: &hir::Lit, + lit_val: u128, + max: u128, + expr: &'tcx hir::Expr<'tcx>, + ty: &str, +) -> bool { + // Look past casts to support cases like `0..256 as u8` + let (expr, lit_span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::Cast(_, _) = par_expr.kind + { + (par_expr, expr.span) + } else { + (expr, expr.span) + }; + + // We only want to handle exclusive (`..`) ranges, + // which are represented as `ExprKind::Struct`. + let Node::ExprField(field) = cx.tcx.parent_hir_node(expr.hir_id) else { return false }; + let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else { return false }; + if !is_range_literal(struct_expr) { + return false; + }; + let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else { return false }; + + // We can suggest using an inclusive range + // (`..=`) instead only if it is the `end` that is + // overflowing and only by 1. + if !(end.expr.hir_id == expr.hir_id && lit_val - 1 == max) { + return false; + }; + + use rustc_ast::{LitIntType, LitKind}; + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", + _ => bug!(), + }; + + let sub_sugg = if expr.span.lo() == lit_span.lo() { + let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else { return false }; + UseInclusiveRange::WithoutParen { + sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()), + start, + literal: lit_val - 1, + suffix, + } + } else { + UseInclusiveRange::WithParen { + eq_sugg: expr.span.shrink_to_lo(), + lit_sugg: lit_span, + literal: lit_val - 1, + suffix, + } + }; + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + struct_expr.span, + RangeEndpointOutOfRange { ty, sub: sub_sugg }, + ); + + // We've just emitted a lint, special cased for `(...)..MAX+1` ranges, + // return `true` so the callers don't also emit a lint + true +} + +// For `isize` & `usize`, be conservative with the warnings, so that the +// warnings are consistent between 32- and 64-bit platforms. +pub(crate) fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) { + match int_ty { + ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), + ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), + ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), + ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), + ty::IntTy::I128 => (i128::MIN, i128::MAX), + } +} + +pub(crate) fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) { + let max = match uint_ty { + ty::UintTy::Usize => u64::MAX.into(), + ty::UintTy::U8 => u8::MAX.into(), + ty::UintTy::U16 => u16::MAX.into(), + ty::UintTy::U32 => u32::MAX.into(), + ty::UintTy::U64 => u64::MAX.into(), + ty::UintTy::U128 => u128::MAX, + }; + (0, max) +} + +fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> { + let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?; + let firstch = src.chars().next()?; + + if firstch == '0' { + match src.chars().nth(1) { + Some('x' | 'b') => return Some(src), + _ => return None, + } + } + + None +} + +fn report_bin_hex_error( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + ty: attr::IntType, + size: Size, + repr_str: String, + val: u128, + negative: bool, +) { + let (t, actually) = match ty { + attr::IntType::SignedInt(t) => { + let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) }; + (t.name_str(), actually.to_string()) + } + attr::IntType::UnsignedInt(t) => { + let actually = size.truncate(val); + (t.name_str(), actually.to_string()) + } + }; + let sign = + if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; + let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( + |suggestion_ty| { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } + } else { + OverflowingBinHexSub::Help { suggestion_ty } + } + }, + ); + let sign_bit_sub = (!negative) + .then(|| { + let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { + return None; + }; + + let Some(bit_width) = int_ty.bit_width() else { + return None; // isize case + }; + + // Skip if sign bit is not set + if (val & (1 << (bit_width - 1))) == 0 { + return None; + } + + let lit_no_suffix = + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + repr_str.split_at(pos).0 + } else { + &repr_str + }; + + Some(OverflowingBinHexSignBitSub { + span: expr.span, + lit_no_suffix, + negative_val: actually.clone(), + int_ty: int_ty.name_str(), + uint_ty: int_ty.to_unsigned().name_str(), + }) + }) + .flatten(); + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + expr.span, + OverflowingBinHex { + ty: t, + lit: repr_str.clone(), + dec: val, + actually, + sign, + sub, + sign_bit_sub, + }, + ) +} + +// Find the "next" fitting integer and return a suggestion string +// +// No suggestion is offered for `{i,u}size`. Otherwise, we try to suggest an equal-sized type. +fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> { + match t.kind() { + ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, + ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), + ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()), + ty::Int(int) => { + let signed = Integer::fit_signed(val as i128); + let unsigned = Integer::fit_unsigned(val); + Some(if Some(unsigned.size().bits()) == int.bit_width() { + unsigned.uint_ty_str() + } else { + signed.int_ty_str() + }) + } + _ => None, + } +} + +fn lint_int_literal<'tcx>( + cx: &LateContext<'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, + t: ty::IntTy, + v: u128, +) { + let int_type = t.normalize(cx.sess().target.pointer_width); + let (min, max) = int_ty_range(int_type); + let max = max as u128; + let negative = type_limits.negated_expr_id == Some(e.hir_id); + + // Detect literal value out of range [min, max] inclusive + // avoiding use of -min to prevent overflow/panic + if (negative && v > max + 1) || (!negative && v > max) { + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error( + cx, + e, + attr::IntType::SignedInt(ty::ast_int_ty(t)), + Integer::from_int_ty(cx, t).size(), + repr_str, + v, + negative, + ); + return; + } + + if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. + return; + } + + let span = if negative { type_limits.negated_expr_span.unwrap() } else { e.span }; + let lit = cx + .sess() + .source_map() + .span_to_snippet(span) + .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() }); + let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) + .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); + + cx.emit_span_lint( + OVERFLOWING_LITERALS, + span, + OverflowingInt { ty: t.name_str(), lit, min, max, help }, + ); + } +} + +fn lint_uint_literal<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, + t: ty::UintTy, +) { + let uint_type = t.normalize(cx.sess().target.pointer_width); + let (min, max) = uint_ty_range(uint_type); + let lit_val: u128 = match lit.node { + // _v is u8, within range by definition + ast::LitKind::Byte(_v) => return, + ast::LitKind::Int(v, _) => v.get(), + _ => bug!(), + }; + + if lit_val < min || lit_val > max { + if let Node::Expr(par_e) = cx.tcx.parent_hir_node(e.hir_id) { + match par_e.kind { + hir::ExprKind::Cast(..) => { + if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + par_e.span, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, + ); + return; + } + } + _ => {} + } + } + if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. + return; + } + if let Some(repr_str) = get_bin_hex_repr(cx, lit) { + report_bin_hex_error( + cx, + e, + attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + Integer::from_uint_ty(cx, t).size(), + repr_str, + lit_val, + false, + ); + return; + } + cx.emit_span_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingUInt { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| lit_val.to_string()), + min, + max, + }, + ); + } +} + +pub(crate) fn lint_literal<'tcx>( + cx: &LateContext<'tcx>, + type_limits: &TypeLimits, + e: &'tcx hir::Expr<'tcx>, + lit: &hir::Lit, +) { + match *cx.typeck_results().node_type(e.hir_id).kind() { + ty::Int(t) => { + match lit.node { + ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { + lint_int_literal(cx, type_limits, e, lit, t, v.get()) + } + _ => bug!(), + }; + } + ty::Uint(t) => lint_uint_literal(cx, e, lit, t), + ty::Float(t) => { + let (is_infinite, sym) = match lit.node { + ast::LitKind::Float(v, _) => match t { + // FIXME(f16_f128): add this check once `is_infinite` is reliable (ABI + // issues resolved). + ty::FloatTy::F16 => (Ok(false), v), + ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v), + ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v), + ty::FloatTy::F128 => (Ok(false), v), + }, + _ => bug!(), + }; + if is_infinite == Ok(true) { + cx.emit_span_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingLiteral { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| sym.to_string()), + }, + ); + } + } + _ => {} + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9b6d63c2ef4..acdedb06141 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -99,7 +99,6 @@ declare_lint_pass! { SINGLE_USE_LIFETIMES, SOFT_UNSTABLE, STABLE_FEATURES, - STATIC_MUT_REFS, TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TRIVIAL_CASTS, @@ -1928,57 +1927,6 @@ declare_lint! { } declare_lint! { - /// The `static_mut_refs` lint checks for shared or mutable references - /// of mutable static inside `unsafe` blocks and `unsafe` functions. - /// - /// ### Example - /// - /// ```rust,edition2021 - /// fn main() { - /// static mut X: i32 = 23; - /// static mut Y: i32 = 24; - /// - /// unsafe { - /// let y = &X; - /// let ref x = X; - /// let (x, y) = (&X, &Y); - /// foo(&X); - /// } - /// } - /// - /// unsafe fn _foo() { - /// static mut X: i32 = 23; - /// static mut Y: i32 = 24; - /// - /// let y = &X; - /// let ref x = X; - /// let (x, y) = (&X, &Y); - /// foo(&X); - /// } - /// - /// fn foo<'a>(_x: &'a i32) {} - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// Shared or mutable references of mutable static are almost always a mistake and - /// can lead to undefined behavior and various other problems in your code. - /// - /// This lint is "warn" by default on editions up to 2021, in 2024 there is - /// a hard error instead. - pub STATIC_MUT_REFS, - Warn, - "shared references or mutable references of mutable static is discouraged", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), - reference: "issue #114447 <https://github.com/rust-lang/rust/issues/114447>", - explain_reason: false, - }; -} - -declare_lint! { /// The `absolute_paths_not_starting_with_crate` lint detects fully /// qualified paths that start with a module name instead of `crate`, /// `self`, or an extern crate name diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 5c4b7e5664d..f5ed69658ce 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -12,7 +12,7 @@ use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_hir::def::Namespace; use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_span::edition::Edition; +pub use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent}; use rustc_span::{sym, Span, Symbol}; use rustc_target::spec::abi::Abi; diff --git a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp index a8c278741a7..feac6a5649c 100644 --- a/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/ArchiveWrapper.cpp @@ -196,14 +196,10 @@ extern "C" LLVMRustResult LLVMRustWriteArchive( } } -#if LLVM_VERSION_LT(18, 0) - auto Result = writeArchive(Dst, Members, WriteSymbtab, Kind, true, false); -#else auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab : SymtabWritingMode::NoSymtab; auto Result = writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC); -#endif if (!Result) return LLVMRustResult::Success; LLVMRustSetLastError(toString(std::move(Result)).c_str()); diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index e842f47f48c..4532fd8d48d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -58,17 +58,10 @@ fromRust(LLVMRustCounterMappingRegionKind Kind) { return coverage::CounterMappingRegion::GapRegion; case LLVMRustCounterMappingRegionKind::BranchRegion: return coverage::CounterMappingRegion::BranchRegion; -#if LLVM_VERSION_GE(18, 0) case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: return coverage::CounterMappingRegion::MCDCDecisionRegion; case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: return coverage::CounterMappingRegion::MCDCBranchRegion; -#else - case LLVMRustCounterMappingRegionKind::MCDCDecisionRegion: - break; - case LLVMRustCounterMappingRegionKind::MCDCBranchRegion: - break; -#endif } report_fatal_error("Bad LLVMRustCounterMappingRegionKind!"); } @@ -100,7 +93,7 @@ struct LLVMRustMCDCParameters { // https://github.com/rust-lang/llvm-project/blob/66a2881a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L253-L263 // and representations in 19 // https://github.com/llvm/llvm-project/blob/843cc474faefad1d639f4c44c1cf3ad7dbda76c8/llvm/include/llvm/ProfileData/Coverage/MCDCTypes.h -#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) +#if LLVM_VERSION_LT(19, 0) static coverage::CounterMappingRegion::MCDCParameters fromRust(LLVMRustMCDCParameters Params) { auto parameter = coverage::CounterMappingRegion::MCDCParameters{}; @@ -126,7 +119,7 @@ fromRust(LLVMRustMCDCParameters Params) { } report_fatal_error("Bad LLVMRustMCDCParametersTag!"); } -#elif LLVM_VERSION_GE(19, 0) +#else static coverage::mcdc::Parameters fromRust(LLVMRustMCDCParameters Params) { switch (Params.Tag) { case LLVMRustMCDCParametersTag::None: @@ -221,7 +214,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( RustMappingRegions, NumMappingRegions)) { MappingRegions.emplace_back( fromRust(Region.Count), fromRust(Region.FalseCount), -#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) +#if LLVM_VERSION_LT(19, 0) // LLVM 19 may move this argument to last. fromRust(Region.MCDCParameters), #endif diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h index a493abbbc7e..73bbc9de855 100644 --- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h +++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h @@ -25,7 +25,6 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include "llvm/Transforms/IPO.h" -#include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Scalar.h" #define LLVM_VERSION_GE(major, minor) \ @@ -34,6 +33,12 @@ #define LLVM_VERSION_LT(major, minor) (!LLVM_VERSION_GE((major), (minor))) +#if LLVM_VERSION_GE(20, 0) +#include "llvm/Transforms/Utils/Instrumentation.h" +#else +#include "llvm/Transforms/Instrumentation.h" +#endif + #include "llvm/IR/LegacyPassManager.h" #include "llvm/Bitcode/BitcodeReader.h" diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index da27db29c87..9f3e0080110 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -26,22 +26,19 @@ #include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/TimeProfiler.h" #include "llvm/Support/VirtualFileSystem.h" #include "llvm/Target/TargetMachine.h" +#include "llvm/TargetParser/Host.h" #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/FunctionImport.h" #include "llvm/Transforms/IPO/Internalize.h" #include "llvm/Transforms/IPO/LowerTypeTests.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" -#include "llvm/Transforms/Utils/AddDiscriminators.h" -#include "llvm/Transforms/Utils/FunctionImportUtils.h" -#if LLVM_VERSION_GE(18, 0) -#include "llvm/TargetParser/Host.h" -#endif -#include "llvm/Support/TimeProfiler.h" -#include "llvm/Transforms/Instrumentation.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" +#include "llvm/Transforms/Utils/AddDiscriminators.h" +#include "llvm/Transforms/Utils/FunctionImportUtils.h" #if LLVM_VERSION_GE(19, 0) #include "llvm/Support/PGOOptions.h" #endif @@ -241,11 +238,7 @@ enum class LLVMRustCodeGenOptLevel { Aggressive, }; -#if LLVM_VERSION_GE(18, 0) using CodeGenOptLevelEnum = llvm::CodeGenOptLevel; -#else -using CodeGenOptLevelEnum = llvm::CodeGenOpt::Level; -#endif static CodeGenOptLevelEnum fromRust(LLVMRustCodeGenOptLevel Level) { switch (Level) { @@ -371,21 +364,16 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, } extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) { -#if LLVM_VERSION_GE(18, 0) const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures(); return FeatTable.size(); -#else - return 0; -#endif } extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, const char **Feature, const char **Desc) { -#if LLVM_VERSION_GE(18, 0) const TargetMachine *Target = unwrap(TM); const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo(); const ArrayRef<SubtargetFeatureKV> FeatTable = @@ -393,7 +381,6 @@ extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index, const SubtargetFeatureKV Feat = FeatTable[Index]; *Feature = Feat.Key; *Desc = Feat.Desc; -#endif } extern "C" const char *LLVMRustGetHostCPUName(size_t *len) { @@ -570,17 +557,9 @@ enum class LLVMRustFileType { static CodeGenFileType fromRust(LLVMRustFileType Type) { switch (Type) { case LLVMRustFileType::AssemblyFile: -#if LLVM_VERSION_GE(18, 0) return CodeGenFileType::AssemblyFile; -#else - return CGFT_AssemblyFile; -#endif case LLVMRustFileType::ObjectFile: -#if LLVM_VERSION_GE(18, 0) return CodeGenFileType::ObjectFile; -#else - return CGFT_ObjectFile; -#endif default: report_fatal_error("Bad FileType."); } @@ -866,11 +845,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( // cargo run tests in multhreading mode by default // so use atomics for coverage counters Options.Atomic = true; -#if LLVM_VERSION_GE(18, 0) MPM.addPass(InstrProfilingLoweringPass(Options, false)); -#else - MPM.addPass(InstrProfiling(Options, false)); -#endif }); } @@ -1211,7 +1186,6 @@ struct LLVMRustThinLTOData { // Not 100% sure what these are, but they impact what's internalized and // what's inlined across modules, I believe. -#if LLVM_VERSION_GE(18, 0) #if LLVM_VERSION_GE(20, 0) FunctionImporter::ImportListsTy ImportLists; #else @@ -1219,11 +1193,6 @@ struct LLVMRustThinLTOData { #endif DenseMap<StringRef, FunctionImporter::ExportSetTy> ExportLists; DenseMap<StringRef, GVSummaryMapTy> ModuleToDefinedGVSummaries; -#else - StringMap<FunctionImporter::ImportMapTy> ImportLists; - StringMap<FunctionImporter::ExportSetTy> ExportLists; - StringMap<GVSummaryMapTy> ModuleToDefinedGVSummaries; -#endif StringMap<std::map<GlobalValue::GUID, GlobalValue::LinkageTypes>> ResolvedODR; LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} @@ -1275,11 +1244,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, int num_modules, Ret->ModuleMap[module->identifier] = mem_buffer; -#if LLVM_VERSION_GE(18, 0) if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index)) { -#else - if (Error Err = readModuleSummaryIndex(mem_buffer, Ret->Index, i)) { -#endif LLVMRustSetLastError(toString(std::move(Err)).c_str()); return nullptr; } @@ -1425,13 +1390,13 @@ LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data, return true; } -extern "C" bool LLVMRustPrepareThinLTOImport(LLVMRustThinLTOData *Data, +extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data, LLVMModuleRef M, LLVMTargetMachineRef TM) { Module &Mod = *unwrap(M); TargetMachine &Target = *unwrap(TM); - const auto &ImportList = Data->ImportLists[Mod.getModuleIdentifier()]; + const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier()); auto Loader = [&](StringRef Identifier) { const auto &Memory = Data->ModuleMap.lookup(Identifier); auto &Context = Mod.getContext(); @@ -1614,7 +1579,7 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, LLVMRustThinLTOData *Data) { SmallString<40> Key; llvm::lto::Config conf; - const auto &ImportList = Data->ImportLists[ModId]; + const auto &ImportList = Data->ImportLists.lookup(ModId); const auto &ExportList = Data->ExportLists.lookup(ModId); const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index ed12318c88d..f9fc2bd6da3 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -310,16 +310,10 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) { return Attribute::SafeStack; case FnRetThunkExtern: return Attribute::FnRetThunkExtern; -#if LLVM_VERSION_GE(18, 0) case Writable: return Attribute::Writable; case DeadOnUnwind: return Attribute::DeadOnUnwind; -#else - case Writable: - case DeadOnUnwind: - report_fatal_error("Not supported on this LLVM version"); -#endif } report_fatal_error("bad AttributeKind"); } @@ -1061,11 +1055,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( return wrap(Builder->createStaticMemberType( unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File), LineNo, unwrapDI<DIType>(Ty), fromRust(Flags), - unwrap<llvm::ConstantInt>(val), -#if LLVM_VERSION_GE(18, 0) - llvm::dwarf::DW_TAG_member, -#endif - AlignInBits)); + unwrap<llvm::ConstantInt>(val), llvm::dwarf::DW_TAG_member, AlignInBits)); } extern "C" LLVMMetadataRef @@ -1182,10 +1172,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), unwrapDI<DIFile>(File), LineNumber, SizeInBits, AlignInBits, DINodeArray(unwrapDI<MDTuple>(Elements)), unwrapDI<DIType>(ClassTy), -#if LLVM_VERSION_GE(18, 0) - /* RunTimeLang */ 0, -#endif - "", IsScoped)); + /* RunTimeLang */ 0, "", IsScoped)); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( @@ -1552,27 +1539,19 @@ LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_GE(18, 0) return wrap(llvm::Intrinsic::getDeclaration( unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters)); -#else - report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); -#endif } extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_GE(18, 0) return wrap(llvm::Intrinsic::getDeclaration( unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update)); -#else - report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions"); -#endif } extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) { -#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0) +#if LLVM_VERSION_LT(19, 0) return wrap(llvm::Intrinsic::getDeclaration( unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update)); #else diff --git a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp index d625935d925..54ee79dc290 100644 --- a/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp @@ -150,11 +150,9 @@ extern "C" bool LLVMRustIsECObject(char *BufPtr, size_t BufLen) { return cast<llvm::object::COFFObjectFile>(&*Obj)->getMachine() != COFF::IMAGE_FILE_MACHINE_ARM64; -#if LLVM_VERSION_GE(18, 0) if (Obj->isCOFFImportFile()) return cast<llvm::object::COFFImportFile>(&*Obj)->getMachine() != COFF::IMAGE_FILE_MACHINE_ARM64; -#endif if (Obj->isIR()) { Expected<std::string> TripleStr = diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 53da07aeaa6..b9d287730fa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -290,6 +290,7 @@ provide! { tcx, def_id, other, cdata, fn_arg_names => { table } coroutine_kind => { table_direct } coroutine_for_closure => { table } + coroutine_by_move_body_def_id => { table } eval_static_initializer => { Ok(cdata .root diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b617d5236b9..46dc0696638 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1488,9 +1488,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { + let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); self.tables .coroutine_for_closure - .set_some(def_id.index, self.tcx.coroutine_for_closure(def_id).into()); + .set_some(def_id.index, coroutine_for_closure.into()); + + // If this async closure has a by-move body, record it too. + if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { + self.tables.coroutine_by_move_body_def_id.set_some( + coroutine_for_closure.index, + self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(), + ); + } } if let DefKind::Static { .. } = def_kind { if !self.tcx.is_foreign_item(def_id) { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 8180a507a51..c791a1f541c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -446,6 +446,7 @@ define_tables! { fn_arg_names: Table<DefIndex, LazyArray<Ident>>, coroutine_kind: Table<DefIndex, hir::CoroutineKind>, coroutine_for_closure: Table<DefIndex, RawDefId>, + coroutine_by_move_body_def_id: Table<DefIndex, RawDefId>, eval_static_initializer: Table<DefIndex, LazyValue<mir::interpret::ConstAllocation<'static>>>, trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>, trait_item_def_id: Table<DefIndex, RawDefId>, diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index fd6e2ad79b1..23cd247e884 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -33,6 +33,7 @@ #![feature(allocator_api)] #![feature(array_windows)] #![feature(assert_matches)] +#![feature(associated_type_defaults)] #![feature(box_as_ptr)] #![feature(box_patterns)] #![feature(closure_track_caller)] diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 1236c9efb41..5a32078760e 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -104,6 +104,9 @@ pub enum Adjust<'tcx> { /// Cast into a dyn* object. DynStar, + + /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. + ReborrowPin(ty::Region<'tcx>, hir::Mutability), } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 6708ae60562..1a584cf2890 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -521,6 +521,10 @@ impl<'tcx> Const<'tcx> { self.try_to_valtree()?.try_to_scalar() } + pub fn try_to_bool(self) -> Option<bool> { + self.try_to_scalar()?.to_bool().ok() + } + #[inline] pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> { self.try_to_valtree()?.try_to_target_usize(tcx) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 6d878ab7654..877c54c5649 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -627,7 +627,7 @@ pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; pub trait LayoutOfHelpers<'tcx>: HasDataLayout + HasTyCtxt<'tcx> + HasParamEnv<'tcx> { /// The `TyAndLayout`-wrapping type (or `TyAndLayout` itself), which will be /// returned from `layout_of` (see also `handle_layout_err`). - type LayoutOfResult: MaybeResult<TyAndLayout<'tcx>>; + type LayoutOfResult: MaybeResult<TyAndLayout<'tcx>> = TyAndLayout<'tcx>; /// `Span` to use for `tcx.at(span)`, from `layout_of`. // FIXME(eddyb) perhaps make this mandatory to get contexts to track it better? @@ -1233,7 +1233,7 @@ pub enum FnAbiRequest<'tcx> { pub trait FnAbiOfHelpers<'tcx>: LayoutOfHelpers<'tcx> { /// The `&FnAbi`-wrapping type (or `&FnAbi` itself), which will be /// returned from `fn_abi_of_*` (see also `handle_fn_abi_err`). - type FnAbiOfResult: MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>>; + type FnAbiOfResult: MaybeResult<&'tcx FnAbi<'tcx, Ty<'tcx>>> = &'tcx FnAbi<'tcx, Ty<'tcx>>; /// Helper used for `fn_abi_of_*`, to adapt `tcx.fn_abi_of_*(...)` into a /// `Self::FnAbiOfResult` (which does not need to be a `Result<...>`). diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 9e429f5a4c7..6163fa2932d 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -56,7 +56,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> { && let Some(node) = tcx.hir().get_if_local(def_id) && let Some(sig) = node.fn_sig() { - sig.decl.inputs.len() + sig.decl.implicit_self.has_implicit_self() as usize + sig.decl.inputs.len() } else { tcx.dcx().abort_if_errors(); unreachable!() diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index c7fcfe3ce2a..4fe984b961e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -218,6 +218,13 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { warnings: self.warnings, suggest_unsafe_block: self.suggest_unsafe_block, }; + // params in THIR may be unsafe, e.g. a union pattern. + for param in &inner_thir.params { + if let Some(param_pat) = param.pat.as_deref() { + inner_visitor.visit_pat(param_pat); + } + } + // Visit the body. inner_visitor.visit_expr(&inner_thir[expr]); // Unsafe blocks can be used in the inner body, make sure to take it into account self.safety_context = inner_visitor.safety_context; @@ -315,14 +322,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { | PatKind::DerefPattern { .. } | PatKind::Range { .. } | PatKind::Slice { .. } - | PatKind::Array { .. } => { + | PatKind::Array { .. } + // Never constitutes a witness of uninhabitedness. + | PatKind::Never => { self.requires_unsafe(pat.span, AccessToUnionField); return; // we can return here since this already requires unsafe } - // wildcard/never don't take anything + // wildcard doesn't read anything. PatKind::Wild | - PatKind::Never | - // these just wrap other patterns + // these just wrap other patterns, which we recurse on below. PatKind::Or { .. } | PatKind::InlineConstant { .. } | PatKind::AscribeUserType { .. } | @@ -1032,6 +1040,13 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) { warnings: &mut warnings, suggest_unsafe_block: true, }; + // params in THIR may be unsafe, e.g. a union pattern. + for param in &thir.params { + if let Some(param_pat) = param.pat.as_deref() { + visitor.visit_pat(param_pat); + } + } + // Visit the body. visitor.visit_expr(&thir[expr]); warnings.sort_by_key(|w| w.block_span); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index aa8ccc8b7dd..3f730b5d183 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -74,6 +74,7 @@ impl<'tcx> Cx<'tcx> { self.thir.exprs.push(expr) } + #[instrument(level = "trace", skip(self, expr, span))] fn apply_adjustment( &mut self, hir_expr: &'tcx hir::Expr<'tcx>, @@ -146,6 +147,67 @@ impl<'tcx> Cx<'tcx> { ExprKind::RawBorrow { mutability, arg: self.thir.exprs.push(expr) } } Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) }, + Adjust::ReborrowPin(region, mutbl) => { + debug!("apply ReborrowPin adjustment"); + // Rewrite `$expr` as `Pin { __pointer: &(mut)? *($expr).__pointer }` + + // We'll need these types later on + let pin_ty_args = match expr.ty.kind() { + ty::Adt(_, args) => args, + _ => bug!("ReborrowPin with non-Pin type"), + }; + let pin_ty = pin_ty_args.iter().next().unwrap().expect_ty(); + let ptr_target_ty = match pin_ty.kind() { + ty::Ref(_, ty, _) => *ty, + _ => bug!("ReborrowPin with non-Ref type"), + }; + + // pointer = ($expr).__pointer + let pointer_target = ExprKind::Field { + lhs: self.thir.exprs.push(expr), + variant_index: FIRST_VARIANT, + name: FieldIdx::from(0u32), + }; + let arg = Expr { temp_lifetime, ty: pin_ty, span, kind: pointer_target }; + let arg = self.thir.exprs.push(arg); + + // arg = *pointer + let expr = ExprKind::Deref { arg }; + let arg = self.thir.exprs.push(Expr { + temp_lifetime, + ty: ptr_target_ty, + span, + kind: expr, + }); + + // expr = &mut target + let borrow_kind = match mutbl { + hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default }, + hir::Mutability::Not => BorrowKind::Shared, + }; + let new_pin_target = Ty::new_ref(self.tcx, region, ptr_target_ty, mutbl); + let expr = self.thir.exprs.push(Expr { + temp_lifetime, + ty: new_pin_target, + span, + kind: ExprKind::Borrow { borrow_kind, arg }, + }); + + // kind = Pin { __pointer: pointer } + let pin_did = self.tcx.require_lang_item(rustc_hir::LangItem::Pin, Some(span)); + let args = self.tcx.mk_args(&[new_pin_target.into()]); + let kind = ExprKind::Adt(Box::new(AdtExpr { + adt_def: self.tcx.adt_def(pin_did), + variant_index: FIRST_VARIANT, + args, + fields: Box::new([FieldExpr { name: FieldIdx::from(0u32), expr }]), + user_ty: None, + base: None, + })); + + debug!(?kind); + kind + } }; Expr { temp_lifetime, ty: adjustment.target, span, kind } @@ -1014,7 +1076,7 @@ impl<'tcx> Cx<'tcx> { // Reconstruct the output assuming it's a reference with the // same region and mutability as the receiver. This holds for - // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. + // `Deref(Mut)::deref(_mut)` and `Index(Mut)::index(_mut)`. let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else { span_bug!(span, "overloaded_place: receiver is not a reference"); }; diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 7e3ecad1bce..ef4031c5c03 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -95,11 +95,33 @@ impl CoverageCounters { this } - fn make_counter(&mut self, site: CounterIncrementSite) -> BcbCounter { + /// Shared helper used by [`Self::make_phys_node_counter`] and + /// [`Self::make_phys_edge_counter`]. Don't call this directly. + fn make_counter_inner(&mut self, site: CounterIncrementSite) -> BcbCounter { let id = self.counter_increment_sites.push(site); BcbCounter::Counter { id } } + /// Creates a new physical counter attached a BCB node. + /// The node must not already have a counter. + fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter { + let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb }); + debug!(?bcb, ?counter, "node gets a physical counter"); + self.set_bcb_counter(bcb, counter) + } + + /// Creates a new physical counter attached to a BCB edge. + /// The edge must not already have a counter. + fn make_phys_edge_counter( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + ) -> BcbCounter { + let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb }); + debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter"); + self.set_bcb_edge_counter(from_bcb, to_bcb, counter) + } + fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter { let new_expr = BcbExpression { lhs, op, rhs }; *self @@ -294,25 +316,27 @@ impl<'a> MakeBcbCounters<'a> { let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice(); - // If this node doesn't have multiple out-edges, or all of its out-edges - // already have counters, then we don't need to create edge counters. - let needs_out_edge_counters = successors.len() > 1 - && successors.iter().any(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)); - if !needs_out_edge_counters { + // If this node's out-edges won't sum to the node's counter, + // then there's no reason to create edge counters here. + if !self.basic_coverage_blocks[from_bcb].is_out_summable { return; } - if tracing::enabled!(tracing::Level::DEBUG) { - let _span = - debug_span!("node has some out-edges without counters", ?from_bcb).entered(); - for &to_bcb in successors { - debug!(?to_bcb, counter=?self.edge_counter(from_bcb, to_bcb)); - } - } + // Determine the set of out-edges that don't yet have edge counters. + let candidate_successors = self.basic_coverage_blocks.successors[from_bcb] + .iter() + .copied() + .filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) + .collect::<Vec<_>>(); + debug!(?candidate_successors); - // Of the out-edges that don't have counters yet, one can be given an expression - // (computed from the other out-edges) instead of a dedicated counter. - let expression_to_bcb = self.choose_out_edge_for_expression(traversal, from_bcb); + // If there are out-edges without counters, choose one to be given an expression + // (computed from this node and the other out-edges) instead of a physical counter. + let Some(expression_to_bcb) = + self.choose_out_edge_for_expression(traversal, &candidate_successors) + else { + return; + }; // For each out-edge other than the one that was chosen to get an expression, // ensure that it has a counter (existing counter/expression or a new counter), @@ -324,10 +348,11 @@ impl<'a> MakeBcbCounters<'a> { .filter(|&to_bcb| to_bcb != expression_to_bcb) .map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb)) .collect::<Vec<_>>(); - let sum_of_all_other_out_edges: BcbCounter = self - .coverage_counters - .make_sum(&other_out_edge_counters) - .expect("there must be at least one other out-edge"); + let Some(sum_of_all_other_out_edges) = + self.coverage_counters.make_sum(&other_out_edge_counters) + else { + return; + }; // Now create an expression for the chosen edge, by taking the counter // for its source node and subtracting the sum of its sibling out-edges. @@ -338,10 +363,13 @@ impl<'a> MakeBcbCounters<'a> { ); debug!("{expression_to_bcb:?} gets an expression: {expression:?}"); - if self.basic_coverage_blocks.bcb_has_multiple_in_edges(expression_to_bcb) { - self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); - } else { + if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) { + // This edge normally wouldn't get its own counter, so attach the expression + // to its target node instead, so that `edge_has_no_counter` can see it. + assert_eq!(sole_pred, from_bcb); self.coverage_counters.set_bcb_counter(expression_to_bcb, expression); + } else { + self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression); } } @@ -353,28 +381,21 @@ impl<'a> MakeBcbCounters<'a> { return counter_kind; } - // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`). - // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the - // program results in a tight infinite loop, but it should still compile. - let one_path_to_target = !self.basic_coverage_blocks.bcb_has_multiple_in_edges(bcb); - if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { - let counter_kind = - self.coverage_counters.make_counter(CounterIncrementSite::Node { bcb }); - if one_path_to_target { - debug!("{bcb:?} gets a new counter: {counter_kind:?}"); - } else { - debug!( - "{bcb:?} has itself as its own predecessor. It can't be part of its own \ - Expression sum, so it will get its own new counter: {counter_kind:?}. \ - (Note, the compiled code will generate an infinite loop.)", - ); - } - return self.coverage_counters.set_bcb_counter(bcb, counter_kind); + let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice(); + + // Handle cases where we can't compute a node's count from its in-edges: + // - START_BCB has no in-edges, so taking the sum would panic (or be wrong). + // - For nodes with one in-edge, or that directly loop to themselves, + // trying to get the in-edge counts would require this node's counter, + // leading to infinite recursion. + if predecessors.len() <= 1 || predecessors.contains(&bcb) { + debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor"); + return self.coverage_counters.make_phys_node_counter(bcb); } // A BCB with multiple incoming edges can compute its count by ensuring that counters // exist for each of those edges, and then adding them up to get a total count. - let in_edge_counters = self.basic_coverage_blocks.predecessors[bcb] + let in_edge_counters = predecessors .iter() .copied() .map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb)) @@ -394,16 +415,19 @@ impl<'a> MakeBcbCounters<'a> { from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, ) -> BcbCounter { - // If the target BCB has only one in-edge (i.e. this one), then create - // a node counter instead, since it will have the same value. - if !self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) { - assert_eq!([from_bcb].as_slice(), self.basic_coverage_blocks.predecessors[to_bcb]); + // If the target node has exactly one in-edge (i.e. this one), then just + // use the node's counter, since it will have the same value. + if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { + assert_eq!(sole_pred, from_bcb); + // This call must take care not to invoke `get_or_make_edge` for + // this edge, since that would result in infinite recursion! return self.get_or_make_node_counter(to_bcb); } - // If the source BCB has only one successor (assumed to be the given target), an edge - // counter is unnecessary. Just get or make a counter for the source BCB. - if self.bcb_successors(from_bcb).len() == 1 { + // If the source node has exactly one out-edge (i.e. this one) and would have + // the same execution count as that edge, then just use the node's counter. + if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) { + assert_eq!(simple_succ, to_bcb); return self.get_or_make_node_counter(from_bcb); } @@ -416,118 +440,81 @@ impl<'a> MakeBcbCounters<'a> { } // Make a new counter to count this edge. - let counter_kind = - self.coverage_counters.make_counter(CounterIncrementSite::Edge { from_bcb, to_bcb }); - debug!("Edge {from_bcb:?}->{to_bcb:?} gets a new counter: {counter_kind:?}"); - self.coverage_counters.set_bcb_edge_counter(from_bcb, to_bcb, counter_kind) + self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb) } - /// Choose one of the out-edges of `from_bcb` to receive an expression - /// instead of a physical counter, and returns that edge's target node. - /// - /// - Precondition: The node must have at least one out-edge without a counter. - /// - Postcondition: The selected edge does not have an edge counter. + /// Given a set of candidate out-edges (represented by their successor node), + /// choose one to be given a counter expression instead of a physical counter. fn choose_out_edge_for_expression( &self, traversal: &TraverseCoverageGraphWithLoops<'_>, - from_bcb: BasicCoverageBlock, - ) -> BasicCoverageBlock { - if let Some(reloop_target) = self.find_good_reloop_edge(traversal, from_bcb) { - assert!(self.edge_has_no_counter(from_bcb, reloop_target)); + candidate_successors: &[BasicCoverageBlock], + ) -> Option<BasicCoverageBlock> { + // Try to find a candidate that leads back to the top of a loop, + // because reloop edges tend to be executed more times than loop-exit edges. + if let Some(reloop_target) = self.find_good_reloop_edge(traversal, &candidate_successors) { debug!("Selecting reloop target {reloop_target:?} to get an expression"); - return reloop_target; + return Some(reloop_target); } - // We couldn't identify a "good" edge, so just choose any edge that - // doesn't already have a counter. - let arbitrary_target = self - .bcb_successors(from_bcb) - .iter() - .copied() - .find(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb)) - .expect("precondition: at least one out-edge without a counter"); + // We couldn't identify a "good" edge, so just choose an arbitrary one. + let arbitrary_target = candidate_successors.first().copied()?; debug!(?arbitrary_target, "selecting arbitrary out-edge to get an expression"); - arbitrary_target + Some(arbitrary_target) } - /// Tries to find an edge that leads back to the top of a loop, and that - /// doesn't already have a counter. Such edges are good candidates to - /// be given an expression (instead of a physical counter), because they - /// will tend to be executed more times than a loop-exit edge. + /// Given a set of candidate out-edges (represented by their successor node), + /// tries to find one that leads back to the top of a loop. + /// + /// Reloop edges are good candidates for counter expressions, because they + /// will tend to be executed more times than a loop-exit edge, so it's nice + /// for them to be able to avoid a physical counter increment. fn find_good_reloop_edge( &self, traversal: &TraverseCoverageGraphWithLoops<'_>, - from_bcb: BasicCoverageBlock, + candidate_successors: &[BasicCoverageBlock], ) -> Option<BasicCoverageBlock> { - let successors = self.bcb_successors(from_bcb); + // If there are no candidates, avoid iterating over the loop stack. + if candidate_successors.is_empty() { + return None; + } // Consider each loop on the current traversal context stack, top-down. for reloop_bcbs in traversal.reloop_bcbs_per_loop() { - let mut all_edges_exit_this_loop = true; - - // Try to find an out-edge that doesn't exit this loop and doesn't - // already have a counter. - for &target_bcb in successors { + // Try to find a candidate edge that doesn't exit this loop. + for &target_bcb in candidate_successors { // An edge is a reloop edge if its target dominates any BCB that has // an edge back to the loop header. (Otherwise it's an exit edge.) let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| { self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb) }); - if is_reloop_edge { - all_edges_exit_this_loop = false; - if self.edge_has_no_counter(from_bcb, target_bcb) { - // We found a good out-edge to be given an expression. - return Some(target_bcb); - } - // Keep looking for another reloop edge without a counter. - } else { - // This edge exits the loop. + // We found a good out-edge to be given an expression. + return Some(target_bcb); } } - if !all_edges_exit_this_loop { - // We found one or more reloop edges, but all of them already - // have counters. Let the caller choose one of the other edges. - debug!("All reloop edges had counters; skipping the other loops"); - return None; - } - - // All of the out-edges exit this loop, so keep looking for a good - // reloop edge for one of the outer loops. + // All of the candidate edges exit this loop, so keep looking + // for a good reloop edge for one of the outer loops. } None } #[inline] - fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] { - &self.basic_coverage_blocks.predecessors[bcb] - } - - #[inline] - fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &[BasicCoverageBlock] { - &self.basic_coverage_blocks.successors[bcb] - } - - #[inline] fn edge_has_no_counter( &self, from_bcb: BasicCoverageBlock, to_bcb: BasicCoverageBlock, ) -> bool { - self.edge_counter(from_bcb, to_bcb).is_none() - } + let edge_counter = + if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) { + assert_eq!(sole_pred, from_bcb); + self.coverage_counters.bcb_counters[to_bcb] + } else { + self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied() + }; - fn edge_counter( - &self, - from_bcb: BasicCoverageBlock, - to_bcb: BasicCoverageBlock, - ) -> Option<&BcbCounter> { - if self.basic_coverage_blocks.bcb_has_multiple_in_edges(to_bcb) { - self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)) - } else { - self.coverage_counters.bcb_counters[to_bcb].as_ref() - } + edge_counter.is_none() } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 0d874a6c8ba..743aa679058 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -87,7 +87,11 @@ impl CoverageGraph { for &bb in basic_blocks.iter() { bb_to_bcb[bb] = Some(bcb); } - let bcb_data = BasicCoverageBlockData::from(basic_blocks); + + let is_out_summable = basic_blocks.last().map_or(false, |&bb| { + bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable() + }); + let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable }; debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); bcbs.push(bcb_data); }; @@ -161,23 +165,33 @@ impl CoverageGraph { self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b) } - /// Returns true if the given node has 2 or more in-edges, i.e. 2 or more - /// predecessors. - /// - /// This property is interesting to code that assigns counters to nodes and - /// edges, because if a node _doesn't_ have multiple in-edges, then there's - /// no benefit in having a separate counter for its in-edge, because it - /// would have the same value as the node's own counter. - /// - /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]? - #[inline(always)] - pub(crate) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool { - // Even though bcb0 conceptually has an extra virtual in-edge due to - // being the entry point, we've already asserted that it has no _other_ - // in-edges, so there's no possibility of it having _multiple_ in-edges. - // (And since its virtual in-edge doesn't exist in the graph, that edge - // can't have a separate counter anyway.) - self.predecessors[bcb].len() > 1 + /// Returns the source of this node's sole in-edge, if it has exactly one. + /// That edge can be assumed to have the same execution count as the node + /// itself (in the absence of panics). + pub(crate) fn sole_predecessor( + &self, + to_bcb: BasicCoverageBlock, + ) -> Option<BasicCoverageBlock> { + // Unlike `simple_successor`, there is no need for extra checks here. + if let &[from_bcb] = self.predecessors[to_bcb].as_slice() { Some(from_bcb) } else { None } + } + + /// Returns the target of this node's sole out-edge, if it has exactly + /// one, but only if that edge can be assumed to have the same execution + /// count as the node itself (in the absence of panics). + pub(crate) fn simple_successor( + &self, + from_bcb: BasicCoverageBlock, + ) -> Option<BasicCoverageBlock> { + // If a node's count is the sum of its out-edges, and it has exactly + // one out-edge, then that edge has the same count as the node. + if self.bcbs[from_bcb].is_out_summable + && let &[to_bcb] = self.successors[from_bcb].as_slice() + { + Some(to_bcb) + } else { + None + } } } @@ -266,14 +280,16 @@ rustc_index::newtype_index! { #[derive(Debug, Clone)] pub(crate) struct BasicCoverageBlockData { pub(crate) basic_blocks: Vec<BasicBlock>, + + /// If true, this node's execution count can be assumed to be the sum of the + /// execution counts of all of its **out-edges** (assuming no panics). + /// + /// Notably, this is false for a node ending with [`TerminatorKind::Yield`], + /// because the yielding coroutine might not be resumed. + pub(crate) is_out_summable: bool, } impl BasicCoverageBlockData { - fn from(basic_blocks: Vec<BasicBlock>) -> Self { - assert!(basic_blocks.len() > 0); - Self { basic_blocks } - } - #[inline(always)] pub(crate) fn leader_bb(&self) -> BasicBlock { self.basic_blocks[0] @@ -295,6 +311,9 @@ enum CoverageSuccessors<'a> { Chainable(BasicBlock), /// The block cannot be combined into the same BCB as its successor(s). NotChainable(&'a [BasicBlock]), + /// Yield terminators are not chainable, and their execution count can also + /// differ from the execution count of their out-edge. + Yield(BasicBlock), } impl CoverageSuccessors<'_> { @@ -302,6 +321,17 @@ impl CoverageSuccessors<'_> { match self { Self::Chainable(_) => true, Self::NotChainable(_) => false, + Self::Yield(_) => false, + } + } + + /// Returns true if the terminator itself is assumed to have the same + /// execution count as the sum of its out-edges (assuming no panics). + fn is_out_summable(&self) -> bool { + match self { + Self::Chainable(_) => true, + Self::NotChainable(_) => true, + Self::Yield(_) => false, } } } @@ -312,7 +342,9 @@ impl IntoIterator for CoverageSuccessors<'_> { fn into_iter(self) -> Self::IntoIter { match self { - Self::Chainable(bb) => Some(bb).into_iter().chain((&[]).iter().copied()), + Self::Chainable(bb) | Self::Yield(bb) => { + Some(bb).into_iter().chain((&[]).iter().copied()) + } Self::NotChainable(bbs) => None.into_iter().chain(bbs.iter().copied()), } } @@ -331,7 +363,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera // A yield terminator has exactly 1 successor, but should not be chained, // because its resume edge has a different execution count. - Yield { ref resume, .. } => CoverageSuccessors::NotChainable(std::slice::from_ref(resume)), + Yield { resume, .. } => CoverageSuccessors::Yield(resume), // These terminators have exactly one coverage-relevant successor, // and can be chained into it. @@ -341,15 +373,15 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera | FalseUnwind { real_target: target, .. } | Goto { target } => CoverageSuccessors::Chainable(target), - // A call terminator can normally be chained, except when they have no - // successor because they are known to diverge. + // A call terminator can normally be chained, except when it has no + // successor because it is known to diverge. Call { target: maybe_target, .. } => match maybe_target { Some(target) => CoverageSuccessors::Chainable(target), None => CoverageSuccessors::NotChainable(&[]), }, - // An inline asm terminator can normally be chained, except when it diverges or uses asm - // goto. + // An inline asm terminator can normally be chained, except when it + // diverges or uses asm goto. InlineAsm { ref targets, .. } => { if let [target] = targets[..] { CoverageSuccessors::Chainable(target) diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index ce109ef7674..42cbece32d8 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -24,7 +24,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // This just reproduces the logic from Instance::requires_inline. match tcx.def_kind(def_id) { - DefKind::Ctor(..) | DefKind::Closure => return true, + DefKind::Ctor(..) | DefKind::Closure | DefKind::SyntheticCoroutineBody => return true, DefKind::Fn | DefKind::AssocFn => {} _ => return false, } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 870cb180ce1..2de75e2ef50 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -357,16 +357,6 @@ impl<'tcx> Inliner<'tcx> { } if callee_def_id.is_local() { - // Avoid a cycle here by only using `instance_mir` only if we have - // a lower `DefPathHash` than the callee. This ensures that the callee will - // not inline us. This trick even works with incremental compilation, - // since `DefPathHash` is stable. - if self.tcx.def_path_hash(caller_def_id).local_hash() - < self.tcx.def_path_hash(callee_def_id).local_hash() - { - return Ok(()); - } - // If we know for sure that the function we're calling will itself try to // call us, then we avoid inlining that function. if self.tcx.mir_callgraph_reachable((callee, caller_def_id.expect_local())) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 424e7008326..0868f4b3d88 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -21,9 +21,8 @@ use rustc_const_eval::util; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::steal::Steal; use rustc_hir as hir; -use rustc_hir::def::DefKind; +use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; use rustc_index::IndexVec; use rustc_middle::mir::{ AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl, @@ -224,26 +223,31 @@ fn is_mir_available(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// MIR associated with them. fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> { // All body-owners have MIR associated with them. - let set: FxIndexSet<_> = tcx.hir().body_owners().collect(); + let mut set: FxIndexSet<_> = tcx.hir().body_owners().collect(); - // Additionally, tuple struct/variant constructors have MIR, but - // they don't have a BodyId, so we need to build them separately. - struct GatherCtors { - set: FxIndexSet<LocalDefId>, + // Coroutine-closures (e.g. async closures) have an additional by-move MIR + // body that isn't in the HIR. + for body_owner in tcx.hir().body_owners() { + if let DefKind::Closure = tcx.def_kind(body_owner) + && tcx.needs_coroutine_by_move_body_def_id(body_owner.to_def_id()) + { + set.insert(tcx.coroutine_by_move_body_def_id(body_owner).expect_local()); + } } - impl<'tcx> Visitor<'tcx> for GatherCtors { - fn visit_variant_data(&mut self, v: &'tcx hir::VariantData<'tcx>) { - if let hir::VariantData::Tuple(_, _, def_id) = *v { - self.set.insert(def_id); + + // tuple struct/variant constructors have MIR, but they don't have a BodyId, + // so we need to build them separately. + for item in tcx.hir_crate_items(()).free_items() { + if let DefKind::Struct | DefKind::Enum = tcx.def_kind(item.owner_id) { + for variant in tcx.adt_def(item.owner_id).variants() { + if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { + set.insert(ctor_def_id.expect_local()); + } } - intravisit::walk_struct_def(self, v) } } - let mut gather_ctors = GatherCtors { set }; - tcx.hir().visit_all_item_likes_in_crate(&mut gather_ctors); - - gather_ctors.set + set } fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs { diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 5acfec3dee3..4d743c05190 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -157,6 +157,17 @@ where }, ); + // HACK: We bail with overflow if the response would have too many non-region + // inference variables. This tends to only happen if we encounter a lot of + // ambiguous alias types which get replaced with fresh inference variables + // during generalization. This prevents a hang in nalgebra. + let num_non_region_vars = canonical.variables.iter().filter(|c| !c.is_region()).count(); + if num_non_region_vars > self.cx().recursion_limit() { + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { + suggest_increasing_limit: true, + })); + } + Ok(canonical) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ec9a676ea31..6cb851eb8df 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -670,7 +670,7 @@ parse_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call parse_parenthesized_lifetime = parenthesized lifetime bounds are not supported parse_parenthesized_lifetime_suggestion = remove the parentheses -parse_path_single_colon = path separator must be a double colon +parse_path_double_colon = path separator must be a double colon .suggestion = use a double colon instead parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies @@ -803,15 +803,17 @@ parse_unexpected_expr_in_pat = expected {$is_bound -> [true] a pattern range bound *[false] a pattern - }, found {$is_method_call -> - [true] a method call - *[false] an expression - } + }, found an expression + + .label = arbitrary expressions are not allowed in patterns + +parse_unexpected_expr_in_pat_const_sugg = consider extracting the expression into a `const` + +parse_unexpected_expr_in_pat_create_guard_sugg = consider moving the expression to a match arm guard + +parse_unexpected_expr_in_pat_inline_const_sugg = consider wrapping the expression in an inline `const` (requires `{"#"}![feature(inline_const_pat)]`) - .label = {$is_method_call -> - [true] method calls - *[false] arbitrary expressions - } are not allowed in patterns +parse_unexpected_expr_in_pat_update_guard_sugg = consider moving the expression to the match arm guard parse_unexpected_if_with_if = unexpected `if` in the condition expression .suggestion = remove the `if` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index da1103a4fe5..e9fe2e6c1dd 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1,3 +1,5 @@ +// ignore-tidy-filelength + use std::borrow::Cow; use rustc_ast::token::Token; @@ -1569,7 +1571,7 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword { } #[derive(Diagnostic)] -#[diag(parse_path_single_colon)] +#[diag(parse_path_double_colon)] pub(crate) struct PathSingleColon { #[primary_span] pub span: Span, @@ -1582,6 +1584,14 @@ pub(crate) struct PathSingleColon { } #[derive(Diagnostic)] +#[diag(parse_path_double_colon)] +pub(crate) struct PathTripleColon { + #[primary_span] + #[suggestion(applicability = "maybe-incorrect", code = "", style = "verbose")] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(parse_colon_as_semi)] pub(crate) struct ColonAsSemi { #[primary_span] @@ -2592,13 +2602,86 @@ pub(crate) struct ExpectedCommaAfterPatternField { #[derive(Diagnostic)] #[diag(parse_unexpected_expr_in_pat)] pub(crate) struct UnexpectedExpressionInPattern { + /// The unexpected expr's span. #[primary_span] #[label] pub span: Span, /// Was a `RangePatternBound` expected? pub is_bound: bool, - /// Was the unexpected expression a `MethodCallExpression`? - pub is_method_call: bool, + /// The unexpected expr's precedence (used in match arm guard suggestions). + pub expr_precedence: i8, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnexpectedExpressionInPatternSugg { + #[multipart_suggestion( + parse_unexpected_expr_in_pat_create_guard_sugg, + applicability = "maybe-incorrect" + )] + CreateGuard { + /// Where to put the suggested identifier. + #[suggestion_part(code = "{ident}")] + ident_span: Span, + /// Where to put the match arm. + #[suggestion_part(code = " if {ident} == {expr}")] + pat_hi: Span, + /// The suggested identifier. + ident: String, + /// The unexpected expression. + expr: String, + }, + + #[multipart_suggestion( + parse_unexpected_expr_in_pat_update_guard_sugg, + applicability = "maybe-incorrect" + )] + UpdateGuard { + /// Where to put the suggested identifier. + #[suggestion_part(code = "{ident}")] + ident_span: Span, + /// The beginning of the match arm guard's expression (insert a `(` if `Some`). + #[suggestion_part(code = "(")] + guard_lo: Option<Span>, + /// The end of the match arm guard's expression. + #[suggestion_part(code = "{guard_hi_paren} && {ident} == {expr}")] + guard_hi: Span, + /// Either `")"` or `""`. + guard_hi_paren: &'static str, + /// The suggested identifier. + ident: String, + /// The unexpected expression. + expr: String, + }, + + #[multipart_suggestion( + parse_unexpected_expr_in_pat_const_sugg, + applicability = "has-placeholders" + )] + Const { + /// Where to put the extracted constant declaration. + #[suggestion_part(code = "{indentation}const {ident}: /* Type */ = {expr};\n")] + stmt_lo: Span, + /// Where to put the suggested identifier. + #[suggestion_part(code = "{ident}")] + ident_span: Span, + /// The suggested identifier. + ident: String, + /// The unexpected expression. + expr: String, + /// The statement's block's indentation. + indentation: String, + }, + + #[multipart_suggestion( + parse_unexpected_expr_in_pat_inline_const_sugg, + applicability = "maybe-incorrect" + )] + InlineConst { + #[suggestion_part(code = "const {{ ")] + start_span: Span, + #[suggestion_part(code = " }}")] + end_span: Span, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index b7232ff21ca..898c4779b08 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -299,6 +299,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> { lifetime_name += lifetime_name_without_tick; let sym = Symbol::intern(&lifetime_name); + // Make sure we mark this as a raw identifier. + self.psess.raw_identifier_spans.push(self.mk_sp(start, self.pos)); + token::Lifetime(sym, IdentIsRaw::Yes) } else { // Otherwise, this should be parsed like `'r`. Warn about it though. diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index bee73c58cb7..fd488cf1d31 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -16,7 +16,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ pluralize, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PErr, PResult, - Subdiagnostic, + Subdiagnostic, Suggestions, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::edit_distance::find_best_match_for_name; @@ -775,7 +775,7 @@ impl<'a> Parser<'a> { } // Check for misspelled keywords if there are no suggestions added to the diagnostic. - if err.suggestions.as_ref().is_ok_and(|code_suggestions| code_suggestions.is_empty()) { + if matches!(&err.suggestions, Suggestions::Enabled(list) if list.is_empty()) { self.check_for_misspelled_kw(&mut err, &expected); } Err(err) @@ -803,6 +803,9 @@ impl<'a> Parser<'a> { && let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords) { err.subdiagnostic(misspelled_kw); + // We don't want other suggestions to be added as they are most likely meaningless + // when there is a misspelled keyword. + err.seal_suggestions(); } else if let Some((prev_ident, _)) = self.prev_token.ident() && !prev_ident.is_used_keyword() { @@ -818,6 +821,9 @@ impl<'a> Parser<'a> { // positives like suggesting keyword `for` for `extern crate foo {}`. if let Some(misspelled_kw) = find_similar_kw(prev_ident, &all_keywords) { err.subdiagnostic(misspelled_kw); + // We don't want other suggestions to be added as they are most likely meaningless + // when there is a misspelled keyword. + err.seal_suggestions(); } } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 2d5a1914fa6..f19cba4c1bf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -41,7 +41,7 @@ use super::{ use crate::{errors, maybe_recover_from_interpolated_ty_qpath}; #[derive(Debug)] -enum DestructuredFloat { +pub(super) enum DestructuredFloat { /// 1e2 Single(Symbol, Span), /// 1. @@ -1041,7 +1041,7 @@ impl<'a> Parser<'a> { // support pushing "future tokens" (would be also helpful to `break_and_eat`), or // we should break everything including floats into more basic proc-macro style // tokens in the lexer (probably preferable). - fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat { + pub(super) fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat { #[derive(Debug)] enum FloatComponent { IdentLike(String), diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 104678e081c..afd9871a635 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -707,7 +707,7 @@ impl<'a> Parser<'a> { }) }; - let (ident, item_kind) = if self.eat(&token::PathSep) { + let (ident, item_kind) = if self.eat_path_sep() { let suffixes = if self.eat(&token::BinOp(token::Star)) { None } else { @@ -1054,7 +1054,7 @@ impl<'a> Parser<'a> { { // `use *;` or `use ::*;` or `use {...};` or `use ::{...};` let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat(&token::PathSep) { + if self.eat_path_sep() { prefix .segments .push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); @@ -1065,7 +1065,7 @@ impl<'a> Parser<'a> { // `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;` prefix = self.parse_path(PathStyle::Mod)?; - if self.eat(&token::PathSep) { + if self.eat_path_sep() { self.parse_use_tree_glob_or_nested()? } else { // Recover from using a colon as path separator. diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 9d9265d5318..3b58b2337f3 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1562,12 +1562,25 @@ impl<'a> Parser<'a> { }) } + /// Checks for `::` or, potentially, `:::` and then look ahead after it. + fn check_path_sep_and_look_ahead(&mut self, looker: impl Fn(&Token) -> bool) -> bool { + if self.check(&token::PathSep) { + if self.may_recover() && self.look_ahead(1, |t| t.kind == token::Colon) { + debug_assert!(!self.look_ahead(1, &looker), "Looker must not match on colon"); + self.look_ahead(2, looker) + } else { + self.look_ahead(1, looker) + } + } else { + false + } + } + /// `::{` or `::*` fn is_import_coupler(&mut self) -> bool { - self.check(&token::PathSep) - && self.look_ahead(1, |t| { - *t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star) - }) + self.check_path_sep_and_look_ahead(|t| { + matches!(t.kind, token::OpenDelim(Delimiter::Brace) | token::BinOp(token::Star)) + }) } // Debug view of the parser's token stream, up to `{lookahead}` tokens. diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index daced411b8f..647df25c82e 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,12 +1,14 @@ -use rustc_ast::mut_visit::{walk_pat, MutVisitor}; +use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; +use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ - self as ast, AttrVec, BindingMode, ByRef, Expr, ExprKind, MacCall, Mutability, Pat, PatField, - PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, + self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, ExprPrecedence, + LocalKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, + RangeSyntax, Stmt, StmtKind, }; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, Diag, PResult}; +use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::{respan, Spanned}; use rustc_span::symbol::{kw, sym, Ident}; @@ -21,11 +23,11 @@ use crate::errors::{ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern, ParenRangeSuggestion, PatternOnWrongSideOfAt, RemoveLet, RepeatedMutInPattern, SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg, - TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedLifetimeInPattern, - UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, + TrailingVertNotAllowed, UnexpectedExpressionInPattern, UnexpectedExpressionInPatternSugg, + UnexpectedLifetimeInPattern, UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens, }; -use crate::parser::expr::could_be_unclosed_char_literal; +use crate::parser::expr::{could_be_unclosed_char_literal, DestructuredFloat}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; #[derive(PartialEq, Copy, Clone)] @@ -342,7 +344,7 @@ impl<'a> Parser<'a> { } } - /// Ensures that the last parsed pattern (or pattern range bound) is not followed by a method call or an operator. + /// Ensures that the last parsed pattern (or pattern range bound) is not followed by an expression. /// /// `is_end_bound` indicates whether the last parsed thing was the end bound of a range pattern (see [`parse_pat_range_end`](Self::parse_pat_range_end)) /// in order to say "expected a pattern range bound" instead of "expected a pattern"; @@ -350,38 +352,64 @@ impl<'a> Parser<'a> { /// 0..=1 + 2 /// ^^^^^ /// ``` - /// Only the end bound is spanned, and this function have no idea if there were a `..=` before `pat_span`, hence the parameter. + /// Only the end bound is spanned in this case, and this function has no idea if there was a `..=` before `pat_span`, hence the parameter. + /// + /// This function returns `Some` if a trailing expression was recovered, and said expression's span. #[must_use = "the pattern must be discarded as `PatKind::Err` if this function returns Some"] fn maybe_recover_trailing_expr( &mut self, pat_span: Span, is_end_bound: bool, - ) -> Option<ErrorGuaranteed> { + ) -> Option<(ErrorGuaranteed, Span)> { if self.prev_token.is_keyword(kw::Underscore) || !self.may_recover() { // Don't recover anything after an `_` or if recovery is disabled. return None; } - // Check for `.hello()`, but allow `.Hello()` to be recovered as `, Hello()` in `parse_seq_to_before_tokens()`. - let has_trailing_method = self.check_noexpect(&token::Dot) + // Returns `true` iff `token` is an unsuffixed integer. + let is_one_tuple_index = |_: &Self, token: &Token| -> bool { + use token::{Lit, LitKind}; + + matches!( + token.kind, + token::Literal(Lit { kind: LitKind::Integer, symbol: _, suffix: None }) + ) + }; + + // Returns `true` iff `token` is an unsuffixed `x.y` float. + let is_two_tuple_indexes = |this: &Self, token: &Token| -> bool { + use token::{Lit, LitKind}; + + if let token::Literal(Lit { kind: LitKind::Float, symbol, suffix: None }) = token.kind + && let DestructuredFloat::MiddleDot(..) = this.break_up_float(symbol, token.span) + { + true + } else { + false + } + }; + + // Check for `.hello` or `.0`. + let has_dot_expr = self.check_noexpect(&token::Dot) // `.` && self.look_ahead(1, |tok| { - tok.ident() - .and_then(|(ident, _)| ident.name.as_str().chars().next()) - .is_some_and(char::is_lowercase) - }) - && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Parenthesis)); + tok.is_ident() // `hello` + || is_one_tuple_index(&self, &tok) // `0` + || is_two_tuple_indexes(&self, &tok) // `0.0` + }); // Check for operators. // `|` is excluded as it is used in pattern alternatives and lambdas, // `?` is included for error propagation, // `[` is included for indexing operations, - // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`) + // `[]` is excluded as `a[]` isn't an expression and should be recovered as `a, []` (cf. `tests/ui/parser/pat-lt-bracket-7.rs`), + // `as` is included for type casts let has_trailing_operator = matches!(self.token.kind, token::BinOp(op) if op != BinOpToken::Or) || self.token == token::Question || (self.token == token::OpenDelim(Delimiter::Bracket) - && self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket))); + && self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket))) // excludes `[]` + || self.token.is_keyword(kw::As); - if !has_trailing_method && !has_trailing_operator { + if !has_dot_expr && !has_trailing_operator { // Nothing to recover here. return None; } @@ -391,44 +419,248 @@ impl<'a> Parser<'a> { snapshot.restrictions.insert(Restrictions::IS_PAT); // Parse `?`, `.f`, `(arg0, arg1, ...)` or `[expr]` until they've all been eaten. - if let Ok(expr) = snapshot + let Ok(expr) = snapshot .parse_expr_dot_or_call_with( AttrVec::new(), self.mk_expr(pat_span, ExprKind::Dummy), // equivalent to transforming the parsed pattern into an `Expr` pat_span, ) .map_err(|err| err.cancel()) - { - let non_assoc_span = expr.span; + else { + // We got a trailing method/operator, but that wasn't an expression. + return None; + }; - // Parse an associative expression such as `+ expr`, `% expr`, ... - // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - if let Ok((expr, _)) = - snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) - { - // We got a valid expression. - self.restore_snapshot(snapshot); - self.restrictions.remove(Restrictions::IS_PAT); + // Parse an associative expression such as `+ expr`, `% expr`, ... + // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. + let Ok((expr, _)) = + snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) + else { + // We got a trailing method/operator, but that wasn't an expression. + return None; + }; - let is_bound = is_end_bound - // is_start_bound: either `..` or `)..` - || self.token.is_range_separator() - || self.token == token::CloseDelim(Delimiter::Parenthesis) - && self.look_ahead(1, Token::is_range_separator); + // We got a valid expression. + self.restore_snapshot(snapshot); + self.restrictions.remove(Restrictions::IS_PAT); - // Check that `parse_expr_assoc_with` didn't eat a rhs. - let is_method_call = has_trailing_method && non_assoc_span == expr.span; + let is_bound = is_end_bound + // is_start_bound: either `..` or `)..` + || self.token.is_range_separator() + || self.token == token::CloseDelim(Delimiter::Parenthesis) + && self.look_ahead(1, Token::is_range_separator); - return Some(self.dcx().emit_err(UnexpectedExpressionInPattern { - span: expr.span, + let span = expr.span; + + Some(( + self.dcx() + .create_err(UnexpectedExpressionInPattern { + span, is_bound, - is_method_call, - })); + expr_precedence: expr.precedence().order(), + }) + .stash(span, StashKey::ExprInPat) + .unwrap(), + span, + )) + } + + /// Called by [`Parser::parse_stmt_without_recovery`], used to add statement-aware subdiagnostics to the errors stashed + /// by [`Parser::maybe_recover_trailing_expr`]. + pub(super) fn maybe_augment_stashed_expr_in_pats_with_suggestions(&mut self, stmt: &Stmt) { + if self.dcx().has_errors().is_none() { + // No need to walk the statement if there's no stashed errors. + return; + } + + struct PatVisitor<'a> { + /// `self` + parser: &'a Parser<'a>, + /// The freshly-parsed statement. + stmt: &'a Stmt, + /// The current match arm (for arm guard suggestions). + arm: Option<&'a Arm>, + /// The current struct field (for variable name suggestions). + field: Option<&'a PatField>, + } + + impl<'a> PatVisitor<'a> { + /// Looks for stashed [`StashKey::ExprInPat`] errors in `stash_span`, and emit them with suggestions. + /// `stash_span` is contained in `expr_span`, the latter being larger in borrow patterns; + /// ```txt + /// &mut x.y + /// -----^^^ `stash_span` + /// | + /// `expr_span` + /// ``` + /// `is_range_bound` is used to exclude arm guard suggestions in range pattern bounds. + fn maybe_add_suggestions_then_emit( + &self, + stash_span: Span, + expr_span: Span, + is_range_bound: bool, + ) { + self.parser.dcx().try_steal_modify_and_emit_err( + stash_span, + StashKey::ExprInPat, + |err| { + // Includes pre-pats (e.g. `&mut <err>`) in the diagnostic. + err.span.replace(stash_span, expr_span); + + let sm = self.parser.psess.source_map(); + let stmt = self.stmt; + let line_lo = sm.span_extend_to_line(stmt.span).shrink_to_lo(); + let indentation = sm.indentation_before(stmt.span).unwrap_or_default(); + let Ok(expr) = self.parser.span_to_snippet(expr_span) else { + // FIXME: some suggestions don't actually need the snippet; see PR #123877's unresolved conversations. + return; + }; + + if let StmtKind::Let(local) = &stmt.kind { + match &local.kind { + LocalKind::Decl | LocalKind::Init(_) => { + // It's kinda hard to guess what the user intended, so don't make suggestions. + return; + } + + LocalKind::InitElse(_, _) => {} + } + } + + // help: use an arm guard `if val == expr` + // FIXME(guard_patterns): suggest this regardless of a match arm. + if let Some(arm) = &self.arm + && !is_range_bound + { + let (ident, ident_span) = match self.field { + Some(field) => { + (field.ident.to_string(), field.ident.span.to(expr_span)) + } + None => ("val".to_owned(), expr_span), + }; + + // Are parentheses required around `expr`? + // HACK: a neater way would be preferable. + let expr = match &err.args["expr_precedence"] { + DiagArgValue::Number(expr_precedence) => { + if *expr_precedence + <= ExprPrecedence::Binary(BinOpKind::Eq).order() as i32 + { + format!("({expr})") + } else { + format!("{expr}") + } + } + _ => unreachable!(), + }; + + match &arm.guard { + None => { + err.subdiagnostic( + UnexpectedExpressionInPatternSugg::CreateGuard { + ident_span, + pat_hi: arm.pat.span.shrink_to_hi(), + ident, + expr, + }, + ); + } + Some(guard) => { + // Are parentheses required around the old guard? + let wrap_guard = guard.precedence().order() + <= ExprPrecedence::Binary(BinOpKind::And).order(); + + err.subdiagnostic( + UnexpectedExpressionInPatternSugg::UpdateGuard { + ident_span, + guard_lo: if wrap_guard { + Some(guard.span.shrink_to_lo()) + } else { + None + }, + guard_hi: guard.span.shrink_to_hi(), + guard_hi_paren: if wrap_guard { ")" } else { "" }, + ident, + expr, + }, + ); + } + } + } + + // help: extract the expr into a `const VAL: _ = expr` + let ident = match self.field { + Some(field) => field.ident.as_str().to_uppercase(), + None => "VAL".to_owned(), + }; + err.subdiagnostic(UnexpectedExpressionInPatternSugg::Const { + stmt_lo: line_lo, + ident_span: expr_span, + expr, + ident, + indentation, + }); + + // help: wrap the expr in a `const { expr }` + // FIXME(inline_const_pat): once stabilized, remove this check and remove the `(requires #[feature(inline_const_pat)])` note from the message + if self.parser.psess.unstable_features.is_nightly_build() { + err.subdiagnostic(UnexpectedExpressionInPatternSugg::InlineConst { + start_span: expr_span.shrink_to_lo(), + end_span: expr_span.shrink_to_hi(), + }); + } + }, + ); } } - // We got a trailing method/operator, but we couldn't parse an expression. - None + impl<'a> Visitor<'a> for PatVisitor<'a> { + fn visit_arm(&mut self, a: &'a Arm) -> Self::Result { + self.arm = Some(a); + visit::walk_arm(self, a); + self.arm = None; + } + + fn visit_pat_field(&mut self, fp: &'a PatField) -> Self::Result { + self.field = Some(fp); + visit::walk_pat_field(self, fp); + self.field = None; + } + + fn visit_pat(&mut self, p: &'a Pat) -> Self::Result { + match &p.kind { + // Base expression + PatKind::Err(_) | PatKind::Lit(_) => { + self.maybe_add_suggestions_then_emit(p.span, p.span, false) + } + + // Sub-patterns + // FIXME: this doesn't work with recursive subpats (`&mut &mut <err>`) + PatKind::Box(subpat) | PatKind::Ref(subpat, _) + if matches!(subpat.kind, PatKind::Err(_) | PatKind::Lit(_)) => + { + self.maybe_add_suggestions_then_emit(subpat.span, p.span, false) + } + + // Sub-expressions + PatKind::Range(start, end, _) => { + if let Some(start) = start { + self.maybe_add_suggestions_then_emit(start.span, start.span, true); + } + + if let Some(end) = end { + self.maybe_add_suggestions_then_emit(end.span, end.span, true); + } + } + + // Walk continuation + _ => visit::walk_pat(self, p), + } + } + } + + // Starts the visit. + PatVisitor { parser: self, stmt, arm: None, field: None }.visit_stmt(stmt); } /// Parses a pattern, with a setting whether modern range patterns (e.g., `a..=b`, `a..b` are @@ -544,7 +776,7 @@ impl<'a> Parser<'a> { self.parse_pat_tuple_struct(qself, path)? } else { match self.maybe_recover_trailing_expr(span, false) { - Some(guar) => PatKind::Err(guar), + Some((guar, _)) => PatKind::Err(guar), None => PatKind::Path(qself, path), } } @@ -577,10 +809,10 @@ impl<'a> Parser<'a> { // Try to parse everything else as literal with optional minus match self.parse_literal_maybe_minus() { Ok(begin) => { - let begin = match self.maybe_recover_trailing_expr(begin.span, false) { - Some(guar) => self.mk_expr_err(begin.span, guar), - None => begin, - }; + let begin = self + .maybe_recover_trailing_expr(begin.span, false) + .map(|(guar, sp)| self.mk_expr_err(sp, guar)) + .unwrap_or(begin); match self.parse_range_end() { Some(form) => self.parse_pat_range_begin_with(begin, form)?, @@ -721,7 +953,8 @@ impl<'a> Parser<'a> { // For backward compatibility, `(..)` is a tuple pattern as well. let paren_pattern = fields.len() == 1 && !(matches!(trailing_comma, Trailing::Yes) || fields[0].is_rest()); - if paren_pattern { + + let pat = if paren_pattern { let pat = fields.into_iter().next().unwrap(); let close_paren = self.prev_token.span; @@ -739,7 +972,7 @@ impl<'a> Parser<'a> { }, }); - self.parse_pat_range_begin_with(begin.clone(), form) + self.parse_pat_range_begin_with(begin.clone(), form)? } // recover ranges with parentheses around the `(start)..` PatKind::Err(guar) @@ -754,15 +987,20 @@ impl<'a> Parser<'a> { }, }); - self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form) + self.parse_pat_range_begin_with(self.mk_expr_err(pat.span, *guar), form)? } // (pat) with optional parentheses - _ => Ok(PatKind::Paren(pat)), + _ => PatKind::Paren(pat), } } else { - Ok(PatKind::Tuple(fields)) - } + PatKind::Tuple(fields) + }; + + Ok(match self.maybe_recover_trailing_expr(open_paren.to(self.prev_token.span), false) { + None => pat, + Some((guar, _)) => PatKind::Err(guar), + }) } /// Parse a mutable binding with the `mut` token already eaten. @@ -816,7 +1054,7 @@ impl<'a> Parser<'a> { self.0 = true; *m = Mutability::Mut; } - walk_pat(self, pat); + mut_visit::walk_pat(self, pat); } } @@ -1015,7 +1253,7 @@ impl<'a> Parser<'a> { } Ok(match recovered { - Some(guar) => self.mk_expr_err(bound.span, guar), + Some((guar, sp)) => self.mk_expr_err(sp, guar), None => bound, }) } @@ -1084,7 +1322,7 @@ impl<'a> Parser<'a> { // but not `ident @ subpat` as `subpat` was already checked and `ident` continues with `@`. let pat = if sub.is_none() - && let Some(guar) = self.maybe_recover_trailing_expr(ident.span, false) + && let Some((guar, _)) = self.maybe_recover_trailing_expr(ident.span, false) { PatKind::Err(guar) } else { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 42039c621d6..961679b1f56 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -16,7 +16,7 @@ use tracing::debug; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{Parser, Restrictions, TokenType}; -use crate::errors::PathSingleColon; +use crate::errors::{PathSingleColon, PathTripleColon}; use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma}; use crate::{errors, maybe_whole}; @@ -210,7 +210,7 @@ impl<'a> Parser<'a> { let lo = self.token.span; let mut segments = ThinVec::new(); let mod_sep_ctxt = self.token.span.ctxt(); - if self.eat(&token::PathSep) { + if self.eat_path_sep() { segments.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt))); } self.parse_path_segments(&mut segments, style, ty_generics)?; @@ -246,7 +246,7 @@ impl<'a> Parser<'a> { } segments.push(segment); - if self.is_import_coupler() || !self.eat(&token::PathSep) { + if self.is_import_coupler() || !self.eat_path_sep() { if style == PathStyle::Expr && self.may_recover() && self.token == token::Colon @@ -272,6 +272,18 @@ impl<'a> Parser<'a> { } } + /// Eat `::` or, potentially, `:::`. + #[must_use] + pub(super) fn eat_path_sep(&mut self) -> bool { + let result = self.eat(&token::PathSep); + if result && self.may_recover() { + if self.eat_noexpect(&token::Colon) { + self.dcx().emit_err(PathTripleColon { span: self.prev_token.span }); + } + } + result + } + pub(super) fn parse_path_segment( &mut self, style: PathStyle, @@ -297,9 +309,7 @@ impl<'a> Parser<'a> { Ok( if style == PathStyle::Type && check_args_start(self) - || style != PathStyle::Mod - && self.check(&token::PathSep) - && self.look_ahead(1, |t| is_args_start(t)) + || style != PathStyle::Mod && self.check_path_sep_and_look_ahead(is_args_start) { // We use `style == PathStyle::Expr` to check if this is in a recursion or not. If // it isn't, then we reset the unmatched angle bracket count as we're about to start @@ -310,7 +320,8 @@ impl<'a> Parser<'a> { // Generic arguments are found - `<`, `(`, `::<` or `::(`. // First, eat `::` if it exists. - let _ = self.eat(&token::PathSep); + let _ = self.eat_path_sep(); + let lo = self.token.span; let args = if self.eat_lt() { // `<'a, T, A = U>` diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 26ad39e06cd..92fba89d28a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -29,6 +29,9 @@ use crate::{errors, maybe_whole}; impl<'a> Parser<'a> { /// Parses a statement. This stops just before trailing semicolons on everything but items. /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. + /// + /// If `force_collect` is [`ForceCollect::Yes`], forces collection of tokens regardless of + /// whether or not we have attributes. // Public for rustfmt usage. pub(super) fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> { Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| { @@ -66,7 +69,7 @@ impl<'a> Parser<'a> { }); } - Ok(Some(if self.token.is_keyword(kw::Let) { + let stmt = if self.token.is_keyword(kw::Let) { self.collect_tokens(None, attrs, force_collect, |this, attrs| { this.expect_keyword(kw::Let)?; let local = this.parse_local(attrs)?; @@ -163,7 +166,10 @@ impl<'a> Parser<'a> { } else { self.error_outer_attrs(attrs); return Ok(None); - })) + }; + + self.maybe_augment_stashed_expr_in_pats_with_suggestions(&stmt); + Ok(Some(stmt)) } fn parse_stmt_path_start(&mut self, lo: Span, attrs: AttrWrapper) -> PResult<'a, Stmt> { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ee892c17376..2f4dc8abfcd 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1779,6 +1779,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match hint.name_or_empty() { sym::Rust => { is_explicit_rust = true; + match target { + Target::Struct | Target::Union | Target::Enum => continue, + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: hint.span(), + span, + }); + } + } } sym::C => { is_c = true; diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 312cc3a26ef..4fe11589907 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -2,7 +2,7 @@ use rustc_ast::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; -use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout}; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers}; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; @@ -139,8 +139,6 @@ struct UnwrapLayoutCx<'tcx> { } impl<'tcx> LayoutOfHelpers<'tcx> for UnwrapLayoutCx<'tcx> { - type LayoutOfResult = TyAndLayout<'tcx>; - fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { span_bug!(span, "`#[rustc_layout(..)]` test resulted in `layout_of({ty}) = Err({err})`",); } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4bf2cc287da..ac03a3ac42c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -17,7 +17,7 @@ use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKin use rustc_ast::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey}; +use rustc_errors::{Applicability, DiagArgValue, IntoDiagArg, StashKey, Suggestions}; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS}; use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; @@ -4085,17 +4085,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { err.sort_span = parent_err.sort_span; err.is_lint = parent_err.is_lint.clone(); - // merge the parent's suggestions with the typo suggestions - fn append_result<T, E>(res1: &mut Result<Vec<T>, E>, res2: Result<Vec<T>, E>) { - match res1 { - Ok(vec1) => match res2 { - Ok(mut vec2) => vec1.append(&mut vec2), - Err(e) => *res1 = Err(e), - }, - Err(_) => (), - }; + // merge the parent_err's suggestions with the typo (err's) suggestions + match &mut err.suggestions { + Suggestions::Enabled(typo_suggestions) => match &mut parent_err.suggestions { + Suggestions::Enabled(parent_suggestions) => { + // If both suggestions are enabled, append parent_err's suggestions to err's suggestions. + typo_suggestions.append(parent_suggestions) + } + Suggestions::Sealed(_) | Suggestions::Disabled => { + // If the parent's suggestions are either sealed or disabled, it signifies that + // new suggestions cannot be added or removed from the diagnostic. Therefore, + // we assign both types of suggestions to err's suggestions and discard the + // existing suggestions in err. + err.suggestions = std::mem::take(&mut parent_err.suggestions); + } + }, + Suggestions::Sealed(_) | Suggestions::Disabled => (), } - append_result(&mut err.suggestions, parent_err.suggestions.clone()); parent_err.cancel(); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index a0a0dd058ff..9c2f869f357 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -145,7 +145,7 @@ fn encode_const<'tcx>( let _ = write!(s, "{val}"); } ty::Bool => { - let val = c.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + let val = c.try_to_bool().expect("expected monomorphic const in cfi"); let _ = write!(s, "{val}"); } _ => { @@ -411,7 +411,7 @@ pub fn encode_ty<'tcx>( ty::Array(ty0, len) => { // A<array-length><element-type> - let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); + let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi"); let mut s = String::from("A"); let _ = write!(s, "{len}"); s.push_str(&encode_ty(tcx, *ty0, dict, options)); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 34763adde4f..e4231d75506 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -146,7 +146,10 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> { !is_zst }); if let Some(field) = field { - let ty0 = self.tcx.erase_regions(field.ty(self.tcx, args)); + let ty0 = self.tcx.normalize_erasing_regions( + ty::ParamEnv::reveal_all(), + field.ty(self.tcx, args), + ); // Generalize any repr(transparent) user-defined type that is either a // pointer or reference, and either references itself or any other type that // contains or references itself, to avoid a reference cycle. diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 908d50a041e..1132d0efebf 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1376,7 +1376,7 @@ enum OptionStability { pub struct RustcOptGroup { pub apply: Box<dyn Fn(&mut getopts::Options) -> &mut getopts::Options>, - name: &'static str, + pub name: &'static str, stability: OptionStability, } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cbe2bafef21..de4532bcb99 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -309,6 +309,7 @@ symbols! { RwLockReadGuard, RwLockWriteGuard, Saturating, + SeekFrom, Send, SeqCst, Sized, @@ -342,6 +343,7 @@ symbols! { Upvars, Vec, VecDeque, + Waker, Wrapper, Wrapping, Yield, @@ -488,6 +490,7 @@ symbols! { begin_panic, bench, bin, + binaryheap_iter, bind_by_move_pattern_guards, bindings_after_at, bitand, @@ -500,6 +503,7 @@ symbols! { black_box, block, bool, + bool_then, borrowck_graphviz_format, borrowck_graphviz_postflow, box_new, @@ -511,6 +515,9 @@ symbols! { breakpoint, bridge, bswap, + btreemap_contains_key, + btreemap_insert, + btreeset_iter, builtin_syntax, c, c_str, @@ -678,6 +685,7 @@ symbols! { crt_dash_static: "crt-static", csky_target_feature, cstr_type, + cstring_as_c_str, cstring_type, ctlz, ctlz_nonzero, @@ -833,6 +841,7 @@ symbols! { f16_nan, f16c_target_feature, f32, + f32_epsilon, f32_legacy_const_digits, f32_legacy_const_epsilon, f32_legacy_const_infinity, @@ -849,6 +858,7 @@ symbols! { f32_legacy_const_radix, f32_nan, f64, + f64_epsilon, f64_legacy_const_digits, f64_legacy_const_epsilon, f64_legacy_const_infinity, @@ -886,6 +896,7 @@ symbols! { field, field_init_shorthand, file, + file_options, float, float_to_int_unchecked, floorf128, @@ -971,6 +982,17 @@ symbols! { half_open_range_patterns, half_open_range_patterns_in_slices, hash, + hashmap_contains_key, + hashmap_drain_ty, + hashmap_insert, + hashmap_iter_mut_ty, + hashmap_iter_ty, + hashmap_keys_ty, + hashmap_values_mut_ty, + hashmap_values_ty, + hashset_drain_ty, + hashset_iter, + hashset_iter_ty, hexagon_target_feature, hidden, homogeneous_aggregate, @@ -1049,6 +1071,7 @@ symbols! { inline_const, inline_const_pat, inout, + instant_now, instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, @@ -1077,6 +1100,9 @@ symbols! { item, item_like_imports, iter, + iter_cloned, + iter_copied, + iter_filter, iter_mut, iter_repeat, iterator, @@ -1346,6 +1372,7 @@ symbols! { on, on_unimplemented, opaque, + open_options_new, ops, opt_out_copy, optimize, @@ -1353,10 +1380,14 @@ symbols! { optin_builtin_traits, option, option_env, + option_expect, + option_unwrap, options, or, or_patterns, ord_cmp_method, + os_str_to_os_string, + os_string_as_os_str, other, out, overflow_checks, @@ -1410,14 +1441,19 @@ symbols! { pat_param, patchable_function_entry, path, + path_main_separator, + path_to_pathbuf, + pathbuf_as_path, pattern_complexity, pattern_parentheses, pattern_type, pattern_types, + permissions_from_mode, phantom_data, pic, pie, pin, + pin_ergonomics, platform_intrinsics, plugin, plugin_registrar, @@ -1565,6 +1601,7 @@ symbols! { residual, result, result_ffi_guarantees, + result_ok_method, resume, return_position_impl_trait_in_trait, return_type_notation, @@ -1817,6 +1854,8 @@ symbols! { slice, slice_from_raw_parts, slice_from_raw_parts_mut, + slice_into_vec, + slice_iter, slice_len_fn, slice_patterns, slicing_syntax, @@ -1849,16 +1888,26 @@ symbols! { stop_after_dataflow, store, str, + str_chars, + str_ends_with, str_from_utf8, str_from_utf8_mut, str_from_utf8_unchecked, str_from_utf8_unchecked_mut, + str_len, str_split_whitespace, + str_starts_with, str_trim, str_trim_end, str_trim_start, strict_provenance, + string_as_mut_str, + string_as_str, string_deref_patterns, + string_from_utf8, + string_insert_str, + string_new, + string_push_str, stringify, struct_field_attributes, struct_inherit, @@ -2063,7 +2112,15 @@ symbols! { var, variant_count, vec, + vec_as_mut_slice, + vec_as_slice, + vec_from_elem, + vec_is_empty, vec_macro, + vec_new, + vec_pop, + vec_with_capacity, + vecdeque_iter, version, vfp2, vis, diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 0c97cda81c8..78e6b9ec6e8 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -227,7 +227,11 @@ fn compute_symbol_name<'tcx>( // and we want to be sure to avoid any symbol conflicts here. let is_globally_shared_function = matches!( tcx.def_kind(instance.def_id()), - DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Ctor(..) + DefKind::Fn + | DefKind::AssocFn + | DefKind::Closure + | DefKind::SyntheticCoroutineBody + | DefKind::Ctor(..) ) && matches!( MonoItem::Fn(instance).instantiation_mode(tcx), InstantiationMode::GloballyShared { may_conflict: true } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index ba35a37c32c..79de9bbb351 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -330,8 +330,12 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { ty::Float(FloatTy::F128) => "C4f128", ty::Never => "z", - // Placeholders (should be demangled as `_`). - ty::Param(_) | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => "p", + // Should only be encountered with polymorphization, + // or within the identity-substituted impl header of an + // item nested within an impl item. + ty::Param(_) => "p", + + ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) | ty::Error(_) => bug!(), _ => "", }; @@ -416,12 +420,18 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { // Mangle all nominal types as paths. ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), args) | ty::FnDef(def_id, args) - | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) | ty::Closure(def_id, args) | ty::CoroutineClosure(def_id, args) | ty::Coroutine(def_id, args) => { self.print_def_path(def_id, args)?; } + + // We may still encounter projections here due to the printing + // logic sometimes passing identity-substituted impl headers. + ty::Alias(ty::Projection, ty::AliasTy { def_id, args, .. }) => { + self.print_def_path(def_id, args)?; + } + ty::Foreign(def_id) => { self.print_def_path(def_id, &[])?; } @@ -467,8 +477,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { r.print(self)?; } - ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"), - ty::Alias(ty::Weak, _) => bug!("symbol_names: unexpected weak projection"), + ty::Alias(..) => bug!("symbol_names: unexpected alias"), ty::CoroutineWitness(..) => bug!("symbol_names: unexpected `CoroutineWitness`"), } @@ -550,21 +559,26 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> { let (ct_ty, valtree) = match ct.kind() { ty::ConstKind::Value(ty, val) => (ty, val), - // Placeholders (should be demangled as `_`). - // NOTE(eddyb) despite `Unevaluated` having a `DefId` (and therefore - // a path), even for it we still need to encode a placeholder, as - // the path could refer back to e.g. an `impl` using the constant. - ty::ConstKind::Unevaluated(_) - | ty::ConstKind::Expr(_) - | ty::ConstKind::Param(_) - | ty::ConstKind::Infer(_) - | ty::ConstKind::Bound(..) - | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Error(_) => { + // Should only be encountered with polymorphization, + // or within the identity-substituted impl header of an + // item nested within an impl item. + ty::ConstKind::Param(_) => { // Never cached (single-character). self.push("p"); return Ok(()); } + + // We may still encounter unevaluated consts due to the printing + // logic sometimes passing identity-substituted impl headers. + ty::Unevaluated(ty::UnevaluatedConst { def, args, .. }) => { + return self.print_def_path(def, args); + } + + ty::ConstKind::Expr(_) + | ty::ConstKind::Infer(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Error(_) => bug!(), }; if let Some(&i) = self.consts.get(&ct) { diff --git a/compiler/rustc_target/src/abi/call/m68k.rs b/compiler/rustc_target/src/abi/call/m68k.rs index b6bd68b66fc..82fe81f8c52 100644 --- a/compiler/rustc_target/src/abi/call/m68k.rs +++ b/compiler/rustc_target/src/abi/call/m68k.rs @@ -14,7 +14,7 @@ fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) { return; } if arg.layout.is_aggregate() { - arg.make_indirect_byval(None); + arg.pass_by_stack_offset(None); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index c2826b55dc5..060ee4a1bc2 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -64,7 +64,7 @@ pub enum PassMode { /// (which ensures that padding is preserved and that we do not rely on LLVM's struct layout), /// and will use the alignment specified in `attrs.pointee_align` (if `Some`) or the type's /// alignment (if `None`). This means that the alignment will not always - /// match the Rust type's alignment; see documentation of `make_indirect_byval` for more info. + /// match the Rust type's alignment; see documentation of `pass_by_stack_offset` for more info. /// /// `on_stack` cannot be true for unsized arguments, i.e., when `meta_attrs` is `Some`. Indirect { attrs: ArgAttributes, meta_attrs: Option<ArgAttributes>, on_stack: bool }, @@ -681,7 +681,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> { /// either in the caller (if the type's alignment is lower than the byval alignment) /// or in the callee (if the type's alignment is higher than the byval alignment), /// to ensure that Rust code never sees an underaligned pointer. - pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) { + pub fn pass_by_stack_offset(&mut self, byval_align: Option<Align>) { assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); self.make_indirect(); match self.mode { @@ -879,8 +879,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { { if abi == spec::abi::Abi::X86Interrupt { if let Some(arg) = self.args.first_mut() { - // FIXME(pcwalton): This probably should use the x86 `byval` ABI... - arg.make_indirect_byval(None); + arg.pass_by_stack_offset(None); } return Ok(()); } diff --git a/compiler/rustc_target/src/abi/call/wasm.rs b/compiler/rustc_target/src/abi/call/wasm.rs index 4ae8b9490dd..3c4cd76a754 100644 --- a/compiler/rustc_target/src/abi/call/wasm.rs +++ b/compiler/rustc_target/src/abi/call/wasm.rs @@ -40,7 +40,7 @@ where } arg.extend_integer_width_to(32); if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { - arg.make_indirect_byval(None); + arg.make_indirect(); } } diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index bdb14350ded..d9af83d3205 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -122,7 +122,7 @@ where align_4 }; - arg.make_indirect_byval(Some(byval_align)); + arg.pass_by_stack_offset(Some(byval_align)); } else { arg.extend_integer_width_to(32); } diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs index 1209204debf..9910e623ac9 100644 --- a/compiler/rustc_target/src/abi/call/x86_64.rs +++ b/compiler/rustc_target/src/abi/call/x86_64.rs @@ -219,7 +219,7 @@ where if is_arg { // The x86_64 ABI doesn't have any special requirements for `byval` alignment, // the type's alignment is always used. - arg.make_indirect_byval(None); + arg.pass_by_stack_offset(None); } else { // `sret` parameter thus one less integer register available arg.make_indirect(); diff --git a/compiler/rustc_target/src/abi/call/xtensa.rs b/compiler/rustc_target/src/abi/call/xtensa.rs index d7b5fe9d4cc..e1728b08a39 100644 --- a/compiler/rustc_target/src/abi/call/xtensa.rs +++ b/compiler/rustc_target/src/abi/call/xtensa.rs @@ -68,7 +68,7 @@ where *arg_gprs_left -= needed_arg_gprs; if must_use_stack { - arg.make_indirect_byval(None); + arg.pass_by_stack_offset(None); } else if is_xtensa_aggregate(arg) { // Aggregates which are <= max_size will be passed in // registers if possible, so coerce to integers. diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f12e3e595ad..1d478f84c43 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1714,8 +1714,10 @@ supported_targets! { ("x86_64-apple-ios-macabi", x86_64_apple_ios_macabi), ("aarch64-apple-ios-macabi", aarch64_apple_ios_macabi), ("aarch64-apple-ios-sim", aarch64_apple_ios_sim), + ("aarch64-apple-tvos", aarch64_apple_tvos), ("aarch64-apple-tvos-sim", aarch64_apple_tvos_sim), + ("arm64e-apple-tvos", arm64e_apple_tvos), ("x86_64-apple-tvos", x86_64_apple_tvos), ("armv7k-apple-watchos", armv7k_apple_watchos), diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs new file mode 100644 index 00000000000..d4d66c92857 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs @@ -0,0 +1,24 @@ +use crate::spec::base::apple::{base, Arch, TargetAbi}; +use crate::spec::{FramePointer, Target, TargetOptions}; + +pub(crate) fn target() -> Target { + let (opts, llvm_target, arch) = base("tvos", Arch::Arm64e, TargetAbi::Normal); + Target { + llvm_target, + metadata: crate::spec::TargetMetadata { + description: Some("ARM64e Apple tvOS".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:o-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch, + options: TargetOptions { + features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+pauth".into(), + max_atomic_width: Some(128), + frame_pointer: FramePointer::NonLeaf, + ..opts + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs index e0a6c230fd6..71086daaf2c 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_gnu.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { // ABI. Pass the -vector feature string to LLVM to respect this assumption. On LLVM < 16, we // also strip v128 from the data_layout below to match the older LLVM's expectation. base.features = "-vector".into(); - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); base.min_global_align = Some(16); base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = diff --git a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs index 6aabe9ca519..016ff5abe4c 100644 --- a/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/s390x_unknown_linux_musl.rs @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { // ABI. Pass the -vector feature string to LLVM to respect this assumption. On LLVM < 16, we // also strip v128 from the data_layout below to match the older LLVM's expectation. base.features = "-vector".into(); - base.max_atomic_width = Some(64); + base.max_atomic_width = Some(128); base.min_global_align = Some(16); base.static_position_independent_executables = true; base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5918686213a..2de6ee9cf91 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, - StringPart, + StringPart, Suggestions, }; use rustc_hir::def::Namespace; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; @@ -1669,6 +1669,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let name = self.tcx.crate_name(trait_def_id.krate); let spans: Vec<_> = [trait_def_id, found_type] .into_iter() + .filter(|def_id| def_id.krate != LOCAL_CRATE) .filter_map(|def_id| self.tcx.extern_crate(def_id.krate)) .map(|data| { let dependency = if data.dependency_of == LOCAL_CRATE { @@ -2136,8 +2137,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(span) = err.span.primary_span() && let Some(mut diag) = self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) - && let Ok(ref mut s1) = err.suggestions - && let Ok(ref mut s2) = diag.suggestions + && let Suggestions::Enabled(ref mut s1) = err.suggestions + && let Suggestions::Enabled(ref mut s2) = diag.suggestions { s1.append(s2); diag.cancel() diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c82eaa5143d..538e23f4449 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -409,6 +409,9 @@ pub fn normalize_param_env_or_error<'tcx>( debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); let elaborated_env = ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()); + if !normalize::needs_normalization(&elaborated_env, unnormalized_env.reveal()) { + return elaborated_env; + } // HACK: we are trying to normalize the param-env inside *itself*. The problem is that // normalization expects its param-env to be already normalized, which means we have diff --git a/config.example.toml b/config.example.toml index c66d65e639a..47ebb20d8fa 100644 --- a/config.example.toml +++ b/config.example.toml @@ -53,7 +53,7 @@ # # Note that many of the LLVM options are not currently supported for # downloading. Currently only the "assertions" option can be toggled. -#download-ci-llvm = if rust.channel == "dev" || rust.download-rustc != false { "if-unchanged" } else { false } +#download-ci-llvm = true # Indicates whether the LLVM build is a Release or Debug build #optimize = true diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index de58b06545b..a91659b6de5 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -171,7 +171,13 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { - unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) } + unsafe { + // Make sure we don't accidentally allow omitting the allocator shim in + // stable code until it is actually stabilized. + core::ptr::read_volatile(&__rust_no_alloc_shim_is_unstable); + + __rust_alloc_zeroed(layout.size(), layout.align()) + } } #[cfg(not(test))] diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index fe9f1010d32..a19a044fc7e 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -959,6 +959,7 @@ impl<T, A: Allocator> BinaryHeap<T, A> { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "binaryheap_iter")] pub fn iter(&self) -> Iter<'_, T> { Iter { iter: self.data.iter() } } diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 60e08b47e3d..0eadc9ecac8 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -916,6 +916,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> { /// assert_eq!(map.contains_key(&2), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "btreemap_contains_key")] pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool where K: Borrow<Q> + Ord, @@ -981,6 +982,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "put", "set")] + #[cfg_attr(not(test), rustc_diagnostic_item = "btreemap_insert")] pub fn insert(&mut self, key: K, value: V) -> Option<V> where K: Ord, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 2b5bebcd8cd..770cd80ea9c 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1132,6 +1132,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> { /// assert_eq!(set_iter.next(), None); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "btreeset_iter")] pub fn iter(&self) -> Iter<'_, T> { Iter { iter: self.map.keys() } } diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index 9b3c9ac5ce5..c93e5813b11 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::panic::{catch_unwind, AssertUnwindSafe}; use std::thread; diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 8c9db063105..a438517b75b 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1201,6 +1201,7 @@ impl<T, A: Allocator> VecDeque<T, A> { /// assert_eq!(&c[..], b); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_iter")] pub fn iter(&self) -> Iter<'_, T> { let (a, b) = self.as_slices(); Iter::new(a.iter(), b.iter()) diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index f8ce4ca9788..c90679f1797 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use core::iter::TrustedLen; use super::*; diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index e32676a6543..45037aa1615 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -576,6 +576,7 @@ impl CString { #[inline] #[must_use] #[stable(feature = "as_c_str", since = "1.20.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "cstring_as_c_str")] pub fn as_c_str(&self) -> &CStr { &*self } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index f98c0cca1db..f0597f295b3 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -110,7 +110,6 @@ #![feature(const_cow_is_borrowed)] #![feature(const_eval_select)] #![feature(const_heap)] -#![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_write)] #![feature(const_option)] #![feature(const_pin)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 88c7a12db23..b9a92749aae 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -460,42 +460,7 @@ impl<T> Rc<T> { where F: FnOnce(&Weak<T>) -> T, { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new(RcBox { - strong: Cell::new(0), - weak: Cell::new(1), - value: mem::MaybeUninit::<T>::uninit(), - })) - .into(); - - let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: Global }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).value), data); - - let prev_value = (*inner).strong.get(); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - (*inner).strong.set(1); - - Rc::from_inner(init_ptr) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong + Self::new_cyclic_in(data_fn, Global) } /// Constructs a new `Rc` with uninitialized contents. @@ -762,6 +727,84 @@ impl<T, A: Allocator> Rc<T, A> { } } + /// Constructs a new `Rc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Rc<T, A>` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic_in` first allocates the managed allocation for the `Rc<T, A>`, + /// then calls your closure, giving it a `Weak<T, A>` to this allocation, + /// and only afterwards completes the construction of the `Rc<T, A>` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Rc<T, A>` is not fully-constructed until `Rc<T, A>::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak<T, A>`] is dropped normally. + /// + /// # Examples + /// + /// See [`new_cyclic`]. + /// + /// [`new_cyclic`]: Rc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Rc<T, A> + where + F: FnOnce(&Weak<T, A>) -> T, + { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + RcBox { + strong: Cell::new(0), + weak: Cell::new(1), + value: mem::MaybeUninit::<T>::uninit(), + }, + alloc, + )); + let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); + let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).value), data); + + let prev_value = (*inner).strong.get(); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + (*inner).strong.set(1); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + // Calling into_raw_with_allocator has the double effect of giving us back the allocator, + // and forgetting the weak reference. + let alloc = weak.into_raw_with_allocator().1; + + Rc::from_inner_in(init_ptr, alloc) + }; + + strong + } + /// Constructs a new `Rc<T>` in the provided allocator, returning an error if the allocation /// fails /// diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 8cdba166c9d..45fb88969c6 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -496,6 +496,7 @@ impl<T> [T] { #[rustc_allow_incoherent_impl] #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg_attr(not(test), rustc_diagnostic_item = "slice_into_vec")] pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> { // N.B., see the `hack` module in this file for more details. hack::into_vec(self) diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6daab5bc73a..d58a016b502 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -440,6 +440,7 @@ impl String { /// ``` #[inline] #[rustc_const_stable(feature = "const_string_new", since = "1.39.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_new")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> String { @@ -571,6 +572,7 @@ impl String { /// [`into_bytes`]: String::into_bytes #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_from_utf8")] pub fn from_utf8(vec: Vec<u8>) -> Result<String, FromUtf8Error> { match str::from_utf8(&vec) { Ok(..) => Ok(String { vec }), @@ -1073,6 +1075,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_str")] pub fn as_str(&self) -> &str { self } @@ -1092,6 +1095,7 @@ impl String { #[inline] #[must_use] #[stable(feature = "string_as_str", since = "1.7.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_as_mut_str")] pub fn as_mut_str(&mut self) -> &mut str { self } @@ -1111,6 +1115,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("append", "push")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_push_str")] pub fn push_str(&mut self, string: &str) { self.vec.extend_from_slice(string.as_bytes()) } @@ -1745,6 +1750,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[inline] #[stable(feature = "insert_str", since = "1.16.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "string_insert_str")] pub fn insert_str(&mut self, idx: usize, string: &str) { assert!(self.is_char_boundary(idx)); diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 43684f31cb7..4d4e84bfda5 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -450,54 +450,7 @@ impl<T> Arc<T> { where F: FnOnce(&Weak<T>) -> T, { - // Construct the inner in the "uninitialized" state with a single - // weak reference. - let uninit_ptr: NonNull<_> = Box::leak(Box::new(ArcInner { - strong: atomic::AtomicUsize::new(0), - weak: atomic::AtomicUsize::new(1), - data: mem::MaybeUninit::<T>::uninit(), - })) - .into(); - let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast(); - - let weak = Weak { ptr: init_ptr, alloc: Global }; - - // It's important we don't give up ownership of the weak pointer, or - // else the memory might be freed by the time `data_fn` returns. If - // we really wanted to pass ownership, we could create an additional - // weak pointer for ourselves, but this would result in additional - // updates to the weak reference count which might not be necessary - // otherwise. - let data = data_fn(&weak); - - // Now we can properly initialize the inner value and turn our weak - // reference into a strong reference. - let strong = unsafe { - let inner = init_ptr.as_ptr(); - ptr::write(ptr::addr_of_mut!((*inner).data), data); - - // The above write to the data field must be visible to any threads which - // observe a non-zero strong count. Therefore we need at least "Release" ordering - // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. - // - // "Acquire" ordering is not required. When considering the possible behaviours - // of `data_fn` we only need to look at what it could do with a reference to a - // non-upgradeable `Weak`: - // - It can *clone* the `Weak`, increasing the weak reference count. - // - It can drop those clones, decreasing the weak reference count (but never to zero). - // - // These side effects do not impact us in any way, and no other side effects are - // possible with safe code alone. - let prev_value = (*inner).strong.fetch_add(1, Release); - debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); - - Arc::from_inner(init_ptr) - }; - - // Strong references should collectively own a shared weak reference, - // so don't run the destructor for our old weak reference. - mem::forget(weak); - strong + Self::new_cyclic_in(data_fn, Global) } /// Constructs a new `Arc` with uninitialized contents. @@ -781,6 +734,98 @@ impl<T, A: Allocator> Arc<T, A> { } } + /// Constructs a new `Arc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation, + /// to allow you to construct a `T` which holds a weak pointer to itself. + /// + /// Generally, a structure circularly referencing itself, either directly or + /// indirectly, should not hold a strong reference to itself to prevent a memory leak. + /// Using this function, you get access to the weak pointer during the + /// initialization of `T`, before the `Arc<T, A>` is created, such that you can + /// clone and store it inside the `T`. + /// + /// `new_cyclic_in` first allocates the managed allocation for the `Arc<T, A>`, + /// then calls your closure, giving it a `Weak<T, A>` to this allocation, + /// and only afterwards completes the construction of the `Arc<T, A>` by placing + /// the `T` returned from your closure into the allocation. + /// + /// Since the new `Arc<T, A>` is not fully-constructed until `Arc<T, A>::new_cyclic_in` + /// returns, calling [`upgrade`] on the weak reference inside your closure will + /// fail and result in a `None` value. + /// + /// # Panics + /// + /// If `data_fn` panics, the panic is propagated to the caller, and the + /// temporary [`Weak<T>`] is dropped normally. + /// + /// # Example + /// + /// See [`new_cyclic`] + /// + /// [`new_cyclic`]: Arc::new_cyclic + /// [`upgrade`]: Weak::upgrade + #[cfg(not(no_global_oom_handling))] + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Arc<T, A> + where + F: FnOnce(&Weak<T, A>) -> T, + { + // Construct the inner in the "uninitialized" state with a single + // weak reference. + let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in( + ArcInner { + strong: atomic::AtomicUsize::new(0), + weak: atomic::AtomicUsize::new(1), + data: mem::MaybeUninit::<T>::uninit(), + }, + alloc, + )); + let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); + let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast(); + + let weak = Weak { ptr: init_ptr, alloc: alloc }; + + // It's important we don't give up ownership of the weak pointer, or + // else the memory might be freed by the time `data_fn` returns. If + // we really wanted to pass ownership, we could create an additional + // weak pointer for ourselves, but this would result in additional + // updates to the weak reference count which might not be necessary + // otherwise. + let data = data_fn(&weak); + + // Now we can properly initialize the inner value and turn our weak + // reference into a strong reference. + let strong = unsafe { + let inner = init_ptr.as_ptr(); + ptr::write(ptr::addr_of_mut!((*inner).data), data); + + // The above write to the data field must be visible to any threads which + // observe a non-zero strong count. Therefore we need at least "Release" ordering + // in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`. + // + // "Acquire" ordering is not required. When considering the possible behaviours + // of `data_fn` we only need to look at what it could do with a reference to a + // non-upgradeable `Weak`: + // - It can *clone* the `Weak`, increasing the weak reference count. + // - It can drop those clones, decreasing the weak reference count (but never to zero). + // + // These side effects do not impact us in any way, and no other side effects are + // possible with safe code alone. + let prev_value = (*inner).strong.fetch_add(1, Release); + debug_assert_eq!(prev_value, 0, "No prior strong references should exist"); + + // Strong references should collectively own a shared weak reference, + // so don't run the destructor for our old weak reference. + // Calling into_raw_with_allocator has the double effect of giving us back the allocator, + // and forgetting the weak reference. + let alloc = weak.into_raw_with_allocator().1; + + Arc::from_inner_in(init_ptr, alloc) + }; + + strong + } + /// Constructs a new `Pin<Arc<T, A>>` in the provided allocator. If `T` does not implement `Unpin`, /// then `data` will be pinned in memory and unable to be moved. #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 2afb5dd0d1a..1984cfeefc1 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -416,6 +416,7 @@ impl<T> Vec<T> { /// ``` #[inline] #[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_new")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub const fn new() -> Self { @@ -476,6 +477,7 @@ impl<T> Vec<T> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_with_capacity")] pub fn with_capacity(capacity: usize) -> Self { Self::with_capacity_in(capacity, Global) } @@ -1545,6 +1547,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_slice")] pub fn as_slice(&self) -> &[T] { self } @@ -1562,6 +1565,7 @@ impl<T, A: Allocator> Vec<T, A> { /// ``` #[inline] #[stable(feature = "vec_as_slice", since = "1.7.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_as_mut_slice")] pub fn as_mut_slice(&mut self) -> &mut [T] { self } @@ -1580,7 +1584,8 @@ impl<T, A: Allocator> Vec<T, A> { /// /// This method guarantees that for the purpose of the aliasing model, this method /// does not materialize a reference to the underlying slice, and thus the returned pointer - /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], + /// and [`as_non_null`]. /// Note that calling other methods that materialize mutable references to the slice, /// or mutable references to specific elements you are planning on accessing through this pointer, /// as well as writing to those elements, may still invalidate this pointer. @@ -1617,6 +1622,7 @@ impl<T, A: Allocator> Vec<T, A> { /// /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr + /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_never_returns_null_ptr] #[inline] @@ -1636,7 +1642,8 @@ impl<T, A: Allocator> Vec<T, A> { /// /// This method guarantees that for the purpose of the aliasing model, this method /// does not materialize a reference to the underlying slice, and thus the returned pointer - /// will remain valid when mixed with other calls to [`as_ptr`] and [`as_mut_ptr`]. + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], + /// and [`as_non_null`]. /// Note that calling other methods that materialize references to the slice, /// or references to specific elements you are planning on accessing through this pointer, /// may still invalidate this pointer. @@ -1676,6 +1683,7 @@ impl<T, A: Allocator> Vec<T, A> { /// /// [`as_mut_ptr`]: Vec::as_mut_ptr /// [`as_ptr`]: Vec::as_ptr + /// [`as_non_null`]: Vec::as_non_null #[stable(feature = "vec_as_ptr", since = "1.37.0")] #[rustc_never_returns_null_ptr] #[inline] @@ -1685,6 +1693,69 @@ impl<T, A: Allocator> Vec<T, A> { self.buf.ptr() } + /// Returns a `NonNull` pointer to the vector's buffer, or a dangling + /// `NonNull` pointer valid for zero sized reads if the vector didn't allocate. + /// + /// The caller must ensure that the vector outlives the pointer this + /// function returns, or else it will end up dangling. + /// Modifying the vector may cause its buffer to be reallocated, + /// which would also make any pointers to it invalid. + /// + /// This method guarantees that for the purpose of the aliasing model, this method + /// does not materialize a reference to the underlying slice, and thus the returned pointer + /// will remain valid when mixed with other calls to [`as_ptr`], [`as_mut_ptr`], + /// and [`as_non_null`]. + /// Note that calling other methods that materialize references to the slice, + /// or references to specific elements you are planning on accessing through this pointer, + /// may still invalidate this pointer. + /// See the second example below for how this guarantee can be used. + /// + /// # Examples + /// + /// ``` + /// #![feature(box_vec_non_null)] + /// + /// // Allocate vector big enough for 4 elements. + /// let size = 4; + /// let mut x: Vec<i32> = Vec::with_capacity(size); + /// let x_ptr = x.as_non_null(); + /// + /// // Initialize elements via raw pointer writes, then set length. + /// unsafe { + /// for i in 0..size { + /// x_ptr.add(i).write(i as i32); + /// } + /// x.set_len(size); + /// } + /// assert_eq!(&*x, &[0, 1, 2, 3]); + /// ``` + /// + /// Due to the aliasing guarantee, the following code is legal: + /// + /// ```rust + /// #![feature(box_vec_non_null)] + /// + /// unsafe { + /// let mut v = vec![0]; + /// let ptr1 = v.as_non_null(); + /// ptr1.write(1); + /// let ptr2 = v.as_non_null(); + /// ptr2.write(2); + /// // Notably, the write to `ptr2` did *not* invalidate `ptr1`: + /// ptr1.write(3); + /// } + /// ``` + /// + /// [`as_mut_ptr`]: Vec::as_mut_ptr + /// [`as_ptr`]: Vec::as_ptr + /// [`as_non_null`]: Vec::as_non_null + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[inline] + pub fn as_non_null(&mut self) -> NonNull<T> { + // SAFETY: A `Vec` always has a non-null pointer. + unsafe { NonNull::new_unchecked(self.as_mut_ptr()) } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] @@ -2380,6 +2451,7 @@ impl<T, A: Allocator> Vec<T, A> { /// Takes *O*(1) time. #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_pop")] pub fn pop(&mut self) -> Option<T> { if self.len == 0 { None @@ -2573,6 +2645,7 @@ impl<T, A: Allocator> Vec<T, A> { /// assert!(!v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_is_empty")] pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -3044,6 +3117,7 @@ impl<T: PartialEq, A: Allocator> Vec<T, A> { #[doc(hidden)] #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "vec_from_elem")] pub fn from_elem<T: Clone>(elem: T, n: usize) -> Vec<T> { <T as SpecFromElem>::from_elem(elem, n, Global) } diff --git a/library/alloc/tests/fmt.rs b/library/alloc/tests/fmt.rs index ce24a40f4c0..c13074c53b7 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloc/tests/fmt.rs @@ -1,4 +1,6 @@ #![deny(warnings)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] use std::cell::RefCell; use std::fmt::{self, Write}; diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index 3722fb06a6a..cf2ca4f0d65 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use core::alloc::{Allocator, Layout}; use core::num::NonZero; use core::ptr::NonNull; @@ -1284,6 +1287,8 @@ fn test_from_iter_specialization_panic_during_iteration_drops() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[cfg_attr(not(bootstrap), allow(static_mut_refs))] fn test_from_iter_specialization_panic_during_drop_doesnt_leak() { static mut DROP_COUNTER_OLD: [usize; 5] = [0; 5]; static mut DROP_COUNTER_NEW: [usize; 2] = [0; 2]; diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index f32ba8d5aa4..0891d99fe66 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use core::num::NonZero; use std::assert_matches::assert_matches; use std::collections::vec_deque::Drain; diff --git a/library/core/src/alloc/layout.rs b/library/core/src/alloc/layout.rs index ad3f9d80878..107d82267e5 100644 --- a/library/core/src/alloc/layout.rs +++ b/library/core/src/alloc/layout.rs @@ -5,8 +5,10 @@ // Your performance intuition is useless. Run perf. use crate::error::Error; +use crate::intrinsics::{unchecked_add, unchecked_mul, unchecked_sub}; +use crate::mem::SizedTypeProperties; use crate::ptr::{Alignment, NonNull}; -use crate::{assert_unsafe_precondition, cmp, fmt, mem}; +use crate::{assert_unsafe_precondition, fmt, mem}; // While this function is used in one place and its implementation // could be inlined, the previous attempts to do so made rustc @@ -98,7 +100,10 @@ impl Layout { // // Above implies that checking for summation overflow is both // necessary and sufficient. - isize::MAX as usize - (align.as_usize() - 1) + + // SAFETY: the maximum possible alignment is `isize::MAX + 1`, + // so the subtraction cannot overflow. + unsafe { unchecked_sub(isize::MAX as usize + 1, align.as_usize()) } } /// Internal helper constructor to skip revalidating alignment validity. @@ -252,9 +257,14 @@ impl Layout { /// Returns an error if the combination of `self.size()` and the given /// `align` violates the conditions listed in [`Layout::from_size_align`]. #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[inline] - pub fn align_to(&self, align: usize) -> Result<Self, LayoutError> { - Layout::from_size_align(self.size(), cmp::max(self.align(), align)) + pub const fn align_to(&self, align: usize) -> Result<Self, LayoutError> { + if let Some(align) = Alignment::new(align) { + Layout::from_size_alignment(self.size, Alignment::max(self.align, align)) + } else { + Err(LayoutError) + } } /// Returns the amount of padding we must insert after `self` @@ -279,29 +289,42 @@ impl Layout { without modifying the `Layout`"] #[inline] pub const fn padding_needed_for(&self, align: usize) -> usize { - let len = self.size(); + // FIXME: Can we just change the type on this to `Alignment`? + let Some(align) = Alignment::new(align) else { return usize::MAX }; + let len_rounded_up = self.size_rounded_up_to_custom_align(align); + // SAFETY: Cannot overflow because the rounded-up value is never less + unsafe { unchecked_sub(len_rounded_up, self.size) } + } + /// Returns the smallest multiple of `align` greater than or equal to `self.size()`. + /// + /// This can return at most `Alignment::MAX` (aka `isize::MAX + 1`) + /// because the original size is at most `isize::MAX`. + #[inline] + const fn size_rounded_up_to_custom_align(&self, align: Alignment) -> usize { + // SAFETY: // Rounded up value is: - // len_rounded_up = (len + align - 1) & !(align - 1); - // and then we return the padding difference: `len_rounded_up - len`. + // size_rounded_up = (size + align - 1) & !(align - 1); // - // We use modular arithmetic throughout: + // The arithmetic we do here can never overflow: // // 1. align is guaranteed to be > 0, so align - 1 is always // valid. // - // 2. `len + align - 1` can overflow by at most `align - 1`, - // so the &-mask with `!(align - 1)` will ensure that in the - // case of overflow, `len_rounded_up` will itself be 0. - // Thus the returned padding, when added to `len`, yields 0, - // which trivially satisfies the alignment `align`. + // 2. size is at most `isize::MAX`, so adding `align - 1` (which is at + // most `isize::MAX`) can never overflow a `usize`. // - // (Of course, attempts to allocate blocks of memory whose - // size and padding overflow in the above manner should cause - // the allocator to yield an error anyway.) - - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - len_rounded_up.wrapping_sub(len) + // 3. masking by the alignment can remove at most `align - 1`, + // which is what we just added, thus the value we return is never + // less than the original `size`. + // + // (Size 0 Align MAX is already aligned, so stays the same, but things like + // Size 1 Align MAX or Size isize::MAX Align 2 round up to `isize::MAX + 1`.) + unsafe { + let align_m1 = unchecked_sub(align.as_usize(), 1); + let size_rounded_up = unchecked_add(self.size, align_m1) & !align_m1; + size_rounded_up + } } /// Creates a layout by rounding the size of this layout up to a multiple @@ -315,12 +338,11 @@ impl Layout { without modifying the original"] #[inline] pub const fn pad_to_align(&self) -> Layout { - let pad = self.padding_needed_for(self.align()); // This cannot overflow. Quoting from the invariant of Layout: // > `size`, when rounded up to the nearest multiple of `align`, // > must not overflow isize (i.e., the rounded value must be // > less than or equal to `isize::MAX`) - let new_size = self.size() + pad; + let new_size = self.size_rounded_up_to_custom_align(self.align); // SAFETY: padded size is guaranteed to not exceed `isize::MAX`. unsafe { Layout::from_size_align_unchecked(new_size, self.align()) } @@ -333,20 +355,36 @@ impl Layout { /// layout of the array and `offs` is the distance between the start /// of each element in the array. /// + /// (That distance between elements is sometimes known as "stride".) + /// /// On arithmetic overflow, returns `LayoutError`. + /// + /// # Examples + /// + /// ``` + /// #![feature(alloc_layout_extra)] + /// use std::alloc::Layout; + /// + /// // All rust types have a size that's a multiple of their alignment. + /// let normal = Layout::from_size_align(12, 4).unwrap(); + /// let repeated = normal.repeat(3).unwrap(); + /// assert_eq!(repeated, (Layout::from_size_align(36, 4).unwrap(), 12)); + /// + /// // But you can manually make layouts which don't meet that rule. + /// let padding_needed = Layout::from_size_align(6, 4).unwrap(); + /// let repeated = padding_needed.repeat(3).unwrap(); + /// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8)); + /// ``` #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[inline] - pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> { - // This cannot overflow. Quoting from the invariant of Layout: - // > `size`, when rounded up to the nearest multiple of `align`, - // > must not overflow isize (i.e., the rounded value must be - // > less than or equal to `isize::MAX`) - let padded_size = self.size() + self.padding_needed_for(self.align()); - let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?; - - // The safe constructor is called here to enforce the isize size limit. - let layout = Layout::from_size_alignment(alloc_size, self.align)?; - Ok((layout, padded_size)) + pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> { + let padded = self.pad_to_align(); + if let Ok(repeated) = padded.repeat_packed(n) { + Ok((repeated, padded.size())) + } else { + Err(LayoutError) + } } /// Creates a layout describing the record for `self` followed by @@ -395,17 +433,23 @@ impl Layout { /// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16]))); /// ``` #[stable(feature = "alloc_layout_manipulation", since = "1.44.0")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[inline] - pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { - let new_align = cmp::max(self.align, next.align); - let pad = self.padding_needed_for(next.align()); - - let offset = self.size().checked_add(pad).ok_or(LayoutError)?; - let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?; - - // The safe constructor is called here to enforce the isize size limit. - let layout = Layout::from_size_alignment(new_size, new_align)?; - Ok((layout, offset)) + pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> { + let new_align = Alignment::max(self.align, next.align); + let offset = self.size_rounded_up_to_custom_align(next.align); + + // SAFETY: `offset` is at most `isize::MAX + 1` (such as from aligning + // to `Alignment::MAX`) and `next.size` is at most `isize::MAX` (from the + // `Layout` type invariant). Thus the largest possible `new_size` is + // `isize::MAX + 1 + isize::MAX`, which is `usize::MAX`, and cannot overflow. + let new_size = unsafe { unchecked_add(offset, next.size) }; + + if let Ok(layout) = Layout::from_size_alignment(new_size, new_align) { + Ok((layout, offset)) + } else { + Err(LayoutError) + } } /// Creates a layout describing the record for `n` instances of @@ -421,11 +465,15 @@ impl Layout { /// /// On arithmetic overflow, returns `LayoutError`. #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[inline] - pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> { - let size = self.size().checked_mul(n).ok_or(LayoutError)?; - // The safe constructor is called here to enforce the isize size limit. - Layout::from_size_alignment(size, self.align) + pub const fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> { + if let Some(size) = self.size.checked_mul(n) { + // The safe constructor is called here to enforce the isize size limit. + Layout::from_size_alignment(size, self.align) + } else { + Err(LayoutError) + } } /// Creates a layout describing the record for `self` followed by @@ -435,10 +483,13 @@ impl Layout { /// /// On arithmetic overflow, returns `LayoutError`. #[unstable(feature = "alloc_layout_extra", issue = "55724")] + #[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")] #[inline] - pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> { - let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?; - // The safe constructor is called here to enforce the isize size limit. + pub const fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> { + // SAFETY: each `size` is at most `isize::MAX == usize::MAX/2`, so the + // sum is at most `usize::MAX/2*2 == usize::MAX - 1`, and cannot overflow. + let new_size = unsafe { unchecked_add(self.size, next.size) }; + // The safe constructor enforces that the new size isn't too big for the alignment Layout::from_size_alignment(new_size, self.align) } @@ -451,14 +502,12 @@ impl Layout { #[inline] pub const fn array<T>(n: usize) -> Result<Self, LayoutError> { // Reduce the amount of code we need to monomorphize per `T`. - return inner(mem::size_of::<T>(), Alignment::of::<T>(), n); + return inner(T::LAYOUT, n); #[inline] - const fn inner( - element_size: usize, - align: Alignment, - n: usize, - ) -> Result<Layout, LayoutError> { + const fn inner(element_layout: Layout, n: usize) -> Result<Layout, LayoutError> { + let Layout { size: element_size, align } = element_layout; + // We need to check two things about the size: // - That the total size won't overflow a `usize`, and // - That the total size still fits in an `isize`. @@ -473,7 +522,7 @@ impl Layout { // This is a useless hint inside this function, but after inlining this helps // deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's // allocation path) before/after this multiplication. - let array_size = unsafe { element_size.unchecked_mul(n) }; + let array_size = unsafe { unchecked_mul(element_size, n) }; // SAFETY: We just checked above that the `array_size` will not // exceed `isize::MAX` even when rounded up to the alignment. diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 03cdff9b13b..58a870d2e07 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -55,6 +55,7 @@ impl bool { /// assert_eq!(a, 1); /// ``` #[stable(feature = "lazy_bool_to_option", since = "1.50.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "bool_then")] #[inline] pub fn then<T, F: FnOnce() -> T>(self, f: F) -> Option<T> { if self { Some(f()) } else { None } diff --git a/library/core/src/cell/lazy.rs b/library/core/src/cell/lazy.rs index 6ec1d2a33be..5bc13779af9 100644 --- a/library/core/src/cell/lazy.rs +++ b/library/core/src/cell/lazy.rs @@ -1,4 +1,5 @@ use super::UnsafeCell; +use crate::hint::unreachable_unchecked; use crate::ops::Deref; use crate::{fmt, mem}; @@ -82,7 +83,7 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> { match this.state.into_inner() { State::Init(data) => Ok(data), State::Uninit(f) => Err(f), - State::Poisoned => panic!("LazyCell instance has previously been poisoned"), + State::Poisoned => panic_poisoned(), } } @@ -114,7 +115,72 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> { State::Init(data) => data, // SAFETY: The state is uninitialized. State::Uninit(_) => unsafe { LazyCell::really_init(this) }, - State::Poisoned => panic!("LazyCell has previously been poisoned"), + State::Poisoned => panic_poisoned(), + } + } + + /// Forces the evaluation of this lazy value and returns a mutable reference to + /// the result. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// use std::cell::LazyCell; + /// + /// let mut lazy = LazyCell::new(|| 92); + /// + /// let p = LazyCell::force_mut(&mut lazy); + /// assert_eq!(*p, 92); + /// *p = 44; + /// assert_eq!(*lazy, 44); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn force_mut(this: &mut LazyCell<T, F>) -> &mut T { + #[cold] + /// # Safety + /// May only be called when the state is `Uninit`. + unsafe fn really_init_mut<T, F: FnOnce() -> T>(state: &mut State<T, F>) -> &mut T { + // INVARIANT: Always valid, but the value may not be dropped. + struct PoisonOnPanic<T, F>(*mut State<T, F>); + impl<T, F> Drop for PoisonOnPanic<T, F> { + #[inline] + fn drop(&mut self) { + // SAFETY: Invariant states it is valid, and we don't drop the old value. + unsafe { + self.0.write(State::Poisoned); + } + } + } + + let State::Uninit(f) = state else { + // `unreachable!()` here won't optimize out because the function is cold. + // SAFETY: Precondition. + unsafe { unreachable_unchecked() }; + }; + // SAFETY: We never drop the state after we read `f`, and we write a valid value back + // in any case, panic or success. `f` can't access the `LazyCell` because it is mutably + // borrowed. + let f = unsafe { core::ptr::read(f) }; + // INVARIANT: Initiated from mutable reference, don't drop because we read it. + let guard = PoisonOnPanic(state); + let data = f(); + // SAFETY: `PoisonOnPanic` invariant, and we don't drop the old value. + unsafe { + core::ptr::write(guard.0, State::Init(data)); + } + core::mem::forget(guard); + let State::Init(data) = state else { unreachable!() }; + data + } + + let state = this.state.get_mut(); + match state { + State::Init(data) => data, + // SAFETY: `state` is `Uninit`. + State::Uninit(_) => unsafe { really_init_mut(state) }, + State::Poisoned => panic_poisoned(), } } @@ -152,13 +218,55 @@ impl<T, F: FnOnce() -> T> LazyCell<T, F> { } impl<T, F> LazyCell<T, F> { + /// Returns a reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::cell::LazyCell; + /// + /// let mut lazy = LazyCell::new(|| 92); + /// + /// assert_eq!(LazyCell::get_mut(&mut lazy), None); + /// let _ = LazyCell::force(&lazy); + /// *LazyCell::get_mut(&mut lazy).unwrap() = 44; + /// assert_eq!(*lazy, 44); + /// ``` #[inline] - fn get(&self) -> Option<&T> { + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get_mut(this: &mut LazyCell<T, F>) -> Option<&mut T> { + let state = this.state.get_mut(); + match state { + State::Init(data) => Some(data), + _ => None, + } + } + + /// Returns a mutable reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::cell::LazyCell; + /// + /// let lazy = LazyCell::new(|| 92); + /// + /// assert_eq!(LazyCell::get(&lazy), None); + /// let _ = LazyCell::force(&lazy); + /// assert_eq!(LazyCell::get(&lazy), Some(&92)); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get(this: &LazyCell<T, F>) -> Option<&T> { // SAFETY: // This is sound for the same reason as in `force`: once the state is // initialized, it will not be mutably accessed again, so this reference // will stay valid for the duration of the borrow to `self`. - let state = unsafe { &*self.state.get() }; + let state = unsafe { &*this.state.get() }; match state { State::Init(data) => Some(data), _ => None, @@ -188,10 +296,16 @@ impl<T: Default> Default for LazyCell<T> { impl<T: fmt::Debug, F> fmt::Debug for LazyCell<T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyCell"); - match self.get() { + match LazyCell::get(self) { Some(data) => d.field(data), None => d.field(&format_args!("<uninit>")), }; d.finish() } } + +#[cold] +#[inline(never)] +fn panic_poisoned() -> ! { + panic!("LazyCell instance has previously been poisoned") +} diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index bc5c7c32490..092d427ecea 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1,6 +1,7 @@ //! impl char {} use super::*; +use crate::intrinsics::const_eval_select; use crate::slice; use crate::str::from_utf8_unchecked_mut; use crate::unicode::printable::is_printable; @@ -672,8 +673,9 @@ impl char { /// 'ß'.encode_utf8(&mut b); /// ``` #[stable(feature = "unicode_encode_char", since = "1.15.0")] + #[rustc_const_unstable(feature = "const_char_encode_utf8", issue = "130512")] #[inline] - pub fn encode_utf8(self, dst: &mut [u8]) -> &mut str { + pub const fn encode_utf8(self, dst: &mut [u8]) -> &mut str { // SAFETY: `char` is not a surrogate, so this is valid UTF-8. unsafe { from_utf8_unchecked_mut(encode_utf8_raw(self as u32, dst)) } } @@ -1735,14 +1737,11 @@ impl EscapeDebugExtArgs { #[inline] const fn len_utf8(code: u32) -> usize { - if code < MAX_ONE_B { - 1 - } else if code < MAX_TWO_B { - 2 - } else if code < MAX_THREE_B { - 3 - } else { - 4 + match code { + ..MAX_ONE_B => 1, + ..MAX_TWO_B => 2, + ..MAX_THREE_B => 3, + _ => 4, } } @@ -1760,11 +1759,21 @@ const fn len_utf8(code: u32) -> usize { /// Panics if the buffer is not large enough. /// A buffer of length four is large enough to encode any `char`. #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] +#[rustc_const_unstable(feature = "const_char_encode_utf8", issue = "130512")] #[doc(hidden)] #[inline] -pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { +pub const fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { + const fn panic_at_const(_code: u32, _len: usize, _dst_len: usize) { + // Note that we cannot format in constant expressions. + panic!("encode_utf8: buffer does not have enough bytes to encode code point"); + } + fn panic_at_rt(code: u32, len: usize, dst_len: usize) { + panic!( + "encode_utf8: need {len} bytes to encode U+{code:04X} but buffer has just {dst_len}", + ); + } let len = len_utf8(code); - match (len, &mut dst[..]) { + match (len, &mut *dst) { (1, [a, ..]) => { *a = code as u8; } @@ -1783,14 +1792,11 @@ pub fn encode_utf8_raw(code: u32, dst: &mut [u8]) -> &mut [u8] { *c = (code >> 6 & 0x3F) as u8 | TAG_CONT; *d = (code & 0x3F) as u8 | TAG_CONT; } - _ => panic!( - "encode_utf8: need {} bytes to encode U+{:X}, but the buffer has {}", - len, - code, - dst.len(), - ), + // FIXME(const-hack): We would prefer to have streamlined panics when formatters become const-friendly. + _ => const_eval_select((code, len, dst.len()), panic_at_const, panic_at_rt), }; - &mut dst[..len] + // SAFETY: `<&mut [u8]>::as_mut_ptr` is guaranteed to return a valid pointer and `len` has been tested to be within bounds. + unsafe { slice::from_raw_parts_mut(dst.as_mut_ptr(), len) } } /// Encodes a raw u32 value as UTF-16 into the provided `u16` buffer, diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a1ed993b7d9..818a36002e7 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -901,7 +901,6 @@ pub trait Ord: Eq + PartialOrd<Self> { fn clamp(self, min: Self, max: Self) -> Self where Self: Sized, - Self: PartialOrd, { assert!(min <= max); if self < min { diff --git a/library/core/src/iter/sources/repeat_n.rs b/library/core/src/iter/sources/repeat_n.rs index 9c062193363..7e162ff387b 100644 --- a/library/core/src/iter/sources/repeat_n.rs +++ b/library/core/src/iter/sources/repeat_n.rs @@ -1,5 +1,6 @@ +use crate::fmt; use crate::iter::{FusedIterator, TrustedLen, UncheckedIterator}; -use crate::mem::ManuallyDrop; +use crate::mem::{self, MaybeUninit}; use crate::num::NonZero; /// Creates a new iterator that repeats a single element a given number of times. @@ -58,14 +59,12 @@ use crate::num::NonZero; #[inline] #[stable(feature = "iter_repeat_n", since = "1.82.0")] pub fn repeat_n<T: Clone>(element: T, count: usize) -> RepeatN<T> { - let mut element = ManuallyDrop::new(element); - - if count == 0 { - // SAFETY: we definitely haven't dropped it yet, since we only just got - // passed it in, and because the count is zero the instance we're about - // to create won't drop it, so to avoid leaking we need to now. - unsafe { ManuallyDrop::drop(&mut element) }; - } + let element = if count == 0 { + // `element` gets dropped eagerly. + MaybeUninit::uninit() + } else { + MaybeUninit::new(element) + }; RepeatN { element, count } } @@ -74,15 +73,23 @@ pub fn repeat_n<T: Clone>(element: T, count: usize) -> RepeatN<T> { /// /// This `struct` is created by the [`repeat_n()`] function. /// See its documentation for more. -#[derive(Clone, Debug)] #[stable(feature = "iter_repeat_n", since = "1.82.0")] pub struct RepeatN<A> { count: usize, - // Invariant: has been dropped iff count == 0. - element: ManuallyDrop<A>, + // Invariant: uninit iff count == 0. + element: MaybeUninit<A>, } impl<A> RepeatN<A> { + /// Returns the element if it hasn't been dropped already. + fn element_ref(&self) -> Option<&A> { + if self.count > 0 { + // SAFETY: The count is non-zero, so it must be initialized. + Some(unsafe { self.element.assume_init_ref() }) + } else { + None + } + } /// If we haven't already dropped the element, return it in an option. /// /// Clears the count so it won't be dropped again later. @@ -90,9 +97,10 @@ impl<A> RepeatN<A> { fn take_element(&mut self) -> Option<A> { if self.count > 0 { self.count = 0; + let element = mem::replace(&mut self.element, MaybeUninit::uninit()); // SAFETY: We just set count to zero so it won't be dropped again, // and it used to be non-zero so it hasn't already been dropped. - unsafe { Some(ManuallyDrop::take(&mut self.element)) } + unsafe { Some(element.assume_init()) } } else { None } @@ -100,6 +108,26 @@ impl<A> RepeatN<A> { } #[stable(feature = "iter_repeat_n", since = "1.82.0")] +impl<A: Clone> Clone for RepeatN<A> { + fn clone(&self) -> RepeatN<A> { + RepeatN { + count: self.count, + element: self.element_ref().cloned().map_or_else(MaybeUninit::uninit, MaybeUninit::new), + } + } +} + +#[stable(feature = "iter_repeat_n", since = "1.82.0")] +impl<A: fmt::Debug> fmt::Debug for RepeatN<A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RepeatN") + .field("count", &self.count) + .field("element", &self.element_ref()) + .finish() + } +} + +#[stable(feature = "iter_repeat_n", since = "1.82.0")] impl<A> Drop for RepeatN<A> { fn drop(&mut self) { self.take_element(); @@ -194,9 +222,11 @@ impl<A: Clone> UncheckedIterator for RepeatN<A> { // SAFETY: the check above ensured that the count used to be non-zero, // so element hasn't been dropped yet, and we just lowered the count to // zero so it won't be dropped later, and thus it's okay to take it here. - unsafe { ManuallyDrop::take(&mut self.element) } + unsafe { mem::replace(&mut self.element, MaybeUninit::uninit()).assume_init() } } else { - A::clone(&self.element) + // SAFETY: the count is non-zero, so it must have not been dropped yet. + let element = unsafe { self.element.assume_init_ref() }; + A::clone(element) } } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 8352486ad41..f53beeb007d 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -876,6 +876,7 @@ pub trait Iterator { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_do_not_const_check] + #[cfg_attr(not(test), rustc_diagnostic_item = "iter_filter")] fn filter<P>(self, predicate: P) -> Filter<Self, P> where Self: Sized, @@ -3412,6 +3413,7 @@ pub trait Iterator { /// ``` #[stable(feature = "iter_copied", since = "1.36.0")] #[rustc_do_not_const_check] + #[cfg_attr(not(test), rustc_diagnostic_item = "iter_copied")] fn copied<'a, T: 'a>(self) -> Copied<Self> where Self: Sized + Iterator<Item = &'a T>, @@ -3460,6 +3462,7 @@ pub trait Iterator { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_do_not_const_check] + #[cfg_attr(not(test), rustc_diagnostic_item = "iter_cloned")] fn cloned<'a, T: 'a>(self) -> Cloned<Self> where Self: Sized + Iterator<Item = &'a T>, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 058dcf34532..8826bf52b41 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -119,6 +119,7 @@ #![feature(const_bigint_helper_methods)] #![feature(const_black_box)] #![feature(const_cell_into_inner)] +#![feature(const_char_encode_utf8)] #![feature(const_eval_select)] #![feature(const_exact_div)] #![feature(const_float_classify)] @@ -131,7 +132,6 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(const_likely)] -#![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_maybe_uninit_assume_init)] #![feature(const_nonnull_new)] #![feature(const_num_midpoint)] @@ -170,6 +170,7 @@ #![feature(is_ascii_octdigit)] #![feature(is_val_statically_known)] #![feature(isqrt)] +#![feature(lazy_get)] #![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 0154caa7c24..c67796ad3db 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -393,7 +393,6 @@ impl<T> MaybeUninit<T> { // These are OK to allow since we do not leak &mut to user-visible API #[rustc_allow_const_fn_unstable(const_mut_refs)] #[rustc_allow_const_fn_unstable(const_ptr_write)] - #[rustc_allow_const_fn_unstable(const_maybe_uninit_as_mut_ptr)] #[rustc_const_stable(feature = "const_maybe_uninit_zeroed", since = "1.75.0")] pub const fn zeroed() -> MaybeUninit<T> { let mut u = MaybeUninit::<T>::uninit(); @@ -570,7 +569,11 @@ impl<T> MaybeUninit<T> { /// (Notice that the rules around references to uninitialized data are not finalized yet, but /// until they are, it is advisable to avoid them.) #[stable(feature = "maybe_uninit", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_maybe_uninit_as_mut_ptr", issue = "75251")] + #[rustc_const_stable( + feature = "const_maybe_uninit_as_mut_ptr", + since = "CURRENT_RUSTC_VERSION" + )] + #[cfg_attr(bootstrap, rustc_allow_const_fn_unstable(const_mut_refs))] #[inline(always)] pub const fn as_mut_ptr(&mut self) -> *mut T { // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer. diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 4c2a4ee3b32..d0eb6bb3f28 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -415,6 +415,7 @@ impl f32 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f32::MANTISSA_DIGITS #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "f32_epsilon")] pub const EPSILON: f32 = 1.19209290e-07_f32; /// Smallest finite `f32` value. diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 87fb5fe7ebe..4bc275ad147 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -414,6 +414,7 @@ impl f64 { /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon /// [`MANTISSA_DIGITS`]: f64::MANTISSA_DIGITS #[stable(feature = "assoc_int_consts", since = "1.43.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "f64_epsilon")] pub const EPSILON: f64 = 2.2204460492503131e-16_f64; /// Smallest finite `f64` value. diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index dca644ebef4..15de4fa15c4 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -23,6 +23,16 @@ macro_rules! unlikely { }; } +// Use this when the generated code should differ between signed and unsigned types. +macro_rules! sign_dependent_expr { + (signed ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { + $signed_case + }; + (unsigned ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { + $unsigned_case + }; +} + // All these modules are technically private and only exposed for coretests: #[cfg(not(no_fp_fmt_parse))] pub mod bignum; @@ -1410,15 +1420,25 @@ const fn from_str_radix_panic(radix: u32) { } macro_rules! from_str_radix { - ($($int_ty:ty)+) => {$( + ($signedness:ident $($int_ty:ty)+) => {$( impl $int_ty { /// Converts a string slice in a given base to an integer. /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: + /// The string is expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// also represent an error. /// + /// Digits are a subset of these characters, depending on `radix`: /// * `0-9` /// * `a-z` /// * `A-Z` @@ -1430,10 +1450,13 @@ macro_rules! from_str_radix { /// # Examples /// /// Basic usage: - /// /// ``` #[doc = concat!("assert_eq!(", stringify!($int_ty), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` + /// Trailing space returns error: + /// ``` + #[doc = concat!("assert!(", stringify!($int_ty), "::from_str_radix(\"1 \", 10).is_err());")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$int_ty, ParseIntError> { @@ -1535,20 +1558,31 @@ macro_rules! from_str_radix { )+} } -from_str_radix! { i8 u8 i16 u16 i32 u32 i64 u64 i128 u128 } +from_str_radix! { unsigned u8 u16 u32 u64 u128 } +from_str_radix! { signed i8 i16 i32 i64 i128 } // Re-use the relevant implementation of from_str_radix for isize and usize to avoid outputting two // identical functions. macro_rules! from_str_radix_size_impl { - ($($t:ident $size:ty),*) => {$( + ($($signedness:ident $t:ident $size:ty),*) => {$( impl $size { /// Converts a string slice in a given base to an integer. /// - /// The string is expected to be an optional `+` sign - /// followed by digits. - /// Leading and trailing whitespace represent an error. - /// Digits are a subset of these characters, depending on `radix`: + /// The string is expected to be an optional + #[doc = sign_dependent_expr!{ + $signedness ? + if signed { + " `+` or `-` " + } + if unsigned { + " `+` " + } + }] + /// sign followed by only digits. Leading and trailing non-digit characters (including + /// whitespace) represent an error. Underscores (which are accepted in rust literals) + /// also represent an error. /// + /// Digits are a subset of these characters, depending on `radix`: /// * `0-9` /// * `a-z` /// * `A-Z` @@ -1560,10 +1594,13 @@ macro_rules! from_str_radix_size_impl { /// # Examples /// /// Basic usage: - /// /// ``` #[doc = concat!("assert_eq!(", stringify!($size), "::from_str_radix(\"A\", 16), Ok(10));")] /// ``` + /// Trailing space returns error: + /// ``` + #[doc = concat!("assert!(", stringify!($size), "::from_str_radix(\"1 \", 10).is_err());")] + /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_from_str", since = "1.82.0")] pub const fn from_str_radix(src: &str, radix: u32) -> Result<$size, ParseIntError> { @@ -1576,8 +1613,8 @@ macro_rules! from_str_radix_size_impl { } #[cfg(target_pointer_width = "16")] -from_str_radix_size_impl! { i16 isize, u16 usize } +from_str_radix_size_impl! { signed i16 isize, unsigned u16 usize } #[cfg(target_pointer_width = "32")] -from_str_radix_size_impl! { i32 isize, u32 usize } +from_str_radix_size_impl! { signed i32 isize, unsigned u32 usize } #[cfg(target_pointer_width = "64")] -from_str_radix_size_impl! { i64 isize, u64 usize } +from_str_radix_size_impl! { signed i64 isize, unsigned u64 usize } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 8b888f12da0..e5c9a7e086a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1972,16 +1972,6 @@ macro_rules! nonzero_integer_signedness_dependent_methods { }; } -// Use this when the generated code should differ between signed and unsigned types. -macro_rules! sign_dependent_expr { - (signed ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { - $signed_case - }; - (unsigned ? if signed { $signed_case:expr } if unsigned { $unsigned_case:expr } ) => { - $unsigned_case - }; -} - nonzero_integer! { Self = NonZeroU8, Primitive = unsigned u8, diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 265f056dc87..5ba13969605 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -923,6 +923,7 @@ impl<T> Option<T> { #[inline] #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "option_expect")] #[rustc_const_unstable(feature = "const_option", issue = "67441")] pub const fn expect(self, msg: &str) -> T { match self { @@ -960,6 +961,7 @@ impl<T> Option<T> { #[inline(always)] #[track_caller] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "option_unwrap")] #[rustc_const_unstable(feature = "const_option", issue = "67441")] pub const fn unwrap(self) -> T { match self { diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index e4d0c897b65..af2c83b5460 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -12,7 +12,7 @@ use crate::panic::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] #[derive(Debug)] pub struct PanicInfo<'a> { - message: fmt::Arguments<'a>, + message: &'a fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, @@ -26,13 +26,13 @@ pub struct PanicInfo<'a> { /// See [`PanicInfo::message`]. #[stable(feature = "panic_info_message", since = "1.81.0")] pub struct PanicMessage<'a> { - message: fmt::Arguments<'a>, + message: &'a fmt::Arguments<'a>, } impl<'a> PanicInfo<'a> { #[inline] pub(crate) fn new( - message: fmt::Arguments<'a>, + message: &'a fmt::Arguments<'a>, location: &'a Location<'a>, can_unwind: bool, force_no_backtrace: bool, @@ -146,7 +146,7 @@ impl Display for PanicInfo<'_> { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; formatter.write_str(":\n")?; - formatter.write_fmt(self.message)?; + formatter.write_fmt(*self.message)?; Ok(()) } } @@ -177,7 +177,7 @@ impl<'a> PanicMessage<'a> { impl Display for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_fmt(self.message) + formatter.write_fmt(*self.message) } } @@ -185,6 +185,6 @@ impl Display for PanicMessage<'_> { impl fmt::Debug for PanicMessage<'_> { #[inline] fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_fmt(self.message) + formatter.write_fmt(*self.message) } } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index e4a62304087..7420579e3ce 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -64,7 +64,7 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { } let pi = PanicInfo::new( - fmt, + &fmt, Location::caller(), /* can_unwind */ true, /* force_no_backtrace */ false, @@ -102,7 +102,7 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo // PanicInfo with the `can_unwind` flag set to false forces an abort. let pi = PanicInfo::new( - fmt, + &fmt, Location::caller(), /* can_unwind */ false, force_no_backtrace, diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 834cec9dcfc..ceb5906d226 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -154,6 +154,11 @@ impl Alignment { // SAFETY: The alignment is always nonzero, and therefore decrementing won't overflow. !(unsafe { self.as_usize().unchecked_sub(1) }) } + + // Remove me once `Ord::max` is usable in const + pub(crate) const fn max(a: Self, b: Self) -> Self { + if a.as_usize() > b.as_usize() { a } else { b } + } } #[unstable(feature = "ptr_alignment_type", issue = "102070")] diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 02f6f783b51..9edd58259ba 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -653,6 +653,7 @@ impl<T, E> Result<T, E> { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "result_ok_method")] pub fn ok(self) -> Option<T> { match self { Ok(x) => Some(x), diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b1948ce69c7..cdcca0eee88 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1010,6 +1010,7 @@ impl<T> [T] { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg_attr(not(test), rustc_diagnostic_item = "slice_iter")] pub fn iter(&self) -> Iter<'_, T> { Iter::new(self) } diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index e947686487e..712bf011c27 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -134,6 +134,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_str_len", since = "1.39.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_len")] #[must_use] #[inline] pub const fn len(&self) -> usize { @@ -832,6 +833,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_chars")] pub fn chars(&self) -> Chars<'_> { Chars { iter: self.as_bytes().iter() } } @@ -1156,6 +1158,7 @@ impl str { /// assert!(bananas.starts_with(&['a', 'b', 'c', 'd'])); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_starts_with")] pub fn starts_with<P: Pattern>(&self, pat: P) -> bool { pat.is_prefix_of(self) } @@ -1180,6 +1183,7 @@ impl str { /// assert!(!bananas.ends_with("nana")); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_ends_with")] pub fn ends_with<P: Pattern>(&self, pat: P) -> bool where for<'a> P::Searcher<'a>: ReverseSearcher<'a>, diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 5e559ad8d2c..a5103499c8a 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -414,6 +414,7 @@ impl<'a> ContextBuilder<'a> { /// [`Wake`]: ../../alloc/task/trait.Wake.html #[repr(transparent)] #[stable(feature = "futures_api", since = "1.36.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Waker")] pub struct Waker { waker: RawWaker, } diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs index 0d1c72a6892..2bdaeb3845a 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs @@ -228,6 +228,8 @@ fn static_init() { } #[test] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[cfg_attr(not(bootstrap), allow(static_mut_refs))] fn atomic_access_bool() { static mut ATOMIC: AtomicBool = AtomicBool::new(false); diff --git a/library/core/tests/iter/sources.rs b/library/core/tests/iter/sources.rs index eb8c80dd087..506febaa056 100644 --- a/library/core/tests/iter/sources.rs +++ b/library/core/tests/iter/sources.rs @@ -156,3 +156,27 @@ fn test_repeat_n_drop() { drop((x0, x1, x2)); assert_eq!(count.get(), 3); } + +#[test] +fn test_repeat_n_soundness() { + let x = std::iter::repeat_n(String::from("use after free"), 0); + println!("{x:?}"); + + pub struct PanicOnClone; + + impl Clone for PanicOnClone { + fn clone(&self) -> Self { + unreachable!() + } + } + + // `repeat_n` should drop the element immediately if `count` is zero. + // `Clone` should then not try to clone the element. + let x = std::iter::repeat_n(PanicOnClone, 0); + let _ = x.clone(); + + let mut y = std::iter::repeat_n(Box::new(0), 1); + let x = y.next().unwrap(); + let _z = y; + assert_eq!(0, *x); +} diff --git a/library/core/tests/lazy.rs b/library/core/tests/lazy.rs index a3b89f15b3f..711511eaf4a 100644 --- a/library/core/tests/lazy.rs +++ b/library/core/tests/lazy.rs @@ -114,6 +114,27 @@ fn lazy_type_inference() { } #[test] +#[should_panic = "LazyCell instance has previously been poisoned"] +fn lazy_force_mut_panic() { + let mut lazy = LazyCell::<String>::new(|| panic!()); + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = LazyCell::force_mut(&mut lazy); + })) + .unwrap_err(); + let _ = &*lazy; +} + +#[test] +fn lazy_force_mut() { + let s = "abc".to_owned(); + let mut lazy = LazyCell::new(move || s); + LazyCell::force_mut(&mut lazy); + let p = LazyCell::force_mut(&mut lazy); + p.clear(); + LazyCell::force_mut(&mut lazy); +} + +#[test] fn aliasing_in_get() { let x = OnceCell::new(); x.set(42).unwrap(); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 948ea9bc098..5315ac856f6 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -26,7 +26,6 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(const_likely)] -#![feature(const_maybe_uninit_as_mut_ptr)] #![feature(const_nonnull_new)] #![feature(const_option)] #![feature(const_option_ext)] @@ -76,6 +75,7 @@ #![feature(iterator_try_collect)] #![feature(iterator_try_reduce)] #![feature(layout_for_ptr)] +#![feature(lazy_get)] #![feature(maybe_uninit_fill)] #![feature(maybe_uninit_uninit_array_transpose)] #![feature(maybe_uninit_write_slice)] diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index 830a96204ca..6a26bd15a66 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -244,6 +244,8 @@ macro_rules! int_module { assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>); assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>); + assert_eq!($T::from_str_radix("10_0", 10).ok(), None::<$T>); + assert_eq!(u32::from_str_radix("-9", 10).ok(), None::<u32>); } #[test] diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 070c11926f6..9e74a45a0e2 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -291,6 +291,8 @@ cfg_if::cfg_if! { } } +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[allow(static_mut_refs)] pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 { use core::intrinsics::atomic_store_seqcst; diff --git a/library/proc_macro/src/bridge/buffer.rs b/library/proc_macro/src/bridge/buffer.rs index 78fcd1999b2..3760749d83a 100644 --- a/library/proc_macro/src/bridge/buffer.rs +++ b/library/proc_macro/src/bridge/buffer.rs @@ -119,9 +119,7 @@ impl Write for Buffer { } impl Drop for Buffer { - // HACK(nbdd0121): Hack to prevent LLVM < 17.0.4 from misoptimising, - // change to `#[inline]` if fixed. - #[inline(never)] + #[inline] fn drop(&mut self) { let b = self.take(); (b.drop)(b); diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 7d21d37f40d..1a18721b15e 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1037,6 +1037,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_contains_key")] pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool where K: Borrow<Q>, @@ -1100,6 +1101,7 @@ where #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("push", "append", "put")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_insert")] pub fn insert(&mut self, k: K, v: V) -> Option<V> { self.base.insert(k, v) } @@ -1391,6 +1393,7 @@ where /// let iter = map.iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_ty")] pub struct Iter<'a, K: 'a, V: 'a> { base: base::Iter<'a, K, V>, } @@ -1429,6 +1432,7 @@ impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { /// let iter = map.iter_mut(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_iter_mut_ty")] pub struct IterMut<'a, K: 'a, V: 'a> { base: base::IterMut<'a, K, V>, } @@ -1489,6 +1493,7 @@ impl<K, V> IntoIter<K, V> { /// let iter_keys = map.keys(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_keys_ty")] pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } @@ -1527,6 +1532,7 @@ impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> { /// let iter_values = map.values(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_ty")] pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, } @@ -1565,6 +1571,7 @@ impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { /// let iter = map.drain(); /// ``` #[stable(feature = "drain", since = "1.6.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_drain_ty")] pub struct Drain<'a, K: 'a, V: 'a> { base: base::Drain<'a, K, V>, } @@ -1622,6 +1629,7 @@ where /// let iter_values = map.values_mut(); /// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_values_mut_ty")] pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index d611353b0d3..4a113ddea3a 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -187,6 +187,7 @@ impl<T, S> HashSet<T, S> { #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter")] pub fn iter(&self) -> Iter<'_, T> { Iter { base: self.base.iter() } } @@ -1270,6 +1271,7 @@ where /// let mut iter = a.iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_iter_ty")] pub struct Iter<'a, K: 'a> { base: base::Iter<'a, K>, } @@ -1312,6 +1314,7 @@ pub struct IntoIter<K> { /// let mut drain = a.drain(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "hashset_drain_ty")] pub struct Drain<'a, K: 'a> { base: base::Drain<'a, K>, } diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 28916130b19..97a1b846a91 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -935,106 +935,147 @@ impl fmt::Debug for ArgsOs { pub mod consts { use crate::sys::env::os; - /// A string describing the architecture of the CPU that is currently - /// in use. + /// A string describing the architecture of the CPU that is currently in use. + /// An example value may be: `"x86"`, `"arm"` or `"riscv64"`. /// - /// Some possible values: + /// <details><summary>Full list of possible values</summary> /// - /// - x86 - /// - x86_64 - /// - arm - /// - aarch64 - /// - loongarch64 - /// - m68k - /// - csky - /// - mips - /// - mips64 - /// - powerpc - /// - powerpc64 - /// - riscv64 - /// - s390x - /// - sparc64 + /// * `"x86"` + /// * `"x86_64"` + /// * `"arm"` + /// * `"aarch64"` + /// * `"m68k"` + /// * `"mips"` + /// * `"mips32r6"` + /// * `"mips64"` + /// * `"mips64r6"` + /// * `"csky"` + /// * `"powerpc"` + /// * `"powerpc64"` + /// * `"riscv32"` + /// * `"riscv64"` + /// * `"s390x"` + /// * `"sparc"` + /// * `"sparc64"` + /// * `"hexagon"` + /// * `"loongarch64"` + /// + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const ARCH: &str = env!("STD_ENV_ARCH"); - /// The family of the operating system. Example value is `unix`. + /// A string describing the family of the operating system. + /// An example value may be: `"unix"`, or `"windows"`. + /// + /// This value may be an empty string if the family is unknown. + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"unix"` + /// * `"windows"` + /// * `"itron"` + /// * `"wasm"` + /// * `""` /// - /// - unix - /// - windows + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const FAMILY: &str = os::FAMILY; /// A string describing the specific operating system in use. - /// Example value is `linux`. + /// An example value may be: `"linux"`, or `"freebsd"`. /// - /// Some possible values: + /// <details><summary>Full list of possible values</summary> /// - /// - linux - /// - macos - /// - ios - /// - freebsd - /// - dragonfly - /// - netbsd - /// - openbsd - /// - solaris - /// - android - /// - windows + /// * `"linux"` + /// * `"windows"` + /// * `"macos"` + /// * `"android"` + /// * `"ios"` + /// * `"openbsd"` + /// * `"freebsd"` + /// * `"netbsd"` + /// * `"wasi"` + /// * `"hermit"` + /// * `"aix"` + /// * `"apple"` + /// * `"dragonfly"` + /// * `"emscripten"` + /// * `"espidf"` + /// * `"fortanix"` + /// * `"uefi"` + /// * `"fuchsia"` + /// * `"haiku"` + /// * `"hermit"` + /// * `"watchos"` + /// * `"visionos"` + /// * `"tvos"` + /// * `"horizon"` + /// * `"hurd"` + /// * `"illumos"` + /// * `"l4re"` + /// * `"nto"` + /// * `"redox"` + /// * `"solaris"` + /// * `"solid_asp3` + /// * `"vita"` + /// * `"vxworks"` + /// * `"xous"` + /// + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const OS: &str = os::OS; - /// Specifies the filename prefix used for shared libraries on this - /// platform. Example value is `lib`. - /// - /// Some possible values: - /// - /// - lib - /// - `""` (an empty string) + /// Specifies the filename prefix, if any, used for shared libraries on this platform. + /// This is either `"lib"` or an empty string. (`""`). #[stable(feature = "env", since = "1.0.0")] pub const DLL_PREFIX: &str = os::DLL_PREFIX; - /// Specifies the filename suffix used for shared libraries on this - /// platform. Example value is `.so`. - /// - /// Some possible values: + /// Specifies the filename suffix, if any, used for shared libraries on this platform. + /// An example value may be: `".so"`, `".elf"`, or `".dll"`. /// - /// - .so - /// - .dylib - /// - .dll + /// The possible values are identical to those of [`DLL_EXTENSION`], but with the leading period included. #[stable(feature = "env", since = "1.0.0")] pub const DLL_SUFFIX: &str = os::DLL_SUFFIX; - /// Specifies the file extension used for shared libraries on this - /// platform that goes after the dot. Example value is `so`. + /// Specifies the file extension, if any, used for shared libraries on this platform that goes after the dot. + /// An example value may be: `"so"`, `"elf"`, or `"dll"`. + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"so"` + /// * `"dylib"` + /// * `"dll"` + /// * `"sgxs"` + /// * `"a"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) /// - /// - so - /// - dylib - /// - dll + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const DLL_EXTENSION: &str = os::DLL_EXTENSION; - /// Specifies the filename suffix used for executable binaries on this - /// platform. Example value is `.exe`. + /// Specifies the filename suffix, if any, used for executable binaries on this platform. + /// An example value may be: `".exe"`, or `".efi"`. /// - /// Some possible values: - /// - /// - .exe - /// - .nexe - /// - .pexe - /// - `""` (an empty string) + /// The possible values are identical to those of [`EXE_EXTENSION`], but with the leading period included. #[stable(feature = "env", since = "1.0.0")] pub const EXE_SUFFIX: &str = os::EXE_SUFFIX; - /// Specifies the file extension, if any, used for executable binaries - /// on this platform. Example value is `exe`. + /// Specifies the file extension, if any, used for executable binaries on this platform. + /// An example value may be: `"exe"`, or an empty string (`""`). + /// + /// <details><summary>Full list of possible values</summary> /// - /// Some possible values: + /// * `"exe"` + /// * `"efi"` + /// * `"js"` + /// * `"sgxs"` + /// * `"elf"` + /// * `"wasm"` + /// * `""` (an empty string) /// - /// - exe - /// - `""` (an empty string) + /// </details> #[stable(feature = "env", since = "1.0.0")] pub const EXE_EXTENSION: &str = os::EXE_EXTENSION; } diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 99bea676e12..0f905803bb8 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -196,6 +196,7 @@ impl OsString { /// let os_str = OsStr::new("foo"); /// assert_eq!(os_string.as_os_str(), os_str); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "os_string_as_os_str")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -918,6 +919,7 @@ impl OsStr { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] + #[cfg_attr(not(test), rustc_diagnostic_item = "os_str_to_os_string")] pub fn to_os_string(&self) -> OsString { OsString { inner: self.inner.to_owned() } } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 6a0d9f47960..55f3b628ce8 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -466,6 +466,7 @@ impl File { /// ``` #[must_use] #[stable(feature = "with_options", since = "1.58.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "file_options")] pub fn options() -> OpenOptions { OpenOptions::new() } @@ -835,7 +836,7 @@ impl Read for &File { } #[stable(feature = "rust1", since = "1.0.0")] impl Write for &File { - /// Writes some bytes from the file. + /// Writes some bytes to the file. /// /// See [`Write::write`] docs for more info. /// @@ -1009,6 +1010,7 @@ impl OpenOptions { /// let mut options = OpenOptions::new(); /// let file = options.read(true).open("foo.txt"); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "open_options_new")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] pub fn new() -> Self { @@ -1989,6 +1991,11 @@ impl AsInner<fs_imp::DirEntry> for DirEntry { /// * The file doesn't exist. /// * The user lacks permissions to remove the file. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2446,6 +2453,11 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// * The user lacks permissions to remove the directory at the provided `path`. /// * The directory isn't empty. /// +/// This function will only ever return an error of kind `NotFound` if the given +/// path does not exist. Note that the inverse is not true, +/// ie. if a path does not exist, its removal may fail for a number of reasons, +/// such as insufficient permissions. +/// /// # Examples /// /// ```no_run @@ -2471,16 +2483,15 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { /// # Platform-specific behavior /// /// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions -/// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`, -/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile` functions on -/// Windows. Note that, this [may change in the future][changes]. +/// on Unix (except for REDOX) and the `CreateFileW`, `GetFileInformationByHandleEx`, +/// `SetFileInformationByHandle`, and `NtCreateFile` functions on Windows. Note that, this +/// [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior /// -/// On macOS before version 10.10 and REDOX, as well as when running in Miri for any target, this -/// function is not protected against time-of-check to time-of-use (TOCTOU) race conditions, and -/// should not be used in security-sensitive code on those platforms. All other platforms are -/// protected. +/// On REDOX, as well as when running in Miri for any target, this function is not protected against +/// time-of-check to time-of-use (TOCTOU) race conditions, and should not be used in +/// security-sensitive code on those platforms. All other platforms are protected. /// /// # Errors /// diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 644b294db8d..2a4262b2367 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2058,6 +2058,7 @@ pub trait Seek { /// It is used by the [`Seek`] trait. #[derive(Copy, PartialEq, Eq, Clone, Debug)] #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "SeekFrom")] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index bc5369ddc3d..e4ba5e47613 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -337,6 +337,7 @@ #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(ip)] +#![feature(lazy_get)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index caf6980afd9..a964db2e0ac 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -334,6 +334,7 @@ pub trait PermissionsExt { /// assert_eq!(permissions.mode(), 0o644); /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "permissions_from_mode")] fn from_mode(mode: u32) -> Self; } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index c94df9b5366..aa9f63d915a 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -263,6 +263,7 @@ pub fn is_separator(c: char) -> bool { /// /// For example, `/` on Unix and `\` on Windows. #[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "path_main_separator")] pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP; /// The primary separator of path components for the current platform. @@ -1226,6 +1227,7 @@ impl PathBuf { /// let p = PathBuf::from("/test"); /// assert_eq!(Path::new("/test"), p.as_path()); /// ``` + #[cfg_attr(not(test), rustc_diagnostic_item = "pathbuf_as_path")] #[stable(feature = "rust1", since = "1.0.0")] #[must_use] #[inline] @@ -2264,6 +2266,7 @@ impl Path { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "path_to_pathbuf")] pub fn to_path_buf(&self) -> PathBuf { PathBuf::from(self.inner.to_os_string()) } diff --git a/library/std/src/sync/lazy_lock.rs b/library/std/src/sync/lazy_lock.rs index 953aef40e7b..b05615035d7 100644 --- a/library/std/src/sync/lazy_lock.rs +++ b/library/std/src/sync/lazy_lock.rs @@ -44,8 +44,6 @@ union Data<T, F> { /// /// // The `String` is built, stored in the `LazyLock`, and returned as `&String`. /// let _ = &*DEEP_THOUGHT; -/// // The `String` is retrieved from the `LazyLock` and returned as `&String`. -/// let _ = &*DEEP_THOUGHT; /// ``` /// /// Initialize fields with `LazyLock`. @@ -121,7 +119,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { pub fn into_inner(mut this: Self) -> Result<T, F> { let state = this.once.state(); match state { - ExclusiveState::Poisoned => panic!("LazyLock instance has previously been poisoned"), + ExclusiveState::Poisoned => panic_poisoned(), state => { let this = ManuallyDrop::new(this); let data = unsafe { ptr::read(&this.data) }.into_inner(); @@ -134,6 +132,60 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { } } + /// Forces the evaluation of this lazy value and returns a mutable reference to + /// the result. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// use std::sync::LazyLock; + /// + /// let mut lazy = LazyLock::new(|| 92); + /// + /// let p = LazyLock::force_mut(&mut lazy); + /// assert_eq!(*p, 92); + /// *p = 44; + /// assert_eq!(*lazy, 44); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn force_mut(this: &mut LazyLock<T, F>) -> &mut T { + #[cold] + /// # Safety + /// May only be called when the state is `Incomplete`. + unsafe fn really_init_mut<T, F: FnOnce() -> T>(this: &mut LazyLock<T, F>) -> &mut T { + struct PoisonOnPanic<'a, T, F>(&'a mut LazyLock<T, F>); + impl<T, F> Drop for PoisonOnPanic<'_, T, F> { + #[inline] + fn drop(&mut self) { + self.0.once.set_state(ExclusiveState::Poisoned); + } + } + + // SAFETY: We always poison if the initializer panics (then we never check the data), + // or set the data on success. + let f = unsafe { ManuallyDrop::take(&mut this.data.get_mut().f) }; + // INVARIANT: Initiated from mutable reference, don't drop because we read it. + let guard = PoisonOnPanic(this); + let data = f(); + guard.0.data.get_mut().value = ManuallyDrop::new(data); + guard.0.once.set_state(ExclusiveState::Complete); + core::mem::forget(guard); + // SAFETY: We put the value there above. + unsafe { &mut this.data.get_mut().value } + } + + let state = this.once.state(); + match state { + ExclusiveState::Poisoned => panic_poisoned(), + // SAFETY: The `Once` states we completed the initialization. + ExclusiveState::Complete => unsafe { &mut this.data.get_mut().value }, + // SAFETY: The state is `Incomplete`. + ExclusiveState::Incomplete => unsafe { really_init_mut(this) }, + } + } + /// Forces the evaluation of this lazy value and returns a reference to /// result. This is equivalent to the `Deref` impl, but is explicit. /// @@ -174,13 +226,58 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> { } impl<T, F> LazyLock<T, F> { - /// Gets the inner value if it has already been initialized. - fn get(&self) -> Option<&T> { - if self.once.is_completed() { + /// Returns a reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::sync::LazyLock; + /// + /// let mut lazy = LazyLock::new(|| 92); + /// + /// assert_eq!(LazyLock::get_mut(&mut lazy), None); + /// let _ = LazyLock::force(&lazy); + /// *LazyLock::get_mut(&mut lazy).unwrap() = 44; + /// assert_eq!(*lazy, 44); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get_mut(this: &mut LazyLock<T, F>) -> Option<&mut T> { + // `state()` does not perform an atomic load, so prefer it over `is_complete()`. + let state = this.once.state(); + match state { + // SAFETY: + // The closure has been run successfully, so `value` has been initialized. + ExclusiveState::Complete => Some(unsafe { &mut this.data.get_mut().value }), + _ => None, + } + } + + /// Returns a mutable reference to the value if initialized, or `None` if not. + /// + /// # Examples + /// + /// ``` + /// #![feature(lazy_get)] + /// + /// use std::sync::LazyLock; + /// + /// let lazy = LazyLock::new(|| 92); + /// + /// assert_eq!(LazyLock::get(&lazy), None); + /// let _ = LazyLock::force(&lazy); + /// assert_eq!(LazyLock::get(&lazy), Some(&92)); + /// ``` + #[inline] + #[unstable(feature = "lazy_get", issue = "129333")] + pub fn get(this: &LazyLock<T, F>) -> Option<&T> { + if this.once.is_completed() { // SAFETY: // The closure has been run successfully, so `value` has been initialized // and will not be modified again. - Some(unsafe { &*(*self.data.get()).value }) + Some(unsafe { &(*this.data.get()).value }) } else { None } @@ -228,7 +325,7 @@ impl<T: Default> Default for LazyLock<T> { impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_tuple("LazyLock"); - match self.get() { + match LazyLock::get(self) { Some(v) => d.field(v), None => d.field(&format_args!("<uninit>")), }; @@ -236,6 +333,12 @@ impl<T: fmt::Debug, F> fmt::Debug for LazyLock<T, F> { } } +#[cold] +#[inline(never)] +fn panic_poisoned() -> ! { + panic!("LazyLock instance has previously been poisoned") +} + // We never create a `&F` from a `&LazyLock<T, F>` so it is fine // to not impl `Sync` for `F`. #[stable(feature = "lazy_cell", since = "1.80.0")] diff --git a/library/std/src/sync/lazy_lock/tests.rs b/library/std/src/sync/lazy_lock/tests.rs index 8a6ab4ac4fd..94044368305 100644 --- a/library/std/src/sync/lazy_lock/tests.rs +++ b/library/std/src/sync/lazy_lock/tests.rs @@ -142,3 +142,24 @@ fn is_sync_send() { fn assert_traits<T: Send + Sync>() {} assert_traits::<LazyLock<String>>(); } + +#[test] +#[should_panic = "has previously been poisoned"] +fn lazy_force_mut_panic() { + let mut lazy = LazyLock::<String>::new(|| panic!()); + crate::panic::catch_unwind(crate::panic::AssertUnwindSafe(|| { + let _ = LazyLock::force_mut(&mut lazy); + })) + .unwrap_err(); + let _ = &*lazy; +} + +#[test] +fn lazy_force_mut() { + let s = "abc".to_owned(); + let mut lazy = LazyLock::new(move || s); + LazyLock::force_mut(&mut lazy); + let p = LazyLock::force_mut(&mut lazy); + p.clear(); + LazyLock::force_mut(&mut lazy); +} diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index d0ba8cc3b47..70b419a1e33 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -9,6 +9,9 @@ //! Consider the following code, operating on some global static variables: //! //! ```rust +//! // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +//! #![allow(static_mut_refs)] +//! //! static mut A: u32 = 0; //! static mut B: u32 = 0; //! static mut C: u32 = 0; diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index bf595fdea2d..5a1cd7d0b8b 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -314,6 +314,16 @@ impl Once { pub(crate) fn state(&mut self) -> ExclusiveState { self.inner.state() } + + /// Sets current state of the `Once` instance. + /// + /// Since this takes a mutable reference, no initialization can currently + /// be running, so the state must be either "incomplete", "poisoned" or + /// "complete". + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + self.inner.set_state(new_state); + } } #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index ef9d753d7f8..a308fafc68b 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -16,6 +16,9 @@ //! The crate itself provides a global allocator which on wasm has no //! synchronization as there are no threads! +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use crate::alloc::{GlobalAlloc, Layout, System}; static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::Dlmalloc::new(); diff --git a/library/std/src/sys/pal/unsupported/process.rs b/library/std/src/sys/pal/unsupported/process.rs index 40231bfc90b..fee81744f09 100644 --- a/library/std/src/sys/pal/unsupported/process.rs +++ b/library/std/src/sys/pal/unsupported/process.rs @@ -255,11 +255,11 @@ impl ExitStatusError { } #[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); +pub struct ExitCode(u8); impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); pub fn as_i32(&self) -> i32 { self.0 as i32 @@ -268,10 +268,7 @@ impl ExitCode { impl From<u8> for ExitCode { fn from(code: u8) -> Self { - match code { - 0 => Self::SUCCESS, - 1..=255 => Self::FAILURE, - } + Self(code) } } diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs index e7234ed8e5f..9416049da78 100644 --- a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs +++ b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs @@ -71,10 +71,12 @@ unsafe fn nt_open_file( } /// Open the file `path` in the directory `parent`, requesting the given `access` rights. +/// `options` will be OR'd with `FILE_OPEN_REPARSE_POINT`. fn open_link_no_reparse( parent: &File, path: &[u16], access: u32, + options: u32, ) -> Result<Option<File>, WinError> { // This is implemented using the lower level `NtOpenFile` function as // unfortunately opening a file relative to a parent is not supported by @@ -96,7 +98,7 @@ fn open_link_no_reparse( ..c::OBJECT_ATTRIBUTES::default() }; let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE; - let options = c::FILE_OPEN_REPARSE_POINT; + let options = c::FILE_OPEN_REPARSE_POINT | options; let result = nt_open_file(access, &object, share, options); // Retry without OBJ_DONT_REPARSE if it's not supported. @@ -128,13 +130,20 @@ fn open_link_no_reparse( } fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> { - open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY) + // Open the directory for synchronous directory listing. + open_link_no_reparse( + parent, + name, + c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY, + // "_IO_NONALERT" means that a synchronous call won't be interrupted. + c::FILE_SYNCHRONOUS_IO_NONALERT, + ) } fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> { // Note that the `delete` function consumes the opened file to ensure it's // dropped immediately. See module comments for why this is important. - match open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::DELETE) { + match open_link_no_reparse(parent, name, c::DELETE, 0) { Ok(Some(f)) => f.delete(), Ok(None) => Ok(()), Err(e) => Err(e), diff --git a/library/std/src/sys/sync/once/futex.rs b/library/std/src/sys/sync/once/futex.rs index 2c8a054282b..25588a4217b 100644 --- a/library/std/src/sys/sync/once/futex.rs +++ b/library/std/src/sys/sync/once/futex.rs @@ -91,6 +91,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + *self.state_and_queued.get_mut() = match new_state { + ExclusiveState::Incomplete => INCOMPLETE, + ExclusiveState::Poisoned => POISONED, + ExclusiveState::Complete => COMPLETE, + }; + } + #[cold] #[track_caller] pub fn wait(&self, ignore_poisoning: bool) { diff --git a/library/std/src/sys/sync/once/no_threads.rs b/library/std/src/sys/sync/once/no_threads.rs index 12c1d9f5a6c..cdcffe790f5 100644 --- a/library/std/src/sys/sync/once/no_threads.rs +++ b/library/std/src/sys/sync/once/no_threads.rs @@ -55,6 +55,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + self.state.set(match new_state { + ExclusiveState::Incomplete => State::Incomplete, + ExclusiveState::Poisoned => State::Poisoned, + ExclusiveState::Complete => State::Complete, + }); + } + #[cold] #[track_caller] pub fn wait(&self, _ignore_poisoning: bool) { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 86f72c82008..17abaf0bf26 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -140,6 +140,15 @@ impl Once { } } + #[inline] + pub(crate) fn set_state(&mut self, new_state: ExclusiveState) { + *self.state_and_queue.get_mut() = match new_state { + ExclusiveState::Incomplete => ptr::without_provenance_mut(INCOMPLETE), + ExclusiveState::Poisoned => ptr::without_provenance_mut(POISONED), + ExclusiveState::Complete => ptr::without_provenance_mut(COMPLETE), + }; + } + #[cold] #[track_caller] pub fn wait(&self, ignore_poisoning: bool) { diff --git a/library/std/src/thread/local/tests.rs b/library/std/src/thread/local/tests.rs index 25019b554bb..6abb9b85a2e 100644 --- a/library/std/src/thread/local/tests.rs +++ b/library/std/src/thread/local/tests.rs @@ -103,6 +103,9 @@ fn smoke_dtor() { #[test] fn circular() { + // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint + #![allow(static_mut_refs)] + struct S1(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); struct S2(&'static LocalKey<UnsafeCell<Option<S1>>>, &'static LocalKey<UnsafeCell<Option<S2>>>); thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell::new(None)); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 0fc63c5081b..33cb7e0bcca 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1503,6 +1503,54 @@ impl Thread { self.inner.name.as_str() } + /// Consumes the `Thread`, returning a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted + /// back into a `Thread` using [`Thread::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(thread_raw)] + /// + /// use std::thread::{self, Thread}; + /// + /// let thread = thread::current(); + /// let id = thread.id(); + /// let ptr = Thread::into_raw(thread); + /// unsafe { + /// assert_eq!(Thread::from_raw(ptr).id(), id); + /// } + /// ``` + #[unstable(feature = "thread_raw", issue = "97523")] + pub fn into_raw(self) -> *const () { + // Safety: We only expose an opaque pointer, which maintains the `Pin` invariant. + let inner = unsafe { Pin::into_inner_unchecked(self.inner) }; + Arc::into_raw(inner) as *const () + } + + /// Constructs a `Thread` from a raw pointer. + /// + /// The raw pointer must have been previously returned + /// by a call to [`Thread::into_raw`]. + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead + /// to memory unsafety, even if the returned `Thread` is never + /// accessed. + /// + /// Creating a `Thread` from a pointer other than one returned + /// from [`Thread::into_raw`] is **undefined behavior**. + /// + /// Calling this function twice on the same raw pointer can lead + /// to a double-free if both `Thread` instances are dropped. + #[unstable(feature = "thread_raw", issue = "97523")] + pub unsafe fn from_raw(ptr: *const ()) -> Thread { + // Safety: Upheld by caller. + unsafe { Thread { inner: Pin::new_unchecked(Arc::from_raw(ptr as *const Inner)) } } + } + fn cname(&self) -> Option<&CStr> { self.inner.name.as_cstr() } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index ae46670c25e..f28a0568a3c 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -280,6 +280,7 @@ impl Instant { /// ``` #[must_use] #[stable(feature = "time2", since = "1.8.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "instant_now")] pub fn now() -> Instant { Instant(time::Instant::now()) } diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 357c6175152..952db063636 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -84,9 +84,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -537,6 +540,12 @@ dependencies = [ ] [[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] name = "syn" version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 07f7444aaff..1959d0a9662 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -37,7 +37,7 @@ test = false # Most of the time updating these dependencies requires modifications to the # bootstrap codebase(e.g., https://github.com/rust-lang/rust/issues/124565); # otherwise, some targets will fail. That's why these dependencies are explicitly pinned. -cc = "=1.0.97" +cc = "=1.1.19" cmake = "=0.1.48" build_helper = { path = "../tools/build_helper" } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 3755f4a33cb..979671cfa9d 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -1058,6 +1058,13 @@ tool_doc!( is_library = true, crates = ["run_make_support"] ); +tool_doc!( + Compiletest, + "src/tools/compiletest", + rustc_tool = false, + is_library = true, + crates = ["compiletest"] +); #[derive(Ord, PartialOrd, Debug, Clone, Hash, PartialEq, Eq)] pub struct ErrorIndex { diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 08e24ecc340..d8752d03761 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -580,11 +580,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = command(llvm_config).arg("--version").run_capture_stdout(builder).stdout(); 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 >= 17 { + if major >= 18 { return; } } - panic!("\n\nbad LLVM version: {version}, need >=17.0\n\n") + panic!("\n\nbad LLVM version: {version}, need >=18\n\n") } fn configure_cmake( diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index a7e9352bb1c..2047345d78a 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1733,6 +1733,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let is_rustdoc = suite.ends_with("rustdoc-ui") || suite.ends_with("rustdoc-js"); + if mode == "run-make" { + let cargo = builder.ensure(tool::Cargo { compiler, target: compiler.host }); + cmd.arg("--cargo-path").arg(cargo); + } + // Avoid depending on rustdoc when we don't need it. if mode == "rustdoc" || mode == "run-make" diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 003b11ec7cf..2124890b94f 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -945,6 +945,7 @@ impl<'a> Builder<'a> { doc::Releases, doc::RunMakeSupport, doc::BuildHelper, + doc::Compiletest, ), Kind::Dist => describe!( dist::Docs, diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 555a6a7f8bd..dcecd7f8084 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2738,6 +2738,8 @@ impl Config { download_ci_llvm: Option<StringOrBool>, asserts: bool, ) -> bool { + let download_ci_llvm = download_ci_llvm.unwrap_or(StringOrBool::Bool(true)); + let if_unchanged = || { if self.rust_info.is_from_tarball() { // Git is needed for running "if-unchanged" logic. @@ -2761,10 +2763,7 @@ impl Config { }; match download_ci_llvm { - None => { - (self.channel == "dev" || self.download_rustc_commit.is_some()) && if_unchanged() - } - Some(StringOrBool::Bool(b)) => { + StringOrBool::Bool(b) => { if !b && self.download_rustc_commit.is_some() { panic!( "`llvm.download-ci-llvm` cannot be set to `false` if `rust.download-rustc` is set to `true` or `if-unchanged`." @@ -2774,8 +2773,8 @@ impl Config { // If download-ci-llvm=true we also want to check that CI llvm is available b && llvm::is_ci_llvm_available(self, asserts) } - Some(StringOrBool::String(s)) if s == "if-unchanged" => if_unchanged(), - Some(StringOrBool::String(other)) => { + StringOrBool::String(s) if s == "if-unchanged" => if_unchanged(), + StringOrBool::String(other) => { panic!("unrecognized option for download-ci-llvm: {:?}", other) } } diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs index f54a5d3b512..a45e73b5d95 100644 --- a/src/bootstrap/src/core/config/tests.rs +++ b/src/bootstrap/src/core/config/tests.rs @@ -32,7 +32,7 @@ fn download_ci_llvm() { assert!(!parse_llvm("llvm.download-ci-llvm = false")); assert_eq!(parse_llvm(""), if_unchanged); assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged); - assert!(!parse_llvm("rust.channel = \"stable\"")); + assert!(parse_llvm("rust.channel = \"stable\"")); assert_eq!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""), if_unchanged); assert_eq!( parse_llvm( diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 379cd568647..e6f7f105fa2 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -265,4 +265,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `dist.vendor` added to control whether bootstrap should vendor dependencies for dist tarball.", }, + ChangeInfo { + change_id: 130529, + severity: ChangeSeverity::Info, + summary: "If `llvm.download-ci-llvm` is not defined, it defaults to `true`.", + }, ]; diff --git a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile index c9a6a2dd069..32b5058bce7 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-musl/Dockerfile @@ -19,6 +19,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ patch \ libssl-dev \ pkg-config \ + zlib1g-dev \ && rm -rf /var/lib/apt/lists/* WORKDIR /build/ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile deleted file mode 100644 index 3acc2ceb135..00000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ /dev/null @@ -1,62 +0,0 @@ -FROM ubuntu:23.10 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-17-tools \ - llvm-17-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install powershell (universal package) so we can test x.ps1 on Linux -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV EXTERNAL_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-17 \ - --enable-llvm-link-shared \ - --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10 - -COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ -COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ - -RUN /scripts/build-gccjit.sh /scripts - -COPY scripts/x86_64-gnu-llvm.sh /tmp/script.sh -ENV SCRIPT /tmp/script.sh diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index 3476b10a3ad..487da580152 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -51,6 +51,7 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-18 \ --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile index 44328b078ad..4991908fe77 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile @@ -51,6 +51,7 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-19 \ --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index f65083eec47..6379f1ade1c 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -89,7 +89,7 @@ pr: - image: mingw-check-tidy continue_on_error: true <<: *job-linux-4c - - image: x86_64-gnu-llvm-17 + - image: x86_64-gnu-llvm-18 env: ENABLE_GCC_CODEGEN: "1" <<: *job-linux-16c @@ -261,11 +261,6 @@ auto: RUST_BACKTRACE: 1 <<: *job-linux-8c - - image: x86_64-gnu-llvm-17 - env: - RUST_BACKTRACE: 1 - <<: *job-linux-8c - - image: x86_64-gnu-nopt <<: *job-linux-4c diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 3e199539694..b1a5bd604eb 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -25,6 +25,7 @@ - [\*-apple-ios-macabi](platform-support/apple-ios-macabi.md) - [arm64e-apple-ios](platform-support/arm64e-apple-ios.md) - [\*-apple-tvos](platform-support/apple-tvos.md) + - [arm64e-apple-tvos](platform-support/arm64e-apple-tvos.md) - [\*-apple-watchos](platform-support/apple-watchos.md) - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 9a35b35af71..827a7065f3e 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -245,8 +245,9 @@ host tools. target | std | host | notes -------|:---:|:----:|------- -[`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md) | ✓ | ✓ | ARM64e Apple Darwin +[`arm64e-apple-ios`](platform-support/arm64e-apple-ios.md) | ✓ | | ARM64e Apple iOS +[`arm64e-apple-tvos`](platform-support/arm64e-apple-tvos.md) | ✓ | | ARM64e Apple tvOS [`aarch64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS [`aarch64-apple-tvos-sim`](platform-support/apple-tvos.md) | ✓ | | ARM64 tvOS Simulator [`aarch64-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | ARM64 Apple WatchOS diff --git a/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md b/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md new file mode 100644 index 00000000000..d49383fb853 --- /dev/null +++ b/src/doc/rustc/src/platform-support/arm64e-apple-tvos.md @@ -0,0 +1,37 @@ +# `arm64e-apple-tvos` + +**Tier: 3** + +ARM64e tvOS (10.0+) + +## Target maintainers + +- Artyom Tetyukhin ([@arttet](https://github.com/https://github.com/arttet)) + +## Requirements + +This target is cross-compiled and supports `std`. +To build this target Xcode 12 or higher on macOS is required. + +## Building the target + +You can build Rust with support for the targets by adding it to the `target` list in `config.toml`: + +```toml +[build] +target = [ "arm64e-apple-tvos" ] +``` + +## Building Rust programs + +Rust does not yet ship pre-compiled artifacts for this target. +To compile for this target, you will need to build Rust with the target enabled (see [Building the target](#building-the-target) above). + +## Testing + +The target does support running binaries on tvOS platforms with `arm64e` architecture. + +## Cross-compilation toolchains and C code + +The targets do support `C` code. +To build compatible `C` code, you have to use XCode with the same compiler and flags. diff --git a/src/doc/rustc/src/platform-support/trusty.md b/src/doc/rustc/src/platform-support/trusty.md index dec3b96ff64..6b37543b600 100644 --- a/src/doc/rustc/src/platform-support/trusty.md +++ b/src/doc/rustc/src/platform-support/trusty.md @@ -8,7 +8,8 @@ Environment (TEE) for Android. ## Target maintainers - Nicole LeGare (@randomPoison) -- Stephen Crane (@rinon) +- Andrei Homescu (@ahomescu) +- Chris Wailes (chriswailes@google.com) - As a fallback trusty-dev-team@google.com can be contacted ## Requirements diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4fafef65792..b5648bf2c13 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -262,7 +262,7 @@ pub(crate) fn create_config( output_file: None, output_dir: None, file_loader: None, - locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps, psess_created: None, hash_untracked_state: None, diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 05ef7289201..73eafaf78ca 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -143,7 +143,7 @@ pub(crate) fn run( output_file: None, output_dir: None, file_loader: None, - locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES, + locale_resources: rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps, psess_created: None, hash_untracked_state: None, diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index 5c0898f28fc..71a0e3d72de 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -122,23 +122,14 @@ impl<'a, 'tcx> HirCollector<'a, 'tcx> { // anything else, this will combine them for us. let attrs = Attributes::from_ast(ast_attrs); if let Some(doc) = attrs.opt_doc_value() { - // Use the outermost invocation, so that doctest names come from where the docs were written. - let span = ast_attrs - .iter() - .find(|attr| attr.doc_str().is_some()) - .map(|attr| attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span)) - .unwrap_or(DUMMY_SP); + let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp); self.collector.position = span; markdown::find_testable_code( &doc, &mut self.collector, self.codes, self.enable_per_target_ignores, - Some(&crate::html::markdown::ExtraInfo::new( - self.tcx, - def_id, - span_of_fragments(&attrs.doc_strings).unwrap_or(sp), - )), + Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)), ); } diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a402d799504..668bd391348 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2538,7 +2538,6 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c &cx.root_path(), highlight::DecorationInfo(decoration_info), sources::SourceContext::Embedded(sources::ScrapedInfo { - needs_prev_next_buttons: line_ranges.len() > 1, needs_expansion, offset: line_min, name: &call_data.display_name, diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 551bb56685c..0d6a4603cd2 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -292,7 +292,6 @@ where pub(crate) struct ScrapedInfo<'a> { pub(crate) offset: usize, - pub(crate) needs_prev_next_buttons: bool, pub(crate) name: &'a str, pub(crate) url: &'a str, pub(crate) title: &'a str, diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index 38154dee3e2..04b0eba7450 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -801,6 +801,9 @@ both the code example and the line numbers, so we need to remove the radius in t * and we include additional 10px for padding. */ max-height: calc(1.5em * 5 + 10px); } +.more-scraped-examples .scraped-example:not(.expanded) .example-wrap { + max-height: calc(1.5em * 10 + 10px); +} .rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers, .rustdoc:not(.src) .scraped-example:not(.expanded) .src-line-numbers > pre, @@ -828,10 +831,14 @@ both the code example and the line numbers, so we need to remove the radius in t -webkit-user-select: none; user-select: none; padding: 14px 8px; + padding-right: 2px; color: var(--src-line-numbers-span-color); } -.rustdoc .scraped-example .src-line-numbers { +.rustdoc .scraped-example .example-wrap .src-line-numbers { + padding: 0; +} +.rustdoc .src-line-numbers pre { padding: 14px 0; } .src-line-numbers a, .src-line-numbers span { @@ -890,7 +897,7 @@ both the code example and the line numbers, so we need to remove the radius in t } .docblock code, .docblock-short code, -pre, .rustdoc.src .example-wrap { +pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers { background-color: var(--code-block-background-color); } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 858753a1917..a0ec45b5ef3 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -1855,12 +1855,8 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Since the button will be added, no need to keep this listener around. elem.removeEventListener("mouseover", addCopyButton); - // If this is a scrapped example, there will already be a "button-holder" element. - let parent = elem.querySelector(".button-holder"); - if (!parent) { - parent = document.createElement("div"); - parent.className = "button-holder"; - } + const parent = document.createElement("div"); + parent.className = "button-holder"; const runButton = elem.querySelector(".test-arrow"); if (runButton !== null) { @@ -1876,6 +1872,12 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyButtonAnimation(copyButton); }); parent.appendChild(copyButton); + + if (!elem.parentElement.classList.contains("scraped-example")) { + return; + } + const scrapedWrapped = elem.parentElement; + window.updateScrapedExample(scrapedWrapped, parent); } function showHideCodeExampleButtons(event) { diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 06e42814d33..98c53b8656f 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -36,13 +36,30 @@ elt.querySelector(".rust").scrollTo(0, scrollOffset); } - function updateScrapedExample(example, isHidden) { - const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); + function createScrapeButton(parent, className, content) { + const button = document.createElement("button"); + button.className = className; + button.innerText = content; + parent.insertBefore(button, parent.firstChild); + return button; + } + + window.updateScrapedExample = (example, buttonHolder) => { let locIndex = 0; const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight")); const link = example.querySelector(".scraped-example-title a"); + let expandButton = null; + + if (!example.classList.contains("expanded")) { + expandButton = createScrapeButton(buttonHolder, "expand", "↕"); + } + const isHidden = example.parentElement.classList.contains("more-scraped-examples"); + const locs = example.locs; if (locs.length > 1) { + const next = createScrapeButton(buttonHolder, "next", "≻"); + const prev = createScrapeButton(buttonHolder, "prev", "≺"); + // Toggle through list of examples in a given file const onChangeLoc = changeIndex => { removeClass(highlights[locIndex], "focus"); @@ -57,22 +74,19 @@ link.innerHTML = title; }; - example.querySelector(".prev") - .addEventListener("click", () => { - onChangeLoc(() => { - locIndex = (locIndex - 1 + locs.length) % locs.length; - }); + prev.addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex - 1 + locs.length) % locs.length; }); + }); - example.querySelector(".next") - .addEventListener("click", () => { - onChangeLoc(() => { - locIndex = (locIndex + 1) % locs.length; - }); + next.addEventListener("click", () => { + onChangeLoc(() => { + locIndex = (locIndex + 1) % locs.length; }); + }); } - const expandButton = example.querySelector(".expand"); if (expandButton) { expandButton.addEventListener("click", () => { if (hasClass(example, "expanded")) { @@ -83,13 +97,16 @@ } }); } + }; + function setupLoc(example, isHidden) { + example.locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); // Start with the first example in view - scrollToLoc(example, locs[0][0], isHidden); + scrollToLoc(example, example.locs[0][0], isHidden); } const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example"); - onEachLazy(firstExamples, el => updateScrapedExample(el, false)); + onEachLazy(firstExamples, el => setupLoc(el, false)); onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => { // Allow users to click the left border of the <details> section to close it, // since the section can be large and finding the [+] button is annoying. @@ -102,11 +119,11 @@ const moreExamples = toggle.querySelectorAll(".scraped-example"); toggle.querySelector("summary").addEventListener("click", () => { // Wrapping in setTimeout ensures the update happens after the elements are actually - // visible. This is necessary since updateScrapedExample calls scrollToLoc which + // visible. This is necessary since setupLoc calls scrollToLoc which // depends on offsetHeight, a property that requires an element to be visible to // compute correctly. setTimeout(() => { - onEachLazy(moreExamples, el => updateScrapedExample(el, true)); + onEachLazy(moreExamples, el => setupLoc(el, true)); }); }, {once: true}); }); diff --git a/src/librustdoc/html/templates/scraped_source.html b/src/librustdoc/html/templates/scraped_source.html index e1fc2e69378..bd54bbf58d5 100644 --- a/src/librustdoc/html/templates/scraped_source.html +++ b/src/librustdoc/html/templates/scraped_source.html @@ -1,8 +1,8 @@ <div class="scraped-example{% if !info.needs_expansion +%} expanded{% endif %}" data-locs="{{info.locations}}"> {# #} <div class="scraped-example-title"> {{info.name +}} (<a href="{{info.url}}">{{info.title}}</a>) {# #} - </div> - <div class="example-wrap"> {# #} + </div> {# #} + <div class="example-wrap"> {# https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr Do not show "1 2 3 4 5 ..." in web search results. #} <div class="src-line-numbers" data-nosnippet> {# #} @@ -18,16 +18,5 @@ {{code_html|safe}} </code> {# #} </pre> {# #} - {% if info.needs_prev_next_buttons || info.needs_expansion %} - <div class="button-holder"> - {% if info.needs_prev_next_buttons %} - <button class="prev">≺</button> {# #} - <button class="next">≻</button> - {% endif %} - {% if info.needs_expansion %} - <button class="expand">↕</button> - {% endif %} - </div> - {% endif %} </div> {# #} </div> {# #} diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 92b21f7dbaa..eb5faeeaf9a 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -51,9 +51,7 @@ static HOSTS: &[&str] = &[ static TARGETS: &[&str] = &[ "aarch64-apple-darwin", - "arm64e-apple-darwin", "aarch64-apple-ios", - "arm64e-apple-ios", "aarch64-apple-ios-macabi", "aarch64-apple-ios-sim", "aarch64-unknown-fuchsia", @@ -68,6 +66,9 @@ static TARGETS: &[&str] = &[ "aarch64-unknown-none-softfloat", "aarch64-unknown-redox", "aarch64-unknown-uefi", + "arm64e-apple-darwin", + "arm64e-apple-ios", + "arm64e-apple-tvos", "arm-linux-androideabi", "arm-unknown-linux-gnueabi", "arm-unknown-linux-gnueabihf", diff --git a/src/tools/cargo b/src/tools/cargo -Subproject a9a418d1a22f29e7dfd034e3b93f15657e608a2 +Subproject eaee77dc1584be45949b75e4c4c9a841605e3a4 diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index b7c9d3d0385..9c5100a8c1a 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::{ - can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, - paths, peel_hir_expr_while, SpanlessEq, + can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, + peel_hir_expr_while, SpanlessEq, }; use core::fmt::{self, Write}; use rustc_errors::Applicability; @@ -11,7 +11,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{sym, Span, SyntaxContext, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -269,9 +269,9 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio key, call_ctxt: expr.span.ctxt(), }; - if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) { + if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) { Some((MapType::BTree, expr)) - } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) { + } else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) { Some((MapType::Hash, expr)) } else { None @@ -306,7 +306,7 @@ struct InsertExpr<'tcx> { fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { + if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) { Some(InsertExpr { map, key, value }) } else { None diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index d05c5a01f41..c6f03c3a7cf 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{higher, match_def_path, paths}; +use clippy_utils::higher; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { let arg = match expr.kind { ExprKind::MethodCall(_, _, [arg], _) => { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && match_def_path(cx, fn_def_id, &paths::PUSH_STR) + && cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) { arg } else { diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index f41fdf3203c..6d6820311b6 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -112,7 +112,7 @@ impl LateLintPass<'_> for InstantSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + && cx.tcx.is_diagnostic_item(sym::instant_now, fn_id) { true } else { diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index fb29d982417..f162948bb44 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -1,10 +1,5 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::higher::ForLoop; -use clippy_utils::match_any_def_paths; -use clippy_utils::paths::{ - HASHMAP_DRAIN, HASHMAP_ITER, HASHMAP_ITER_MUT, HASHMAP_KEYS, HASHMAP_VALUES, HASHMAP_VALUES_MUT, HASHSET_DRAIN, - HASHSET_ITER_TY, -}; use clippy_utils::ty::is_type_diagnostic_item; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -44,28 +39,23 @@ declare_lint_pass!(IterOverHashType => [ITER_OVER_HASH_TYPE]); impl LateLintPass<'_> for IterOverHashType { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + let hash_iter_tys = [ + sym::HashMap, + sym::HashSet, + sym::hashmap_keys_ty, + sym::hashmap_values_ty, + sym::hashmap_values_mut_ty, + sym::hashmap_iter_ty, + sym::hashmap_iter_mut_ty, + sym::hashmap_drain_ty, + sym::hashset_iter_ty, + sym::hashset_drain_ty, + ]; + if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && let Some(adt) = ty.ty_adt_def() - && let did = adt.did() - && (match_any_def_paths( - cx, - did, - &[ - &HASHMAP_KEYS, - &HASHMAP_VALUES, - &HASHMAP_VALUES_MUT, - &HASHMAP_ITER, - &HASHMAP_ITER_MUT, - &HASHMAP_DRAIN, - &HASHSET_ITER_TY, - &HASHSET_DRAIN, - ], - ) - .is_some() - || is_type_diagnostic_item(cx, ty, sym::HashMap) - || is_type_diagnostic_item(cx, ty, sym::HashSet)) + && hash_iter_tys.into_iter().any(|sym| is_type_diagnostic_item(cx, ty, sym)) { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs index 3d1c666dfea..8206c75927b 100644 --- a/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs +++ b/src/tools/clippy/clippy_lints/src/lines_filter_map_ok.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_diag_item_method, is_trait_method, match_def_path, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, is_trait_method, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::{Body, Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -96,7 +96,7 @@ fn should_lint(cx: &LateContext<'_>, args: &[Expr<'_>], method_str: &str) -> boo ExprKind::Path(qpath) => cx .qpath_res(qpath, fm_arg.hir_id) .opt_def_id() - .is_some_and(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)), + .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::result_ok_method, did)), // Detect `|x| x.ok()` ExprKind::Closure(Closure { body, .. }) => { if let Body { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index 57434f35544..55d1b9ee676 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{match_def_path, paths, SpanlessEq}; +use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::LateContext; -use rustc_span::Span; +use rustc_span::{sym, Symbol, Span}; use std::borrow::Cow; use super::MANUAL_WHILE_LET_SOME; @@ -47,20 +47,20 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>, ); } -fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: &[&str]) -> bool { +fn match_method_call(cx: &LateContext<'_>, expr: &Expr<'_>, method: Symbol) -> bool { if let ExprKind::MethodCall(..) = expr.kind && let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - match_def_path(cx, id, method) + cx.tcx.is_diagnostic_item(method, id) } else { false } } fn is_vec_pop_unwrap(cx: &LateContext<'_>, expr: &Expr<'_>, is_empty_recv: &Expr<'_>) -> bool { - if (match_method_call(cx, expr, &paths::OPTION_UNWRAP) || match_method_call(cx, expr, &paths::OPTION_EXPECT)) + if (match_method_call(cx, expr, sym::option_unwrap) || match_method_call(cx, expr, sym::option_expect)) && let ExprKind::MethodCall(_, unwrap_recv, ..) = expr.kind - && match_method_call(cx, unwrap_recv, &paths::VEC_POP) + && match_method_call(cx, unwrap_recv, sym::vec_pop) && let ExprKind::MethodCall(_, pop_recv, ..) = unwrap_recv.kind { // make sure they're the same `Vec` @@ -96,7 +96,7 @@ fn check_call_arguments(cx: &LateContext<'_>, stmt: &Stmt<'_>, is_empty_recv: &E pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, full_cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>, loop_span: Span) { if let ExprKind::Unary(UnOp::Not, cond) = full_cond.kind && let ExprKind::MethodCall(_, is_empty_recv, _, _) = cond.kind - && match_method_call(cx, cond, &paths::VEC_IS_EMPTY) + && match_method_call(cx, cond, sym::vec_is_empty) && let ExprKind::Block(body, _) = body.kind && let Some(stmt) = body.stmts.first() { diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs index db491a8c8f6..5198d7838a2 100644 --- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs +++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs @@ -1,7 +1,7 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs}; +use clippy_utils::{is_trait_method, peel_hir_expr_refs}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, Mutability, QPath}; @@ -56,7 +56,7 @@ impl LateLintPass<'_> for ManualMainSeparatorStr { && let Res::Def(DefKind::Const, receiver_def_id) = path.res && is_trait_method(cx, target, sym::ToString) && self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) - && match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) + && cx.tcx.is_diagnostic_item(sym::path_main_separator, receiver_def_id) && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() && ty.is_str() { diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index d4e53f8f74b..a35b0f914e0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -3,22 +3,22 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; -use clippy_utils::{match_def_path, paths, SpanlessEq}; +use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::ExprKind::Assign; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; -const ACCEPTABLE_METHODS: [&[&str]; 5] = [ - &paths::BINARYHEAP_ITER, - &paths::HASHSET_ITER, - &paths::BTREESET_ITER, - &paths::SLICE_INTO, - &paths::VEC_DEQUE_ITER, +const ACCEPTABLE_METHODS: [Symbol; 5] = [ + sym::binaryheap_iter, + sym::hashset_iter, + sym::btreeset_iter, + sym::slice_iter, + sym::vecdeque_iter, ]; declare_clippy_lint! { @@ -84,7 +84,7 @@ fn check_into_iter( ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) - && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) + && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id) && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn() @@ -127,14 +127,14 @@ fn check_iter( ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) - && (match_def_path(cx, copied_def_id, &paths::CORE_ITER_COPIED) - || match_def_path(cx, copied_def_id, &paths::CORE_ITER_CLONED)) + && (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id) + || cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id)) && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id) - && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) + && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &iter_expr.kind && let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id) - && match_acceptable_def_path(cx, iter_expr_def_id) + && match_acceptable_sym(cx, iter_expr_def_id) && match_acceptable_type(cx, left_expr, msrv) && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind @@ -189,10 +189,10 @@ fn check_to_owned( && cx.tcx.is_diagnostic_item(sym::to_owned_method, to_owned_def_id) && let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id) - && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER) + && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) && let hir::ExprKind::MethodCall(_, str_expr, [], _) = &chars_expr.kind && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id) - && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS) + && cx.tcx.is_diagnostic_item(sym::str_chars, chars_expr_def_id) && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs() && is_type_lang_item(cx, ty, hir::LangItem::String) && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) @@ -247,10 +247,10 @@ fn make_sugg( } } -fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { +fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { ACCEPTABLE_METHODS .iter() - .any(|&method| match_def_path(cx, collect_def_id, method)) + .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id)) } fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 85cabd2800a..02d433ecc5a 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -4,7 +4,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; +use clippy_utils::{eq_expr_value, higher}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -14,7 +14,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::Span; +use rustc_span::{sym, Span}; use std::iter; declare_clippy_lint! { @@ -76,9 +76,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { && self.msrv.meets(msrvs::STR_STRIP_PREFIX) && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) { - let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { + let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) { StripKind::Prefix - } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { + } else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) { StripKind::Suffix } else { return; @@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(_, arg, [], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && match_def_path(cx, method_def_id, &paths::STR_LEN) + && cx.tcx.is_diagnostic_item(sym::str_len, method_def_id) { Some(arg) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 4fbf661727d..77a62fbb4fb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,9 +1,8 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_copy; -use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks}; +use clippy_utils::{is_from_proc_macro, is_trait_method, peel_blocks}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; @@ -35,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & && let ExprKind::Closure(then_closure) = then_arg.kind && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value) && let Some(def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id) - && match_def_path(cx, def_id, &BOOL_THEN) + && cx.tcx.is_diagnostic_item(sym::bool_then, def_id) && !is_from_proc_macro(cx, expr) // Count the number of derefs needed to get to the bool because we need those in the suggestion && let needed_derefs = cx.typeck_results().expr_adjustments(recv) diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index cbeb48b6cc3..f1a3c81cebb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -126,17 +126,18 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - match_any_def_paths( - cx, - did, - &[ - &paths::TOKIO_IO_OPEN_OPTIONS_NEW, - &paths::OPEN_OPTIONS_NEW, - &paths::FILE_OPTIONS, - &paths::TOKIO_FILE_OPTIONS, - ], - ) - .is_some() + let std_file_options = [ + sym::file_options, + sym::open_options_new, + ]; + + let tokio_file_options: &[&[&str]] = &[ + &paths::TOKIO_IO_OPEN_OPTIONS_NEW, + &paths::TOKIO_FILE_OPTIONS, + ]; + + let is_std_options = std_file_options.into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, did)); + is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index cb57689b0c4..9a18d8a1421 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -2,12 +2,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; +use clippy_utils::{path_to_local_id, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; +use rustc_span::{sym, Symbol}; use super::OPTION_AS_REF_DEREF; @@ -31,14 +31,14 @@ pub(super) fn check( return; } - let deref_aliases: [&[&str]; 7] = [ - &paths::CSTRING_AS_C_STR, - &paths::OS_STRING_AS_OS_STR, - &paths::PATH_BUF_AS_PATH, - &paths::STRING_AS_STR, - &paths::STRING_AS_MUT_STR, - &paths::VEC_AS_SLICE, - &paths::VEC_AS_MUT_SLICE, + let deref_aliases: [Symbol; 7] = [ + sym::cstring_as_c_str, + sym::os_string_as_os_str, + sym::pathbuf_as_path, + sym::string_as_str, + sym::string_as_mut_str, + sym::vec_as_slice, + sym::vec_as_mut_slice, ]; let is_deref = match map_arg.kind { @@ -48,7 +48,7 @@ pub(super) fn check( .map_or(false, |fun_def_id| { cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path)) + || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) }) }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { @@ -69,7 +69,7 @@ pub(super) fn check( let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); cx.tcx.is_diagnostic_item(sym::deref_method, method_did) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path)) + || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs index e45c7962f13..8bc535ac47a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs @@ -8,7 +8,7 @@ use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; -use clippy_utils::{match_def_path, paths}; +use clippy_utils::is_enum_variant_ctor; use super::SEEK_FROM_CURRENT; @@ -36,8 +36,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { if let ExprKind::Call(f, args) = expr.kind && let ExprKind::Path(ref path) = f.kind - && let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() - && match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) + && let Some(ctor_call_id) = cx.qpath_res(path, f.hir_id).opt_def_id() + && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Current), ctor_call_id) { // check if argument of `SeekFrom::Current` is `0` if args.len() == 1 diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index cc4cb47e35c..7c09cc252e1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_expr_used_or_unified, match_def_path, paths}; +use clippy_utils::{is_expr_used_or_unified, is_enum_variant_ctor}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; @@ -28,8 +28,8 @@ pub(super) fn check<'tcx>( && implements_trait(cx, ty, seek_trait_id, &[]) && let ExprKind::Call(func, args1) = arg.kind && let ExprKind::Path(ref path) = func.kind - && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) + && let Some(ctor_call_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && is_enum_variant_ctor(cx, sym::SeekFrom, sym!(Start), ctor_call_id) && args1.len() == 1 && let ExprKind::Lit(lit) = args1[0].kind && let LitKind::Int(Pu128(0), LitIntType::Unsuffixed) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs index 81450fd8c6c..ccdf5529d53 100644 --- a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs +++ b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs @@ -1,13 +1,13 @@ use crate::methods::{single_char_insert_string, single_char_push_string}; -use clippy_utils::{match_def_path, paths}; use rustc_hir as hir; use rustc_lint::LateContext; +use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) { single_char_push_string::check(cx, expr, receiver, args); - } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) { + } else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) { single_char_insert_string::check(cx, expr, receiver, args); } } 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 69c5bc57e29..bf7555a29e2 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 @@ -6,7 +6,7 @@ use clippy_utils::source::{snippet, SpanRangeExt}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, peel_middle_ty_refs, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, }; use rustc_errors::Applicability; @@ -250,7 +250,7 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, if let Some((call, arg)) = skip_addr_of_ancestors(cx, expr) && !arg.span.from_expansion() && let ExprKind::Call(callee, _) = call.kind - && fn_def_id(cx, call).is_some_and(|did| match_def_path(cx, did, &paths::STRING_FROM_UTF8)) + && fn_def_id(cx, call).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::string_from_utf8, did)) && let Some(unwrap_call) = get_parent_expr(cx, call) && let ExprKind::MethodCall(unwrap_method_name, ..) = unwrap_call.kind && matches!(unwrap_method_name.ident.name, sym::unwrap | sym::expect) diff --git a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs index da66632d55f..9b64cc7589c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs +++ b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_trait_method, match_def_path, paths}; +use clippy_utils::is_trait_method; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -12,7 +12,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' let ty = cx.typeck_results().expr_ty(recv); if let Some(did) = ty.ty_adt_def() - && match_def_path(cx, did.did(), &paths::WAKER) + && cx.tcx.is_diagnostic_item(sym::Waker, did.did()) && let ExprKind::MethodCall(_, waker_ref, &[], _) = recv.kind && is_trait_method(cx, recv, sym::Clone) { diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index b915df52762..cfc15d92715 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; -use clippy_utils::{match_def_path, paths}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { ExprKind::Call(func, [param]) => { if let ExprKind::Path(ref path) = func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() - && match_def_path(cx, def_id, &paths::PERMISSIONS_FROM_MODE) + && cx.tcx.is_diagnostic_item(sym::permissions_from_mode, def_id) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) && param diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index cace85a2451..be97ad389bf 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths, sugg}; +use clippy_utils::sugg; use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::source_map::Spanned; +use rustc_span::{sym, source_map::Spanned}; use super::FLOAT_EQUALITY_WITHOUT_ABS; @@ -36,7 +36,7 @@ pub(crate) fn check<'tcx>( // right hand side matches either f32::EPSILON or f64::EPSILON && let ExprKind::Path(ref epsilon_path) = rhs.kind && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) - && (match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON)) + && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id))) // values of the subtractions on the left hand side are of the type float && let t_val_l = cx.typeck_results().expr_ty(val_l) diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index bfdc1cbeed7..4e24ddad83a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; -use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths}; +use clippy_utils::fn_has_unsatisfiable_preds; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{def_id, Body, FnDecl, LangItem}; @@ -102,8 +102,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { && is_type_lang_item(cx, arg_ty, LangItem::String)); let from_deref = !from_borrow - && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF) - || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING)); + && (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id) + || cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id)); if !from_borrow && !from_deref { continue; diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index 678681ea425..08de10f69b0 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths}; +use clippy_utils::{expr_or_init, fn_def_id}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -67,7 +67,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) { if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some() && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr) - && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) + && fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did)) && !len_expr.span.from_expansion() && let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr)) { @@ -91,7 +91,7 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { if !expr.span.from_expansion() && fn_def_id(cx, expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::iter_repeat, did)) && let ExprKind::Call(_, [repeat_expr]) = expr.kind - && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY)) + && fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did)) && !repeat_expr.span.from_expansion() { emit_lint( diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index b0e25c02265..04c16281ec4 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local, - path_to_local_id, paths, SpanlessEq, + get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, + path_to_local_id, SpanlessEq, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; @@ -150,10 +150,10 @@ impl SlowVectorInit { } if let ExprKind::Call(func, [len_expr]) = expr.kind - && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY) + && is_path_diagnostic_item(cx, func, sym::vec_with_capacity) { Some(InitializedSize::Initialized(len_expr)) - } else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) { + } else if matches!(expr.kind, ExprKind::Call(func, _) if is_path_diagnostic_item(cx, func, sym::vec_new)) { Some(InitializedSize::Uninitialized) } else { None diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs index 6b5e6c6ab20..f01cb457af8 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -1,6 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{match_def_path, paths}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() && inner_str.is_str() { - if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) { span_lint_and_sugg( cx, UNNECESSARY_OWNED_EMPTY_STRINGS, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 8970b4d1229..e09cc9edf3a 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -4,7 +4,7 @@ use crate::consts::{ConstEvalCtxt, Constant}; use crate::ty::is_type_diagnostic_item; -use crate::{is_expn_of, match_def_path, paths}; +use crate::is_expn_of; use rustc_ast::ast; use rustc_hir as hir; @@ -297,10 +297,10 @@ impl<'a> VecArgs<'a> { && is_expn_of(fun.span, "vec").is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() { - return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 { + return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { // `vec![elem; size]` case Some(VecArgs::Repeat(&args[0], &args[1])) - } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { + } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 { // `vec![a, b, c]` case if let ExprKind::Call(_, [arg]) = &args[0].kind && let ExprKind::Array(args) = arg.kind @@ -309,7 +309,7 @@ impl<'a> VecArgs<'a> { } else { None } - } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() { + } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() { Some(VecArgs::Vec(&[])) } else { None diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 5db14872c36..0d0d6219c5e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -263,24 +263,18 @@ pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> } } -pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool { - if let Res::Def(DefKind::Ctor(..), id) = res - && let Some(id) = cx.tcx.opt_parent(id) - { - cx.tcx.is_diagnostic_item(diag_item, id) - } else { - false - } -} -/// Checks if a `QPath` resolves to a constructor of a diagnostic item. -pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool { - if let QPath::Resolved(_, path) = qpath { - if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { - return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id)); - } - } - false +/// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. +pub fn is_enum_variant_ctor(cx: &LateContext<'_>, enum_item: Symbol, variant_name: Symbol, ctor_call_id: DefId) -> bool { + let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else { + return false; + }; + + let variants = cx.tcx.adt_def(enum_def_id).variants().iter(); + variants + .filter(|variant| variant.name == variant_name) + .filter_map(|variant| variant.ctor.as_ref()) + .any(|(_, ctor_def_id)| *ctor_def_id == ctor_call_id) } /// Checks if the `DefId` matches the given diagnostic item or it's constructor. diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index 684c645c199..a30edc48fff 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -4,6 +4,7 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information. +// Paths inside rustc pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"]; pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "Unspecified"], @@ -12,56 +13,36 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [ ["rustc_lint_defs", "Applicability", "MachineApplicable"], ]; pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"]; -pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "iter"]; -pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"]; -pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"]; -pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"]; -pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"]; -pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"]; -pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"]; -pub const CORE_RESULT_OK_METHOD: [&str; 4] = ["core", "result", "Result", "ok"]; -pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"]; pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"]; pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]; -pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"]; -pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"]; -pub const FILE_OPTIONS: [&str; 4] = ["std", "fs", "File", "options"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates -pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; -pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; -pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"]; -pub const HASHMAP_ITER: [&str; 5] = ["std", "collections", "hash", "map", "Iter"]; -pub const HASHMAP_ITER_MUT: [&str; 5] = ["std", "collections", "hash", "map", "IterMut"]; -pub const HASHMAP_KEYS: [&str; 5] = ["std", "collections", "hash", "map", "Keys"]; -pub const HASHMAP_VALUES: [&str; 5] = ["std", "collections", "hash", "map", "Values"]; -pub const HASHMAP_DRAIN: [&str; 5] = ["std", "collections", "hash", "map", "Drain"]; -pub const HASHMAP_VALUES_MUT: [&str; 5] = ["std", "collections", "hash", "map", "ValuesMut"]; -pub const HASHSET_ITER_TY: [&str; 5] = ["std", "collections", "hash", "set", "Iter"]; -pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"]; -pub const HASHSET_DRAIN: [&str; 5] = ["std", "collections", "hash", "set", "Drain"]; pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"]; pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"]; -pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"]; -pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"]; pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"]; pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"]; +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; + +// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. +// ... none currently! + +// Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"]; -pub const OPEN_OPTIONS_NEW: [&str; 4] = ["std", "fs", "OpenOptions", "new"]; -pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"]; -pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"]; + +// Paths in external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"]; pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"]; pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"]; -pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"]; -pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"]; -pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"]; -#[cfg_attr(not(unix), allow(clippy::invalid_paths))] -pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"]; -pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"]; pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"]; pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"]; pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"]; @@ -70,24 +51,6 @@ pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"]; pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"]; pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"]; pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"]; -pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"]; -pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"]; -pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"]; -pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"]; -pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; -pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; -pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; -pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"]; -pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"]; -pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"]; -pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"]; -pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"]; -pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"]; -pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"]; -pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"]; -pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; -pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; -pub const STRING_FROM_UTF8: [&str; 4] = ["alloc", "string", "String", "from_utf8"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates @@ -98,16 +61,3 @@ pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_wri pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"]; #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"]; -pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; -pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"]; -pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"]; -pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; -pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"]; -pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"]; -pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"]; -pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"]; -pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"]; -pub const WAKER: [&str; 4] = ["core", "task", "wake", "Waker"]; -pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"]; -pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"]; -pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"]; diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index f7e338935a7..d92c03f4888 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,5 +236,16 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 25 previous errors +error: creating a shared reference to mutable static is discouraged + --> tests/ui/checked_unwrap/simple_conditionals.rs:174:12 + | +LL | if X.is_some() { + | ^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = note: `-D static-mut-refs` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(static_mut_refs)]` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 6b8a103d4a9..87d3517cd5f 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -4,6 +4,7 @@ #![allow(deref_nullptr)] #![allow(clippy::unnecessary_operation)] #![allow(dropping_copy_types)] +#![allow(clippy::assign_op_pattern)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr index e732bde0707..a9417a9ef91 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:37:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5 | LL | / unsafe { LL | | STATIC += 1; @@ -8,12 +8,12 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:38:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:39:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:39:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ @@ -21,7 +21,7 @@ LL | not_very_safe(); = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:46:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:47:5 | LL | / unsafe { LL | | drop(u.u); @@ -30,18 +30,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:47:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:48:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:48:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:49:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:53:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:54:5 | LL | / unsafe { LL | | asm!("nop"); @@ -51,23 +51,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:57:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:63:5 | LL | / unsafe { LL | | drop(u.u); @@ -79,55 +79,55 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:63:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:65:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:107:5 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:107:14 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:39 + --> tests/ui/multiple_unsafe_ops_per_block.rs:107:39 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) } | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:124:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:125:5 | LL | / unsafe { LL | | x(); @@ -136,18 +136,18 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:125:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:126:9 | LL | x(); | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:126:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9 | LL | x(); | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:135:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:136:9 | LL | / unsafe { LL | | T::X(); @@ -156,18 +156,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:136:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:137:13 | LL | T::X(); | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:137:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:138:13 | LL | T::X(); | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:146:5 | LL | / unsafe { LL | | x.0(); @@ -176,12 +176,12 @@ LL | | } | |_____^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:146:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:147:9 | LL | x.0(); | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:147:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:148:9 | LL | x.0(); | ^^^^^ diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index c057eba4aca..adb266ffbc8 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -1,5 +1,10 @@ #![feature(never_type)] -#![allow(unused_mut, clippy::redundant_allocation, clippy::needless_pass_by_ref_mut)] +#![allow( + unused_mut, + clippy::redundant_allocation, + clippy::needless_pass_by_ref_mut, + static_mut_refs +)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 36019652006..49bb16af788 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -1,5 +1,10 @@ #![feature(never_type)] -#![allow(unused_mut, clippy::redundant_allocation, clippy::needless_pass_by_ref_mut)] +#![allow( + unused_mut, + clippy::redundant_allocation, + clippy::needless_pass_by_ref_mut, + static_mut_refs +)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr index c64636ba442..2117e37866e 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.stderr +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:11:1 + --> tests/ui/must_use_candidates.rs:16:1 | LL | pub fn pure(i: u8) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn pure(i: u8) -> u8` @@ -8,25 +8,25 @@ LL | pub fn pure(i: u8) -> u8 { = help: to override `-D warnings` add `#[allow(clippy::must_use_candidate)]` error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:5 + --> tests/ui/must_use_candidates.rs:21:5 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn inherent_pure(&self) -> u8` error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:47:1 + --> tests/ui/must_use_candidates.rs:52:1 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool` error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:59:1 + --> tests/ui/must_use_candidates.rs:64:1 | LL | pub fn rcd(_x: Rc<u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn rcd(_x: Rc<u32>) -> bool` error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:1 + --> tests/ui/must_use_candidates.rs:72:1 | LL | pub fn arcd(_x: Arc<u32>) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add the attribute: `#[must_use] pub fn arcd(_x: Arc<u32>) -> bool` diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed index 9787bb635e7..3d1c78bd12d 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed @@ -1,4 +1,6 @@ #![allow(unused)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] #[derive(Debug)] struct Foo; diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs index b5a4827fa94..5932f14b8d9 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.rs @@ -1,4 +1,6 @@ #![allow(unused)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] #[derive(Debug)] struct Foo; diff --git a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr index 5c5e2f2a573..48871eba2dc 100644 --- a/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr @@ -1,5 +1,5 @@ error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:6:17 + --> tests/ui/redundant_static_lifetimes.rs:8:17 | LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR: Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` @@ -8,103 +8,103 @@ LL | const VAR_ONE: &'static str = "Test constant #1"; // ERROR: Consider removi = help: to override `-D warnings` add `#[allow(clippy::redundant_static_lifetimes)]` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:10:21 + --> tests/ui/redundant_static_lifetimes.rs:12:21 | LL | const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:12:32 + --> tests/ui/redundant_static_lifetimes.rs:14:32 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:12:47 + --> tests/ui/redundant_static_lifetimes.rs:14:47 | LL | const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR: Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:14:17 + --> tests/ui/redundant_static_lifetimes.rs:16:17 | LL | const VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:16:20 + --> tests/ui/redundant_static_lifetimes.rs:18:20 | LL | const VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:18:19 + --> tests/ui/redundant_static_lifetimes.rs:20:19 | LL | const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR: Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:20:19 + --> tests/ui/redundant_static_lifetimes.rs:22:19 | LL | const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: constants have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:22:19 + --> tests/ui/redundant_static_lifetimes.rs:24:19 | LL | const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:24:25 + --> tests/ui/redundant_static_lifetimes.rs:26:25 | LL | static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR: Consider removing 'static. | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:28:29 + --> tests/ui/redundant_static_lifetimes.rs:30:29 | LL | static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR: Consider removing 'static | -^^^^^^^---- help: consider removing `'static`: `&str` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:30:25 + --> tests/ui/redundant_static_lifetimes.rs:32:25 | LL | static STATIC_VAR_SIX: &'static u8 = &5; | -^^^^^^^--- help: consider removing `'static`: `&u8` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:32:28 + --> tests/ui/redundant_static_lifetimes.rs:34:28 | LL | static STATIC_VAR_HEIGHT: &'static Foo = &Foo {}; | -^^^^^^^---- help: consider removing `'static`: `&Foo` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:34:27 + --> tests/ui/redundant_static_lifetimes.rs:36:27 | LL | static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR: Consider removing 'static. | -^^^^^^^----- help: consider removing `'static`: `&[u8]` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:36:27 + --> tests/ui/redundant_static_lifetimes.rs:38:27 | LL | static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR: Consider removing 'static. | -^^^^^^^--------- help: consider removing `'static`: `&(u8, u8)` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:38:27 + --> tests/ui/redundant_static_lifetimes.rs:40:27 | LL | static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR: Consider removing 'static. | -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:40:31 + --> tests/ui/redundant_static_lifetimes.rs:42:31 | LL | static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0]; | -^^^^^^^---------- help: consider removing `'static`: `&mut [u32]` error: statics have by default a `'static` lifetime - --> tests/ui/redundant_static_lifetimes.rs:69:16 + --> tests/ui/redundant_static_lifetimes.rs:71:16 | LL | static V: &'static u8 = &17; | -^^^^^^^--- help: consider removing `'static`: `&u8` diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index ce00fde2f99..eff617a8016 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -1,5 +1,7 @@ #![deny(clippy::useless_conversion)] #![allow(clippy::needless_if, clippy::unnecessary_wraps)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] fn test_generic<T: Copy>(val: T) -> T { let _ = val; diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index 39979619586..64b06620789 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -1,5 +1,7 @@ #![deny(clippy::useless_conversion)] #![allow(clippy::needless_if, clippy::unnecessary_wraps)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] fn test_generic<T: Copy>(val: T) -> T { let _ = T::from(val); diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 82f945c5e89..b149357bcf4 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:5:13 + --> tests/ui/useless_conversion.rs:7:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,217 +11,217 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:6:5 + --> tests/ui/useless_conversion.rs:8:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:18:22 + --> tests/ui/useless_conversion.rs:20:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:48:22 + --> tests/ui/useless_conversion.rs:50:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:53:21 + --> tests/ui/useless_conversion.rs:55:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:59:22 + --> tests/ui/useless_conversion.rs:61:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range<i32>` - --> tests/ui/useless_conversion.rs:65:13 + --> tests/ui/useless_conversion.rs:67:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range<i32>` - --> tests/ui/useless_conversion.rs:70:17 + --> tests/ui/useless_conversion.rs:72:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:132:21 + --> tests/ui/useless_conversion.rs:134:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:133:21 + --> tests/ui/useless_conversion.rs:135:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:134:13 + --> tests/ui/useless_conversion.rs:136:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:135:13 + --> tests/ui/useless_conversion.rs:137:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:136:13 + --> tests/ui/useless_conversion.rs:138:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter<i32>` - --> tests/ui/useless_conversion.rs:137:13 + --> tests/ui/useless_conversion.rs:139:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:138:21 + --> tests/ui/useless_conversion.rs:140:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:143:13 + --> tests/ui/useless_conversion.rs:145:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:149:23 + --> tests/ui/useless_conversion.rs:151:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:151:13 + --> tests/ui/useless_conversion.rs:153:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter<Foo<'a'>>` - --> tests/ui/useless_conversion.rs:153:13 + --> tests/ui/useless_conversion.rs:155:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:185:7 + --> tests/ui/useless_conversion.rs:187:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:175:13 + --> tests/ui/useless_conversion.rs:177:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:186:7 + --> tests/ui/useless_conversion.rs:188:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:176:18 + --> tests/ui/useless_conversion.rs:178:18 | LL | fn c(_: impl IntoIterator<Item = i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:187:7 + --> tests/ui/useless_conversion.rs:189:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:179:12 + --> tests/ui/useless_conversion.rs:181:12 | LL | T: IntoIterator<Item = i32>, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:190:7 + --> tests/ui/useless_conversion.rs:192:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:175:13 + --> tests/ui/useless_conversion.rs:177:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:191:7 + --> tests/ui/useless_conversion.rs:193:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:175:13 + --> tests/ui/useless_conversion.rs:177:13 | LL | fn b<T: IntoIterator<Item = i32>>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:237:24 + --> tests/ui/useless_conversion.rs:239:24 | LL | foo2::<i32, _>([1, 2, 3].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:216:12 + --> tests/ui/useless_conversion.rs:218:12 | LL | I: IntoIterator<Item = i32> + Helper<X>, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:245:14 + --> tests/ui/useless_conversion.rs:247:14 | LL | foo3([1, 2, 3].into_iter()); | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:225:12 + --> tests/ui/useless_conversion.rs:227:12 | LL | I: IntoIterator<Item = i32>, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:254:16 + --> tests/ui/useless_conversion.rs:256:16 | LL | S1.foo([1, 2].into_iter()); | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:251:27 + --> tests/ui/useless_conversion.rs:253:27 | LL | pub fn foo<I: IntoIterator>(&self, _: I) {} | ^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:273:44 + --> tests/ui/useless_conversion.rs:275:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:260:20 + --> tests/ui/useless_conversion.rs:262:20 | LL | J: IntoIterator, | ^^^^^^^^^^^^ diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 5c18179b6fe..414f9f3a7f1 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -183,6 +183,9 @@ pub struct Config { /// The rustc executable. pub rustc_path: PathBuf, + /// The cargo executable. + pub cargo_path: Option<PathBuf>, + /// The rustdoc executable. pub rustdoc_path: Option<PathBuf>, diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 933913eb47c..93df6aa7255 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -125,7 +125,7 @@ pub struct TestProps { // Build documentation for all specified aux-builds as well pub build_aux_docs: bool, /// Build the documentation for each crate in a unique output directory. - /// Uses <root output directory>/docs/<test name>/doc + /// Uses `<root output directory>/docs/<test name>/doc`. pub unique_doc_out_dir: bool, // Flag to force a crate to be built with the host architecture pub force_host: bool, @@ -1304,12 +1304,12 @@ pub fn llvm_has_libzstd(config: &Config) -> bool { false } -/// Takes a directive of the form "<version1> [- <version2>]", -/// returns the numeric representation of <version1> and <version2> as -/// tuple: (<version1> as u32, <version2> as u32) +/// Takes a directive of the form `"<version1> [- <version2>]"`, +/// returns the numeric representation of `<version1>` and `<version2>` as +/// tuple: `(<version1> as u32, <version2> as u32)`. /// -/// If the <version2> part is omitted, the second component of the tuple -/// is the same as <version1>. +/// If the `<version2>` part is omitted, the second component of the tuple +/// is the same as `<version1>`. fn extract_version_range<F>(line: &str, parse: F) -> Option<(u32, u32)> where F: Fn(&str) -> Option<u32>, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 250b5084d13..3339116d542 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -47,6 +47,7 @@ pub fn parse_config(args: Vec<String>) -> Config { opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH") .reqopt("", "run-lib-path", "path to target shared libraries", "PATH") .reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH") + .optopt("", "cargo-path", "path to cargo to use for compiling", "PATH") .optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH") .optopt("", "coverage-dump-path", "path to coverage-dump to use in tests", "PATH") .reqopt("", "python", "path to python to use for doc tests", "PATH") @@ -260,6 +261,7 @@ pub fn parse_config(args: Vec<String>) -> Config { compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")), run_lib_path: make_absolute(opt_path(matches, "run-lib-path")), rustc_path: opt_path(matches, "rustc-path"), + cargo_path: matches.opt_str("cargo-path").map(PathBuf::from), rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from), coverage_dump_path: matches.opt_str("coverage-dump-path").map(PathBuf::from), python: matches.opt_str("python").unwrap(), @@ -364,6 +366,7 @@ pub fn log_config(config: &Config) { logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path)); logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); + logv(c, format!("cargo_path: {:?}", config.cargo_path)); logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path)); logv(c, format!("src_base: {:?}", config.src_base.display())); logv(c, format!("build_base: {:?}", config.build_base.display())); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index c18f569e528..7b23aa34639 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; -use std::ffi::{OsStr, OsString}; -use std::fs::{self, create_dir_all, File, OpenOptions}; +use std::ffi::OsString; +use std::fs::{self, create_dir_all, File}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::io::prelude::*; use std::io::{self, BufReader}; @@ -14,10 +14,7 @@ use std::{env, iter, str}; use anyhow::Context; use colored::Colorize; -use glob::glob; -use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; use regex::{Captures, Regex}; -use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; use tracing::*; use crate::common::{ @@ -31,12 +28,29 @@ use crate::compute_diff::{write_diff, write_filtered_diff}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::read2::{read2_abbreviated, Truncated}; -use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt}; -use crate::{extract_gdb_version, is_android_gdb_target, json, ColorConfig}; +use crate::util::{add_dylib_path, logv, static_regex, PathBufExt}; +use crate::{json, ColorConfig}; -mod coverage; mod debugger; -use debugger::DebuggerCommands; + +// Helper modules that implement test running logic for each test suite. +// tidy-alphabet-start +mod assembly; +mod codegen; +mod codegen_units; +mod coverage; +mod crash; +mod debuginfo; +mod incremental; +mod js_doc; +mod mir_opt; +mod pretty; +mod run_make; +mod rustdoc; +mod rustdoc_json; +mod ui; +mod valgrind; +// tidy-alphabet-end #[cfg(test)] mod tests; @@ -325,81 +339,6 @@ impl<'test> TestCx<'test> { } } - fn run_cfail_test(&self) { - let pm = self.pass_mode(); - let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); - self.check_if_test_should_compile(&proc_res, pm); - self.check_no_compiler_crash(&proc_res, self.props.should_ice); - - let output_to_check = self.get_output(&proc_res); - let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); - if !expected_errors.is_empty() { - if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() - { - self.fatal("both error pattern and expected errors specified"); - } - self.check_expected_errors(expected_errors, &proc_res); - } else { - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } - if self.props.should_ice { - match proc_res.status.code() { - Some(101) => (), - _ => self.fatal("expected ICE"), - } - } - - self.check_forbid_output(&output_to_check, &proc_res); - } - - fn run_crash_test(&self) { - let pm = self.pass_mode(); - let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); - - if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() { - eprintln!("{}", proc_res.status); - eprintln!("{}", proc_res.stdout); - eprintln!("{}", proc_res.stderr); - eprintln!("{}", proc_res.cmdline); - } - - // if a test does not crash, consider it an error - if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { - self.fatal(&format!( - "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \ - add a doc-comment to the start of the test explaining why it exists and \ - move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \ - ensures that the corresponding ticket is auto-closed upon merge." - )); - } - } - - fn run_rfail_test(&self) { - let pm = self.pass_mode(); - let should_run = self.run_if_enabled(); - let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm)); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - if let WillExecute::Disabled = should_run { - return; - } - - let proc_res = self.exec_compiled_test(); - - // The value our Makefile configures valgrind to return on failure - const VALGRIND_ERR: i32 = 100; - if proc_res.status.code() == Some(VALGRIND_ERR) { - self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res); - } - - let output_to_check = self.get_output(&proc_res); - self.check_correct_failure_status(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } - fn get_output(&self, proc_res: &ProcRes) -> String { if self.props.check_stdout { format!("{}{}", proc_res.stdout, proc_res.stderr) @@ -423,73 +362,6 @@ impl<'test> TestCx<'test> { } } - fn run_cpass_test(&self) { - let emit_metadata = self.should_emit_metadata(self.pass_mode()); - let proc_res = self.compile_test(WillExecute::No, emit_metadata); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - // FIXME(#41968): Move this check to tidy? - if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() { - self.fatal("compile-pass tests with expected warnings should be moved to ui/"); - } - } - - fn run_rpass_test(&self) { - let emit_metadata = self.should_emit_metadata(self.pass_mode()); - let should_run = self.run_if_enabled(); - let proc_res = self.compile_test(should_run, emit_metadata); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - // FIXME(#41968): Move this check to tidy? - if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() { - self.fatal("run-pass tests with expected warnings should be moved to ui/"); - } - - if let WillExecute::Disabled = should_run { - return; - } - - let proc_res = self.exec_compiled_test(); - if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); - } - } - - fn run_valgrind_test(&self) { - assert!(self.revision.is_none(), "revisions not relevant here"); - - if self.config.valgrind_path.is_none() { - assert!(!self.config.force_valgrind); - return self.run_rpass_test(); - } - - let should_run = self.run_if_enabled(); - let mut proc_res = self.compile_test(should_run, Emit::None); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - if let WillExecute::Disabled = should_run { - return; - } - - let mut new_config = self.config.clone(); - new_config.runner = new_config.valgrind_path.clone(); - let new_cx = TestCx { config: &new_config, ..*self }; - proc_res = new_cx.exec_compiled_test(); - - if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); - } - } - /// Runs a [`Command`] and waits for it to finish, then converts its exit /// status and output streams into a [`ProcRes`]. /// @@ -517,104 +389,6 @@ impl<'test> TestCx<'test> { proc_res } - fn run_pretty_test(&self) { - if self.props.pp_exact.is_some() { - logv(self.config, "testing for exact pretty-printing".to_owned()); - } else { - logv(self.config, "testing for converging pretty-printing".to_owned()); - } - - let rounds = match self.props.pp_exact { - Some(_) => 1, - None => 2, - }; - - let src = fs::read_to_string(&self.testpaths.file).unwrap(); - let mut srcs = vec![src]; - - let mut round = 0; - while round < rounds { - logv( - self.config, - format!("pretty-printing round {} revision {:?}", round, self.revision), - ); - let read_from = - if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) }; - - let proc_res = self.print_source(read_from, &self.props.pretty_mode); - if !proc_res.status.success() { - self.fatal_proc_rec( - &format!( - "pretty-printing failed in round {} revision {:?}", - round, self.revision - ), - &proc_res, - ); - } - - let ProcRes { stdout, .. } = proc_res; - srcs.push(stdout); - round += 1; - } - - let mut expected = match self.props.pp_exact { - Some(ref file) => { - let filepath = self.testpaths.file.parent().unwrap().join(file); - fs::read_to_string(&filepath).unwrap() - } - None => srcs[srcs.len() - 2].clone(), - }; - let mut actual = srcs[srcs.len() - 1].clone(); - - if self.props.pp_exact.is_some() { - // Now we have to care about line endings - let cr = "\r".to_owned(); - actual = actual.replace(&cr, ""); - expected = expected.replace(&cr, ""); - } - - if !self.config.bless { - self.compare_source(&expected, &actual); - } else if expected != actual { - let filepath_buf; - let filepath = match &self.props.pp_exact { - Some(file) => { - filepath_buf = self.testpaths.file.parent().unwrap().join(file); - &filepath_buf - } - None => &self.testpaths.file, - }; - fs::write(filepath, &actual).unwrap(); - } - - // If we're only making sure that the output matches then just stop here - if self.props.pretty_compare_only { - return; - } - - // Finally, let's make sure it actually appears to remain valid code - let proc_res = self.typecheck_source(actual); - if !proc_res.status.success() { - self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res); - } - - if !self.props.pretty_expanded { - return; - } - - // additionally, run `-Zunpretty=expanded` and try to build it. - let proc_res = self.print_source(ReadFrom::Path, "expanded"); - if !proc_res.status.success() { - self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res); - } - - let ProcRes { stdout: expanded_src, .. } = proc_res; - let proc_res = self.typecheck_source(expanded_src); - if !proc_res.status.success() { - self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res); - } - } - fn print_source(&self, read_from: ReadFrom, pretty_type: &str) -> ProcRes { let aux_dir = self.aux_output_dir_name(); let input: &str = match read_from { @@ -727,500 +501,6 @@ impl<'test> TestCx<'test> { self.compose_and_run_compiler(rustc, Some(src), self.testpaths) } - fn run_debuginfo_test(&self) { - match self.config.debugger.unwrap() { - Debugger::Cdb => self.run_debuginfo_cdb_test(), - Debugger::Gdb => self.run_debuginfo_gdb_test(), - Debugger::Lldb => self.run_debuginfo_lldb_test(), - } - } - - fn run_debuginfo_cdb_test(&self) { - let config = Config { - target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), - host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), - ..self.config.clone() - }; - - let test_cx = TestCx { config: &config, ..*self }; - - test_cx.run_debuginfo_cdb_test_no_opt(); - } - - fn run_debuginfo_cdb_test_no_opt(&self) { - let exe_file = self.make_exe_name(); - - // Existing PDB files are update in-place. When changing the debuginfo - // the compiler generates for something, this can lead to the situation - // where both the old and the new version of the debuginfo for the same - // type is present in the PDB, which is very confusing. - // Therefore we delete any existing PDB file before compiling the test - // case. - // FIXME: If can reliably detect that MSVC's link.exe is used, then - // passing `/INCREMENTAL:NO` might be a cleaner way to do this. - let pdb_file = exe_file.with_extension(".pdb"); - if pdb_file.exists() { - std::fs::remove_file(pdb_file).unwrap(); - } - - // compile test file (it should have 'compile-flags:-g' in the header) - let should_run = self.run_if_enabled(); - let compile_result = self.compile_test(should_run, Emit::None); - if !compile_result.status.success() { - self.fatal_proc_rec("compilation failed!", &compile_result); - } - if let WillExecute::Disabled = should_run { - return; - } - - let prefixes = { - static PREFIXES: &[&str] = &["cdb", "cdbg"]; - // No "native rust support" variation for CDB yet. - PREFIXES - }; - - // Parse debugger commands etc from test files - let dbg_cmds = DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - prefixes, - self.revision, - ) - .unwrap_or_else(|e| self.fatal(&e)); - - // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands - let mut script_str = String::with_capacity(2048); - script_str.push_str("version\n"); // List CDB (and more) version info in test output - script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug - - // If a .js file exists next to the source file being tested, then this is a JavaScript - // debugging extension that needs to be loaded. - let mut js_extension = self.testpaths.file.clone(); - js_extension.set_extension("cdb.js"); - if js_extension.exists() { - script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy())); - } - - // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); - for line in &dbg_cmds.breakpoint_lines { - script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line)); - } - - // Append the other `cdb-command:`s - for line in &dbg_cmds.commands { - script_str.push_str(line); - script_str.push('\n'); - } - - script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any) - - // Write the script into a file - debug!("script_str = {}", script_str); - self.dump_output_file(&script_str, "debugger.script"); - let debugger_script = self.make_out_name("debugger.script"); - - let cdb_path = &self.config.cdb.as_ref().unwrap(); - let mut cdb = Command::new(cdb_path); - cdb.arg("-lines") // Enable source line debugging. - .arg("-cf") - .arg(&debugger_script) - .arg(&exe_file); - - let debugger_run_result = self.compose_and_run( - cdb, - self.config.run_lib_path.to_str().unwrap(), - None, // aux_path - None, // input - ); - - if !debugger_run_result.status.success() { - self.fatal_proc_rec("Error while running CDB", &debugger_run_result); - } - - if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { - self.fatal_proc_rec(&e, &debugger_run_result); - } - } - - fn run_debuginfo_gdb_test(&self) { - let config = Config { - target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), - host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), - ..self.config.clone() - }; - - let test_cx = TestCx { config: &config, ..*self }; - - test_cx.run_debuginfo_gdb_test_no_opt(); - } - - fn run_debuginfo_gdb_test_no_opt(&self) { - let dbg_cmds = DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - &["gdb"], - self.revision, - ) - .unwrap_or_else(|e| self.fatal(&e)); - let mut cmds = dbg_cmds.commands.join("\n"); - - // compile test file (it should have 'compile-flags:-g' in the header) - let should_run = self.run_if_enabled(); - let compiler_run_result = self.compile_test(should_run, Emit::None); - if !compiler_run_result.status.success() { - self.fatal_proc_rec("compilation failed!", &compiler_run_result); - } - if let WillExecute::Disabled = should_run { - return; - } - - let exe_file = self.make_exe_name(); - - let debugger_run_result; - if is_android_gdb_target(&self.config.target) { - cmds = cmds.replace("run", "continue"); - - let tool_path = match self.config.android_cross_path.to_str() { - Some(x) => x.to_owned(), - None => self.fatal("cannot find android cross path"), - }; - - // write debugger script - let mut script_str = String::with_capacity(2048); - script_str.push_str(&format!("set charset {}\n", Self::charset())); - script_str.push_str(&format!("set sysroot {}\n", tool_path)); - script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap())); - script_str.push_str("target remote :5039\n"); - script_str.push_str(&format!( - "set solib-search-path \ - ./{}/stage2/lib/rustlib/{}/lib/\n", - self.config.host, self.config.target - )); - for line in &dbg_cmds.breakpoint_lines { - script_str.push_str( - format!( - "break {:?}:{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), - *line - ) - .as_str(), - ); - } - script_str.push_str(&cmds); - script_str.push_str("\nquit\n"); - - debug!("script_str = {}", script_str); - self.dump_output_file(&script_str, "debugger.script"); - - let adb_path = &self.config.adb_path; - - Command::new(adb_path) - .arg("push") - .arg(&exe_file) - .arg(&self.config.adb_test_dir) - .status() - .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); - - Command::new(adb_path) - .args(&["forward", "tcp:5039", "tcp:5039"]) - .status() - .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); - - let adb_arg = format!( - "export LD_LIBRARY_PATH={}; \ - gdbserver{} :5039 {}/{}", - self.config.adb_test_dir.clone(), - if self.config.target.contains("aarch64") { "64" } else { "" }, - self.config.adb_test_dir.clone(), - exe_file.file_name().unwrap().to_str().unwrap() - ); - - debug!("adb arg: {}", adb_arg); - let mut adb = Command::new(adb_path) - .args(&["shell", &adb_arg]) - .stdout(Stdio::piped()) - .stderr(Stdio::inherit()) - .spawn() - .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); - - // Wait for the gdbserver to print out "Listening on port ..." - // at which point we know that it's started and then we can - // execute the debugger below. - let mut stdout = BufReader::new(adb.stdout.take().unwrap()); - let mut line = String::new(); - loop { - line.truncate(0); - stdout.read_line(&mut line).unwrap(); - if line.starts_with("Listening on port 5039") { - break; - } - } - drop(stdout); - - let mut debugger_script = OsString::from("-command="); - debugger_script.push(self.make_out_name("debugger.script")); - let debugger_opts: &[&OsStr] = - &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script]; - - let gdb_path = self.config.gdb.as_ref().unwrap(); - let Output { status, stdout, stderr } = Command::new(&gdb_path) - .args(debugger_opts) - .output() - .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}")); - let cmdline = { - let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); - gdb.args(debugger_opts); - let cmdline = self.make_cmdline(&gdb, ""); - logv(self.config, format!("executing {}", cmdline)); - cmdline - }; - - debugger_run_result = ProcRes { - status, - stdout: String::from_utf8(stdout).unwrap(), - stderr: String::from_utf8(stderr).unwrap(), - truncated: Truncated::No, - cmdline, - }; - if adb.kill().is_err() { - println!("Adb process is already finished."); - } - } else { - let rust_src_root = - self.config.find_rust_src_root().expect("Could not find Rust source root"); - let rust_pp_module_rel_path = Path::new("./src/etc"); - let rust_pp_module_abs_path = - rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned(); - // write debugger script - let mut script_str = String::with_capacity(2048); - script_str.push_str(&format!("set charset {}\n", Self::charset())); - script_str.push_str("show version\n"); - - match self.config.gdb_version { - Some(version) => { - println!("NOTE: compiletest thinks it is using GDB version {}", version); - - if version > extract_gdb_version("7.4").unwrap() { - // Add the directory containing the pretty printers to - // GDB's script auto loading safe path - script_str.push_str(&format!( - "add-auto-load-safe-path {}\n", - rust_pp_module_abs_path.replace(r"\", r"\\") - )); - - let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned(); - - // Add the directory containing the output binary to - // include embedded pretty printers to GDB's script - // auto loading safe path - script_str.push_str(&format!( - "add-auto-load-safe-path {}\n", - output_base_dir.replace(r"\", r"\\") - )); - } - } - _ => { - println!( - "NOTE: compiletest does not know which version of \ - GDB it is using" - ); - } - } - - // The following line actually doesn't have to do anything with - // pretty printing, it just tells GDB to print values on one line: - script_str.push_str("set print pretty off\n"); - - // Add the pretty printer directory to GDB's source-file search path - script_str - .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\"))); - - // Load the target executable - script_str - .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\"))); - - // Force GDB to print values in the Rust format. - script_str.push_str("set language rust\n"); - - // Add line breakpoints - for line in &dbg_cmds.breakpoint_lines { - script_str.push_str(&format!( - "break '{}':{}\n", - self.testpaths.file.file_name().unwrap().to_string_lossy(), - *line - )); - } - - script_str.push_str(&cmds); - script_str.push_str("\nquit\n"); - - debug!("script_str = {}", script_str); - self.dump_output_file(&script_str, "debugger.script"); - - let mut debugger_script = OsString::from("-command="); - debugger_script.push(self.make_out_name("debugger.script")); - - let debugger_opts: &[&OsStr] = - &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script]; - - let mut gdb = Command::new(self.config.gdb.as_ref().unwrap()); - let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { - format!("{pp}:{rust_pp_module_abs_path}") - } else { - rust_pp_module_abs_path - }; - gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); - - debugger_run_result = - self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None); - } - - if !debugger_run_result.status.success() { - self.fatal_proc_rec("gdb failed to execute", &debugger_run_result); - } - - if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { - self.fatal_proc_rec(&e, &debugger_run_result); - } - } - - fn run_debuginfo_lldb_test(&self) { - if self.config.lldb_python_dir.is_none() { - self.fatal("Can't run LLDB test because LLDB's python path is not set."); - } - - let config = Config { - target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), - host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), - ..self.config.clone() - }; - - let test_cx = TestCx { config: &config, ..*self }; - - test_cx.run_debuginfo_lldb_test_no_opt(); - } - - fn run_debuginfo_lldb_test_no_opt(&self) { - // compile test file (it should have 'compile-flags:-g' in the header) - let should_run = self.run_if_enabled(); - let compile_result = self.compile_test(should_run, Emit::None); - if !compile_result.status.success() { - self.fatal_proc_rec("compilation failed!", &compile_result); - } - if let WillExecute::Disabled = should_run { - return; - } - - let exe_file = self.make_exe_name(); - - match self.config.lldb_version { - Some(ref version) => { - println!("NOTE: compiletest thinks it is using LLDB version {}", version); - } - _ => { - println!( - "NOTE: compiletest does not know which version of \ - LLDB it is using" - ); - } - } - - // Parse debugger commands etc from test files - let dbg_cmds = DebuggerCommands::parse_from( - &self.testpaths.file, - self.config, - &["lldb"], - self.revision, - ) - .unwrap_or_else(|e| self.fatal(&e)); - - // Write debugger script: - // We don't want to hang when calling `quit` while the process is still running - let mut script_str = String::from("settings set auto-confirm true\n"); - - // Make LLDB emit its version, so we have it documented in the test output - script_str.push_str("version\n"); - - // Switch LLDB into "Rust mode" - let rust_src_root = - self.config.find_rust_src_root().expect("Could not find Rust source root"); - let rust_pp_module_rel_path = Path::new("./src/etc"); - let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path); - - script_str.push_str(&format!( - "command script import {}/lldb_lookup.py\n", - rust_pp_module_abs_path.to_str().unwrap() - )); - File::open(rust_pp_module_abs_path.join("lldb_commands")) - .and_then(|mut file| file.read_to_string(&mut script_str)) - .expect("Failed to read lldb_commands"); - - // Set breakpoints on every line that contains the string "#break" - let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); - for line in &dbg_cmds.breakpoint_lines { - script_str.push_str(&format!( - "breakpoint set --file '{}' --line {}\n", - source_file_name, line - )); - } - - // Append the other commands - for line in &dbg_cmds.commands { - script_str.push_str(line); - script_str.push('\n'); - } - - // Finally, quit the debugger - script_str.push_str("\nquit\n"); - - // Write the script into a file - debug!("script_str = {}", script_str); - self.dump_output_file(&script_str, "debugger.script"); - let debugger_script = self.make_out_name("debugger.script"); - - // Let LLDB execute the script via lldb_batchmode.py - let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root); - - if !debugger_run_result.status.success() { - self.fatal_proc_rec("Error while running LLDB", &debugger_run_result); - } - - if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { - self.fatal_proc_rec(&e, &debugger_run_result); - } - } - - fn run_lldb( - &self, - test_executable: &Path, - debugger_script: &Path, - rust_src_root: &Path, - ) -> ProcRes { - // Prepare the lldb_batchmode which executes the debugger script - let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py"); - let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { - format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap()) - } else { - self.config.lldb_python_dir.as_ref().unwrap().to_string() - }; - self.run_command_to_procres( - Command::new(&self.config.python) - .arg(&lldb_script_path) - .arg(test_executable) - .arg(debugger_script) - .env("PYTHONUNBUFFERED", "1") // Help debugging #78665 - .env("PYTHONPATH", pythonpath), - ) - } - - fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> { - // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS. - let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()]; - - options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect() - } - fn maybe_add_external_args(&self, cmd: &mut Command, args: &Vec<String>) { // Filter out the arguments that should not be added by runtest here. // @@ -2016,6 +1296,14 @@ impl<'test> TestCx<'test> { || self.config.src_base.ends_with("rustdoc-json") } + fn get_mir_dump_dir(&self) -> PathBuf { + let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); + debug!("input_file: {:?}", self.testpaths.file); + mir_dump_dir.push(&self.testpaths.relative_dir); + mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); + mir_dump_dir + } + fn make_compile_args( &self, input_file: &Path, @@ -2626,75 +1914,11 @@ impl<'test> TestCx<'test> { self.compose_and_run(filecheck, "", None, None) } - fn run_codegen_test(&self) { - if self.config.llvm_filecheck.is_none() { - self.fatal("missing --llvm-filecheck"); - } - - let (proc_res, output_path) = self.compile_test_and_save_ir(); - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - if let Some(PassMode::Build) = self.pass_mode() { - return; - } - let proc_res = self.verify_with_filecheck(&output_path); - if !proc_res.status.success() { - self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); - } - } - - fn run_assembly_test(&self) { - if self.config.llvm_filecheck.is_none() { - self.fatal("missing --llvm-filecheck"); - } - - let (proc_res, output_path) = self.compile_test_and_save_assembly(); - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - let proc_res = self.verify_with_filecheck(&output_path); - if !proc_res.status.success() { - self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); - } - } - fn charset() -> &'static str { // FreeBSD 10.1 defaults to GDB 6.1.1 which doesn't support "auto" charset if cfg!(target_os = "freebsd") { "ISO-8859-1" } else { "UTF-8" } } - fn run_rustdoc_test(&self) { - assert!(self.revision.is_none(), "revisions not relevant here"); - - let out_dir = self.output_base_dir(); - remove_and_create_dir_all(&out_dir); - - let proc_res = self.document(&out_dir, &self.testpaths); - if !proc_res.status.success() { - self.fatal_proc_rec("rustdoc failed!", &proc_res); - } - - if self.props.check_test_line_numbers_match { - self.check_rustdoc_test_option(proc_res); - } else { - let root = self.config.find_rust_src_root().unwrap(); - let mut cmd = Command::new(&self.config.python); - cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file); - if self.config.bless { - cmd.arg("--bless"); - } - let res = self.run_command_to_procres(&mut cmd); - if !res.status.success() { - self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| { - this.compare_to_default_rustdoc(&out_dir) - }); - } - } - } - fn compare_to_default_rustdoc(&mut self, out_dir: &Path) { if !self.config.has_tidy { return; @@ -2847,49 +2071,6 @@ impl<'test> TestCx<'test> { }; } - fn run_rustdoc_json_test(&self) { - //FIXME: Add bless option. - - assert!(self.revision.is_none(), "revisions not relevant here"); - - let out_dir = self.output_base_dir(); - remove_and_create_dir_all(&out_dir); - - let proc_res = self.document(&out_dir, &self.testpaths); - if !proc_res.status.success() { - self.fatal_proc_rec("rustdoc failed!", &proc_res); - } - - let root = self.config.find_rust_src_root().unwrap(); - let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); - json_out.set_extension("json"); - let res = self.run_command_to_procres( - Command::new(self.config.jsondocck_path.as_ref().unwrap()) - .arg("--doc-dir") - .arg(root.join(&out_dir)) - .arg("--template") - .arg(&self.testpaths.file), - ); - - if !res.status.success() { - self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| { - println!("Rustdoc Output:"); - proc_res.print_info(); - }) - } - - let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); - json_out.set_extension("json"); - - let res = self.run_command_to_procres( - Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), - ); - - if !res.status.success() { - self.fatal_proc_rec("jsondoclint failed!", &res); - } - } - fn get_lines<P: AsRef<Path>>( &self, path: &P, @@ -2990,824 +2171,6 @@ impl<'test> TestCx<'test> { } } - fn run_codegen_units_test(&self) { - assert!(self.revision.is_none(), "revisions not relevant here"); - - let proc_res = self.compile_test(WillExecute::No, Emit::None); - - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - - self.check_no_compiler_crash(&proc_res, self.props.should_ice); - - const PREFIX: &str = "MONO_ITEM "; - const CGU_MARKER: &str = "@@"; - - // Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs} - // To prevent the current dir from leaking, we just replace the entire path to the test - // file with TEST_PATH. - let actual: Vec<MonoItem> = proc_res - .stdout - .lines() - .filter(|line| line.starts_with(PREFIX)) - .map(|line| { - line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string() - }) - .map(|line| str_to_mono_item(&line, true)) - .collect(); - - let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None) - .iter() - .map(|e| str_to_mono_item(&e.msg[..], false)) - .collect(); - - let mut missing = Vec::new(); - let mut wrong_cgus = Vec::new(); - - for expected_item in &expected { - let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name); - - if let Some(actual_item) = actual_item_with_same_name { - if !expected_item.codegen_units.is_empty() && - // Also check for codegen units - expected_item.codegen_units != actual_item.codegen_units - { - wrong_cgus.push((expected_item.clone(), actual_item.clone())); - } - } else { - missing.push(expected_item.string.clone()); - } - } - - let unexpected: Vec<_> = actual - .iter() - .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name)) - .map(|acgu| acgu.string.clone()) - .collect(); - - if !missing.is_empty() { - missing.sort(); - - println!("\nThese items should have been contained but were not:\n"); - - for item in &missing { - println!("{}", item); - } - - println!("\n"); - } - - if !unexpected.is_empty() { - let sorted = { - let mut sorted = unexpected.clone(); - sorted.sort(); - sorted - }; - - println!("\nThese items were contained but should not have been:\n"); - - for item in sorted { - println!("{}", item); - } - - println!("\n"); - } - - if !wrong_cgus.is_empty() { - wrong_cgus.sort_by_key(|pair| pair.0.name.clone()); - println!("\nThe following items were assigned to wrong codegen units:\n"); - - for &(ref expected_item, ref actual_item) in &wrong_cgus { - println!("{}", expected_item.name); - println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units)); - println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units)); - println!(); - } - } - - if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) { - panic!(); - } - - #[derive(Clone, Eq, PartialEq)] - struct MonoItem { - name: String, - codegen_units: HashSet<String>, - string: String, - } - - // [MONO_ITEM] name [@@ (cgu)+] - fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem { - let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() }; - - let full_string = format!("{}{}", PREFIX, s); - - let parts: Vec<&str> = - s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect(); - - let name = parts[0].trim(); - - let cgus = if parts.len() > 1 { - let cgus_str = parts[1]; - - cgus_str - .split(' ') - .map(str::trim) - .filter(|s| !s.is_empty()) - .map(|s| { - if cgu_has_crate_disambiguator { - remove_crate_disambiguators_from_set_of_cgu_names(s) - } else { - s.to_string() - } - }) - .collect() - } else { - HashSet::new() - }; - - MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string } - } - - fn codegen_units_to_str(cgus: &HashSet<String>) -> String { - let mut cgus: Vec<_> = cgus.iter().collect(); - cgus.sort(); - - let mut string = String::new(); - for cgu in cgus { - string.push_str(&cgu[..]); - string.push(' '); - } - - string - } - - // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or - // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>, - // remove all crate-disambiguators. - fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String { - let Some(captures) = - static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?") - .captures(cgu) - else { - panic!("invalid cgu name encountered: {cgu}"); - }; - - let mut new_name = cgu.to_owned(); - - if let Some(d2) = captures.name("d2") { - new_name.replace_range(d2.start()..d2.end(), ""); - } - - let d1 = captures.name("d1").unwrap(); - new_name.replace_range(d1.start()..d1.end(), ""); - - new_name - } - - // The name of merged CGUs is constructed as the names of the original - // CGUs joined with "--". This function splits such composite CGU names - // and handles each component individually. - fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { - cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--") - } - } - - fn init_incremental_test(&self) { - // (See `run_incremental_test` for an overview of how incremental tests work.) - - // Before any of the revisions have executed, create the - // incremental workproduct directory. Delete any old - // incremental work products that may be there from prior - // runs. - let incremental_dir = self.props.incremental_dir.as_ref().unwrap(); - if incremental_dir.exists() { - // Canonicalizing the path will convert it to the //?/ format - // on Windows, which enables paths longer than 260 character - let canonicalized = incremental_dir.canonicalize().unwrap(); - fs::remove_dir_all(canonicalized).unwrap(); - } - fs::create_dir_all(&incremental_dir).unwrap(); - - if self.config.verbose { - println!("init_incremental_test: incremental_dir={}", incremental_dir.display()); - } - } - - fn run_incremental_test(&self) { - // Basic plan for a test incremental/foo/bar.rs: - // - load list of revisions rpass1, cfail2, rpass3 - // - each should begin with `cpass`, `rpass`, `cfail`, or `rfail` - // - if `cpass`, expect compilation to succeed, don't execute - // - if `rpass`, expect compilation and execution to succeed - // - if `cfail`, expect compilation to fail - // - if `rfail`, expect compilation to succeed and execution to fail - // - create a directory build/foo/bar.incremental - // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1 - // - because name of revision starts with "rpass", expect success - // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2 - // - because name of revision starts with "cfail", expect an error - // - load expected errors as usual, but filter for those that end in `[rfail2]` - // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3 - // - because name of revision starts with "rpass", expect success - // - execute build/foo/bar.exe and save output - // - // FIXME -- use non-incremental mode as an oracle? That doesn't apply - // to #[rustc_dirty] and clean tests I guess - - let revision = self.revision.expect("incremental tests require a list of revisions"); - - // Incremental workproduct directory should have already been created. - let incremental_dir = self.props.incremental_dir.as_ref().unwrap(); - assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir"); - - if self.config.verbose { - print!("revision={:?} props={:#?}", revision, self.props); - } - - if revision.starts_with("cpass") { - if self.props.should_ice { - self.fatal("can only use should-ice in cfail tests"); - } - self.run_cpass_test(); - } else if revision.starts_with("rpass") { - if self.props.should_ice { - self.fatal("can only use should-ice in cfail tests"); - } - self.run_rpass_test(); - } else if revision.starts_with("rfail") { - if self.props.should_ice { - self.fatal("can only use should-ice in cfail tests"); - } - self.run_rfail_test(); - } else if revision.starts_with("cfail") { - self.run_cfail_test(); - } else { - self.fatal("revision name must begin with cpass, rpass, rfail, or cfail"); - } - } - - fn run_rmake_test(&self) { - let test_dir = &self.testpaths.file; - if test_dir.join("rmake.rs").exists() { - self.run_rmake_v2_test(); - } else if test_dir.join("Makefile").exists() { - self.run_rmake_legacy_test(); - } else { - self.fatal("failed to find either `rmake.rs` or `Makefile`") - } - } - - fn run_rmake_legacy_test(&self) { - let cwd = env::current_dir().unwrap(); - let src_root = self.config.src_base.parent().unwrap().parent().unwrap(); - let src_root = cwd.join(&src_root); - - let tmpdir = cwd.join(self.output_base_name()); - if tmpdir.exists() { - self.aggressive_rm_rf(&tmpdir).unwrap(); - } - create_dir_all(&tmpdir).unwrap(); - - let host = &self.config.host; - let make = if host.contains("dragonfly") - || host.contains("freebsd") - || host.contains("netbsd") - || host.contains("openbsd") - || host.contains("aix") - { - "gmake" - } else { - "make" - }; - - let mut cmd = Command::new(make); - cmd.current_dir(&self.testpaths.file) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .env("TARGET", &self.config.target) - .env("PYTHON", &self.config.python) - .env("S", src_root) - .env("RUST_BUILD_STAGE", &self.config.stage_id) - .env("RUSTC", cwd.join(&self.config.rustc_path)) - .env("TMPDIR", &tmpdir) - .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) - .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) - .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) - .env("LLVM_COMPONENTS", &self.config.llvm_components) - // We for sure don't want these tests to run in parallel, so make - // sure they don't have access to these vars if we run via `make` - // at the top level - .env_remove("MAKEFLAGS") - .env_remove("MFLAGS") - .env_remove("CARGO_MAKEFLAGS"); - - if let Some(ref rustdoc) = self.config.rustdoc_path { - cmd.env("RUSTDOC", cwd.join(rustdoc)); - } - - if let Some(ref node) = self.config.nodejs { - cmd.env("NODE", node); - } - - if let Some(ref linker) = self.config.target_linker { - cmd.env("RUSTC_LINKER", linker); - } - - if let Some(ref clang) = self.config.run_clang_based_tests_with { - cmd.env("CLANG", clang); - } - - if let Some(ref filecheck) = self.config.llvm_filecheck { - cmd.env("LLVM_FILECHECK", filecheck); - } - - if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { - cmd.env("LLVM_BIN_DIR", llvm_bin_dir); - } - - if let Some(ref remote_test_client) = self.config.remote_test_client { - cmd.env("REMOTE_TEST_CLIENT", remote_test_client); - } - - // We don't want RUSTFLAGS set from the outside to interfere with - // compiler flags set in the test cases: - cmd.env_remove("RUSTFLAGS"); - - // Use dynamic musl for tests because static doesn't allow creating dylibs - if self.config.host.contains("musl") { - cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); - } - - if self.config.bless { - cmd.env("RUSTC_BLESS_TEST", "--bless"); - // Assume this option is active if the environment variable is "defined", with _any_ value. - // As an example, a `Makefile` can use this option by: - // - // ifdef RUSTC_BLESS_TEST - // cp "$(TMPDIR)"/actual_something.ext expected_something.ext - // else - // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext - // endif - } - - if self.config.target.contains("msvc") && !self.config.cc.is_empty() { - // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` - // and that `lib.exe` lives next to it. - let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); - - // MSYS doesn't like passing flags of the form `/foo` as it thinks it's - // a path and instead passes `C:\msys64\foo`, so convert all - // `/`-arguments to MSVC here to `-` arguments. - let cflags = self - .config - .cflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::<Vec<_>>() - .join(" "); - let cxxflags = self - .config - .cxxflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::<Vec<_>>() - .join(" "); - - cmd.env("IS_MSVC", "1") - .env("IS_WINDOWS", "1") - .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("MSVC_LIB_PATH", format!("{}", lib.display())) - .env("CC", format!("'{}' {}", self.config.cc, cflags)) - .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); - } else { - cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) - .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags)) - .env("AR", &self.config.ar); - - if self.config.target.contains("windows") { - cmd.env("IS_WINDOWS", "1"); - } - } - - let (output, truncated) = - self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`")); - if !output.status.success() { - let res = ProcRes { - status: output.status, - stdout: String::from_utf8_lossy(&output.stdout).into_owned(), - stderr: String::from_utf8_lossy(&output.stderr).into_owned(), - truncated, - cmdline: format!("{:?}", cmd), - }; - self.fatal_proc_rec("make failed", &res); - } - } - - fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> { - for e in path.read_dir()? { - let entry = e?; - let path = entry.path(); - if entry.file_type()?.is_dir() { - self.aggressive_rm_rf(&path)?; - } else { - // Remove readonly files as well on windows (by default we can't) - fs::remove_file(&path).or_else(|e| { - if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied { - let mut meta = entry.metadata()?.permissions(); - meta.set_readonly(false); - fs::set_permissions(&path, meta)?; - fs::remove_file(&path) - } else { - Err(e) - } - })?; - } - } - fs::remove_dir(path) - } - - fn run_rmake_v2_test(&self) { - // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe - // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust - // library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`. - // - // 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support` - // library. - // 2. We need to run the recipe binary. - - // So we assume the rust-lang/rust project setup looks like the following (our `.` is the - // top-level directory, irrelevant entries to our purposes omitted): - // - // ``` - // . // <- `source_root` - // ├── build/ // <- `build_root` - // ├── compiler/ - // ├── library/ - // ├── src/ - // │ └── tools/ - // │ └── run_make_support/ - // └── tests - // └── run-make/ - // ``` - - // `source_root` is the top-level directory containing the rust-lang/rust checkout. - let source_root = - self.config.find_rust_src_root().expect("could not determine rust source root"); - // `self.config.build_base` is actually the build base folder + "test" + test suite name, it - // looks like `build/<host_triple>/test/run-make`. But we want `build/<host_triple>/`. Note - // that the `build` directory does not need to be called `build`, nor does it need to be - // under `source_root`, so we must compute it based off of `self.config.build_base`. - let build_root = - self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf(); - - // We construct the following directory tree for each rmake.rs test: - // ``` - // <base_dir>/ - // rmake.exe - // rmake_out/ - // ``` - // having the recipe executable separate from the output artifacts directory allows the - // recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove - // a currently running executable because the recipe executable is not under the - // `rmake_out/` directory. - // - // This setup intentionally diverges from legacy Makefile run-make tests. - let base_dir = self.output_base_name(); - if base_dir.exists() { - self.aggressive_rm_rf(&base_dir).unwrap(); - } - let rmake_out_dir = base_dir.join("rmake_out"); - create_dir_all(&rmake_out_dir).unwrap(); - - // Copy all input files (apart from rmake.rs) to the temporary directory, - // so that the input directory structure from `tests/run-make/<test>` is mirrored - // to the `rmake_out` directory. - for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { - let path = path.unwrap().path().to_path_buf(); - if path.file_name().is_some_and(|s| s != "rmake.rs") { - let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); - if path.is_dir() { - copy_dir_all(&path, target).unwrap(); - } else { - fs::copy(&path, target).unwrap(); - } - } - } - - // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want - // the `stage1` part as that is what the output directories of bootstrap are prefixed with. - // Note that this *assumes* build layout from bootstrap is produced as: - // - // ``` - // build/<target_triple>/ // <- this is `build_root` - // ├── stage0 - // ├── stage0-bootstrap-tools - // ├── stage0-codegen - // ├── stage0-rustc - // ├── stage0-std - // ├── stage0-sysroot - // ├── stage0-tools - // ├── stage0-tools-bin - // ├── stage1 - // ├── stage1-std - // ├── stage1-tools - // ├── stage1-tools-bin - // └── test - // ``` - // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so - // we don't have to hack out a `stageN`. - let stage = self.config.stage_id.split('-').next().unwrap(); - - // In order to link in the support library as a rlib when compiling recipes, we need three - // paths: - // 1. Path of the built support library rlib itself. - // 2. Path of the built support library's dependencies directory. - // 3. Path of the built support library's dependencies' dependencies directory. - // - // The paths look like - // - // ``` - // build/<target_triple>/ - // ├── stageN-tools-bin/ - // │ └── librun_make_support.rlib // <- support rlib itself - // ├── stageN-tools/ - // │ ├── release/deps/ // <- deps of deps - // │ └── <host_triple>/release/deps/ // <- deps - // ``` - // - // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the - // support lib and its deps are organized, can't we copy them to the tools-bin dir as - // well?), but this seems to work for now. - - let stage_tools_bin = build_root.join(format!("{stage}-tools-bin")); - let support_lib_path = stage_tools_bin.join("librun_make_support.rlib"); - - let stage_tools = build_root.join(format!("{stage}-tools")); - let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps"); - let support_lib_deps_deps = stage_tools.join("release").join("deps"); - - // To compile the recipe with rustc, we need to provide suitable dynamic library search - // paths to rustc. This includes both: - // 1. The "base" dylib search paths that was provided to compiletest, e.g. `LD_LIBRARY_PATH` - // on some linux distros. - // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc. - - let base_dylib_search_paths = - Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap())); - - let host_dylib_search_paths = { - let mut paths = vec![self.config.compile_lib_path.clone()]; - paths.extend(base_dylib_search_paths.iter().cloned()); - paths - }; - - // Calculate the paths of the recipe binary. As previously discussed, this is placed at - // `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` depending on - // platform. - let recipe_bin = { - let mut p = base_dir.join("rmake"); - p.set_extension(env::consts::EXE_EXTENSION); - p - }; - - let mut rustc = Command::new(&self.config.rustc_path); - rustc - .arg("-o") - .arg(&recipe_bin) - // Specify library search paths for `run_make_support`. - .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) - .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) - // Provide `run_make_support` as extern prelude, so test writers don't need to write - // `extern run_make_support;`. - .arg("--extern") - .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) - .arg("--edition=2021") - .arg(&self.testpaths.file.join("rmake.rs")) - // Provide necessary library search paths for rustc. - .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap()); - - // In test code we want to be very pedantic about values being silently discarded that are - // annotated with `#[must_use]`. - rustc.arg("-Dunused_must_use"); - - // > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc - // > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is - // > compiled using the bootstrap rustc wrapper which sets `--sysroot - // > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile - // > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the - // > `libstd.rlib` against which `librun_make_support.rlib` is compiled. - // - // The gist here is that we have to pass the proper stage0 sysroot if we want - // - // ``` - // $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0 - // ``` - // - // to work correctly. - // - // See <https://github.com/rust-lang/rust/pull/122248> for more background. - if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { - let stage0_sysroot = build_root.join("stage0-sysroot"); - rustc.arg("--sysroot").arg(&stage0_sysroot); - } - - // Now run rustc to build the recipe. - let res = self.run_command_to_procres(&mut rustc); - if !res.status.success() { - self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res); - } - - // To actually run the recipe, we have to provide the recipe with a bunch of information - // provided through env vars. - - // Compute stage-specific standard library paths. - let stage_std_path = build_root.join(&stage).join("lib"); - - // Compute dynamic library search paths for recipes. - let recipe_dylib_search_paths = { - let mut paths = base_dylib_search_paths.clone(); - paths.push(support_lib_path.parent().unwrap().to_path_buf()); - paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); - paths - }; - - // Compute runtime library search paths for recipes. This is target-specific. - let target_runtime_dylib_search_paths = { - let mut paths = vec![rmake_out_dir.clone()]; - paths.extend(base_dylib_search_paths.iter().cloned()); - paths - }; - - // FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and - // `TARGET_RPATH_DIR`, it is **extremely** confusing! - let mut cmd = Command::new(&recipe_bin); - cmd.current_dir(&rmake_out_dir) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - // Provide the target-specific env var that is used to record dylib search paths. For - // example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows. - .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) - // Provide the dylib search paths. - .env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap()) - // Provide runtime dylib search paths. - .env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap()) - // Provide the target. - .env("TARGET", &self.config.target) - // Some tests unfortunately still need Python, so provide path to a Python interpreter. - .env("PYTHON", &self.config.python) - // Provide path to checkout root. This is the top-level directory containing - // rust-lang/rust checkout. - .env("SOURCE_ROOT", &source_root) - // Provide path to stage-corresponding rustc. - .env("RUSTC", &self.config.rustc_path) - // Provide the directory to libraries that are needed to run the *compiler*. This is not - // to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the - // recipe wants to invoke rustc. - .env("HOST_RPATH_DIR", &self.config.compile_lib_path) - // Provide the directory to libraries that might be needed to run compiled binaries - // (further compiled by the recipe!). - .env("TARGET_RPATH_DIR", &self.config.run_lib_path) - // Provide which LLVM components are available (e.g. which LLVM components are provided - // through a specific CI runner). - .env("LLVM_COMPONENTS", &self.config.llvm_components); - - if let Some(ref rustdoc) = self.config.rustdoc_path { - cmd.env("RUSTDOC", source_root.join(rustdoc)); - } - - if let Some(ref node) = self.config.nodejs { - cmd.env("NODE", node); - } - - if let Some(ref linker) = self.config.target_linker { - cmd.env("RUSTC_LINKER", linker); - } - - if let Some(ref clang) = self.config.run_clang_based_tests_with { - cmd.env("CLANG", clang); - } - - if let Some(ref filecheck) = self.config.llvm_filecheck { - cmd.env("LLVM_FILECHECK", filecheck); - } - - if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { - cmd.env("LLVM_BIN_DIR", llvm_bin_dir); - } - - if let Some(ref remote_test_client) = self.config.remote_test_client { - cmd.env("REMOTE_TEST_CLIENT", remote_test_client); - } - - // We don't want RUSTFLAGS set from the outside to interfere with - // compiler flags set in the test cases: - cmd.env_remove("RUSTFLAGS"); - - // Use dynamic musl for tests because static doesn't allow creating dylibs - if self.config.host.contains("musl") { - cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); - } - - if self.config.bless { - // If we're running in `--bless` mode, set an environment variable to tell - // `run_make_support` to bless snapshot files instead of checking them. - // - // The value is this test's source directory, because the support code - // will need that path in order to bless the _original_ snapshot files, - // not the copies in `rmake_out`. - // (See <https://github.com/rust-lang/rust/issues/129038>.) - cmd.env("RUSTC_BLESS_TEST", &self.testpaths.file); - } - - if self.config.target.contains("msvc") && !self.config.cc.is_empty() { - // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` - // and that `lib.exe` lives next to it. - let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); - - // MSYS doesn't like passing flags of the form `/foo` as it thinks it's - // a path and instead passes `C:\msys64\foo`, so convert all - // `/`-arguments to MSVC here to `-` arguments. - let cflags = self - .config - .cflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::<Vec<_>>() - .join(" "); - let cxxflags = self - .config - .cxxflags - .split(' ') - .map(|s| s.replace("/", "-")) - .collect::<Vec<_>>() - .join(" "); - - cmd.env("IS_MSVC", "1") - .env("IS_WINDOWS", "1") - .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) - .env("MSVC_LIB_PATH", format!("{}", lib.display())) - // Note: we diverge from legacy run_make and don't lump `CC` the compiler and - // default flags together. - .env("CC_DEFAULT_FLAGS", &cflags) - .env("CC", &self.config.cc) - .env("CXX_DEFAULT_FLAGS", &cxxflags) - .env("CXX", &self.config.cxx); - } else { - cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags) - .env("CC", &self.config.cc) - .env("CXX_DEFAULT_FLAGS", &self.config.cxxflags) - .env("CXX", &self.config.cxx) - .env("AR", &self.config.ar); - - if self.config.target.contains("windows") { - cmd.env("IS_WINDOWS", "1"); - } - } - - let (Output { stdout, stderr, status }, truncated) = - self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`")); - if !status.success() { - let res = ProcRes { - status, - stdout: String::from_utf8_lossy(&stdout).into_owned(), - stderr: String::from_utf8_lossy(&stderr).into_owned(), - truncated, - cmdline: format!("{:?}", cmd), - }; - self.fatal_proc_rec("rmake recipe failed to complete", &res); - } - } - - fn run_js_doc_test(&self) { - if let Some(nodejs) = &self.config.nodejs { - let out_dir = self.output_base_dir(); - - self.document(&out_dir, &self.testpaths); - - let root = self.config.find_rust_src_root().unwrap(); - let file_stem = - self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); - let res = self.run_command_to_procres( - Command::new(&nodejs) - .arg(root.join("src/tools/rustdoc-js/tester.js")) - .arg("--doc-folder") - .arg(out_dir) - .arg("--crate-name") - .arg(file_stem.replace("-", "_")) - .arg("--test-file") - .arg(self.testpaths.file.with_extension("js")), - ); - if !res.status.success() { - self.fatal_proc_rec("rustdoc-js test failed!", &res); - } - } else { - self.fatal("no nodeJS"); - } - } - fn force_color_svg(&self) -> bool { self.props.compile_flags.iter().any(|s| s.contains("--color=always")) } @@ -3895,377 +2258,6 @@ impl<'test> TestCx<'test> { errors } - fn run_ui_test(&self) { - if let Some(FailMode::Build) = self.props.fail_mode { - // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck). - let pm = Some(PassMode::Check); - let proc_res = - self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new()); - self.check_if_test_should_compile(&proc_res, pm); - } - - let pm = self.pass_mode(); - let should_run = self.should_run(pm); - let emit_metadata = self.should_emit_metadata(pm); - let proc_res = self.compile_test(should_run, emit_metadata); - self.check_if_test_should_compile(&proc_res, pm); - if matches!(proc_res.truncated, Truncated::Yes) - && !self.props.dont_check_compiler_stdout - && !self.props.dont_check_compiler_stderr - { - self.fatal_proc_rec( - "compiler output got truncated, cannot compare with reference file", - &proc_res, - ); - } - - // if the user specified a format in the ui test - // print the output to the stderr file, otherwise extract - // the rendered error messages from json and print them - let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format")); - - let expected_fixed = self.load_expected_output(UI_FIXED); - - self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]); - - let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit); - let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr); - - if self.config.compare_mode.is_some() { - // don't test rustfix with nll right now - } else if self.config.rustfix_coverage { - // Find out which tests have `MachineApplicable` suggestions but are missing - // `run-rustfix` or `run-rustfix-only-machine-applicable` headers. - // - // This will return an empty `Vec` in case the executed test file has a - // `compile-flags: --error-format=xxxx` header with a value other than `json`. - let suggestions = get_suggestions_from_json( - &rustfix_input, - &HashSet::new(), - Filter::MachineApplicableOnly, - ) - .unwrap_or_default(); - if !suggestions.is_empty() - && !self.props.run_rustfix - && !self.props.rustfix_only_machine_applicable - { - let mut coverage_file_path = self.config.build_base.clone(); - coverage_file_path.push("rustfix_missing_coverage.txt"); - debug!("coverage_file_path: {}", coverage_file_path.display()); - - let mut file = OpenOptions::new() - .create(true) - .append(true) - .open(coverage_file_path.as_path()) - .expect("could not create or open file"); - - if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) { - panic!("couldn't write to {}: {e:?}", coverage_file_path.display()); - } - } - } else if self.props.run_rustfix { - // Apply suggestions from rustc to the code itself - let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap(); - let suggestions = get_suggestions_from_json( - &rustfix_input, - &HashSet::new(), - if self.props.rustfix_only_machine_applicable { - Filter::MachineApplicableOnly - } else { - Filter::Everything - }, - ) - .unwrap(); - let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| { - panic!( - "failed to apply suggestions for {:?} with rustfix: {}", - self.testpaths.file, e - ) - }); - - errors += self.compare_output("fixed", &fixed_code, &expected_fixed); - } else if !expected_fixed.is_empty() { - panic!( - "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \ - file was found" - ); - } - - if errors > 0 { - println!("To update references, rerun the tests and pass the `--bless` flag"); - let relative_path_to_file = - self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap()); - println!( - "To only update this specific test, also pass `--test-args {}`", - relative_path_to_file.display(), - ); - self.fatal_proc_rec( - &format!("{} errors occurred comparing output.", errors), - &proc_res, - ); - } - - let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); - - if let WillExecute::Yes = should_run { - let proc_res = self.exec_compiled_test(); - let run_output_errors = if self.props.check_run_results { - self.load_compare_outputs(&proc_res, TestOutput::Run, explicit) - } else { - 0 - }; - if run_output_errors > 0 { - self.fatal_proc_rec( - &format!("{} errors occurred comparing run output.", run_output_errors), - &proc_res, - ); - } - if self.should_run_successfully(pm) { - if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); - } - } else if proc_res.status.success() { - self.fatal_proc_rec("test run succeeded!", &proc_res); - } - - if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() - { - // "// error-pattern" comments - let output_to_check = self.get_output(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } - } - - debug!( - "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \ - proc_res.status={:?} props.error_patterns={:?}", - explicit, - self.config.compare_mode, - expected_errors, - proc_res.status, - self.props.error_patterns - ); - - let check_patterns = should_run == WillExecute::No - && (!self.props.error_patterns.is_empty() - || !self.props.regex_error_patterns.is_empty()); - if !explicit && self.config.compare_mode.is_none() { - let check_annotations = !check_patterns || !expected_errors.is_empty(); - - if check_annotations { - // "//~ERROR comments" - self.check_expected_errors(expected_errors, &proc_res); - } - } else if explicit && !expected_errors.is_empty() { - let msg = format!( - "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead", - expected_errors[0].line_num, - expected_errors[0].kind.unwrap_or(ErrorKind::Error), - ); - self.fatal(&msg); - } - if check_patterns { - // "// error-pattern" comments - let output_to_check = self.get_output(&proc_res); - self.check_all_error_patterns(&output_to_check, &proc_res, pm); - } - - if self.props.run_rustfix && self.config.compare_mode.is_none() { - // And finally, compile the fixed code and make sure it both - // succeeds and has no diagnostics. - let mut rustc = self.make_compile_args( - &self.expected_output_path(UI_FIXED), - TargetLocation::ThisFile(self.make_exe_name()), - emit_metadata, - AllowUnused::No, - LinkToAux::Yes, - Vec::new(), - ); - - // If a test is revisioned, it's fixed source file can be named "a.foo.fixed", which, - // well, "a.foo" isn't a valid crate name. So we explicitly mangle the test name - // (including the revision) here to avoid the test writer having to manually specify a - // `#![crate_name = "..."]` as a workaround. This is okay since we're only checking if - // the fixed code is compilable. - if self.revision.is_some() { - let crate_name = - self.testpaths.file.file_stem().expect("test must have a file stem"); - // crate name must be alphanumeric or `_`. - let crate_name = - crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); - // replace `a.foo` -> `a__foo` for crate name purposes. - // replace `revision-name-with-dashes` -> `revision_name_with_underscore` - let crate_name = crate_name.replace('.', "__"); - let crate_name = crate_name.replace('-', "_"); - rustc.arg("--crate-name"); - rustc.arg(crate_name); - } - - let res = self.compose_and_run_compiler(rustc, None, self.testpaths); - if !res.status.success() { - self.fatal_proc_rec("failed to compile fixed code", &res); - } - if !res.stderr.is_empty() - && !self.props.rustfix_only_machine_applicable - && !json::rustfix_diagnostics_only(&res.stderr).is_empty() - { - self.fatal_proc_rec("fixed code is still producing diagnostics", &res); - } - } - } - - fn run_mir_opt_test(&self) { - let pm = self.pass_mode(); - let should_run = self.should_run(pm); - - let mut test_info = files_for_miropt_test( - &self.testpaths.file, - self.config.get_pointer_width(), - self.config.target_cfg().panic.for_miropt_test_tools(), - ); - - let passes = std::mem::take(&mut test_info.passes); - - let proc_res = self.compile_test_with_passes(should_run, Emit::Mir, passes); - if !proc_res.status.success() { - self.fatal_proc_rec("compilation failed!", &proc_res); - } - self.check_mir_dump(test_info); - - if let WillExecute::Yes = should_run { - let proc_res = self.exec_compiled_test(); - - if !proc_res.status.success() { - self.fatal_proc_rec("test run failed!", &proc_res); - } - } - } - - fn check_mir_dump(&self, test_info: MiroptTest) { - let test_dir = self.testpaths.file.parent().unwrap(); - let test_crate = - self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); - - let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; - - if self.config.bless { - for e in - glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap() - { - std::fs::remove_file(e.unwrap()).unwrap(); - } - for e in - glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap() - { - std::fs::remove_file(e.unwrap()).unwrap(); - } - } - - for MiroptTestFile { from_file, to_file, expected_file } in files { - let dumped_string = if let Some(after) = to_file { - self.diff_mir_files(from_file.into(), after.into()) - } else { - let mut output_file = PathBuf::new(); - output_file.push(self.get_mir_dump_dir()); - output_file.push(&from_file); - debug!( - "comparing the contents of: {} with {}", - output_file.display(), - expected_file.display() - ); - if !output_file.exists() { - panic!( - "Output file `{}` from test does not exist, available files are in `{}`", - output_file.display(), - output_file.parent().unwrap().display() - ); - } - self.check_mir_test_timestamp(&from_file, &output_file); - let dumped_string = fs::read_to_string(&output_file).unwrap(); - self.normalize_output(&dumped_string, &[]) - }; - - if self.config.bless { - let _ = std::fs::remove_file(&expected_file); - std::fs::write(expected_file, dumped_string.as_bytes()).unwrap(); - } else { - if !expected_file.exists() { - panic!("Output file `{}` from test does not exist", expected_file.display()); - } - let expected_string = fs::read_to_string(&expected_file).unwrap(); - if dumped_string != expected_string { - print!("{}", write_diff(&expected_string, &dumped_string, 3)); - panic!( - "Actual MIR output differs from expected MIR output {}", - expected_file.display() - ); - } - } - } - - if run_filecheck { - let output_path = self.output_base_name().with_extension("mir"); - let proc_res = self.verify_with_filecheck(&output_path); - if !proc_res.status.success() { - self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); - } - } - } - - fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String { - let to_full_path = |path: PathBuf| { - let full = self.get_mir_dump_dir().join(&path); - if !full.exists() { - panic!( - "the mir dump file for {} does not exist (requested in {})", - path.display(), - self.testpaths.file.display(), - ); - } - full - }; - let before = to_full_path(before); - let after = to_full_path(after); - debug!("comparing the contents of: {} with {}", before.display(), after.display()); - let before = fs::read_to_string(before).unwrap(); - let after = fs::read_to_string(after).unwrap(); - let before = self.normalize_output(&before, &[]); - let after = self.normalize_output(&after, &[]); - let mut dumped_string = String::new(); - for result in diff::lines(&before, &after) { - use std::fmt::Write; - match result { - diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(), - diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(), - diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(), - } - } - dumped_string - } - - fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) { - let t = |file| fs::metadata(file).unwrap().modified().unwrap(); - let source_file = &self.testpaths.file; - let output_time = t(output_file); - let source_time = t(source_file); - if source_time > output_time { - debug!("source file time: {:?} output file time: {:?}", source_time, output_time); - panic!( - "test source file `{}` is newer than potentially stale output file `{}`.", - source_file.display(), - test_name - ); - } - } - - fn get_mir_dump_dir(&self) -> PathBuf { - let mut mir_dump_dir = PathBuf::from(self.config.build_base.as_path()); - debug!("input_file: {:?}", self.testpaths.file); - mir_dump_dir.push(&self.testpaths.relative_dir); - mir_dump_dir.push(self.testpaths.file.file_stem().unwrap()); - mir_dump_dir - } - fn normalize_output(&self, output: &str, custom_rules: &[(String, String)]) -> String { // Crude heuristic to detect when the output should have JSON-specific // normalization steps applied. @@ -4634,6 +2626,77 @@ impl<'test> TestCx<'test> { let stamp = crate::stamp(&self.config, self.testpaths, self.revision); fs::write(&stamp, compute_stamp_hash(&self.config)).unwrap(); } + + fn init_incremental_test(&self) { + // (See `run_incremental_test` for an overview of how incremental tests work.) + + // Before any of the revisions have executed, create the + // incremental workproduct directory. Delete any old + // incremental work products that may be there from prior + // runs. + let incremental_dir = self.props.incremental_dir.as_ref().unwrap(); + if incremental_dir.exists() { + // Canonicalizing the path will convert it to the //?/ format + // on Windows, which enables paths longer than 260 character + let canonicalized = incremental_dir.canonicalize().unwrap(); + fs::remove_dir_all(canonicalized).unwrap(); + } + fs::create_dir_all(&incremental_dir).unwrap(); + + if self.config.verbose { + println!("init_incremental_test: incremental_dir={}", incremental_dir.display()); + } + } + + // FIXME(jieyouxu): `run_rpass_test` is hoisted out here and not in incremental because + // apparently valgrind test falls back to `run_rpass_test` if valgrind isn't available, which + // seems highly questionable to me. + fn run_rpass_test(&self) { + let emit_metadata = self.should_emit_metadata(self.pass_mode()); + let should_run = self.run_if_enabled(); + let proc_res = self.compile_test(should_run, emit_metadata); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + // FIXME(#41968): Move this check to tidy? + if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() { + self.fatal("run-pass tests with expected warnings should be moved to ui/"); + } + + if let WillExecute::Disabled = should_run { + return; + } + + let proc_res = self.exec_compiled_test(); + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + } + + fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> { + for e in path.read_dir()? { + let entry = e?; + let path = entry.path(); + if entry.file_type()?.is_dir() { + self.aggressive_rm_rf(&path)?; + } else { + // Remove readonly files as well on windows (by default we can't) + fs::remove_file(&path).or_else(|e| { + if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied { + let mut meta = entry.metadata()?.permissions(); + meta.set_readonly(false); + fs::set_permissions(&path, meta)?; + fs::remove_file(&path) + } else { + Err(e) + } + })?; + } + } + fs::remove_dir(path) + } } struct ProcArgs { diff --git a/src/tools/compiletest/src/runtest/assembly.rs b/src/tools/compiletest/src/runtest/assembly.rs new file mode 100644 index 00000000000..430a5534da1 --- /dev/null +++ b/src/tools/compiletest/src/runtest/assembly.rs @@ -0,0 +1,19 @@ +use super::TestCx; + +impl TestCx<'_> { + pub(super) fn run_assembly_test(&self) { + if self.config.llvm_filecheck.is_none() { + self.fatal("missing --llvm-filecheck"); + } + + let (proc_res, output_path) = self.compile_test_and_save_assembly(); + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + let proc_res = self.verify_with_filecheck(&output_path); + if !proc_res.status.success() { + self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); + } + } +} diff --git a/src/tools/compiletest/src/runtest/codegen.rs b/src/tools/compiletest/src/runtest/codegen.rs new file mode 100644 index 00000000000..6e61ab5e46d --- /dev/null +++ b/src/tools/compiletest/src/runtest/codegen.rs @@ -0,0 +1,22 @@ +use super::{PassMode, TestCx}; + +impl TestCx<'_> { + pub(super) fn run_codegen_test(&self) { + if self.config.llvm_filecheck.is_none() { + self.fatal("missing --llvm-filecheck"); + } + + let (proc_res, output_path) = self.compile_test_and_save_ir(); + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + if let Some(PassMode::Build) = self.pass_mode() { + return; + } + let proc_res = self.verify_with_filecheck(&output_path); + if !proc_res.status.success() { + self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); + } + } +} diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs new file mode 100644 index 00000000000..6c866cbef21 --- /dev/null +++ b/src/tools/compiletest/src/runtest/codegen_units.rs @@ -0,0 +1,191 @@ +use std::collections::HashSet; + +use super::{Emit, TestCx, WillExecute}; +use crate::errors; +use crate::util::static_regex; + +impl TestCx<'_> { + pub(super) fn run_codegen_units_test(&self) { + assert!(self.revision.is_none(), "revisions not relevant here"); + + let proc_res = self.compile_test(WillExecute::No, Emit::None); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + self.check_no_compiler_crash(&proc_res, self.props.should_ice); + + const PREFIX: &str = "MONO_ITEM "; + const CGU_MARKER: &str = "@@"; + + // Some MonoItems can contain {closure@/path/to/checkout/tests/codgen-units/test.rs} + // To prevent the current dir from leaking, we just replace the entire path to the test + // file with TEST_PATH. + let actual: Vec<MonoItem> = proc_res + .stdout + .lines() + .filter(|line| line.starts_with(PREFIX)) + .map(|line| { + line.replace(&self.testpaths.file.display().to_string(), "TEST_PATH").to_string() + }) + .map(|line| str_to_mono_item(&line, true)) + .collect(); + + let expected: Vec<MonoItem> = errors::load_errors(&self.testpaths.file, None) + .iter() + .map(|e| str_to_mono_item(&e.msg[..], false)) + .collect(); + + let mut missing = Vec::new(); + let mut wrong_cgus = Vec::new(); + + for expected_item in &expected { + let actual_item_with_same_name = actual.iter().find(|ti| ti.name == expected_item.name); + + if let Some(actual_item) = actual_item_with_same_name { + if !expected_item.codegen_units.is_empty() && + // Also check for codegen units + expected_item.codegen_units != actual_item.codegen_units + { + wrong_cgus.push((expected_item.clone(), actual_item.clone())); + } + } else { + missing.push(expected_item.string.clone()); + } + } + + let unexpected: Vec<_> = actual + .iter() + .filter(|acgu| !expected.iter().any(|ecgu| acgu.name == ecgu.name)) + .map(|acgu| acgu.string.clone()) + .collect(); + + if !missing.is_empty() { + missing.sort(); + + println!("\nThese items should have been contained but were not:\n"); + + for item in &missing { + println!("{}", item); + } + + println!("\n"); + } + + if !unexpected.is_empty() { + let sorted = { + let mut sorted = unexpected.clone(); + sorted.sort(); + sorted + }; + + println!("\nThese items were contained but should not have been:\n"); + + for item in sorted { + println!("{}", item); + } + + println!("\n"); + } + + if !wrong_cgus.is_empty() { + wrong_cgus.sort_by_key(|pair| pair.0.name.clone()); + println!("\nThe following items were assigned to wrong codegen units:\n"); + + for &(ref expected_item, ref actual_item) in &wrong_cgus { + println!("{}", expected_item.name); + println!(" expected: {}", codegen_units_to_str(&expected_item.codegen_units)); + println!(" actual: {}", codegen_units_to_str(&actual_item.codegen_units)); + println!(); + } + } + + if !(missing.is_empty() && unexpected.is_empty() && wrong_cgus.is_empty()) { + panic!(); + } + + #[derive(Clone, Eq, PartialEq)] + struct MonoItem { + name: String, + codegen_units: HashSet<String>, + string: String, + } + + // [MONO_ITEM] name [@@ (cgu)+] + fn str_to_mono_item(s: &str, cgu_has_crate_disambiguator: bool) -> MonoItem { + let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { s.trim() }; + + let full_string = format!("{}{}", PREFIX, s); + + let parts: Vec<&str> = + s.split(CGU_MARKER).map(str::trim).filter(|s| !s.is_empty()).collect(); + + let name = parts[0].trim(); + + let cgus = if parts.len() > 1 { + let cgus_str = parts[1]; + + cgus_str + .split(' ') + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(|s| { + if cgu_has_crate_disambiguator { + remove_crate_disambiguators_from_set_of_cgu_names(s) + } else { + s.to_string() + } + }) + .collect() + } else { + HashSet::new() + }; + + MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string } + } + + fn codegen_units_to_str(cgus: &HashSet<String>) -> String { + let mut cgus: Vec<_> = cgus.iter().collect(); + cgus.sort(); + + let mut string = String::new(); + for cgu in cgus { + string.push_str(&cgu[..]); + string.push(' '); + } + + string + } + + // Given a cgu-name-prefix of the form <crate-name>.<crate-disambiguator> or + // the form <crate-name1>.<crate-disambiguator1>-in-<crate-name2>.<crate-disambiguator2>, + // remove all crate-disambiguators. + fn remove_crate_disambiguator_from_cgu(cgu: &str) -> String { + let Some(captures) = + static_regex!(r"^[^\.]+(?P<d1>\.[[:alnum:]]+)(-in-[^\.]+(?P<d2>\.[[:alnum:]]+))?") + .captures(cgu) + else { + panic!("invalid cgu name encountered: {cgu}"); + }; + + let mut new_name = cgu.to_owned(); + + if let Some(d2) = captures.name("d2") { + new_name.replace_range(d2.start()..d2.end(), ""); + } + + let d1 = captures.name("d1").unwrap(); + new_name.replace_range(d1.start()..d1.end(), ""); + + new_name + } + + // The name of merged CGUs is constructed as the names of the original + // CGUs joined with "--". This function splits such composite CGU names + // and handles each component individually. + fn remove_crate_disambiguators_from_set_of_cgu_names(cgus: &str) -> String { + cgus.split("--").map(remove_crate_disambiguator_from_cgu).collect::<Vec<_>>().join("--") + } + } +} diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 05191a15980..961a1602986 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -18,7 +18,7 @@ impl<'test> TestCx<'test> { .unwrap_or_else(|| self.fatal("missing --coverage-dump")) } - pub(crate) fn run_coverage_map_test(&self) { + pub(super) fn run_coverage_map_test(&self) { let coverage_dump_path = self.coverage_dump_path(); let (proc_res, llvm_ir_path) = self.compile_test_and_save_ir(); @@ -50,7 +50,7 @@ impl<'test> TestCx<'test> { } } - pub(crate) fn run_coverage_run_test(&self) { + pub(super) fn run_coverage_run_test(&self) { let should_run = self.run_if_enabled(); let proc_res = self.compile_test(should_run, Emit::None); diff --git a/src/tools/compiletest/src/runtest/crash.rs b/src/tools/compiletest/src/runtest/crash.rs new file mode 100644 index 00000000000..7f2bec4949b --- /dev/null +++ b/src/tools/compiletest/src/runtest/crash.rs @@ -0,0 +1,25 @@ +use super::{TestCx, WillExecute}; + +impl TestCx<'_> { + pub(super) fn run_crash_test(&self) { + let pm = self.pass_mode(); + let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); + + if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() { + eprintln!("{}", proc_res.status); + eprintln!("{}", proc_res.stdout); + eprintln!("{}", proc_res.stderr); + eprintln!("{}", proc_res.cmdline); + } + + // if a test does not crash, consider it an error + if proc_res.status.success() || matches!(proc_res.status.code(), Some(1 | 0)) { + self.fatal(&format!( + "crashtest no longer crashes/triggers ICE, horray! Please give it a meaningful name, \ + add a doc-comment to the start of the test explaining why it exists and \ + move it to tests/ui or wherever you see fit. Adding 'Fixes #<issueNr>' to your PR description \ + ensures that the corresponding ticket is auto-closed upon merge." + )); + } + } +} diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs new file mode 100644 index 00000000000..36127414ab1 --- /dev/null +++ b/src/tools/compiletest/src/runtest/debuginfo.rs @@ -0,0 +1,509 @@ +use std::ffi::{OsStr, OsString}; +use std::fs::File; +use std::io::{BufRead, BufReader, Read}; +use std::path::Path; +use std::process::{Command, Output, Stdio}; + +use tracing::debug; + +use super::debugger::DebuggerCommands; +use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute}; +use crate::common::Config; +use crate::util::logv; +use crate::{extract_gdb_version, is_android_gdb_target}; + +impl TestCx<'_> { + pub(super) fn run_debuginfo_test(&self) { + match self.config.debugger.unwrap() { + Debugger::Cdb => self.run_debuginfo_cdb_test(), + Debugger::Gdb => self.run_debuginfo_gdb_test(), + Debugger::Lldb => self.run_debuginfo_lldb_test(), + } + } + + fn run_debuginfo_cdb_test(&self) { + let config = Config { + target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), + host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), + ..self.config.clone() + }; + + let test_cx = TestCx { config: &config, ..*self }; + + test_cx.run_debuginfo_cdb_test_no_opt(); + } + + fn run_debuginfo_cdb_test_no_opt(&self) { + let exe_file = self.make_exe_name(); + + // Existing PDB files are update in-place. When changing the debuginfo + // the compiler generates for something, this can lead to the situation + // where both the old and the new version of the debuginfo for the same + // type is present in the PDB, which is very confusing. + // Therefore we delete any existing PDB file before compiling the test + // case. + // FIXME: If can reliably detect that MSVC's link.exe is used, then + // passing `/INCREMENTAL:NO` might be a cleaner way to do this. + let pdb_file = exe_file.with_extension(".pdb"); + if pdb_file.exists() { + std::fs::remove_file(pdb_file).unwrap(); + } + + // compile test file (it should have 'compile-flags:-g' in the header) + let should_run = self.run_if_enabled(); + let compile_result = self.compile_test(should_run, Emit::None); + if !compile_result.status.success() { + self.fatal_proc_rec("compilation failed!", &compile_result); + } + if let WillExecute::Disabled = should_run { + return; + } + + let prefixes = { + static PREFIXES: &[&str] = &["cdb", "cdbg"]; + // No "native rust support" variation for CDB yet. + PREFIXES + }; + + // Parse debugger commands etc from test files + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + prefixes, + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); + + // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands + let mut script_str = String::with_capacity(2048); + script_str.push_str("version\n"); // List CDB (and more) version info in test output + script_str.push_str(".nvlist\n"); // List loaded `*.natvis` files, bulk of custom MSVC debug + + // If a .js file exists next to the source file being tested, then this is a JavaScript + // debugging extension that needs to be loaded. + let mut js_extension = self.testpaths.file.clone(); + js_extension.set_extension("cdb.js"); + if js_extension.exists() { + script_str.push_str(&format!(".scriptload \"{}\"\n", js_extension.to_string_lossy())); + } + + // Set breakpoints on every line that contains the string "#break" + let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + for line in &dbg_cmds.breakpoint_lines { + script_str.push_str(&format!("bp `{}:{}`\n", source_file_name, line)); + } + + // Append the other `cdb-command:`s + for line in &dbg_cmds.commands { + script_str.push_str(line); + script_str.push('\n'); + } + + script_str.push_str("qq\n"); // Quit the debugger (including remote debugger, if any) + + // Write the script into a file + debug!("script_str = {}", script_str); + self.dump_output_file(&script_str, "debugger.script"); + let debugger_script = self.make_out_name("debugger.script"); + + let cdb_path = &self.config.cdb.as_ref().unwrap(); + let mut cdb = Command::new(cdb_path); + cdb.arg("-lines") // Enable source line debugging. + .arg("-cf") + .arg(&debugger_script) + .arg(&exe_file); + + let debugger_run_result = self.compose_and_run( + cdb, + self.config.run_lib_path.to_str().unwrap(), + None, // aux_path + None, // input + ); + + if !debugger_run_result.status.success() { + self.fatal_proc_rec("Error while running CDB", &debugger_run_result); + } + + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { + self.fatal_proc_rec(&e, &debugger_run_result); + } + } + + fn run_debuginfo_gdb_test(&self) { + let config = Config { + target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), + host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), + ..self.config.clone() + }; + + let test_cx = TestCx { config: &config, ..*self }; + + test_cx.run_debuginfo_gdb_test_no_opt(); + } + + fn run_debuginfo_gdb_test_no_opt(&self) { + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + &["gdb"], + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); + let mut cmds = dbg_cmds.commands.join("\n"); + + // compile test file (it should have 'compile-flags:-g' in the header) + let should_run = self.run_if_enabled(); + let compiler_run_result = self.compile_test(should_run, Emit::None); + if !compiler_run_result.status.success() { + self.fatal_proc_rec("compilation failed!", &compiler_run_result); + } + if let WillExecute::Disabled = should_run { + return; + } + + let exe_file = self.make_exe_name(); + + let debugger_run_result; + if is_android_gdb_target(&self.config.target) { + cmds = cmds.replace("run", "continue"); + + let tool_path = match self.config.android_cross_path.to_str() { + Some(x) => x.to_owned(), + None => self.fatal("cannot find android cross path"), + }; + + // write debugger script + let mut script_str = String::with_capacity(2048); + script_str.push_str(&format!("set charset {}\n", Self::charset())); + script_str.push_str(&format!("set sysroot {}\n", tool_path)); + script_str.push_str(&format!("file {}\n", exe_file.to_str().unwrap())); + script_str.push_str("target remote :5039\n"); + script_str.push_str(&format!( + "set solib-search-path \ + ./{}/stage2/lib/rustlib/{}/lib/\n", + self.config.host, self.config.target + )); + for line in &dbg_cmds.breakpoint_lines { + script_str.push_str( + format!( + "break {:?}:{}\n", + self.testpaths.file.file_name().unwrap().to_string_lossy(), + *line + ) + .as_str(), + ); + } + script_str.push_str(&cmds); + script_str.push_str("\nquit\n"); + + debug!("script_str = {}", script_str); + self.dump_output_file(&script_str, "debugger.script"); + + let adb_path = &self.config.adb_path; + + Command::new(adb_path) + .arg("push") + .arg(&exe_file) + .arg(&self.config.adb_test_dir) + .status() + .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); + + Command::new(adb_path) + .args(&["forward", "tcp:5039", "tcp:5039"]) + .status() + .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); + + let adb_arg = format!( + "export LD_LIBRARY_PATH={}; \ + gdbserver{} :5039 {}/{}", + self.config.adb_test_dir.clone(), + if self.config.target.contains("aarch64") { "64" } else { "" }, + self.config.adb_test_dir.clone(), + exe_file.file_name().unwrap().to_str().unwrap() + ); + + debug!("adb arg: {}", adb_arg); + let mut adb = Command::new(adb_path) + .args(&["shell", &adb_arg]) + .stdout(Stdio::piped()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap_or_else(|e| panic!("failed to exec `{adb_path:?}`: {e:?}")); + + // Wait for the gdbserver to print out "Listening on port ..." + // at which point we know that it's started and then we can + // execute the debugger below. + let mut stdout = BufReader::new(adb.stdout.take().unwrap()); + let mut line = String::new(); + loop { + line.truncate(0); + stdout.read_line(&mut line).unwrap(); + if line.starts_with("Listening on port 5039") { + break; + } + } + drop(stdout); + + let mut debugger_script = OsString::from("-command="); + debugger_script.push(self.make_out_name("debugger.script")); + let debugger_opts: &[&OsStr] = + &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script]; + + let gdb_path = self.config.gdb.as_ref().unwrap(); + let Output { status, stdout, stderr } = Command::new(&gdb_path) + .args(debugger_opts) + .output() + .unwrap_or_else(|e| panic!("failed to exec `{gdb_path:?}`: {e:?}")); + let cmdline = { + let mut gdb = Command::new(&format!("{}-gdb", self.config.target)); + gdb.args(debugger_opts); + let cmdline = self.make_cmdline(&gdb, ""); + logv(self.config, format!("executing {}", cmdline)); + cmdline + }; + + debugger_run_result = ProcRes { + status, + stdout: String::from_utf8(stdout).unwrap(), + stderr: String::from_utf8(stderr).unwrap(), + truncated: Truncated::No, + cmdline, + }; + if adb.kill().is_err() { + println!("Adb process is already finished."); + } + } else { + let rust_src_root = + self.config.find_rust_src_root().expect("Could not find Rust source root"); + let rust_pp_module_rel_path = Path::new("./src/etc"); + let rust_pp_module_abs_path = + rust_src_root.join(rust_pp_module_rel_path).to_str().unwrap().to_owned(); + // write debugger script + let mut script_str = String::with_capacity(2048); + script_str.push_str(&format!("set charset {}\n", Self::charset())); + script_str.push_str("show version\n"); + + match self.config.gdb_version { + Some(version) => { + println!("NOTE: compiletest thinks it is using GDB version {}", version); + + if version > extract_gdb_version("7.4").unwrap() { + // Add the directory containing the pretty printers to + // GDB's script auto loading safe path + script_str.push_str(&format!( + "add-auto-load-safe-path {}\n", + rust_pp_module_abs_path.replace(r"\", r"\\") + )); + + let output_base_dir = self.output_base_dir().to_str().unwrap().to_owned(); + + // Add the directory containing the output binary to + // include embedded pretty printers to GDB's script + // auto loading safe path + script_str.push_str(&format!( + "add-auto-load-safe-path {}\n", + output_base_dir.replace(r"\", r"\\") + )); + } + } + _ => { + println!( + "NOTE: compiletest does not know which version of \ + GDB it is using" + ); + } + } + + // The following line actually doesn't have to do anything with + // pretty printing, it just tells GDB to print values on one line: + script_str.push_str("set print pretty off\n"); + + // Add the pretty printer directory to GDB's source-file search path + script_str + .push_str(&format!("directory {}\n", rust_pp_module_abs_path.replace(r"\", r"\\"))); + + // Load the target executable + script_str + .push_str(&format!("file {}\n", exe_file.to_str().unwrap().replace(r"\", r"\\"))); + + // Force GDB to print values in the Rust format. + script_str.push_str("set language rust\n"); + + // Add line breakpoints + for line in &dbg_cmds.breakpoint_lines { + script_str.push_str(&format!( + "break '{}':{}\n", + self.testpaths.file.file_name().unwrap().to_string_lossy(), + *line + )); + } + + script_str.push_str(&cmds); + script_str.push_str("\nquit\n"); + + debug!("script_str = {}", script_str); + self.dump_output_file(&script_str, "debugger.script"); + + let mut debugger_script = OsString::from("-command="); + debugger_script.push(self.make_out_name("debugger.script")); + + let debugger_opts: &[&OsStr] = + &["-quiet".as_ref(), "-batch".as_ref(), "-nx".as_ref(), &debugger_script]; + + let mut gdb = Command::new(self.config.gdb.as_ref().unwrap()); + let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { + format!("{pp}:{rust_pp_module_abs_path}") + } else { + rust_pp_module_abs_path + }; + gdb.args(debugger_opts).env("PYTHONPATH", pythonpath); + + debugger_run_result = + self.compose_and_run(gdb, self.config.run_lib_path.to_str().unwrap(), None, None); + } + + if !debugger_run_result.status.success() { + self.fatal_proc_rec("gdb failed to execute", &debugger_run_result); + } + + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { + self.fatal_proc_rec(&e, &debugger_run_result); + } + } + + fn run_debuginfo_lldb_test(&self) { + if self.config.lldb_python_dir.is_none() { + self.fatal("Can't run LLDB test because LLDB's python path is not set."); + } + + let config = Config { + target_rustcflags: self.cleanup_debug_info_options(&self.config.target_rustcflags), + host_rustcflags: self.cleanup_debug_info_options(&self.config.host_rustcflags), + ..self.config.clone() + }; + + let test_cx = TestCx { config: &config, ..*self }; + + test_cx.run_debuginfo_lldb_test_no_opt(); + } + + fn run_debuginfo_lldb_test_no_opt(&self) { + // compile test file (it should have 'compile-flags:-g' in the header) + let should_run = self.run_if_enabled(); + let compile_result = self.compile_test(should_run, Emit::None); + if !compile_result.status.success() { + self.fatal_proc_rec("compilation failed!", &compile_result); + } + if let WillExecute::Disabled = should_run { + return; + } + + let exe_file = self.make_exe_name(); + + match self.config.lldb_version { + Some(ref version) => { + println!("NOTE: compiletest thinks it is using LLDB version {}", version); + } + _ => { + println!( + "NOTE: compiletest does not know which version of \ + LLDB it is using" + ); + } + } + + // Parse debugger commands etc from test files + let dbg_cmds = DebuggerCommands::parse_from( + &self.testpaths.file, + self.config, + &["lldb"], + self.revision, + ) + .unwrap_or_else(|e| self.fatal(&e)); + + // Write debugger script: + // We don't want to hang when calling `quit` while the process is still running + let mut script_str = String::from("settings set auto-confirm true\n"); + + // Make LLDB emit its version, so we have it documented in the test output + script_str.push_str("version\n"); + + // Switch LLDB into "Rust mode" + let rust_src_root = + self.config.find_rust_src_root().expect("Could not find Rust source root"); + let rust_pp_module_rel_path = Path::new("./src/etc"); + let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path); + + script_str.push_str(&format!( + "command script import {}/lldb_lookup.py\n", + rust_pp_module_abs_path.to_str().unwrap() + )); + File::open(rust_pp_module_abs_path.join("lldb_commands")) + .and_then(|mut file| file.read_to_string(&mut script_str)) + .expect("Failed to read lldb_commands"); + + // Set breakpoints on every line that contains the string "#break" + let source_file_name = self.testpaths.file.file_name().unwrap().to_string_lossy(); + for line in &dbg_cmds.breakpoint_lines { + script_str.push_str(&format!( + "breakpoint set --file '{}' --line {}\n", + source_file_name, line + )); + } + + // Append the other commands + for line in &dbg_cmds.commands { + script_str.push_str(line); + script_str.push('\n'); + } + + // Finally, quit the debugger + script_str.push_str("\nquit\n"); + + // Write the script into a file + debug!("script_str = {}", script_str); + self.dump_output_file(&script_str, "debugger.script"); + let debugger_script = self.make_out_name("debugger.script"); + + // Let LLDB execute the script via lldb_batchmode.py + let debugger_run_result = self.run_lldb(&exe_file, &debugger_script, &rust_src_root); + + if !debugger_run_result.status.success() { + self.fatal_proc_rec("Error while running LLDB", &debugger_run_result); + } + + if let Err(e) = dbg_cmds.check_output(&debugger_run_result) { + self.fatal_proc_rec(&e, &debugger_run_result); + } + } + + fn run_lldb( + &self, + test_executable: &Path, + debugger_script: &Path, + rust_src_root: &Path, + ) -> ProcRes { + // Prepare the lldb_batchmode which executes the debugger script + let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py"); + let pythonpath = if let Ok(pp) = std::env::var("PYTHONPATH") { + format!("{pp}:{}", self.config.lldb_python_dir.as_ref().unwrap()) + } else { + self.config.lldb_python_dir.as_ref().unwrap().to_string() + }; + self.run_command_to_procres( + Command::new(&self.config.python) + .arg(&lldb_script_path) + .arg(test_executable) + .arg(debugger_script) + .env("PYTHONUNBUFFERED", "1") // Help debugging #78665 + .env("PYTHONPATH", pythonpath), + ) + } + + fn cleanup_debug_info_options(&self, options: &Vec<String>) -> Vec<String> { + // Remove options that are either unwanted (-O) or may lead to duplicates due to RUSTFLAGS. + let options_to_remove = ["-O".to_owned(), "-g".to_owned(), "--debuginfo".to_owned()]; + + options.iter().filter(|x| !options_to_remove.contains(x)).cloned().collect() + } +} diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs new file mode 100644 index 00000000000..81b006292e4 --- /dev/null +++ b/src/tools/compiletest/src/runtest/incremental.rs @@ -0,0 +1,128 @@ +use super::{TestCx, WillExecute}; +use crate::errors; + +// FIXME(jieyouxu): `run_rpass_test` got hoisted out of this because apparently valgrind falls back +// to `run_rpass_test` if valgrind isn't available, which is questionable, but keeping it for +// refactoring changes to preserve current behavior. + +impl TestCx<'_> { + pub(super) fn run_incremental_test(&self) { + // Basic plan for a test incremental/foo/bar.rs: + // - load list of revisions rpass1, cfail2, rpass3 + // - each should begin with `cpass`, `rpass`, `cfail`, or `rfail` + // - if `cpass`, expect compilation to succeed, don't execute + // - if `rpass`, expect compilation and execution to succeed + // - if `cfail`, expect compilation to fail + // - if `rfail`, expect compilation to succeed and execution to fail + // - create a directory build/foo/bar.incremental + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1 + // - because name of revision starts with "rpass", expect success + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C cfail2 + // - because name of revision starts with "cfail", expect an error + // - load expected errors as usual, but filter for those that end in `[rfail2]` + // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass3 + // - because name of revision starts with "rpass", expect success + // - execute build/foo/bar.exe and save output + // + // FIXME -- use non-incremental mode as an oracle? That doesn't apply + // to #[rustc_dirty] and clean tests I guess + + let revision = self.revision.expect("incremental tests require a list of revisions"); + + // Incremental workproduct directory should have already been created. + let incremental_dir = self.props.incremental_dir.as_ref().unwrap(); + assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir"); + + if self.config.verbose { + print!("revision={:?} props={:#?}", revision, self.props); + } + + if revision.starts_with("cpass") { + if self.props.should_ice { + self.fatal("can only use should-ice in cfail tests"); + } + self.run_cpass_test(); + } else if revision.starts_with("rpass") { + if self.props.should_ice { + self.fatal("can only use should-ice in cfail tests"); + } + self.run_rpass_test(); + } else if revision.starts_with("rfail") { + if self.props.should_ice { + self.fatal("can only use should-ice in cfail tests"); + } + self.run_rfail_test(); + } else if revision.starts_with("cfail") { + self.run_cfail_test(); + } else { + self.fatal("revision name must begin with cpass, rpass, rfail, or cfail"); + } + } + + fn run_cpass_test(&self) { + let emit_metadata = self.should_emit_metadata(self.pass_mode()); + let proc_res = self.compile_test(WillExecute::No, emit_metadata); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + // FIXME(#41968): Move this check to tidy? + if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() { + self.fatal("compile-pass tests with expected warnings should be moved to ui/"); + } + } + + fn run_cfail_test(&self) { + let pm = self.pass_mode(); + let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm)); + self.check_if_test_should_compile(&proc_res, pm); + self.check_no_compiler_crash(&proc_res, self.props.should_ice); + + let output_to_check = self.get_output(&proc_res); + let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); + if !expected_errors.is_empty() { + if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() + { + self.fatal("both error pattern and expected errors specified"); + } + self.check_expected_errors(expected_errors, &proc_res); + } else { + self.check_all_error_patterns(&output_to_check, &proc_res, pm); + } + if self.props.should_ice { + match proc_res.status.code() { + Some(101) => (), + _ => self.fatal("expected ICE"), + } + } + + self.check_forbid_output(&output_to_check, &proc_res); + } + + fn run_rfail_test(&self) { + let pm = self.pass_mode(); + let should_run = self.run_if_enabled(); + let proc_res = self.compile_test(should_run, self.should_emit_metadata(pm)); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + if let WillExecute::Disabled = should_run { + return; + } + + let proc_res = self.exec_compiled_test(); + + // The value our Makefile configures valgrind to return on failure + const VALGRIND_ERR: i32 = 100; + if proc_res.status.code() == Some(VALGRIND_ERR) { + self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res); + } + + let output_to_check = self.get_output(&proc_res); + self.check_correct_failure_status(&proc_res); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); + } +} diff --git a/src/tools/compiletest/src/runtest/js_doc.rs b/src/tools/compiletest/src/runtest/js_doc.rs new file mode 100644 index 00000000000..68c74cd155c --- /dev/null +++ b/src/tools/compiletest/src/runtest/js_doc.rs @@ -0,0 +1,32 @@ +use std::process::Command; + +use super::TestCx; + +impl TestCx<'_> { + pub(super) fn run_js_doc_test(&self) { + if let Some(nodejs) = &self.config.nodejs { + let out_dir = self.output_base_dir(); + + self.document(&out_dir, &self.testpaths); + + let root = self.config.find_rust_src_root().unwrap(); + let file_stem = + self.testpaths.file.file_stem().and_then(|f| f.to_str()).expect("no file stem"); + let res = self.run_command_to_procres( + Command::new(&nodejs) + .arg(root.join("src/tools/rustdoc-js/tester.js")) + .arg("--doc-folder") + .arg(out_dir) + .arg("--crate-name") + .arg(file_stem.replace("-", "_")) + .arg("--test-file") + .arg(self.testpaths.file.with_extension("js")), + ); + if !res.status.success() { + self.fatal_proc_rec("rustdoc-js test failed!", &res); + } + } else { + self.fatal("no nodeJS"); + } + } +} diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs new file mode 100644 index 00000000000..02289a8df1e --- /dev/null +++ b/src/tools/compiletest/src/runtest/mir_opt.rs @@ -0,0 +1,155 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use glob::glob; +use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile}; +use tracing::debug; + +use super::{Emit, TestCx, WillExecute}; +use crate::compute_diff::write_diff; + +impl TestCx<'_> { + pub(super) fn run_mir_opt_test(&self) { + let pm = self.pass_mode(); + let should_run = self.should_run(pm); + + let mut test_info = files_for_miropt_test( + &self.testpaths.file, + self.config.get_pointer_width(), + self.config.target_cfg().panic.for_miropt_test_tools(), + ); + + let passes = std::mem::take(&mut test_info.passes); + + let proc_res = self.compile_test_with_passes(should_run, Emit::Mir, passes); + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + self.check_mir_dump(test_info); + + if let WillExecute::Yes = should_run { + let proc_res = self.exec_compiled_test(); + + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + } + } + + fn check_mir_dump(&self, test_info: MiroptTest) { + let test_dir = self.testpaths.file.parent().unwrap(); + let test_crate = + self.testpaths.file.file_stem().unwrap().to_str().unwrap().replace('-', "_"); + + let MiroptTest { run_filecheck, suffix, files, passes: _ } = test_info; + + if self.config.bless { + for e in + glob(&format!("{}/{}.*{}.mir", test_dir.display(), test_crate, suffix)).unwrap() + { + fs::remove_file(e.unwrap()).unwrap(); + } + for e in + glob(&format!("{}/{}.*{}.diff", test_dir.display(), test_crate, suffix)).unwrap() + { + fs::remove_file(e.unwrap()).unwrap(); + } + } + + for MiroptTestFile { from_file, to_file, expected_file } in files { + let dumped_string = if let Some(after) = to_file { + self.diff_mir_files(from_file.into(), after.into()) + } else { + let mut output_file = PathBuf::new(); + output_file.push(self.get_mir_dump_dir()); + output_file.push(&from_file); + debug!( + "comparing the contents of: {} with {}", + output_file.display(), + expected_file.display() + ); + if !output_file.exists() { + panic!( + "Output file `{}` from test does not exist, available files are in `{}`", + output_file.display(), + output_file.parent().unwrap().display() + ); + } + self.check_mir_test_timestamp(&from_file, &output_file); + let dumped_string = fs::read_to_string(&output_file).unwrap(); + self.normalize_output(&dumped_string, &[]) + }; + + if self.config.bless { + let _ = fs::remove_file(&expected_file); + fs::write(expected_file, dumped_string.as_bytes()).unwrap(); + } else { + if !expected_file.exists() { + panic!("Output file `{}` from test does not exist", expected_file.display()); + } + let expected_string = fs::read_to_string(&expected_file).unwrap(); + if dumped_string != expected_string { + print!("{}", write_diff(&expected_string, &dumped_string, 3)); + panic!( + "Actual MIR output differs from expected MIR output {}", + expected_file.display() + ); + } + } + } + + if run_filecheck { + let output_path = self.output_base_name().with_extension("mir"); + let proc_res = self.verify_with_filecheck(&output_path); + if !proc_res.status.success() { + self.fatal_proc_rec("verification with 'FileCheck' failed", &proc_res); + } + } + } + + fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String { + let to_full_path = |path: PathBuf| { + let full = self.get_mir_dump_dir().join(&path); + if !full.exists() { + panic!( + "the mir dump file for {} does not exist (requested in {})", + path.display(), + self.testpaths.file.display(), + ); + } + full + }; + let before = to_full_path(before); + let after = to_full_path(after); + debug!("comparing the contents of: {} with {}", before.display(), after.display()); + let before = fs::read_to_string(before).unwrap(); + let after = fs::read_to_string(after).unwrap(); + let before = self.normalize_output(&before, &[]); + let after = self.normalize_output(&after, &[]); + let mut dumped_string = String::new(); + for result in diff::lines(&before, &after) { + use std::fmt::Write; + match result { + diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(), + diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(), + diff::Result::Both(s, _) => writeln!(dumped_string, " {}", s).unwrap(), + } + } + dumped_string + } + + fn check_mir_test_timestamp(&self, test_name: &str, output_file: &Path) { + let t = |file| fs::metadata(file).unwrap().modified().unwrap(); + let source_file = &self.testpaths.file; + let output_time = t(output_file); + let source_time = t(source_file); + if source_time > output_time { + debug!("source file time: {:?} output file time: {:?}", source_time, output_time); + panic!( + "test source file `{}` is newer than potentially stale output file `{}`.", + source_file.display(), + test_name + ); + } + } +} diff --git a/src/tools/compiletest/src/runtest/pretty.rs b/src/tools/compiletest/src/runtest/pretty.rs new file mode 100644 index 00000000000..40e767e84ef --- /dev/null +++ b/src/tools/compiletest/src/runtest/pretty.rs @@ -0,0 +1,104 @@ +use std::fs; + +use super::{ProcRes, ReadFrom, TestCx}; +use crate::util::logv; + +impl TestCx<'_> { + pub(super) fn run_pretty_test(&self) { + if self.props.pp_exact.is_some() { + logv(self.config, "testing for exact pretty-printing".to_owned()); + } else { + logv(self.config, "testing for converging pretty-printing".to_owned()); + } + + let rounds = match self.props.pp_exact { + Some(_) => 1, + None => 2, + }; + + let src = fs::read_to_string(&self.testpaths.file).unwrap(); + let mut srcs = vec![src]; + + let mut round = 0; + while round < rounds { + logv( + self.config, + format!("pretty-printing round {} revision {:?}", round, self.revision), + ); + let read_from = + if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) }; + + let proc_res = self.print_source(read_from, &self.props.pretty_mode); + if !proc_res.status.success() { + self.fatal_proc_rec( + &format!( + "pretty-printing failed in round {} revision {:?}", + round, self.revision + ), + &proc_res, + ); + } + + let ProcRes { stdout, .. } = proc_res; + srcs.push(stdout); + round += 1; + } + + let mut expected = match self.props.pp_exact { + Some(ref file) => { + let filepath = self.testpaths.file.parent().unwrap().join(file); + fs::read_to_string(&filepath).unwrap() + } + None => srcs[srcs.len() - 2].clone(), + }; + let mut actual = srcs[srcs.len() - 1].clone(); + + if self.props.pp_exact.is_some() { + // Now we have to care about line endings + let cr = "\r".to_owned(); + actual = actual.replace(&cr, ""); + expected = expected.replace(&cr, ""); + } + + if !self.config.bless { + self.compare_source(&expected, &actual); + } else if expected != actual { + let filepath_buf; + let filepath = match &self.props.pp_exact { + Some(file) => { + filepath_buf = self.testpaths.file.parent().unwrap().join(file); + &filepath_buf + } + None => &self.testpaths.file, + }; + fs::write(filepath, &actual).unwrap(); + } + + // If we're only making sure that the output matches then just stop here + if self.props.pretty_compare_only { + return; + } + + // Finally, let's make sure it actually appears to remain valid code + let proc_res = self.typecheck_source(actual); + if !proc_res.status.success() { + self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res); + } + + if !self.props.pretty_expanded { + return; + } + + // additionally, run `-Zunpretty=expanded` and try to build it. + let proc_res = self.print_source(ReadFrom::Path, "expanded"); + if !proc_res.status.success() { + self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res); + } + + let ProcRes { stdout: expanded_src, .. } = proc_res; + let proc_res = self.typecheck_source(expanded_src); + if !proc_res.status.success() { + self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res); + } + } +} diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs new file mode 100644 index 00000000000..75fe6a6baaf --- /dev/null +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -0,0 +1,526 @@ +use std::path::Path; +use std::process::{Command, Output, Stdio}; +use std::{env, fs}; + +use super::{ProcRes, TestCx}; +use crate::util::{copy_dir_all, dylib_env_var}; + +impl TestCx<'_> { + pub(super) fn run_rmake_test(&self) { + let test_dir = &self.testpaths.file; + if test_dir.join("rmake.rs").exists() { + self.run_rmake_v2_test(); + } else if test_dir.join("Makefile").exists() { + self.run_rmake_legacy_test(); + } else { + self.fatal("failed to find either `rmake.rs` or `Makefile`") + } + } + + fn run_rmake_legacy_test(&self) { + let cwd = env::current_dir().unwrap(); + let src_root = self.config.src_base.parent().unwrap().parent().unwrap(); + let src_root = cwd.join(&src_root); + + let tmpdir = cwd.join(self.output_base_name()); + if tmpdir.exists() { + self.aggressive_rm_rf(&tmpdir).unwrap(); + } + fs::create_dir_all(&tmpdir).unwrap(); + + let host = &self.config.host; + let make = if host.contains("dragonfly") + || host.contains("freebsd") + || host.contains("netbsd") + || host.contains("openbsd") + || host.contains("aix") + { + "gmake" + } else { + "make" + }; + + let mut cmd = Command::new(make); + cmd.current_dir(&self.testpaths.file) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .env("TARGET", &self.config.target) + .env("PYTHON", &self.config.python) + .env("S", src_root) + .env("RUST_BUILD_STAGE", &self.config.stage_id) + .env("RUSTC", cwd.join(&self.config.rustc_path)) + .env("TMPDIR", &tmpdir) + .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + .env("HOST_RPATH_DIR", cwd.join(&self.config.compile_lib_path)) + .env("TARGET_RPATH_DIR", cwd.join(&self.config.run_lib_path)) + .env("LLVM_COMPONENTS", &self.config.llvm_components) + // We for sure don't want these tests to run in parallel, so make + // sure they don't have access to these vars if we run via `make` + // at the top level + .env_remove("MAKEFLAGS") + .env_remove("MFLAGS") + .env_remove("CARGO_MAKEFLAGS"); + + if let Some(ref cargo) = self.config.cargo_path { + cmd.env("CARGO", cwd.join(cargo)); + } + + if let Some(ref rustdoc) = self.config.rustdoc_path { + cmd.env("RUSTDOC", cwd.join(rustdoc)); + } + + if let Some(ref node) = self.config.nodejs { + cmd.env("NODE", node); + } + + if let Some(ref linker) = self.config.target_linker { + cmd.env("RUSTC_LINKER", linker); + } + + if let Some(ref clang) = self.config.run_clang_based_tests_with { + cmd.env("CLANG", clang); + } + + if let Some(ref filecheck) = self.config.llvm_filecheck { + cmd.env("LLVM_FILECHECK", filecheck); + } + + if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { + cmd.env("LLVM_BIN_DIR", llvm_bin_dir); + } + + if let Some(ref remote_test_client) = self.config.remote_test_client { + cmd.env("REMOTE_TEST_CLIENT", remote_test_client); + } + + // We don't want RUSTFLAGS set from the outside to interfere with + // compiler flags set in the test cases: + cmd.env_remove("RUSTFLAGS"); + + // Use dynamic musl for tests because static doesn't allow creating dylibs + if self.config.host.contains("musl") { + cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); + } + + if self.config.bless { + cmd.env("RUSTC_BLESS_TEST", "--bless"); + // Assume this option is active if the environment variable is "defined", with _any_ value. + // As an example, a `Makefile` can use this option by: + // + // ifdef RUSTC_BLESS_TEST + // cp "$(TMPDIR)"/actual_something.ext expected_something.ext + // else + // $(DIFF) expected_something.ext "$(TMPDIR)"/actual_something.ext + // endif + } + + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { + // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` + // and that `lib.exe` lives next to it. + let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); + + // MSYS doesn't like passing flags of the form `/foo` as it thinks it's + // a path and instead passes `C:\msys64\foo`, so convert all + // `/`-arguments to MSVC here to `-` arguments. + let cflags = self + .config + .cflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::<Vec<_>>() + .join(" "); + let cxxflags = self + .config + .cxxflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::<Vec<_>>() + .join(" "); + + cmd.env("IS_MSVC", "1") + .env("IS_WINDOWS", "1") + .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) + .env("MSVC_LIB_PATH", format!("{}", lib.display())) + .env("CC", format!("'{}' {}", self.config.cc, cflags)) + .env("CXX", format!("'{}' {}", &self.config.cxx, cxxflags)); + } else { + cmd.env("CC", format!("{} {}", self.config.cc, self.config.cflags)) + .env("CXX", format!("{} {}", self.config.cxx, self.config.cxxflags)) + .env("AR", &self.config.ar); + + if self.config.target.contains("windows") { + cmd.env("IS_WINDOWS", "1"); + } + } + + let (output, truncated) = + self.read2_abbreviated(cmd.spawn().expect("failed to spawn `make`")); + if !output.status.success() { + let res = ProcRes { + status: output.status, + stdout: String::from_utf8_lossy(&output.stdout).into_owned(), + stderr: String::from_utf8_lossy(&output.stderr).into_owned(), + truncated, + cmdline: format!("{:?}", cmd), + }; + self.fatal_proc_rec("make failed", &res); + } + } + + fn run_rmake_v2_test(&self) { + // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe + // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust + // library and is available under `build/$TARGET/stageN-tools-bin/librun_make_support.rlib`. + // + // 1. We need to build the recipe `rmake.rs` as a binary and link in the `run_make_support` + // library. + // 2. We need to run the recipe binary. + + // So we assume the rust-lang/rust project setup looks like the following (our `.` is the + // top-level directory, irrelevant entries to our purposes omitted): + // + // ``` + // . // <- `source_root` + // ├── build/ // <- `build_root` + // ├── compiler/ + // ├── library/ + // ├── src/ + // │ └── tools/ + // │ └── run_make_support/ + // └── tests + // └── run-make/ + // ``` + + // `source_root` is the top-level directory containing the rust-lang/rust checkout. + let source_root = + self.config.find_rust_src_root().expect("could not determine rust source root"); + // `self.config.build_base` is actually the build base folder + "test" + test suite name, it + // looks like `build/<host_triple>/test/run-make`. But we want `build/<host_triple>/`. Note + // that the `build` directory does not need to be called `build`, nor does it need to be + // under `source_root`, so we must compute it based off of `self.config.build_base`. + let build_root = + self.config.build_base.parent().and_then(Path::parent).unwrap().to_path_buf(); + + // We construct the following directory tree for each rmake.rs test: + // ``` + // <base_dir>/ + // rmake.exe + // rmake_out/ + // ``` + // having the recipe executable separate from the output artifacts directory allows the + // recipes to `remove_dir_all($TMPDIR)` without running into issues related trying to remove + // a currently running executable because the recipe executable is not under the + // `rmake_out/` directory. + // + // This setup intentionally diverges from legacy Makefile run-make tests. + let base_dir = self.output_base_name(); + if base_dir.exists() { + self.aggressive_rm_rf(&base_dir).unwrap(); + } + let rmake_out_dir = base_dir.join("rmake_out"); + fs::create_dir_all(&rmake_out_dir).unwrap(); + + // Copy all input files (apart from rmake.rs) to the temporary directory, + // so that the input directory structure from `tests/run-make/<test>` is mirrored + // to the `rmake_out` directory. + for path in walkdir::WalkDir::new(&self.testpaths.file).min_depth(1) { + let path = path.unwrap().path().to_path_buf(); + if path.file_name().is_some_and(|s| s != "rmake.rs") { + let target = rmake_out_dir.join(path.strip_prefix(&self.testpaths.file).unwrap()); + if path.is_dir() { + copy_dir_all(&path, target).unwrap(); + } else { + fs::copy(&path, target).unwrap(); + } + } + } + + // `self.config.stage_id` looks like `stage1-<target_triple>`, but we only want + // the `stage1` part as that is what the output directories of bootstrap are prefixed with. + // Note that this *assumes* build layout from bootstrap is produced as: + // + // ``` + // build/<target_triple>/ // <- this is `build_root` + // ├── stage0 + // ├── stage0-bootstrap-tools + // ├── stage0-codegen + // ├── stage0-rustc + // ├── stage0-std + // ├── stage0-sysroot + // ├── stage0-tools + // ├── stage0-tools-bin + // ├── stage1 + // ├── stage1-std + // ├── stage1-tools + // ├── stage1-tools-bin + // └── test + // ``` + // FIXME(jieyouxu): improve the communication between bootstrap and compiletest here so + // we don't have to hack out a `stageN`. + let stage = self.config.stage_id.split('-').next().unwrap(); + + // In order to link in the support library as a rlib when compiling recipes, we need three + // paths: + // 1. Path of the built support library rlib itself. + // 2. Path of the built support library's dependencies directory. + // 3. Path of the built support library's dependencies' dependencies directory. + // + // The paths look like + // + // ``` + // build/<target_triple>/ + // ├── stageN-tools-bin/ + // │ └── librun_make_support.rlib // <- support rlib itself + // ├── stageN-tools/ + // │ ├── release/deps/ // <- deps of deps + // │ └── <host_triple>/release/deps/ // <- deps + // ``` + // + // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the + // support lib and its deps are organized, can't we copy them to the tools-bin dir as + // well?), but this seems to work for now. + + let stage_tools_bin = build_root.join(format!("{stage}-tools-bin")); + let support_lib_path = stage_tools_bin.join("librun_make_support.rlib"); + + let stage_tools = build_root.join(format!("{stage}-tools")); + let support_lib_deps = stage_tools.join(&self.config.host).join("release").join("deps"); + let support_lib_deps_deps = stage_tools.join("release").join("deps"); + + // To compile the recipe with rustc, we need to provide suitable dynamic library search + // paths to rustc. This includes both: + // 1. The "base" dylib search paths that was provided to compiletest, e.g. `LD_LIBRARY_PATH` + // on some linux distros. + // 2. Specific library paths in `self.config.compile_lib_path` needed for running rustc. + + let base_dylib_search_paths = + Vec::from_iter(env::split_paths(&env::var(dylib_env_var()).unwrap())); + + let host_dylib_search_paths = { + let mut paths = vec![self.config.compile_lib_path.clone()]; + paths.extend(base_dylib_search_paths.iter().cloned()); + paths + }; + + // Calculate the paths of the recipe binary. As previously discussed, this is placed at + // `<base_dir>/<bin_name>` with `bin_name` being `rmake` or `rmake.exe` depending on + // platform. + let recipe_bin = { + let mut p = base_dir.join("rmake"); + p.set_extension(env::consts::EXE_EXTENSION); + p + }; + + let mut rustc = Command::new(&self.config.rustc_path); + rustc + .arg("-o") + .arg(&recipe_bin) + // Specify library search paths for `run_make_support`. + .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap().to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_deps.to_string_lossy())) + .arg(format!("-Ldependency={}", &support_lib_deps_deps.to_string_lossy())) + // Provide `run_make_support` as extern prelude, so test writers don't need to write + // `extern run_make_support;`. + .arg("--extern") + .arg(format!("run_make_support={}", &support_lib_path.to_string_lossy())) + .arg("--edition=2021") + .arg(&self.testpaths.file.join("rmake.rs")) + // Provide necessary library search paths for rustc. + .env(dylib_env_var(), &env::join_paths(host_dylib_search_paths).unwrap()); + + // In test code we want to be very pedantic about values being silently discarded that are + // annotated with `#[must_use]`. + rustc.arg("-Dunused_must_use"); + + // > `cg_clif` uses `COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0` for running the rustc + // > test suite. With the introduction of rmake.rs this broke. `librun_make_support.rlib` is + // > compiled using the bootstrap rustc wrapper which sets `--sysroot + // > build/aarch64-unknown-linux-gnu/stage0-sysroot`, but then compiletest will compile + // > `rmake.rs` using the sysroot of the bootstrap compiler causing it to not find the + // > `libstd.rlib` against which `librun_make_support.rlib` is compiled. + // + // The gist here is that we have to pass the proper stage0 sysroot if we want + // + // ``` + // $ COMPILETEST_FORCE_STAGE0=1 ./x test run-make --stage 0 + // ``` + // + // to work correctly. + // + // See <https://github.com/rust-lang/rust/pull/122248> for more background. + if std::env::var_os("COMPILETEST_FORCE_STAGE0").is_some() { + let stage0_sysroot = build_root.join("stage0-sysroot"); + rustc.arg("--sysroot").arg(&stage0_sysroot); + } + + // Now run rustc to build the recipe. + let res = self.run_command_to_procres(&mut rustc); + if !res.status.success() { + self.fatal_proc_rec("run-make test failed: could not build `rmake.rs` recipe", &res); + } + + // To actually run the recipe, we have to provide the recipe with a bunch of information + // provided through env vars. + + // Compute stage-specific standard library paths. + let stage_std_path = build_root.join(&stage).join("lib"); + + // Compute dynamic library search paths for recipes. + let recipe_dylib_search_paths = { + let mut paths = base_dylib_search_paths.clone(); + paths.push(support_lib_path.parent().unwrap().to_path_buf()); + paths.push(stage_std_path.join("rustlib").join(&self.config.host).join("lib")); + paths + }; + + // Compute runtime library search paths for recipes. This is target-specific. + let target_runtime_dylib_search_paths = { + let mut paths = vec![rmake_out_dir.clone()]; + paths.extend(base_dylib_search_paths.iter().cloned()); + paths + }; + + // FIXME(jieyouxu): please rename `TARGET_RPATH_ENV`, `HOST_RPATH_DIR` and + // `TARGET_RPATH_DIR`, it is **extremely** confusing! + let mut cmd = Command::new(&recipe_bin); + cmd.current_dir(&rmake_out_dir) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + // Provide the target-specific env var that is used to record dylib search paths. For + // example, this could be `LD_LIBRARY_PATH` on some linux distros but `PATH` on Windows. + .env("LD_LIB_PATH_ENVVAR", dylib_env_var()) + // Provide the dylib search paths. + .env(dylib_env_var(), &env::join_paths(recipe_dylib_search_paths).unwrap()) + // Provide runtime dylib search paths. + .env("TARGET_RPATH_ENV", &env::join_paths(target_runtime_dylib_search_paths).unwrap()) + // Provide the target. + .env("TARGET", &self.config.target) + // Some tests unfortunately still need Python, so provide path to a Python interpreter. + .env("PYTHON", &self.config.python) + // Provide path to checkout root. This is the top-level directory containing + // rust-lang/rust checkout. + .env("SOURCE_ROOT", &source_root) + // Provide path to stage-corresponding rustc. + .env("RUSTC", &self.config.rustc_path) + // Provide the directory to libraries that are needed to run the *compiler*. This is not + // to be confused with `TARGET_RPATH_ENV` or `TARGET_RPATH_DIR`. This is needed if the + // recipe wants to invoke rustc. + .env("HOST_RPATH_DIR", &self.config.compile_lib_path) + // Provide the directory to libraries that might be needed to run compiled binaries + // (further compiled by the recipe!). + .env("TARGET_RPATH_DIR", &self.config.run_lib_path) + // Provide which LLVM components are available (e.g. which LLVM components are provided + // through a specific CI runner). + .env("LLVM_COMPONENTS", &self.config.llvm_components); + + if let Some(ref cargo) = self.config.cargo_path { + cmd.env("CARGO", source_root.join(cargo)); + } + + if let Some(ref rustdoc) = self.config.rustdoc_path { + cmd.env("RUSTDOC", source_root.join(rustdoc)); + } + + if let Some(ref node) = self.config.nodejs { + cmd.env("NODE", node); + } + + if let Some(ref linker) = self.config.target_linker { + cmd.env("RUSTC_LINKER", linker); + } + + if let Some(ref clang) = self.config.run_clang_based_tests_with { + cmd.env("CLANG", clang); + } + + if let Some(ref filecheck) = self.config.llvm_filecheck { + cmd.env("LLVM_FILECHECK", filecheck); + } + + if let Some(ref llvm_bin_dir) = self.config.llvm_bin_dir { + cmd.env("LLVM_BIN_DIR", llvm_bin_dir); + } + + if let Some(ref remote_test_client) = self.config.remote_test_client { + cmd.env("REMOTE_TEST_CLIENT", remote_test_client); + } + + // We don't want RUSTFLAGS set from the outside to interfere with + // compiler flags set in the test cases: + cmd.env_remove("RUSTFLAGS"); + + // Use dynamic musl for tests because static doesn't allow creating dylibs + if self.config.host.contains("musl") { + cmd.env("RUSTFLAGS", "-Ctarget-feature=-crt-static").env("IS_MUSL_HOST", "1"); + } + + if self.config.bless { + // If we're running in `--bless` mode, set an environment variable to tell + // `run_make_support` to bless snapshot files instead of checking them. + // + // The value is this test's source directory, because the support code + // will need that path in order to bless the _original_ snapshot files, + // not the copies in `rmake_out`. + // (See <https://github.com/rust-lang/rust/issues/129038>.) + cmd.env("RUSTC_BLESS_TEST", &self.testpaths.file); + } + + if self.config.target.contains("msvc") && !self.config.cc.is_empty() { + // We need to pass a path to `lib.exe`, so assume that `cc` is `cl.exe` + // and that `lib.exe` lives next to it. + let lib = Path::new(&self.config.cc).parent().unwrap().join("lib.exe"); + + // MSYS doesn't like passing flags of the form `/foo` as it thinks it's + // a path and instead passes `C:\msys64\foo`, so convert all + // `/`-arguments to MSVC here to `-` arguments. + let cflags = self + .config + .cflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::<Vec<_>>() + .join(" "); + let cxxflags = self + .config + .cxxflags + .split(' ') + .map(|s| s.replace("/", "-")) + .collect::<Vec<_>>() + .join(" "); + + cmd.env("IS_MSVC", "1") + .env("IS_WINDOWS", "1") + .env("MSVC_LIB", format!("'{}' -nologo", lib.display())) + .env("MSVC_LIB_PATH", format!("{}", lib.display())) + // Note: we diverge from legacy run_make and don't lump `CC` the compiler and + // default flags together. + .env("CC_DEFAULT_FLAGS", &cflags) + .env("CC", &self.config.cc) + .env("CXX_DEFAULT_FLAGS", &cxxflags) + .env("CXX", &self.config.cxx); + } else { + cmd.env("CC_DEFAULT_FLAGS", &self.config.cflags) + .env("CC", &self.config.cc) + .env("CXX_DEFAULT_FLAGS", &self.config.cxxflags) + .env("CXX", &self.config.cxx) + .env("AR", &self.config.ar); + + if self.config.target.contains("windows") { + cmd.env("IS_WINDOWS", "1"); + } + } + + let (Output { stdout, stderr, status }, truncated) = + self.read2_abbreviated(cmd.spawn().expect("failed to spawn `rmake`")); + if !status.success() { + let res = ProcRes { + status, + stdout: String::from_utf8_lossy(&stdout).into_owned(), + stderr: String::from_utf8_lossy(&stderr).into_owned(), + truncated, + cmdline: format!("{:?}", cmd), + }; + self.fatal_proc_rec("rmake recipe failed to complete", &res); + } + } +} diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs new file mode 100644 index 00000000000..6a31888527e --- /dev/null +++ b/src/tools/compiletest/src/runtest/rustdoc.rs @@ -0,0 +1,34 @@ +use std::process::Command; + +use super::{remove_and_create_dir_all, TestCx}; + +impl TestCx<'_> { + pub(super) fn run_rustdoc_test(&self) { + assert!(self.revision.is_none(), "revisions not relevant here"); + + let out_dir = self.output_base_dir(); + remove_and_create_dir_all(&out_dir); + + let proc_res = self.document(&out_dir, &self.testpaths); + if !proc_res.status.success() { + self.fatal_proc_rec("rustdoc failed!", &proc_res); + } + + if self.props.check_test_line_numbers_match { + self.check_rustdoc_test_option(proc_res); + } else { + let root = self.config.find_rust_src_root().unwrap(); + let mut cmd = Command::new(&self.config.python); + cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file); + if self.config.bless { + cmd.arg("--bless"); + } + let res = self.run_command_to_procres(&mut cmd); + if !res.status.success() { + self.fatal_proc_rec_with_ctx("htmldocck failed!", &res, |mut this| { + this.compare_to_default_rustdoc(&out_dir) + }); + } + } + } +} diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs new file mode 100644 index 00000000000..a39887ccd02 --- /dev/null +++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs @@ -0,0 +1,48 @@ +use std::process::Command; + +use super::{remove_and_create_dir_all, TestCx}; + +impl TestCx<'_> { + pub(super) fn run_rustdoc_json_test(&self) { + //FIXME: Add bless option. + + assert!(self.revision.is_none(), "revisions not relevant here"); + + let out_dir = self.output_base_dir(); + remove_and_create_dir_all(&out_dir); + + let proc_res = self.document(&out_dir, &self.testpaths); + if !proc_res.status.success() { + self.fatal_proc_rec("rustdoc failed!", &proc_res); + } + + let root = self.config.find_rust_src_root().unwrap(); + let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); + json_out.set_extension("json"); + let res = self.run_command_to_procres( + Command::new(self.config.jsondocck_path.as_ref().unwrap()) + .arg("--doc-dir") + .arg(root.join(&out_dir)) + .arg("--template") + .arg(&self.testpaths.file), + ); + + if !res.status.success() { + self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| { + println!("Rustdoc Output:"); + proc_res.print_info(); + }) + } + + let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap()); + json_out.set_extension("json"); + + let res = self.run_command_to_procres( + Command::new(self.config.jsondoclint_path.as_ref().unwrap()).arg(&json_out), + ); + + if !res.status.success() { + self.fatal_proc_rec("jsondoclint failed!", &res); + } + } +} diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs new file mode 100644 index 00000000000..88a0ec3aa3b --- /dev/null +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -0,0 +1,233 @@ +use std::collections::HashSet; +use std::fs::OpenOptions; +use std::io::Write; + +use rustfix::{apply_suggestions, get_suggestions_from_json, Filter}; +use tracing::debug; + +use super::{ + AllowUnused, Emit, ErrorKind, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, + TestOutput, Truncated, WillExecute, UI_FIXED, +}; +use crate::{errors, json}; + +impl TestCx<'_> { + pub(super) fn run_ui_test(&self) { + if let Some(FailMode::Build) = self.props.fail_mode { + // Make sure a build-fail test cannot fail due to failing analysis (e.g. typeck). + let pm = Some(PassMode::Check); + let proc_res = + self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new()); + self.check_if_test_should_compile(&proc_res, pm); + } + + let pm = self.pass_mode(); + let should_run = self.should_run(pm); + let emit_metadata = self.should_emit_metadata(pm); + let proc_res = self.compile_test(should_run, emit_metadata); + self.check_if_test_should_compile(&proc_res, pm); + if matches!(proc_res.truncated, Truncated::Yes) + && !self.props.dont_check_compiler_stdout + && !self.props.dont_check_compiler_stderr + { + self.fatal_proc_rec( + "compiler output got truncated, cannot compare with reference file", + &proc_res, + ); + } + + // if the user specified a format in the ui test + // print the output to the stderr file, otherwise extract + // the rendered error messages from json and print them + let explicit = self.props.compile_flags.iter().any(|s| s.contains("--error-format")); + + let expected_fixed = self.load_expected_output(UI_FIXED); + + self.check_and_prune_duplicate_outputs(&proc_res, &[], &[]); + + let mut errors = self.load_compare_outputs(&proc_res, TestOutput::Compile, explicit); + let rustfix_input = json::rustfix_diagnostics_only(&proc_res.stderr); + + if self.config.compare_mode.is_some() { + // don't test rustfix with nll right now + } else if self.config.rustfix_coverage { + // Find out which tests have `MachineApplicable` suggestions but are missing + // `run-rustfix` or `run-rustfix-only-machine-applicable` headers. + // + // This will return an empty `Vec` in case the executed test file has a + // `compile-flags: --error-format=xxxx` header with a value other than `json`. + let suggestions = get_suggestions_from_json( + &rustfix_input, + &HashSet::new(), + Filter::MachineApplicableOnly, + ) + .unwrap_or_default(); + if !suggestions.is_empty() + && !self.props.run_rustfix + && !self.props.rustfix_only_machine_applicable + { + let mut coverage_file_path = self.config.build_base.clone(); + coverage_file_path.push("rustfix_missing_coverage.txt"); + debug!("coverage_file_path: {}", coverage_file_path.display()); + + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(coverage_file_path.as_path()) + .expect("could not create or open file"); + + if let Err(e) = writeln!(file, "{}", self.testpaths.file.display()) { + panic!("couldn't write to {}: {e:?}", coverage_file_path.display()); + } + } + } else if self.props.run_rustfix { + // Apply suggestions from rustc to the code itself + let unfixed_code = self.load_expected_output_from_path(&self.testpaths.file).unwrap(); + let suggestions = get_suggestions_from_json( + &rustfix_input, + &HashSet::new(), + if self.props.rustfix_only_machine_applicable { + Filter::MachineApplicableOnly + } else { + Filter::Everything + }, + ) + .unwrap(); + let fixed_code = apply_suggestions(&unfixed_code, &suggestions).unwrap_or_else(|e| { + panic!( + "failed to apply suggestions for {:?} with rustfix: {}", + self.testpaths.file, e + ) + }); + + errors += self.compare_output("fixed", &fixed_code, &expected_fixed); + } else if !expected_fixed.is_empty() { + panic!( + "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \ + file was found" + ); + } + + if errors > 0 { + println!("To update references, rerun the tests and pass the `--bless` flag"); + let relative_path_to_file = + self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap()); + println!( + "To only update this specific test, also pass `--test-args {}`", + relative_path_to_file.display(), + ); + self.fatal_proc_rec( + &format!("{} errors occurred comparing output.", errors), + &proc_res, + ); + } + + let expected_errors = errors::load_errors(&self.testpaths.file, self.revision); + + if let WillExecute::Yes = should_run { + let proc_res = self.exec_compiled_test(); + let run_output_errors = if self.props.check_run_results { + self.load_compare_outputs(&proc_res, TestOutput::Run, explicit) + } else { + 0 + }; + if run_output_errors > 0 { + self.fatal_proc_rec( + &format!("{} errors occurred comparing run output.", run_output_errors), + &proc_res, + ); + } + if self.should_run_successfully(pm) { + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + } else if proc_res.status.success() { + self.fatal_proc_rec("test run succeeded!", &proc_res); + } + + if !self.props.error_patterns.is_empty() || !self.props.regex_error_patterns.is_empty() + { + // "// error-pattern" comments + let output_to_check = self.get_output(&proc_res); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); + } + } + + debug!( + "run_ui_test: explicit={:?} config.compare_mode={:?} expected_errors={:?} \ + proc_res.status={:?} props.error_patterns={:?}", + explicit, + self.config.compare_mode, + expected_errors, + proc_res.status, + self.props.error_patterns + ); + + let check_patterns = should_run == WillExecute::No + && (!self.props.error_patterns.is_empty() + || !self.props.regex_error_patterns.is_empty()); + if !explicit && self.config.compare_mode.is_none() { + let check_annotations = !check_patterns || !expected_errors.is_empty(); + + if check_annotations { + // "//~ERROR comments" + self.check_expected_errors(expected_errors, &proc_res); + } + } else if explicit && !expected_errors.is_empty() { + let msg = format!( + "line {}: cannot combine `--error-format` with {} annotations; use `error-pattern` instead", + expected_errors[0].line_num, + expected_errors[0].kind.unwrap_or(ErrorKind::Error), + ); + self.fatal(&msg); + } + if check_patterns { + // "// error-pattern" comments + let output_to_check = self.get_output(&proc_res); + self.check_all_error_patterns(&output_to_check, &proc_res, pm); + } + + if self.props.run_rustfix && self.config.compare_mode.is_none() { + // And finally, compile the fixed code and make sure it both + // succeeds and has no diagnostics. + let mut rustc = self.make_compile_args( + &self.expected_output_path(UI_FIXED), + TargetLocation::ThisFile(self.make_exe_name()), + emit_metadata, + AllowUnused::No, + LinkToAux::Yes, + Vec::new(), + ); + + // If a test is revisioned, it's fixed source file can be named "a.foo.fixed", which, + // well, "a.foo" isn't a valid crate name. So we explicitly mangle the test name + // (including the revision) here to avoid the test writer having to manually specify a + // `#![crate_name = "..."]` as a workaround. This is okay since we're only checking if + // the fixed code is compilable. + if self.revision.is_some() { + let crate_name = + self.testpaths.file.file_stem().expect("test must have a file stem"); + // crate name must be alphanumeric or `_`. + let crate_name = + crate_name.to_str().expect("crate name implies file name must be valid UTF-8"); + // replace `a.foo` -> `a__foo` for crate name purposes. + // replace `revision-name-with-dashes` -> `revision_name_with_underscore` + let crate_name = crate_name.replace('.', "__"); + let crate_name = crate_name.replace('-', "_"); + rustc.arg("--crate-name"); + rustc.arg(crate_name); + } + + let res = self.compose_and_run_compiler(rustc, None, self.testpaths); + if !res.status.success() { + self.fatal_proc_rec("failed to compile fixed code", &res); + } + if !res.stderr.is_empty() + && !self.props.rustfix_only_machine_applicable + && !json::rustfix_diagnostics_only(&res.stderr).is_empty() + { + self.fatal_proc_rec("fixed code is still producing diagnostics", &res); + } + } + } +} diff --git a/src/tools/compiletest/src/runtest/valgrind.rs b/src/tools/compiletest/src/runtest/valgrind.rs new file mode 100644 index 00000000000..8d72c4be9ff --- /dev/null +++ b/src/tools/compiletest/src/runtest/valgrind.rs @@ -0,0 +1,34 @@ +use super::{Emit, TestCx, WillExecute}; + +impl TestCx<'_> { + pub(super) fn run_valgrind_test(&self) { + assert!(self.revision.is_none(), "revisions not relevant here"); + + // FIXME(jieyouxu): does this really make any sense? If a valgrind test isn't testing + // valgrind, what is it even testing? + if self.config.valgrind_path.is_none() { + assert!(!self.config.force_valgrind); + return self.run_rpass_test(); + } + + let should_run = self.run_if_enabled(); + let mut proc_res = self.compile_test(should_run, Emit::None); + + if !proc_res.status.success() { + self.fatal_proc_rec("compilation failed!", &proc_res); + } + + if let WillExecute::Disabled = should_run { + return; + } + + let mut new_config = self.config.clone(); + new_config.runner = new_config.valgrind_path.clone(); + let new_cx = TestCx { config: &new_config, ..*self }; + proc_res = new_cx.exec_compiled_test(); + + if !proc_res.status.success() { + self.fatal_proc_rec("test run failed!", &proc_res); + } + } +} diff --git a/src/tools/miri/miri-script/src/args.rs b/src/tools/miri/miri-script/src/args.rs index 16a21757b35..c1688ca0fb6 100644 --- a/src/tools/miri/miri-script/src/args.rs +++ b/src/tools/miri/miri-script/src/args.rs @@ -1,7 +1,7 @@ use std::env; use std::iter; -use anyhow::{bail, Result}; +use anyhow::{Result, bail}; pub struct Args { args: iter::Peekable<env::Args>, diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index b18c8453d66..b0e62d5cda6 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -8,13 +8,13 @@ use std::path::PathBuf; use std::process; use std::time::Duration; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{Context, Result, anyhow, bail}; use path_macro::path; use walkdir::WalkDir; -use xshell::{cmd, Shell}; +use xshell::{Shell, cmd}; -use crate::util::*; use crate::Command; +use crate::util::*; /// Used for rustc syncs. const JOSH_FILTER: &str = diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index 92148237107..0620f3aaf09 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -6,7 +6,7 @@ mod util; use std::ops::Range; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{Context, Result, anyhow, bail}; #[derive(Clone, Debug)] pub enum Command { diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs index 9d1a8e4fb1d..f5a6a8188a0 100644 --- a/src/tools/miri/miri-script/src/util.rs +++ b/src/tools/miri/miri-script/src/util.rs @@ -5,10 +5,10 @@ use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::{env, iter, thread}; -use anyhow::{anyhow, bail, Context, Result}; +use anyhow::{Context, Result, anyhow, bail}; use dunce::canonicalize; use path_macro::path; -use xshell::{cmd, Cmd, Shell}; +use xshell::{Cmd, Shell, cmd}; pub fn miri_dir() -> std::io::Result<PathBuf> { const MIRI_SCRIPT_ROOT_DIR: &str = env!("CARGO_MANIFEST_DIR"); diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 3f4d095fc19..c3276d82d4f 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -e2dc1a1c0f97a90319181a721ab317210307617a +c0838c8ebec23fb87855bb6de3a287981cb1df98 diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs index 77fc9f53f9e..f6c16756344 100644 --- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs +++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs @@ -4,7 +4,7 @@ use rand::Rng; use rustc_target::abi::{Align, Size}; -use crate::{concurrency::VClock, MemoryKind, MiriConfig, ThreadId}; +use crate::{MemoryKind, MiriConfig, ThreadId, concurrency::VClock}; const MAX_POOL_SIZE: usize = 64; diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index fc2b3f9c6ea..e7d7cc28eee 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -12,12 +12,12 @@ use std::mem; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Mutability, RetagKind}; -use rustc_middle::ty::{self, layout::HasParamEnv, Ty}; +use rustc_middle::ty::{self, Ty, layout::HasParamEnv}; use rustc_target::abi::{Abi, Size}; use crate::borrow_tracker::{ - stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, GlobalStateInner, ProtectorKind, + stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, }; use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; @@ -913,11 +913,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { new_perm: NewPermission, ) -> InterpResult<'tcx> { let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?; - let val = self.ecx.sb_retag_reference( - &val, - new_perm, - RetagInfo { cause: self.retag_cause, in_field: self.in_field }, - )?; + let val = self.ecx.sb_retag_reference(&val, new_perm, RetagInfo { + cause: self.retag_cause, + in_field: self.in_field, + })?; self.ecx.write_immediate(*val, place)?; Ok(()) } @@ -1003,11 +1002,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { access: Some(AccessKind::Write), protector: Some(ProtectorKind::StrongProtector), }; - this.sb_retag_place( - place, - new_perm, - RetagInfo { cause: RetagCause::InPlaceFnPassing, in_field: false }, - ) + this.sb_retag_place(place, new_perm, RetagInfo { + cause: RetagCause::InPlaceFnPassing, + in_field: false, + }) } /// Mark the given tag as exposed. It was found on a pointer with the given AllocId. diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index 774b36919fe..5c040983142 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -4,11 +4,11 @@ use std::ops::Range; use rustc_data_structures::fx::FxHashSet; use tracing::trace; +use crate::ProvenanceExtra; use crate::borrow_tracker::{ - stacked_borrows::{Item, Permission}, AccessKind, BorTag, + stacked_borrows::{Item, Permission}, }; -use crate::ProvenanceExtra; /// Exactly what cache size we should use is a difficult trade-off. There will always be some /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a753de28a04..44ea7533b00 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -4,12 +4,12 @@ use std::ops::Range; use rustc_data_structures::fx::FxHashMap; use rustc_span::{Span, SpanData}; +use crate::borrow_tracker::ProtectorKind; use crate::borrow_tracker::tree_borrows::{ perms::{PermTransition, Permission}, tree::LocationState, unimap::UniIndex, }; -use crate::borrow_tracker::ProtectorKind; use crate::*; /// Cause of an access: either a real access or one diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 722cb79c66b..89b8ff1af8b 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -1,6 +1,6 @@ use rustc_middle::{ mir::{Mutability, RetagKind}, - ty::{self, layout::HasParamEnv, Ty}, + ty::{self, Ty, layout::HasParamEnv}, }; use rustc_span::def_id::DefId; use rustc_target::abi::{Abi, Size}; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index c29bd719b15..dfb9b8637fd 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -1,9 +1,9 @@ use std::cmp::{Ordering, PartialOrd}; use std::fmt; +use crate::AccessKind; use crate::borrow_tracker::tree_borrows::diagnostics::TransitionError; use crate::borrow_tracker::tree_borrows::tree::AccessRelatedness; -use crate::AccessKind; /// The activation states of a pointer. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -345,18 +345,14 @@ pub mod diagnostics { use super::*; impl fmt::Display for PermissionPriv { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - ReservedFrz { conflicted: false } => "Reserved", - ReservedFrz { conflicted: true } => "Reserved (conflicted)", - ReservedIM => "Reserved (interior mutable)", - Active => "Active", - Frozen => "Frozen", - Disabled => "Disabled", - } - ) + write!(f, "{}", match self { + ReservedFrz { conflicted: false } => "Reserved", + ReservedFrz { conflicted: true } => "Reserved (conflicted)", + ReservedIM => "Reserved (interior mutable)", + Active => "Active", + Frozen => "Frozen", + Disabled => "Disabled", + }) } } @@ -551,7 +547,7 @@ impl Permission { #[cfg(test)] mod propagation_optimization_checks { pub use super::*; - use crate::borrow_tracker::tree_borrows::exhaustive::{precondition, Exhaustive}; + use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition}; impl Exhaustive for PermissionPriv { fn exhaustive() -> Box<dyn Iterator<Item = Self>> { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 53e722259fd..a99c71d96b4 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -19,10 +19,10 @@ use rustc_span::Span; use rustc_target::abi::Size; use crate::borrow_tracker::tree_borrows::{ + Permission, diagnostics::{self, NodeDebugInfo, TbError, TransitionError}, perms::PermTransition, unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap}, - Permission, }; use crate::borrow_tracker::{GlobalState, ProtectorKind}; use crate::*; @@ -587,16 +587,13 @@ impl Tree { let mut debug_info = NodeDebugInfo::new(root_tag, root_default_perm, span); // name the root so that all allocations contain one named pointer debug_info.add_name("root of the allocation"); - nodes.insert( - root_idx, - Node { - tag: root_tag, - parent: None, - children: SmallVec::default(), - default_initial_perm: root_default_perm, - debug_info, - }, - ); + nodes.insert(root_idx, Node { + tag: root_tag, + parent: None, + children: SmallVec::default(), + default_initial_perm: root_default_perm, + debug_info, + }); nodes }; let rperms = { @@ -626,16 +623,13 @@ impl<'tcx> Tree { let idx = self.tag_mapping.insert(new_tag); let parent_idx = self.tag_mapping.get(&parent_tag).unwrap(); // Create the node - self.nodes.insert( - idx, - Node { - tag: new_tag, - parent: Some(parent_idx), - children: SmallVec::default(), - default_initial_perm, - debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span), - }, - ); + self.nodes.insert(idx, Node { + tag: new_tag, + parent: Some(parent_idx), + children: SmallVec::default(), + default_initial_perm, + debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span), + }); // Register new_tag as a child of parent_tag self.nodes.get_mut(parent_idx).unwrap().children.push(idx); // Initialize perms diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs index f64f7bf8e8c..5cd5040f807 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs @@ -2,7 +2,7 @@ #![cfg(test)] use super::*; -use crate::borrow_tracker::tree_borrows::exhaustive::{precondition, Exhaustive}; +use crate::borrow_tracker::tree_borrows::exhaustive::{Exhaustive, precondition}; use std::fmt; impl Exhaustive for LocationState { diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 1f910d885ca..bc4d8056872 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::collections::{hash_map::Entry, VecDeque}; +use std::collections::{VecDeque, hash_map::Entry}; use std::ops::Not; use std::time::Duration; @@ -283,12 +283,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { data: Option<Box<dyn Any>>, ) -> InterpResult<'tcx, MutexId> { let this = self.eval_context_mut(); - this.create_id( - lock, - offset, - |ecx| &mut ecx.machine.sync.mutexes, - Mutex { data, ..Default::default() }, - ) + this.create_id(lock, offset, |ecx| &mut ecx.machine.sync.mutexes, Mutex { + data, + ..Default::default() + }) } /// Lazily create a new mutex. @@ -355,12 +353,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { data: Option<Box<dyn Any>>, ) -> InterpResult<'tcx, CondvarId> { let this = self.eval_context_mut(); - this.create_id( - condvar, - offset, - |ecx| &mut ecx.machine.sync.condvars, - Condvar { data, ..Default::default() }, - ) + this.create_id(condvar, offset, |ecx| &mut ecx.machine.sync.condvars, Condvar { + data, + ..Default::default() + }) } fn condvar_get_or_create_id( diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index 0968e10bbee..901b097c1bd 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -1,5 +1,5 @@ use rustc_index::Idx; -use rustc_span::{Span, SpanData, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, SpanData}; use smallvec::SmallVec; use std::{ cmp::Ordering, diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 92f344d13b7..4445550512b 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -2,7 +2,7 @@ use std::fmt::{self, Write}; use std::num::NonZero; use rustc_errors::{Diag, DiagMessage, Level}; -use rustc_span::{SpanData, Symbol, DUMMY_SP}; +use rustc_span::{DUMMY_SP, SpanData, Symbol}; use rustc_target::abi::{Align, Size}; use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index f95177684ae..8c8ed9c4ddc 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -13,9 +13,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_middle::ty::{ - self, + self, Ty, TyCtxt, layout::{LayoutCx, LayoutOf}, - Ty, TyCtxt, }; use rustc_target::spec::abi::Abi; diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index cba99c0bd7a..10e5882b5ba 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -7,12 +7,12 @@ use std::time::Duration; use rand::RngCore; -use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::Float; +use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_hir::{ - def::{DefKind, Namespace}, - def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE}, Safety, + def::{DefKind, Namespace}, + def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE}, }; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -21,9 +21,8 @@ use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, MaybeResult}; use rustc_middle::ty::{ - self, + self, FloatTy, IntTy, Ty, TyCtxt, UintTy, layout::{LayoutOf, TyAndLayout}, - FloatTy, IntTy, Ty, TyCtxt, UintTy, }; use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 7acc99a8af0..3eeb11dbbb4 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -9,12 +9,12 @@ use rustc_middle::{ mir, ty::{self, FloatTy}, }; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use rustc_target::abi::Size; use crate::*; use atomic::EvalContextExt as _; -use helpers::{check_arg_count, ToHost, ToSoft}; +use helpers::{ToHost, ToSoft, check_arg_count}; use simd::EvalContextExt as _; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index e22306ca82f..2bc11d63a39 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -3,10 +3,10 @@ use either::Either; use rustc_apfloat::{Float, Round}; use rustc_middle::ty::layout::{HasParamEnv, LayoutOf}; use rustc_middle::{mir, ty, ty::FloatTy}; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use rustc_target::abi::{Endian, HasDataLayout}; -use crate::helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool, ToHost, ToSoft}; +use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, check_arg_count, simd_element_to_bool}; use crate::*; #[derive(Copy, Clone)] diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 8a59206943d..b39f88dd1c9 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -113,13 +113,13 @@ pub type PlaceTy<'tcx> = interpret::PlaceTy<'tcx, machine::Provenance>; pub type MPlaceTy<'tcx> = interpret::MPlaceTy<'tcx, machine::Provenance>; pub use crate::intrinsics::EvalContextExt as _; +pub use crate::shims::EmulateItemResult; pub use crate::shims::env::{EnvVars, EvalContextExt as _}; pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _}; pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::tls::TlsData; -pub use crate::shims::EmulateItemResult; pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode}; pub use crate::alloc_bytes::MiriAllocBytes; @@ -140,11 +140,11 @@ pub use crate::concurrency::{ }, }; pub use crate::diagnostics::{ - report_error, EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, + EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error, }; pub use crate::eval::{ - create_ecx, eval_entry, AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith, - ValidationMode, + AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, RejectOpWith, ValidationMode, + create_ecx, eval_entry, }; pub use crate::helpers::{AccessKind, EvalContextExt as _}; pub use crate::machine::{ diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index bde94cec87f..b93feeeee33 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -8,9 +8,9 @@ use std::fmt; use std::path::Path; use std::process; -use rand::rngs::StdRng; use rand::Rng; use rand::SeedableRng; +use rand::rngs::StdRng; use rustc_attr::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -20,9 +20,8 @@ use rustc_middle::{ mir, query::TyCtxtAt, ty::{ - self, + self, Instance, Ty, TyCtxt, layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout}, - Instance, Ty, TyCtxt, }, }; use rustc_session::config::InliningThreshold; @@ -1080,13 +1079,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { // Call the lang item. let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap(); let panic = ty::Instance::mono(ecx.tcx.tcx, panic); - ecx.call_function( - panic, - Abi::Rust, - &[], - None, - StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable }, - )?; + ecx.call_function(panic, Abi::Rust, &[], None, StackPopCleanup::Goto { + ret: None, + unwind: mir::UnwindAction::Unreachable, + })?; Ok(()) } diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs index bc44e672bd8..1b6a7255eef 100644 --- a/src/tools/miri/src/operator.rs +++ b/src/tools/miri/src/operator.rs @@ -1,6 +1,6 @@ use std::iter; -use rand::{seq::IteratorRandom, Rng}; +use rand::{Rng, seq::IteratorRandom}; use rustc_apfloat::{Float, FloatConvert}; use rustc_middle::mir; use rustc_target::abi::Size; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 9bb6777a9b0..edff17c0514 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -2,7 +2,7 @@ use crate::*; use rustc_ast::ast::Mutability; use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::{self, Instance, Ty}; -use rustc_span::{hygiene, BytePos, Loc, Symbol}; +use rustc_span::{BytePos, Loc, Symbol, hygiene}; use rustc_target::{abi::Size, spec::abi::Abi}; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs index 17ac2638a69..788de8162cb 100644 --- a/src/tools/miri/src/shims/extern_static.rs +++ b/src/tools/miri/src/shims/extern_static.rs @@ -63,10 +63,10 @@ impl<'tcx> MiriMachine<'tcx> { match this.tcx.sess.target.os.as_ref() { "linux" => { - Self::null_ptr_extern_statics( - this, - &["__cxa_thread_atexit_impl", "__clock_gettime64"], - )?; + Self::null_ptr_extern_statics(this, &[ + "__cxa_thread_atexit_impl", + "__clock_gettime64", + ])?; Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?; } "freebsd" => { diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 533992e35ab..a1be2ae8b58 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -7,8 +7,8 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(windows)] use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf; use crate::*; diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index ab705ddccab..44f942cb4c5 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -13,8 +13,8 @@ use rustc_ast::Mutability; use rustc_middle::{mir, ty}; -use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; +use rustc_target::spec::abi::Abi; use crate::*; use helpers::check_arg_count; @@ -248,13 +248,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Call the lang item associated with this message. let fn_item = this.tcx.require_lang_item(msg.panic_function(), None); let instance = ty::Instance::mono(this.tcx.tcx, fn_item); - this.call_function( - instance, - Abi::Rust, - &[], - None, - StackPopCleanup::Goto { ret: None, unwind }, - )?; + this.call_function(instance, Abi::Rust, &[], None, StackPopCleanup::Goto { + ret: None, + unwind, + })?; } } Ok(()) diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs index 52d83cd7299..b3ea7098dfe 100644 --- a/src/tools/miri/src/shims/tls.rs +++ b/src/tools/miri/src/shims/tls.rs @@ -1,7 +1,7 @@ //! Implement thread-local storage. -use std::collections::btree_map::Entry as BTreeEntry; use std::collections::BTreeMap; +use std::collections::btree_map::Entry as BTreeEntry; use std::task::Poll; use rustc_middle::ty; diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs index 54bf3a3ae82..324607cc1ed 100644 --- a/src/tools/miri/src/shims/unix/env.rs +++ b/src/tools/miri/src/shims/unix/env.rs @@ -4,8 +4,8 @@ use std::io::ErrorKind; use std::mem; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::Size; use crate::*; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index e1697a47415..f5fe2b4b30b 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fs::{ - read_dir, remove_dir, remove_file, rename, DirBuilder, File, FileType, OpenOptions, ReadDir, + DirBuilder, File, FileType, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, rename, }; use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; @@ -173,7 +173,7 @@ impl FileDescription for FileHandle { use windows_sys::Win32::{ Foundation::{ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, FALSE, HANDLE, TRUE}, Storage::FileSystem::{ - LockFileEx, UnlockFile, LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, + LOCKFILE_EXCLUSIVE_LOCK, LOCKFILE_FAIL_IMMEDIATELY, LockFileEx, UnlockFile, }, }; let fh = self.file.as_raw_handle() as HANDLE; diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index e4e1531157a..dbbae0731e0 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -1,5 +1,5 @@ -use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 1ddc9efcf6b..bdc4fc94469 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -1,14 +1,14 @@ use rustc_apfloat::{ieee::Double, ieee::Single}; use rustc_middle::mir; -use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use super::{ - bin_op_simd_float_all, conditional_dot_product, convert_float_to_int, horizontal_bin_op, - mask_load, mask_store, round_all, test_bits_masked, test_high_bits_masked, unary_op_ps, - FloatBinOp, FloatUnaryOp, + FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, conditional_dot_product, convert_float_to_int, + horizontal_bin_op, mask_load, mask_store, round_all, test_bits_masked, test_high_bits_masked, + unary_op_ps, }; use crate::*; diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 74d4673bd82..1dda1a10a05 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -1,12 +1,12 @@ use rustc_middle::mir; -use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use super::{ - horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb, packusdw, - packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd, ShiftOp, + ShiftOp, horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb, + packusdw, packuswb, pmulhrsw, psign, shift_simd_by_scalar, shift_simd_by_simd, }; use crate::*; diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index d7241f87d0e..7c7a0935c47 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -1,8 +1,8 @@ use rand::Rng as _; -use rustc_apfloat::{ieee::Single, Float}; -use rustc_middle::ty::layout::LayoutOf as _; +use rustc_apfloat::{Float, ieee::Single}; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::{mir, ty}; use rustc_span::Symbol; use rustc_target::abi::Size; diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 8de7ddc7931..254d25bb529 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -3,8 +3,8 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use super::{ - bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps, unary_op_ss, FloatBinOp, - FloatUnaryOp, + FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps, + unary_op_ss, }; use crate::*; diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index bdb52a04a91..7c23e372664 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -3,8 +3,8 @@ use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use super::{ - bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, packssdw, packsswb, - packuswb, shift_simd_by_scalar, FloatBinOp, ShiftOp, + FloatBinOp, ShiftOp, bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int, + packssdw, packsswb, packuswb, shift_simd_by_scalar, }; use crate::*; diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 4e50d5e5dcf..0b6e0d9e0d7 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -1,6 +1,6 @@ use rustc_middle::mir; -use rustc_middle::ty::layout::LayoutOf as _; use rustc_middle::ty::Ty; +use rustc_middle::ty::layout::LayoutOf as _; use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; diff --git a/src/tools/miri/test-cargo-miri/src/main.rs b/src/tools/miri/test-cargo-miri/src/main.rs index 048577ef15a..d171ec1c0d1 100644 --- a/src/tools/miri/test-cargo-miri/src/main.rs +++ b/src/tools/miri/test-cargo-miri/src/main.rs @@ -23,7 +23,7 @@ fn main() { // (We rely on the test runner to always disable isolation when passing no arguments.) if std::env::args().len() <= 1 { fn host_to_target_path(path: String) -> PathBuf { - use std::ffi::{c_char, CStr, CString}; + use std::ffi::{CStr, CString, c_char}; let path = CString::new(path).unwrap(); let mut out = Vec::with_capacity(1024); diff --git a/src/tools/miri/test-cargo-miri/subcrate/main.rs b/src/tools/miri/test-cargo-miri/subcrate/main.rs index 52161098788..96f849e7633 100644 --- a/src/tools/miri/test-cargo-miri/subcrate/main.rs +++ b/src/tools/miri/test-cargo-miri/subcrate/main.rs @@ -5,7 +5,7 @@ fn main() { println!("subcrate running"); fn host_to_target_path(path: String) -> PathBuf { - use std::ffi::{c_char, CStr, CString}; + use std::ffi::{CStr, CString, c_char}; let path = CString::new(path).unwrap(); let mut out = Vec::with_capacity(1024); diff --git a/src/tools/miri/test-cargo-miri/subcrate/test.rs b/src/tools/miri/test-cargo-miri/subcrate/test.rs index 1681c721dc2..b60cf20339b 100644 --- a/src/tools/miri/test-cargo-miri/subcrate/test.rs +++ b/src/tools/miri/test-cargo-miri/subcrate/test.rs @@ -8,7 +8,7 @@ fn main() { println!("subcrate testing"); fn host_to_target_path(path: String) -> PathBuf { - use std::ffi::{c_char, CStr, CString}; + use std::ffi::{CStr, CString, c_char}; let path = CString::new(path).unwrap(); let mut out = Vec::with_capacity(1024); diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs index e28b0343135..279201df867 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.rs @@ -8,7 +8,7 @@ use std::thread; use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0}; -use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE}; +use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject}; // XXX HACK: This is how miri represents the handle for thread 0. // This value can be "legitimately" obtained by using `GetCurrentThread` with `DuplicateHandle` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs index 4d48e839bb4..eee2979f3cf 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.rs @@ -8,7 +8,7 @@ use std::thread; use windows_sys::Win32::Foundation::WAIT_OBJECT_0; -use windows_sys::Win32::System::Threading::{GetCurrentThread, WaitForSingleObject, INFINITE}; +use windows_sys::Win32::System::Threading::{GetCurrentThread, INFINITE, WaitForSingleObject}; fn main() { thread::spawn(|| { diff --git a/src/tools/miri/tests/fail/alloc/deallocate-bad-alignment.rs b/src/tools/miri/tests/fail/alloc/deallocate-bad-alignment.rs index 552a67419b2..f04927beb16 100644 --- a/src/tools/miri/tests/fail/alloc/deallocate-bad-alignment.rs +++ b/src/tools/miri/tests/fail/alloc/deallocate-bad-alignment.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/alloc/deallocate-bad-size.rs b/src/tools/miri/tests/fail/alloc/deallocate-bad-size.rs index 906a3b52a22..d818f3a9304 100644 --- a/src/tools/miri/tests/fail/alloc/deallocate-bad-size.rs +++ b/src/tools/miri/tests/fail/alloc/deallocate-bad-size.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/alloc/deallocate-twice.rs b/src/tools/miri/tests/fail/alloc/deallocate-twice.rs index ee514f4c55e..340ca1f75ef 100644 --- a/src/tools/miri/tests/fail/alloc/deallocate-twice.rs +++ b/src/tools/miri/tests/fail/alloc/deallocate-twice.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/alloc/reallocate-bad-size.rs b/src/tools/miri/tests/fail/alloc/reallocate-bad-size.rs index 174e4f22761..7b9ef1d9121 100644 --- a/src/tools/miri/tests/fail/alloc/reallocate-bad-size.rs +++ b/src/tools/miri/tests/fail/alloc/reallocate-bad-size.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, realloc, Layout}; +use std::alloc::{Layout, alloc, realloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.rs b/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.rs index ecdd3ae5fee..9f9921b1817 100644 --- a/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.rs +++ b/src/tools/miri/tests/fail/alloc/reallocate-change-alloc.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, realloc, Layout}; +use std::alloc::{Layout, alloc, realloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/alloc/reallocate-dangling.rs b/src/tools/miri/tests/fail/alloc/reallocate-dangling.rs index c605f68a7f6..97357c629af 100644 --- a/src/tools/miri/tests/fail/alloc/reallocate-dangling.rs +++ b/src/tools/miri/tests/fail/alloc/reallocate-dangling.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, dealloc, realloc, Layout}; +use std::alloc::{Layout, alloc, dealloc, realloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.rs b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.rs index 170bcf9590a..44ec4129d60 100644 --- a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.rs +++ b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.rs @@ -1,7 +1,7 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::alloc::{alloc, Layout}; +use std::alloc::{Layout, alloc}; fn inner(x: *mut i32, _y: &i32) { // If `x` and `y` alias, retagging is fine with this... but we really diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs index c8060d4c9cb..df9a73a444e 100644 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs +++ b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs @@ -1,6 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; // `x` is strongly protected but covers zero bytes. // Let's see if deallocating the allocation x points to is UB: diff --git a/src/tools/miri/tests/fail/data_race/fence_after_load.rs b/src/tools/miri/tests/fail/data_race/fence_after_load.rs index 92cb4ccccf5..5dfb260c20b 100644 --- a/src/tools/miri/tests/fail/data_race/fence_after_load.rs +++ b/src/tools/miri/tests/fail/data_race/fence_after_load.rs @@ -3,8 +3,8 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::{fence, AtomicUsize, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicUsize, Ordering, fence}; use std::thread; use std::time::Duration; diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs index 61af972b3dc..828b47f0a65 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read.rs @@ -2,7 +2,7 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicU8, AtomicU16, Ordering}; use std::thread; fn convert(a: &AtomicU16) -> &[AtomicU8; 2] { diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs index 12e51bb9429..89afda2fff5 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_write.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_write.rs @@ -2,7 +2,7 @@ // Avoid accidental synchronization via address reuse inside `thread::spawn`. //@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0 -use std::sync::atomic::{AtomicU16, AtomicU8, Ordering}; +use std::sync::atomic::{AtomicU8, AtomicU16, Ordering}; use std::thread; fn convert(a: &AtomicU16) -> &[AtomicU8; 2] { diff --git a/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs b/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs index 545875a582a..cebad507ea9 100644 --- a/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs +++ b/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs @@ -8,7 +8,7 @@ // so we have to stick to C++11 emulation from existing research. use std::sync::atomic::Ordering::*; -use std::sync::atomic::{fence, AtomicUsize}; +use std::sync::atomic::{AtomicUsize, fence}; use std::thread::spawn; // Spins until it reads the given value diff --git a/src/tools/miri/tests/fail/stacked_borrows/illegal_dealloc1.rs b/src/tools/miri/tests/fail/stacked_borrows/illegal_dealloc1.rs index 76a5e6a0b62..49df267c4c3 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/illegal_dealloc1.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/illegal_dealloc1.rs @@ -1,4 +1,4 @@ -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; fn main() { unsafe { diff --git a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs index b5a01cd4324..036267dc11e 100644 --- a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs +++ b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs @@ -8,8 +8,8 @@ #![feature(ptr_internals)] -use core::ptr::addr_of_mut; use core::ptr::Unique; +use core::ptr::addr_of_mut; fn main() { let mut data = 0u8; diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.rs b/src/tools/miri/tests/fail/uninit/padding-enum.rs index a9628799b7d..e1a16bea23c 100644 --- a/src/tools/miri/tests/fail/uninit/padding-enum.rs +++ b/src/tools/miri/tests/fail/uninit/padding-enum.rs @@ -17,10 +17,11 @@ fn main() { assert!(matches!(*p.as_ptr(), E::None)); // Turns out the discriminant is (currently) stored - // in the 2nd pointer, so the first half is padding. + // in the 1st pointer, so the second half is padding. let c = &p as *const _ as *const u8; + let padding_offset = mem::size_of::<&'static ()>(); // Read a padding byte. - let _val = *c.add(0); + let _val = *c.add(padding_offset); //~^ERROR: uninitialized } } diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.stderr b/src/tools/miri/tests/fail/uninit/padding-enum.stderr index 66d3092c9ba..765e7cc4e63 100644 --- a/src/tools/miri/tests/fail/uninit/padding-enum.stderr +++ b/src/tools/miri/tests/fail/uninit/padding-enum.stderr @@ -1,8 +1,8 @@ error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory --> tests/fail/uninit/padding-enum.rs:LL:CC | -LL | let _val = *c.add(0); - | ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory +LL | let _val = *c.add(padding_offset); + | ^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.rs index 8deaa30d50f..5edf4bbb1cc 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic.rs @@ -3,7 +3,7 @@ #![allow(dropping_copy_types)] -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; use std::slice::from_raw_parts; fn main() { diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs index adabff4a2ce..954571f4a22 100644 --- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs +++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs @@ -6,7 +6,7 @@ // Test printing allocations that contain single-byte provenance. -use std::alloc::{alloc, dealloc, Layout}; +use std::alloc::{Layout, alloc, dealloc}; use std::mem::{self, MaybeUninit}; use std::slice::from_raw_parts; diff --git a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs index e84ffee367f..255a93226a9 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs @@ -1,6 +1,9 @@ //@only-target: linux //@compile-flags: -Zmiri-disable-isolation +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::mem::MaybeUninit; use std::ptr::{self, addr_of}; use std::sync::atomic::AtomicI32; diff --git a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs index 87c8a2e1063..2dc09709b8e 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/tls_pthread_drop_order.rs @@ -5,6 +5,9 @@ //! the fallback path in `guard::key::enable`, which uses a *single* pthread_key //! to manage a thread-local list of dtors to call. +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::mem; use std::ptr; diff --git a/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs index afcab7a702d..6853395686a 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_init_once.rs @@ -7,7 +7,7 @@ use std::thread; use windows_sys::Win32::Foundation::{FALSE, TRUE}; use windows_sys::Win32::System::Threading::{ - InitOnceBeginInitialize, InitOnceComplete, INIT_ONCE, INIT_ONCE_INIT_FAILED, + INIT_ONCE, INIT_ONCE_INIT_FAILED, InitOnceBeginInitialize, InitOnceComplete, }; // not in windows-sys diff --git a/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs b/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs index 67e77663110..ce829eee227 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/windows_join_multiple.rs @@ -7,7 +7,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use windows_sys::Win32::Foundation::{HANDLE, WAIT_OBJECT_0}; -use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE}; +use windows_sys::Win32::System::Threading::{INFINITE, WaitForSingleObject}; fn main() { static FLAG: AtomicBool = AtomicBool::new(false); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index 2f65ce13d64..3ea34513376 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -231,10 +231,10 @@ fn test_two_same_fd_in_same_epoll_instance() { //Two notification should be received. let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap(); let expected_value = 5 as u64; - check_epoll_wait::<8>( - epfd, - &[(expected_event, expected_value), (expected_event, expected_value)], - ); + check_epoll_wait::<8>(epfd, &[ + (expected_event, expected_value), + (expected_event, expected_value), + ]); } fn test_epoll_eventfd() { @@ -291,10 +291,10 @@ fn test_epoll_socketpair_both_sides() { let expected_value0 = fds[0] as u64; let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap(); let expected_value1 = fds[1] as u64; - check_epoll_wait::<8>( - epfd, - &[(expected_event0, expected_value0), (expected_event1, expected_value1)], - ); + check_epoll_wait::<8>(epfd, &[ + (expected_event0, expected_value0), + (expected_event1, expected_value1), + ]); // Read from fds[0]. let mut buf: [u8; 5] = [0; 5]; @@ -454,10 +454,10 @@ fn test_socketpair_read() { let expected_value0 = fds[0] as u64; let expected_event1 = u32::try_from(libc::EPOLLOUT).unwrap(); let expected_value1 = fds[1] as u64; - check_epoll_wait::<8>( - epfd, - &[(expected_event0, expected_value0), (expected_event1, expected_value1)], - ); + check_epoll_wait::<8>(epfd, &[ + (expected_event0, expected_value0), + (expected_event1, expected_value1), + ]); // Read 3 bytes from fds[0]. let mut buf: [u8; 3] = [0; 3]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 1e3d486233a..1d084194658 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -2,6 +2,9 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::thread; fn main() { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 11809613749..17e6e507c27 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -5,7 +5,7 @@ #![feature(io_error_uncategorized)] use std::ffi::{CStr, CString, OsString}; -use std::fs::{canonicalize, remove_file, File}; +use std::fs::{File, canonicalize, remove_file}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -169,7 +169,7 @@ fn test_ftruncate<T: From<i32>>( #[cfg(target_os = "linux")] fn test_o_tmpfile_flag() { - use std::fs::{create_dir, OpenOptions}; + use std::fs::{OpenOptions, create_dir}; use std::os::unix::fs::OpenOptionsExt; let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index c6e8355c3f5..76f883e5d8d 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -72,6 +72,8 @@ fn test_pipe_threaded() { thread2.join().unwrap(); } +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#[allow(static_mut_refs)] fn test_race() { static mut VAL: u8 = 0; let mut fds = [-1, -1]; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index c3d6af5a1ef..64819e57679 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -1,6 +1,10 @@ //@ignore-target: windows # No libc socketpair on Windows // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-preemption-rate=0 + +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::thread; fn main() { test_socketpair(); diff --git a/src/tools/miri/tests/pass-dep/tokio/sleep.rs b/src/tools/miri/tests/pass-dep/tokio/sleep.rs index 38f2cdde115..3c409a641f2 100644 --- a/src/tools/miri/tests/pass-dep/tokio/sleep.rs +++ b/src/tools/miri/tests/pass-dep/tokio/sleep.rs @@ -1,6 +1,6 @@ //@only-target: linux # We only support tokio on Linux -use tokio::time::{sleep, Duration, Instant}; +use tokio::time::{Duration, Instant, sleep}; #[tokio::main] async fn main() { diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index 1cbccb2eebd..10f7aed9418 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -22,7 +22,7 @@ // Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf. use std::sync::atomic::Ordering::*; -use std::sync::atomic::{fence, AtomicBool, AtomicI32}; +use std::sync::atomic::{AtomicBool, AtomicI32, fence}; use std::thread::spawn; #[derive(Copy, Clone)] diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs index 92ecbdd29fd..53e3476f620 100644 --- a/src/tools/miri/tests/pass/async-drop.rs +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -10,10 +10,10 @@ #![allow(incomplete_features, dead_code)] // FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests -use core::future::{async_drop_in_place, AsyncDrop, Future}; +use core::future::{AsyncDrop, Future, async_drop_in_place}; use core::hint::black_box; use core::mem::{self, ManuallyDrop}; -use core::pin::{pin, Pin}; +use core::pin::{Pin, pin}; use core::task::{Context, Poll, Waker}; async fn test_async_drop<T>(x: T) { @@ -125,7 +125,10 @@ struct AsyncReference<'a> { } impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> = impl Future<Output = ()> where Self: 'a; + type Dropper<'a> + = impl Future<Output = ()> + where + Self: 'a; fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { async move { diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs index dfdc9b42f81..781cc9bd309 100644 --- a/src/tools/miri/tests/pass/atomic.rs +++ b/src/tools/miri/tests/pass/atomic.rs @@ -1,9 +1,13 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance + #![feature(strict_provenance, strict_provenance_atomic_ptr)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::sync::atomic::{ - compiler_fence, fence, AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*, + AtomicBool, AtomicIsize, AtomicPtr, AtomicU64, Ordering::*, compiler_fence, fence, }; fn main() { diff --git a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs index 18980932f31..a1371242f60 100644 --- a/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs +++ b/src/tools/miri/tests/pass/box-custom-alloc-aliasing.rs @@ -11,7 +11,7 @@ use std::{ alloc::{AllocError, Allocator, Layout}, cell::{Cell, UnsafeCell}, mem, - ptr::{self, addr_of, NonNull}, + ptr::{self, NonNull, addr_of}, thread::{self, ThreadId}, }; diff --git a/src/tools/miri/tests/pass/catch.rs b/src/tools/miri/tests/pass/catch.rs index 4ede23e68ce..bedabf3eb28 100644 --- a/src/tools/miri/tests/pass/catch.rs +++ b/src/tools/miri/tests/pass/catch.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; fn main() { let mut i = 3; diff --git a/src/tools/miri/tests/pass/drop_on_array_elements.rs b/src/tools/miri/tests/pass/drop_on_array_elements.rs index ae1ef036267..4bf241fee2b 100644 --- a/src/tools/miri/tests/pass/drop_on_array_elements.rs +++ b/src/tools/miri/tests/pass/drop_on_array_elements.rs @@ -1,3 +1,5 @@ +#![allow(static_mut_refs)] + struct Bar(u16); // ZSTs are tested separately static mut DROP_COUNT: usize = 0; diff --git a/src/tools/miri/tests/pass/drop_on_fat_ptr_array_elements.rs b/src/tools/miri/tests/pass/drop_on_fat_ptr_array_elements.rs index 40025cd07f7..04723af94c3 100644 --- a/src/tools/miri/tests/pass/drop_on_fat_ptr_array_elements.rs +++ b/src/tools/miri/tests/pass/drop_on_fat_ptr_array_elements.rs @@ -1,3 +1,5 @@ +#![allow(static_mut_refs)] + trait Foo {} struct Bar; diff --git a/src/tools/miri/tests/pass/drop_on_zst_array_elements.rs b/src/tools/miri/tests/pass/drop_on_zst_array_elements.rs index babe098e4e6..71398724f2c 100644 --- a/src/tools/miri/tests/pass/drop_on_zst_array_elements.rs +++ b/src/tools/miri/tests/pass/drop_on_zst_array_elements.rs @@ -1,3 +1,5 @@ +#![allow(static_mut_refs)] + struct Bar; static mut DROP_COUNT: usize = 0; diff --git a/src/tools/miri/tests/pass/drop_through_owned_slice.rs b/src/tools/miri/tests/pass/drop_through_owned_slice.rs index 8cdeb57d02f..2d1d876e6f2 100644 --- a/src/tools/miri/tests/pass/drop_through_owned_slice.rs +++ b/src/tools/miri/tests/pass/drop_through_owned_slice.rs @@ -1,3 +1,5 @@ +#![allow(static_mut_refs)] + struct Bar; static mut DROP_COUNT: usize = 0; diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 4c0d6f52425..cd606a5282a 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -11,7 +11,7 @@ #![allow(incomplete_features, internal_features)] use std::intrinsics::simd as intrinsics; use std::ptr; -use std::simd::{prelude::*, StdFloat}; +use std::simd::{StdFloat, prelude::*}; extern "rust-intrinsic" { #[rustc_nounwind] diff --git a/src/tools/miri/tests/pass/panic/catch_panic.rs b/src/tools/miri/tests/pass/panic/catch_panic.rs index b83902a8b19..06d15a1a7f9 100644 --- a/src/tools/miri/tests/pass/panic/catch_panic.rs +++ b/src/tools/miri/tests/pass/panic/catch_panic.rs @@ -2,7 +2,7 @@ #![allow(unconditional_panic, non_fmt_panics)] use std::cell::Cell; -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use std::process; thread_local! { diff --git a/src/tools/miri/tests/pass/panic/concurrent-panic.rs b/src/tools/miri/tests/pass/panic/concurrent-panic.rs index 7cc1e2a973f..e804df90977 100644 --- a/src/tools/miri/tests/pass/panic/concurrent-panic.rs +++ b/src/tools/miri/tests/pass/panic/concurrent-panic.rs @@ -5,7 +5,7 @@ //! that separate threads have their own panicking state. use std::sync::{Arc, Condvar, Mutex}; -use std::thread::{spawn, JoinHandle}; +use std::thread::{JoinHandle, spawn}; struct BlockOnDrop(Option<JoinHandle<()>>); diff --git a/src/tools/miri/tests/pass/path.rs b/src/tools/miri/tests/pass/path.rs index fe99d38e073..299ee6cfe9d 100644 --- a/src/tools/miri/tests/pass/path.rs +++ b/src/tools/miri/tests/pass/path.rs @@ -1,5 +1,5 @@ //@compile-flags: -Zmiri-disable-isolation -use std::path::{absolute, Path, PathBuf}; +use std::path::{Path, PathBuf, absolute}; #[path = "../utils/mod.rs"] mod utils; diff --git a/src/tools/miri/tests/pass/shims/fs-symlink.rs b/src/tools/miri/tests/pass/shims/fs-symlink.rs index 01bb713926c..30cf234e7a0 100644 --- a/src/tools/miri/tests/pass/shims/fs-symlink.rs +++ b/src/tools/miri/tests/pass/shims/fs-symlink.rs @@ -3,7 +3,7 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation -use std::fs::{read_link, remove_file, File}; +use std::fs::{File, read_link, remove_file}; use std::io::{Read, Result}; use std::path::Path; diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 761164a4ba3..62424ca26b1 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -7,8 +7,8 @@ use std::collections::HashMap; use std::ffi::OsString; use std::fs::{ - canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, File, - OpenOptions, + File, OpenOptions, canonicalize, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, + rename, }; use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; use std::path::Path; diff --git a/src/tools/miri/tests/pass/static_memory_modification.rs b/src/tools/miri/tests/pass/static_memory_modification.rs index 84a524b1ed1..1900250bf4f 100644 --- a/src/tools/miri/tests/pass/static_memory_modification.rs +++ b/src/tools/miri/tests/pass/static_memory_modification.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::sync::atomic::{AtomicUsize, Ordering}; static mut X: usize = 5; diff --git a/src/tools/miri/tests/pass/static_mut.rs b/src/tools/miri/tests/pass/static_mut.rs index 4488b5a09d5..f88c7a16e6d 100644 --- a/src/tools/miri/tests/pass/static_mut.rs +++ b/src/tools/miri/tests/pass/static_mut.rs @@ -1,3 +1,6 @@ +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::ptr::addr_of; static mut FOO: i32 = 42; diff --git a/src/tools/miri/tests/pass/tls/tls_static.rs b/src/tools/miri/tests/pass/tls/tls_static.rs index 8d0e5089d40..cc4eaca72ab 100644 --- a/src/tools/miri/tests/pass/tls/tls_static.rs +++ b/src/tools/miri/tests/pass/tls/tls_static.rs @@ -7,6 +7,8 @@ //! dereferencing the pointer on `t2` resolves to `t1`'s thread-local. In this //! test, we also check that thread-locals act as per-thread statics. +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] #![feature(thread_local)] use std::ptr::addr_of_mut; diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index 1b5c98cd518..5d636431d86 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -8,7 +8,7 @@ // the RNG and always read the latest value from the store buffer. use std::sync::atomic::Ordering::*; -use std::sync::atomic::{fence, AtomicUsize}; +use std::sync::atomic::{AtomicUsize, fence}; use std::thread::spawn; #[allow(dead_code)] diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index d405eb92ad1..2430140eea1 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -12,7 +12,7 @@ use ui_test::custom_flags::edition::Edition; use ui_test::dependencies::DependencyBuilder; use ui_test::per_test_config::TestConfig; use ui_test::spanned::Spanned; -use ui_test::{status_emitter, CommandBuilder, Config, Format, Match, OutputConflictHandling}; +use ui_test::{CommandBuilder, Config, Format, Match, OutputConflictHandling, status_emitter}; #[derive(Copy, Clone, Debug)] enum Mode { @@ -118,24 +118,21 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> config.comment_defaults.base().add_custom("edition", Edition("2021".into())); if with_dependencies { - config.comment_defaults.base().set_custom( - "dependencies", - DependencyBuilder { - program: CommandBuilder { - // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. - // (It's a separate crate, so we don't get an env var from cargo.) - program: miri_path() - .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), - // There is no `cargo miri build` so we just use `cargo miri run`. - args: ["miri", "run"].into_iter().map(Into::into).collect(), - // Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>. - envs: vec![("RUSTFLAGS".into(), None)], - ..CommandBuilder::cargo() - }, - crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"), - build_std: None, + config.comment_defaults.base().set_custom("dependencies", DependencyBuilder { + program: CommandBuilder { + // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. + // (It's a separate crate, so we don't get an env var from cargo.) + program: miri_path() + .with_file_name(format!("cargo-miri{}", env::consts::EXE_SUFFIX)), + // There is no `cargo miri build` so we just use `cargo miri run`. + args: ["miri", "run"].into_iter().map(Into::into).collect(), + // Reset `RUSTFLAGS` to work around <https://github.com/rust-lang/rust/pull/119574#issuecomment-1876878344>. + envs: vec![("RUSTFLAGS".into(), None)], + ..CommandBuilder::cargo() }, - ); + crate_manifest_path: Path::new("test_dependencies").join("Cargo.toml"), + build_std: None, + }); } config } diff --git a/src/tools/run-make-support/src/fs.rs b/src/tools/run-make-support/src/fs.rs index 2c35ba52a62..2ca55ad3b3a 100644 --- a/src/tools/run-make-support/src/fs.rs +++ b/src/tools/run-make-support/src/fs.rs @@ -1,42 +1,6 @@ use std::io; use std::path::{Path, PathBuf}; -// FIXME(jieyouxu): modify create_symlink to panic on windows. - -/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. -#[cfg(target_family = "windows")] -pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { - if link.as_ref().exists() { - std::fs::remove_dir(link.as_ref()).unwrap(); - } - if original.as_ref().is_file() { - std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()).expect(&format!( - "failed to create symlink {:?} for {:?}", - link.as_ref().display(), - original.as_ref().display(), - )); - } else { - std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()).expect(&format!( - "failed to create symlink {:?} for {:?}", - link.as_ref().display(), - original.as_ref().display(), - )); - } -} - -/// Creates a new symlink to a path on the filesystem, adjusting for Windows or Unix. -#[cfg(target_family = "unix")] -pub fn create_symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { - if link.as_ref().exists() { - std::fs::remove_dir(link.as_ref()).unwrap(); - } - std::os::unix::fs::symlink(original.as_ref(), link.as_ref()).expect(&format!( - "failed to create symlink {:?} for {:?}", - link.as_ref().display(), - original.as_ref().display(), - )); -} - /// Copy a directory into another. pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { fn copy_dir_all_inner(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> { @@ -50,7 +14,31 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { if ty.is_dir() { copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?; } else if ty.is_symlink() { - copy_symlink(entry.path(), dst.join(entry.file_name()))?; + // Traverse symlink once to find path of target entity. + let target_path = std::fs::read_link(entry.path())?; + + let new_symlink_path = dst.join(entry.file_name()); + #[cfg(windows)] + { + use std::os::windows::fs::FileTypeExt; + if ty.is_symlink_dir() { + std::os::windows::fs::symlink_dir(&target_path, new_symlink_path)?; + } else { + // Target may be a file or another symlink, in any case we can use + // `symlink_file` here. + std::os::windows::fs::symlink_file(&target_path, new_symlink_path)?; + } + } + #[cfg(unix)] + { + std::os::unix::fs::symlink(target_path, new_symlink_path)?; + } + #[cfg(not(any(windows, unix)))] + { + // Technically there's also wasi, but I have no clue about wasi symlink + // semantics and which wasi targets / environment support symlinks. + unimplemented!("unsupported target"); + } } else { std::fs::copy(entry.path(), dst.join(entry.file_name()))?; } @@ -69,12 +57,6 @@ pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) { } } -fn copy_symlink<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { - let target_path = std::fs::read_link(from).unwrap(); - create_symlink(target_path, to); - Ok(()) -} - /// Helper for reading entries in a given directory. pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F) { for entry in read_dir(dir) { @@ -85,8 +67,17 @@ pub fn read_dir_entries<P: AsRef<Path>, F: FnMut(&Path)>(dir: P, mut callback: F /// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message. #[track_caller] pub fn remove_file<P: AsRef<Path>>(path: P) { - std::fs::remove_file(path.as_ref()) - .expect(&format!("the file in path \"{}\" could not be removed", path.as_ref().display())); + if let Err(e) = std::fs::remove_file(path.as_ref()) { + panic!("failed to remove file at `{}`: {e}", path.as_ref().display()); + } +} + +/// A wrapper around [`std::fs::remove_dir`] which includes the directory path in the panic message. +#[track_caller] +pub fn remove_dir<P: AsRef<Path>>(path: P) { + if let Err(e) = std::fs::remove_dir(path.as_ref()) { + panic!("failed to remove directory at `{}`: {e}", path.as_ref().display()); + } } /// A wrapper around [`std::fs::copy`] which includes the file path in the panic message. @@ -165,13 +156,32 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) { )); } -/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. +/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. Note +/// that this will traverse symlinks and will return metadata about the target file. Use +/// [`symlink_metadata`] if you don't want to traverse symlinks. +/// +/// See [`std::fs::metadata`] docs for more details. #[track_caller] pub fn metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata { - std::fs::metadata(path.as_ref()).expect(&format!( - "the file's metadata in path \"{}\" could not be read", - path.as_ref().display() - )) + match std::fs::metadata(path.as_ref()) { + Ok(m) => m, + Err(e) => panic!("failed to read file metadata at `{}`: {e}", path.as_ref().display()), + } +} + +/// A wrapper around [`std::fs::symlink_metadata`] which includes the file path in the panic +/// message. Note that this will not traverse symlinks and will return metadata about the filesystem +/// entity itself. Use [`metadata`] if you want to traverse symlinks. +/// +/// See [`std::fs::symlink_metadata`] docs for more details. +#[track_caller] +pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> std::fs::Metadata { + match std::fs::symlink_metadata(path.as_ref()) { + Ok(m) => m, + Err(e) => { + panic!("failed to read file metadata (shallow) at `{}`: {e}", path.as_ref().display()) + } + } } /// A wrapper around [`std::fs::rename`] which includes the file path in the panic message. @@ -205,3 +215,73 @@ pub fn shallow_find_dir_entries<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> { } output } + +/// Create a new symbolic link to a directory. +/// +/// # Removing the symlink +/// +/// - On Windows, a symlink-to-directory needs to be removed with a corresponding [`fs::remove_dir`] +/// and not [`fs::remove_file`]. +/// - On Unix, remove the symlink with [`fs::remove_file`]. +/// +/// [`fs::remove_dir`]: crate::fs::remove_dir +/// [`fs::remove_file`]: crate::fs::remove_file +pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { + #[cfg(unix)] + { + if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) { + panic!( + "failed to create symlink: original=`{}`, link=`{}`: {e}", + original.as_ref().display(), + link.as_ref().display() + ); + } + } + #[cfg(windows)] + { + if let Err(e) = std::os::windows::fs::symlink_dir(original.as_ref(), link.as_ref()) { + panic!( + "failed to create symlink-to-directory: original=`{}`, link=`{}`: {e}", + original.as_ref().display(), + link.as_ref().display() + ); + } + } + #[cfg(not(any(windows, unix)))] + { + unimplemented!("target family not currently supported") + } +} + +/// Create a new symbolic link to a file. +/// +/// # Removing the symlink +/// +/// On both Windows and Unix, a symlink-to-file needs to be removed with a corresponding +/// [`fs::remove_file`](crate::fs::remove_file) and not [`fs::remove_dir`](crate::fs::remove_dir). +pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) { + #[cfg(unix)] + { + if let Err(e) = std::os::unix::fs::symlink(original.as_ref(), link.as_ref()) { + panic!( + "failed to create symlink: original=`{}`, link=`{}`: {e}", + original.as_ref().display(), + link.as_ref().display() + ); + } + } + #[cfg(windows)] + { + if let Err(e) = std::os::windows::fs::symlink_file(original.as_ref(), link.as_ref()) { + panic!( + "failed to create symlink-to-file: original=`{}`, link=`{}`: {e}", + original.as_ref().display(), + link.as_ref().display() + ); + } + } + #[cfg(not(any(windows, unix)))] + { + unimplemented!("target family not currently supported") + } +} diff --git a/src/tools/rustfmt/.github/workflows/check_diff.yml b/src/tools/rustfmt/.github/workflows/check_diff.yml index 2f2beb76915..99daa0addf5 100644 --- a/src/tools/rustfmt/.github/workflows/check_diff.yml +++ b/src/tools/rustfmt/.github/workflows/check_diff.yml @@ -21,7 +21,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/src/tools/rustfmt/.github/workflows/integration.yml b/src/tools/rustfmt/.github/workflows/integration.yml index f0dd0cf73bb..bda374562bc 100644 --- a/src/tools/rustfmt/.github/workflows/integration.yml +++ b/src/tools/rustfmt/.github/workflows/integration.yml @@ -64,7 +64,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/linux.yml b/src/tools/rustfmt/.github/workflows/linux.yml index bce9b0c8d5a..3a5e6ab5404 100644 --- a/src/tools/rustfmt/.github/workflows/linux.yml +++ b/src/tools/rustfmt/.github/workflows/linux.yml @@ -26,7 +26,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/mac.yml b/src/tools/rustfmt/.github/workflows/mac.yml index 89a980c42c5..2c766d0573b 100644 --- a/src/tools/rustfmt/.github/workflows/mac.yml +++ b/src/tools/rustfmt/.github/workflows/mac.yml @@ -8,7 +8,6 @@ on: jobs: test: # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/virtual-environments-for-github-hosted-runners#supported-runners-and-hardware-resources - # macOS Catalina 10.15 runs-on: macos-latest name: (${{ matrix.target }}, ${{ matrix.cfg_release_channel }}) env: @@ -23,7 +22,7 @@ jobs: steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml index cd0c3218971..6e8a7ecd7ad 100644 --- a/src/tools/rustfmt/.github/workflows/rustdoc_check.yml +++ b/src/tools/rustfmt/.github/workflows/rustdoc_check.yml @@ -11,7 +11,7 @@ jobs: name: rustdoc check steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: install rustup run: | diff --git a/src/tools/rustfmt/.github/workflows/upload-assets.yml b/src/tools/rustfmt/.github/workflows/upload-assets.yml index 7dfaa4b9204..7a639b469e8 100644 --- a/src/tools/rustfmt/.github/workflows/upload-assets.yml +++ b/src/tools/rustfmt/.github/workflows/upload-assets.yml @@ -31,7 +31,7 @@ jobs: target: x86_64-pc-windows-msvc runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # Run build - name: install rustup diff --git a/src/tools/rustfmt/.github/workflows/windows.yml b/src/tools/rustfmt/.github/workflows/windows.yml index ec37c714b08..728f1b90b13 100644 --- a/src/tools/rustfmt/.github/workflows/windows.yml +++ b/src/tools/rustfmt/.github/workflows/windows.yml @@ -33,7 +33,7 @@ jobs: - name: disable git eol translation run: git config --global core.autocrlf false - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Run build - name: Install Rustup using win.rustup.rs diff --git a/src/tools/rustfmt/CHANGELOG.md b/src/tools/rustfmt/CHANGELOG.md index 89e90fb17dd..8af60f60dc6 100644 --- a/src/tools/rustfmt/CHANGELOG.md +++ b/src/tools/rustfmt/CHANGELOG.md @@ -1,6 +1,66 @@ # Changelog -## [Unreleased] +## [1.8.0] 2024-09-20 + +### Fixed +- Fix issue where rustfmt would crash on Windows when using the `ignore` option [#6178](https://github.com/rust-lang/rustfmt/issues/6178) + +### Changed +- `rustfmt --version` now prints a commit hash that is 10 characters long [#6258](https://github.com/rust-lang/rustfmt/pull/6258) +- `rustfmt --version` will no longer print empty git information when git information isn't available at build time. + For example, git information is not available when building rustfmt from a source tarball [#6266](https://github.com/rust-lang/rustfmt/pull/6266) +- `version` has been soft deprecated and replaced by `style_edition`. + `style_edition=2024` is equivalent to `version=Two` and `style_edition={2015|2018|2021}` + are equivalent to `version=One` [#6247](https://github.com/rust-lang/rustfmt/pull/6247) +- When `style_edition=2024` is configured `overflow_delimited_expr` will default to `true` [#6260](https://github.com/rust-lang/rustfmt/pull/6260). + ```rust + // with style_edition=2015 + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + // with style_edition=2024 + do_thing(x, Bar { + x: value, + y: value2, + }); + ``` +- When `style_edition=2024` is configured rustfmt will apply the [style guide's version sorting algorithm] + when sorting imports [#6284](https://github.com/rust-lang/rustfmt/pull/6284) + ```rust + // with style_edition=2015 + use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; + + // with style_edition=2024 + use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; + ``` + [style guide's version sorting algorithm]: https://doc.rust-lang.org/nightly/style-guide/#sorting +- When parsing rustfmt configurations fails, rustfmt will now include the path to the toml file in the erorr message [#6302](https://github.com/rust-lang/rustfmt/issues/6302) + +### Added +- rustfmt now formats trailing where clauses in type aliases [#5887](https://github.com/rust-lang/rustfmt/pull/5887) + ```rust + type Foo + = Bar + where + A: B, + C: D; + ``` +- Users can now configure which `style_edition` rustfmt uses when formatting their code as specified + in [RFC 3338](https://rust-lang.github.io/rfcs/3338-style-evolution.html). Users are encouraged to configure `style_edition` + in their `rustfmt.toml` files, but the value can also be specified via the cli with `--unstable-features --style-edition={style_edition}`. + When `style_edition` is not explicitly configured it will be inferred from the `edition` configuration. + When neither `style_edition` nor `edition` are configured `style_edition` defaults to `2015` [#6247](https://github.com/rust-lang/rustfmt/pull/6247) + +### Misc +- Removed `tracing-attributes` dependency [#6208](https://github.com/rust-lang/rustfmt/pull/6208) +- Reduced syn's features in the internal `config_proc_macro` crate [#6237](https://github.com/rust-lang/rustfmt/pull/6237) + +## [1.7.1] 2024-06-24 ### Fixed @@ -238,7 +298,7 @@ ### Added -- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) +- New configuration option [`skip_macro_invocations`](https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations) [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726) ### Misc diff --git a/src/tools/rustfmt/Cargo.lock b/src/tools/rustfmt/Cargo.lock index 2a1fffa50fe..e2ceb668ebd 100644 --- a/src/tools/rustfmt/Cargo.lock +++ b/src/tools/rustfmt/Cargo.lock @@ -499,7 +499,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" dependencies = [ "annotate-snippets", "anyhow", @@ -710,22 +710,10 @@ checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", - "tracing-attributes", "tracing-core", ] [[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "tracing-core" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml index a16620ed99b..e497b792342 100644 --- a/src/tools/rustfmt/Cargo.toml +++ b/src/tools/rustfmt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-nightly" -version = "1.7.1" +version = "1.8.0" description = "Tool to find and fix Rust formatting issues" repository = "https://github.com/rust-lang/rustfmt" readme = "README.md" @@ -50,7 +50,7 @@ serde_json = "1.0" term = "0.7" thiserror = "1.0.40" toml = "0.7.4" -tracing = "0.1.37" +tracing = { version = "0.1.37", default-features = false, features = ["std"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } unicode-segmentation = "1.9" unicode-width = "0.1" diff --git a/src/tools/rustfmt/Configurations.md b/src/tools/rustfmt/Configurations.md index f52c2573154..b1f54060392 100644 --- a/src/tools/rustfmt/Configurations.md +++ b/src/tools/rustfmt/Configurations.md @@ -534,7 +534,7 @@ Note that this option may be soft-deprecated in the future once the [ignore](#ig Specifies which edition is used by the parser. - **Default value**: `"2015"` -- **Possible values**: `"2015"`, `"2018"`, `"2021"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` - **Stable**: Yes Rustfmt is able to pick up the edition used by reading the `Cargo.toml` file if executed @@ -2692,6 +2692,17 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `style_edition` + +Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338]) + +- **Default value**: `"2015"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant) +- **Stable**: No + +[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ +[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html + ## `tab_spaces` Number of spaces per tab @@ -3051,9 +3062,7 @@ fn main() { ## `version` -Which version of the formatting rules to use. `Version::One` is backwards-compatible -with Rustfmt 1.0. Other versions are only backwards compatible within a major -version number. +This option is deprecated and has been replaced by [`style_edition`](#style_edition) - **Default value**: `One` - **Possible values**: `One`, `Two` diff --git a/src/tools/rustfmt/Contributing.md b/src/tools/rustfmt/Contributing.md index 2f2ccfb175a..85754a8658a 100644 --- a/src/tools/rustfmt/Contributing.md +++ b/src/tools/rustfmt/Contributing.md @@ -109,17 +109,17 @@ If you want to test modified `cargo-fmt`, or run `rustfmt` on the whole project RUSTFMT="./target/debug/rustfmt" cargo run --bin cargo-fmt -- --manifest-path path/to/project/you/want2test/Cargo.toml ``` -### Version-gate formatting changes +### Gate formatting changes -A change that introduces a different code-formatting should be gated on the -`version` configuration. This is to ensure the formatting of the current major -release is preserved, while allowing fixes to be implemented for the next -release. +A change that introduces a different code-formatting must be gated on the +`style_edition` configuration. This is to ensure rustfmt upholds its formatting +stability guarantees and adheres to the Style Edition process set in [RFC 3338] -This is done by conditionally guarding the change like so: +This can be done by conditionally guarding the formatting change, e.g.: ```rust -if config.version() == Version::One { // if the current major release is 1.x +// if the current stable Style Edition is Edition 2024 +if config.style_edition() <= StyleEdition::Edition2024 { // current formatting } else { // new formatting @@ -129,13 +129,14 @@ if config.version() == Version::One { // if the current major release is 1.x This allows the user to apply the next formatting explicitly via the configuration, while being stable by default. -When the next major release is done, the code block of the previous formatting -can be deleted, e.g., the first block in the example above when going from `1.x` -to `2.x`. +This can then be enhanced as needed if and when there are +new Style Editions with differing formatting prescriptions. | Note: Only formatting changes with default options need to be gated. | | --- | +[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html + ### A quick tour of Rustfmt Rustfmt is basically a pretty printer - that is, its mode of operation is to diff --git a/src/tools/rustfmt/build.rs b/src/tools/rustfmt/build.rs index 9a8bb77a8ed..696c713d726 100644 --- a/src/tools/rustfmt/build.rs +++ b/src/tools/rustfmt/build.rs @@ -25,7 +25,7 @@ fn main() { // (git not installed or if this is not a git repository) just return an empty string. fn commit_info() -> String { match (channel(), commit_hash(), commit_date()) { - (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash.trim_end(), date), + (channel, Some(hash), Some(date)) => format!("{} ({} {})", channel, hash, date), _ => String::new(), } } @@ -39,17 +39,20 @@ fn channel() -> String { } fn commit_hash() -> Option<String> { - Command::new("git") - .args(["rev-parse", "--short", "HEAD"]) + let output = Command::new("git") + .args(["rev-parse", "HEAD"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let mut stdout = output.status.success().then_some(output.stdout)?; + stdout.truncate(10); + String::from_utf8(stdout).ok() } fn commit_date() -> Option<String> { - Command::new("git") + let output = Command::new("git") .args(["log", "-1", "--date=short", "--pretty=format:%cd"]) .output() - .ok() - .and_then(|r| String::from_utf8(r.stdout).ok()) + .ok()?; + let stdout = output.status.success().then_some(output.stdout)?; + String::from_utf8(stdout).ok() } diff --git a/src/tools/rustfmt/check_diff/Cargo.lock b/src/tools/rustfmt/check_diff/Cargo.lock index 6716ccdf9a0..2abf5af2f98 100644 --- a/src/tools/rustfmt/check_diff/Cargo.lock +++ b/src/tools/rustfmt/check_diff/Cargo.lock @@ -3,6 +3,15 @@ version = 3 [[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] name = "anstream" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -52,10 +61,25 @@ dependencies = [ ] [[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] name = "check_diff" version = "0.1.0" dependencies = [ "clap", + "tempfile", + "tracing", + "tracing-subscriber", ] [[package]] @@ -105,6 +129,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -117,6 +157,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" [[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] name = "proc-macro2" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -135,6 +242,78 @@ dependencies = [ ] [[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -152,6 +331,89 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -164,6 +426,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/tools/rustfmt/check_diff/Cargo.toml b/src/tools/rustfmt/check_diff/Cargo.toml index a1ed154481a..4ae8a5f1f3a 100644 --- a/src/tools/rustfmt/check_diff/Cargo.toml +++ b/src/tools/rustfmt/check_diff/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.4.2", features = ["derive"] } +tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +[dev-dependencies] +tempfile = "3" diff --git a/src/tools/rustfmt/check_diff/src/lib.rs b/src/tools/rustfmt/check_diff/src/lib.rs new file mode 100644 index 00000000000..b83d67c8b6e --- /dev/null +++ b/src/tools/rustfmt/check_diff/src/lib.rs @@ -0,0 +1,58 @@ +use std::env; +use std::io; +use std::path::Path; +use std::process::Command; +use tracing::info; + +pub enum GitError { + FailedClone { stdout: Vec<u8>, stderr: Vec<u8> }, + IO(std::io::Error), +} + +impl From<io::Error> for GitError { + fn from(error: io::Error) -> Self { + GitError::IO(error) + } +} + +/// Clone a git repository +/// +/// Parameters: +/// url: git clone url +/// dest: directory where the repo should be cloned +pub fn clone_git_repo(url: &str, dest: &Path) -> Result<(), GitError> { + let git_cmd = Command::new("git") + .env("GIT_TERMINAL_PROMPT", "0") + .args([ + "clone", + "--quiet", + url, + "--depth", + "1", + dest.to_str().unwrap(), + ]) + .output()?; + + // if the git command does not return successfully, + // any command on the repo will fail. So fail fast. + if !git_cmd.status.success() { + let error = GitError::FailedClone { + stdout: git_cmd.stdout, + stderr: git_cmd.stderr, + }; + return Err(error); + } + + info!("Successfully clone repository."); + return Ok(()); +} + +pub fn change_directory_to_path(dest: &Path) -> io::Result<()> { + let dest_path = Path::new(&dest); + env::set_current_dir(&dest_path)?; + info!( + "Current directory: {}", + env::current_dir().unwrap().display() + ); + return Ok(()); +} diff --git a/src/tools/rustfmt/check_diff/src/main.rs b/src/tools/rustfmt/check_diff/src/main.rs index 6d07c1b0df6..01c5926c490 100644 --- a/src/tools/rustfmt/check_diff/src/main.rs +++ b/src/tools/rustfmt/check_diff/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; + /// Inputs for the check_diff script #[derive(Parser)] struct CliInputs { @@ -16,10 +17,5 @@ struct CliInputs { } fn main() { - let args = CliInputs::parse(); - println!( - "remote_repo_url: {:?}, feature_branch: {:?}, - optional_commit_hash: {:?}, optional_rustfmt_config: {:?}", - args.remote_repo_url, args.feature_branch, args.commit_hash, args.rustfmt_config - ); + let _args = CliInputs::parse(); } diff --git a/src/tools/rustfmt/check_diff/tests/bash_commands.rs b/src/tools/rustfmt/check_diff/tests/bash_commands.rs new file mode 100644 index 00000000000..38ee34ef503 --- /dev/null +++ b/src/tools/rustfmt/check_diff/tests/bash_commands.rs @@ -0,0 +1,12 @@ +use check_diff::change_directory_to_path; +use std::env; +use tempfile::Builder; + +#[test] +fn cd_test() { + // Creates an empty directory in the current working directory + let dir = Builder::new().tempdir_in("").unwrap(); + let dest_path = dir.path(); + change_directory_to_path(dest_path).unwrap(); + assert_eq!(env::current_dir().unwrap(), dest_path); +} diff --git a/src/tools/rustfmt/check_diff/tests/git.rs b/src/tools/rustfmt/check_diff/tests/git.rs new file mode 100644 index 00000000000..677c3840e1e --- /dev/null +++ b/src/tools/rustfmt/check_diff/tests/git.rs @@ -0,0 +1,16 @@ +use check_diff::clone_git_repo; + +use tempfile::Builder; + +#[test] +fn clone_repo_test() { + // Creates an empty directory in the current working directory + let dir = Builder::new().tempdir_in("").unwrap(); + let sample_repo = "https://github.com/rust-lang/rustfmt.git"; + let dest_path = dir.path(); + let result = clone_git_repo(sample_repo, dest_path); + assert!(result.is_ok()); + // check whether a .git folder exists after cloning the repo + let git_repo = dest_path.join(".git"); + assert!(git_repo.exists()); +} diff --git a/src/tools/rustfmt/ci/build_and_test.bat b/src/tools/rustfmt/ci/build_and_test.bat index 16608a4aaa7..b6b5ca21364 100755 --- a/src/tools/rustfmt/ci/build_and_test.bat +++ b/src/tools/rustfmt/ci/build_and_test.bat @@ -13,7 +13,13 @@ if "%CFG_RELEASE_CHANNEL%"=="nightly" ( ) cargo test || exit /b 1 -:: Build and test other crates +:: Build and test config_proc_macro cd config_proc_macro || exit /b 1 cargo build --locked || exit /b 1 cargo test || exit /b 1 + +:: Build and test check_diff +cd .. +cd check_diff || exit /b 1 +cargo build --locked || exit /b 1 +cargo test || exit /b 1 diff --git a/src/tools/rustfmt/ci/build_and_test.sh b/src/tools/rustfmt/ci/build_and_test.sh index 207da362fd6..dd9a0c0fd9b 100755 --- a/src/tools/rustfmt/ci/build_and_test.sh +++ b/src/tools/rustfmt/ci/build_and_test.sh @@ -17,7 +17,13 @@ else fi cargo test -# Build and test other crates +# Build and test config_proc_macro cd config_proc_macro cargo build --locked cargo test + +# Build and test check_diff +cd .. +cd check_diff +cargo build --locked +cargo test diff --git a/src/tools/rustfmt/config_proc_macro/Cargo.toml b/src/tools/rustfmt/config_proc_macro/Cargo.toml index eda8a7fce81..ec0db49d71c 100644 --- a/src/tools/rustfmt/config_proc_macro/Cargo.toml +++ b/src/tools/rustfmt/config_proc_macro/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustfmt-config_proc_macro" version = "0.3.0" -edition = "2018" +edition = "2021" description = "A collection of procedural macros for rustfmt" license = "Apache-2.0 OR MIT" categories = ["development-tools::procedural-macro-helpers"] @@ -13,7 +13,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" -syn = { version = "2.0", features = ["full", "visit"] } +syn = { version = "2.0", default-features = false, features = ["full", "parsing", "proc-macro", "printing"] } [dev-dependencies] serde = { version = "1.0.160", features = ["derive"] } diff --git a/src/tools/rustfmt/config_proc_macro/src/utils.rs b/src/tools/rustfmt/config_proc_macro/src/utils.rs index f5cba87b07b..1f5b5cdb604 100644 --- a/src/tools/rustfmt/config_proc_macro/src/utils.rs +++ b/src/tools/rustfmt/config_proc_macro/src/utils.rs @@ -1,5 +1,5 @@ use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; +use quote::{ToTokens, quote}; pub fn fold_quote<F, I, T>(input: impl Iterator<Item = I>, f: F) -> TokenStream where diff --git a/src/tools/rustfmt/rust-toolchain b/src/tools/rustfmt/rust-toolchain index 25e3961d32a..80723123274 100644 --- a/src/tools/rustfmt/rust-toolchain +++ b/src/tools/rustfmt/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-06-13" +channel = "nightly-2024-09-10" components = ["llvm-tools", "rustc-dev"] diff --git a/src/tools/rustfmt/rustfmt.toml b/src/tools/rustfmt/rustfmt.toml index eccd5f9bd19..86447eac2e6 100644 --- a/src/tools/rustfmt/rustfmt.toml +++ b/src/tools/rustfmt/rustfmt.toml @@ -1,3 +1,4 @@ error_on_line_overflow = true error_on_unformatted = true -version = "Two" +style_edition = "2024" +overflow_delimited_expr = false diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index a2c0a28d66e..5c2068b6a22 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/src/attr.rs @@ -1,21 +1,21 @@ //! Format attributes and meta items. -use rustc_ast::ast; use rustc_ast::HasAttrs; -use rustc_span::{symbol::sym, Span}; +use rustc_ast::ast; +use rustc_span::{Span, symbol::sym}; use tracing::debug; use self::doc_comment::DocCommentFormatter; -use crate::comment::{contains_comment, rewrite_doc_comment, CommentStyle}; -use crate::config::lists::*; +use crate::comment::{CommentStyle, contains_comment, rewrite_doc_comment}; use crate::config::IndentStyle; +use crate::config::lists::*; use crate::expr::rewrite_literal; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; use crate::overflow; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; -use crate::types::{rewrite_path, PathContext}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{count_newlines, mk_sp}; mod doc_comment; @@ -100,7 +100,7 @@ fn format_derive( ",", |span| span.lo(), |span| span.hi(), - |span| Some(context.snippet(*span).to_owned()), + |span| Ok(context.snippet(*span).to_owned()), // We update derive attribute spans to start after the opening '(' // This helps us focus parsing to just what's inside #[derive(...)] context.snippet_provider.span_after(attr.span, "("), @@ -148,7 +148,7 @@ fn format_derive( .tactic(tactic) .trailing_separator(trailing_separator) .ends_with_newline(false); - let item_str = write_list(&all_items, &fmt)?; + let item_str = write_list(&all_items, &fmt).ok()?; debug!("item_str: '{}'", item_str); @@ -218,9 +218,9 @@ fn rewrite_initial_doc_comments( context: &RewriteContext<'_>, attrs: &[ast::Attribute], shape: Shape, -) -> Option<(usize, Option<String>)> { +) -> Result<(usize, Option<String>), RewriteError> { if attrs.is_empty() { - return Some((0, None)); + return Ok((0, None)); } // Rewrite doc comments let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_doc_comment()); @@ -230,7 +230,7 @@ fn rewrite_initial_doc_comments( .map(|a| context.snippet(a.span)) .collect::<Vec<_>>() .join("\n"); - return Some(( + return Ok(( sugared_docs.len(), Some(rewrite_doc_comment( &snippet, @@ -240,13 +240,19 @@ fn rewrite_initial_doc_comments( )); } - Some((0, None)) + Ok((0, None)) } impl Rewrite for ast::NestedMetaItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self { - ast::NestedMetaItem::MetaItem(ref meta_item) => meta_item.rewrite(context, shape), + ast::NestedMetaItem::MetaItem(ref meta_item) => { + meta_item.rewrite_result(context, shape) + } ast::NestedMetaItem::Lit(ref l) => { rewrite_literal(context, l.as_token_lit(), l.span, shape) } @@ -275,7 +281,11 @@ fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) { impl Rewrite for ast::MetaItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - Some(match self.kind { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + Ok(match self.kind { ast::MetaItemKind::Word => { rewrite_path(context, PathContext::Type, &None, &self.path, shape)? } @@ -287,7 +297,7 @@ impl Rewrite for ast::MetaItem { &path, list.iter(), // 1 = "]" - shape.sub_width(1)?, + shape.sub_width(1).max_width_error(shape.width, self.span)?, self.span, context.config.attr_fn_like_width(), Some(if has_trailing_comma { @@ -300,7 +310,9 @@ impl Rewrite for ast::MetaItem { ast::MetaItemKind::NameValue(ref lit) => { let path = rewrite_path(context, PathContext::Type, &None, &self.path, shape)?; // 3 = ` = ` - let lit_shape = shape.shrink_left(path.len() + 3)?; + let lit_shape = shape + .shrink_left(path.len() + 3) + .max_width_error(shape.width, self.span)?; // `rewrite_literal` returns `None` when `lit` exceeds max // width. Since a literal is basically unformattable unless it // is a string literal (and only if `format_strings` is set), @@ -308,7 +320,7 @@ impl Rewrite for ast::MetaItem { // is longer than the max width and continue on formatting. // See #2479 for example. let value = rewrite_literal(context, lit.as_token_lit(), lit.span, lit_shape) - .unwrap_or_else(|| context.snippet(lit.span).to_owned()); + .unwrap_or_else(|_| context.snippet(lit.span).to_owned()); format!("{path} = {value}") } }) @@ -317,6 +329,10 @@ impl Rewrite for ast::MetaItem { impl Rewrite for ast::Attribute { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let snippet = context.snippet(self.span); if self.is_doc_comment() { rewrite_doc_comment(snippet, shape.comment(context.config), context.config) @@ -328,7 +344,7 @@ impl Rewrite for ast::Attribute { let prefix = attr_prefix(self); if should_skip || contains_comment(snippet) { - return Some(snippet.to_owned()); + return Ok(snippet.to_owned()); } if let Some(ref meta) = self.meta() { @@ -353,9 +369,11 @@ impl Rewrite for ast::Attribute { } // 1 = `[` - let shape = shape.offset_left(prefix.len() + 1)?; - Some(meta.rewrite(context, shape).map_or_else( - || snippet.to_owned(), + let shape = shape + .offset_left(prefix.len() + 1) + .max_width_error(shape.width, self.span)?; + Ok(meta.rewrite_result(context, shape).map_or_else( + |_| snippet.to_owned(), |rw| match &self.kind { ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety { // For #![feature(unsafe_attributes)] @@ -367,7 +385,7 @@ impl Rewrite for ast::Attribute { }, )) } else { - Some(snippet.to_owned()) + Ok(snippet.to_owned()) } } } @@ -375,8 +393,12 @@ impl Rewrite for ast::Attribute { impl Rewrite for [ast::Attribute] { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { if self.is_empty() { - return Some(String::new()); + return Ok(String::new()); } // The current remaining attributes. @@ -392,7 +414,7 @@ impl Rewrite for [ast::Attribute] { // merging derives into a single attribute. loop { if attrs.is_empty() { - return Some(result); + return Ok(result); } // Handle doc comments. @@ -431,7 +453,7 @@ impl Rewrite for [ast::Attribute] { // Handle derives if we will merge them. if !skip_derives && context.config.merge_derives() && is_derive(&attrs[0]) { let derives = take_while_with_pred(context, attrs, is_derive); - let derive_str = format_derive(derives, shape, context)?; + let derive_str = format_derive(derives, shape, context).unknown_error()?; result.push_str(&derive_str); let missing_span = attrs @@ -464,7 +486,7 @@ impl Rewrite for [ast::Attribute] { // If we get here, then we have a regular attribute, just handle one // at a time. - let formatted_attr = attrs[0].rewrite(context, shape)?; + let formatted_attr = attrs[0].rewrite_result(context, shape)?; result.push_str(&formatted_attr); let missing_span = attrs diff --git a/src/tools/rustfmt/src/bin/main.rs b/src/tools/rustfmt/src/bin/main.rs index 88281d296be..c7d3a060d54 100644 --- a/src/tools/rustfmt/src/bin/main.rs +++ b/src/tools/rustfmt/src/bin/main.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] -use anyhow::{format_err, Result}; +use anyhow::{Result, format_err}; use io::Error as IoError; use thiserror::Error; @@ -11,15 +11,15 @@ use tracing_subscriber::EnvFilter; use std::collections::HashMap; use std::env; use std::fs::File; -use std::io::{self, stdout, Read, Write}; +use std::io::{self, Read, Write, stdout}; use std::path::{Path, PathBuf}; use std::str::FromStr; use getopts::{Matches, Options}; use crate::rustfmt::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, - FormatReportFormatterBuilder, Input, Session, Verbosity, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, + FormatReportFormatterBuilder, Input, Session, StyleEdition, Verbosity, Version, load_config, }; const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rustfmt/issues/new?labels=bug"; @@ -129,7 +129,12 @@ fn make_opts() -> Options { found reverts to the input file path", "[Path for the configuration file]", ); - opts.optopt("", "edition", "Rust edition to use", "[2015|2018|2021]"); + opts.optopt( + "", + "edition", + "Rust edition to use", + "[2015|2018|2021|2024]", + ); opts.optopt( "", "color", @@ -181,6 +186,12 @@ fn make_opts() -> Options { "skip-children", "Don't reformat child modules (unstable).", ); + opts.optopt( + "", + "style-edition", + "The edition of the Style Guide (unstable).", + "[2015|2018|2021|2024]", + ); } opts.optflag("v", "verbose", "Print verbose output"); @@ -263,24 +274,35 @@ fn format_string(input: String, options: GetOptsOptions) -> Result<i32> { let (mut config, _) = load_config(Some(Path::new(".")), Some(options.clone()))?; if options.check { - config.set().emit_mode(EmitMode::Diff); + config.set_cli().emit_mode(EmitMode::Diff); } else { match options.emit_mode { // Emit modes which work with standard input // None means default, which is Stdout. - None | Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => {} + None => { + config + .set() + .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); + } + Some(EmitMode::Stdout) | Some(EmitMode::Checkstyle) | Some(EmitMode::Json) => { + config + .set_cli() + .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); + } Some(emit_mode) => { return Err(OperationError::StdinBadEmit(emit_mode).into()); } } - config - .set() - .emit_mode(options.emit_mode.unwrap_or(EmitMode::Stdout)); } config.set().verbose(Verbosity::Quiet); // parse file_lines - config.set().file_lines(options.file_lines); + if options.file_lines.is_all() { + config.set().file_lines(options.file_lines); + } else { + config.set_cli().file_lines(options.file_lines); + } + for f in config.file_lines().files() { match *f { FileName::Stdin => {} @@ -319,10 +341,10 @@ fn format( for file in files { if !file.exists() { - eprintln!("Error: file `{}` does not exist", file.to_str().unwrap()); + eprintln!("Error: file `{}` does not exist", file.display()); session.add_operational_error(); } else if file.is_dir() { - eprintln!("Error: `{}` is a directory", file.to_str().unwrap()); + eprintln!("Error: `{}` is a directory", file.display()); session.add_operational_error(); } else { // Check the file directory if the config-path could not be read or not provided @@ -428,27 +450,27 @@ are included as out of line modules from `src/lib.rs`." } fn print_version() { - let version_info = format!( - "{}-{}", - option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"), - include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")) - ); + let version_number = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); + let commit_info = include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt")); - println!("rustfmt {version_info}"); + if commit_info.is_empty() { + println!("rustfmt {version_number}"); + } else { + println!("rustfmt {version_number}-{commit_info}"); + } } fn determine_operation(matches: &Matches) -> Result<Operation, OperationError> { if matches.opt_present("h") { - let topic = matches.opt_str("h"); - if topic.is_none() { + let Some(topic) = matches.opt_str("h") else { return Ok(Operation::Help(HelpOp::None)); - } else if topic == Some("config".to_owned()) { - return Ok(Operation::Help(HelpOp::Config)); - } else if topic == Some("file-lines".to_owned()) && is_nightly() { - return Ok(Operation::Help(HelpOp::FileLines)); - } else { - return Err(OperationError::UnknownHelpTopic(topic.unwrap())); - } + }; + + return match topic.as_str() { + "config" => Ok(Operation::Help(HelpOp::Config)), + "file-lines" if is_nightly() => Ok(Operation::Help(HelpOp::FileLines)), + _ => Err(OperationError::UnknownHelpTopic(topic)), + }; } let mut free_matches = matches.free.iter(); @@ -514,6 +536,7 @@ struct GetOptsOptions { backup: bool, check: bool, edition: Option<Edition>, + style_edition: Option<StyleEdition>, color: Option<Color>, file_lines: FileLines, // Default is all lines in all files. unstable_features: bool, @@ -545,6 +568,10 @@ impl GetOptsOptions { if let Some(ref file_lines) = matches.opt_str("file-lines") { options.file_lines = file_lines.parse()?; } + if let Some(ref edition_str) = matches.opt_str("style-edition") { + options.style_edition = + Some(style_edition_from_style_edition_str(edition_str)?); + } } else { let mut unstable_options = vec![]; if matches.opt_present("skip-children") { @@ -556,6 +583,9 @@ impl GetOptsOptions { if matches.opt_present("file-lines") { unstable_options.push("`--file-lines`"); } + if matches.opt_present("style-edition") { + unstable_options.push("`--style-edition`"); + } if !unstable_options.is_empty() { let s = if unstable_options.len() == 1 { "" } else { "s" }; return Err(format_err!( @@ -650,36 +680,49 @@ impl GetOptsOptions { impl CliOptions for GetOptsOptions { fn apply_to(self, config: &mut Config) { if self.verbose { - config.set().verbose(Verbosity::Verbose); + config.set_cli().verbose(Verbosity::Verbose); } else if self.quiet { - config.set().verbose(Verbosity::Quiet); + config.set_cli().verbose(Verbosity::Quiet); } else { config.set().verbose(Verbosity::Normal); } - config.set().file_lines(self.file_lines); - config.set().unstable_features(self.unstable_features); + + if self.file_lines.is_all() { + config.set().file_lines(self.file_lines); + } else { + config.set_cli().file_lines(self.file_lines); + } + + if self.unstable_features { + config.set_cli().unstable_features(self.unstable_features); + } else { + config.set().unstable_features(self.unstable_features); + } if let Some(skip_children) = self.skip_children { - config.set().skip_children(skip_children); + config.set_cli().skip_children(skip_children); } if let Some(error_on_unformatted) = self.error_on_unformatted { - config.set().error_on_unformatted(error_on_unformatted); + config.set_cli().error_on_unformatted(error_on_unformatted); } if let Some(edition) = self.edition { - config.set().edition(edition); + config.set_cli().edition(edition); + } + if let Some(edition) = self.style_edition { + config.set_cli().style_edition(edition); } if self.check { - config.set().emit_mode(EmitMode::Diff); + config.set_cli().emit_mode(EmitMode::Diff); } else if let Some(emit_mode) = self.emit_mode { - config.set().emit_mode(emit_mode); + config.set_cli().emit_mode(emit_mode); } if self.backup { - config.set().make_backup(true); + config.set_cli().make_backup(true); } if let Some(color) = self.color { - config.set().color(color); + config.set_cli().color(color); } if self.print_misformatted_file_names { - config.set().print_misformatted_file_names(true); + config.set_cli().print_misformatted_file_names(true); } for (key, val) in self.inline_config { @@ -690,6 +733,25 @@ impl CliOptions for GetOptsOptions { fn config_path(&self) -> Option<&Path> { self.config_path.as_deref() } + + fn edition(&self) -> Option<Edition> { + self.inline_config + .get("edition") + .map_or(self.edition, |e| Edition::from_str(e).ok()) + } + + fn style_edition(&self) -> Option<StyleEdition> { + self.inline_config + .get("style_edition") + .map_or(self.style_edition, |se| StyleEdition::from_str(se).ok()) + } + + fn version(&self) -> Option<Version> { + self.inline_config + .get("version") + .map(|version| Version::from_str(version).ok()) + .flatten() + } } fn edition_from_edition_str(edition_str: &str) -> Result<Edition> { @@ -702,6 +764,16 @@ fn edition_from_edition_str(edition_str: &str) -> Result<Edition> { } } +fn style_edition_from_style_edition_str(edition_str: &str) -> Result<StyleEdition> { + match edition_str { + "2015" => Ok(StyleEdition::Edition2015), + "2018" => Ok(StyleEdition::Edition2018), + "2021" => Ok(StyleEdition::Edition2021), + "2024" => Ok(StyleEdition::Edition2024), + _ => Err(format_err!("Invalid value for `--style-edition`")), + } +} + fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> { match emit_str { "files" => Ok(EmitMode::Files), @@ -712,3 +784,185 @@ fn emit_mode_from_emit_str(emit_str: &str) -> Result<EmitMode> { _ => Err(format_err!("Invalid value for `--emit`")), } } + +#[cfg(test)] +#[allow(dead_code)] +mod test { + use super::*; + use rustfmt_config_proc_macro::nightly_only_test; + + fn get_config<O: CliOptions>(path: Option<&Path>, options: Option<O>) -> Config { + load_config(path, options).unwrap().0 + } + + #[nightly_only_test] + #[test] + fn flag_sets_style_edition_override_correctly() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2024); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + } + + #[nightly_only_test] + #[test] + fn edition_sets_style_edition_override_correctly() { + let mut options = GetOptsOptions::default(); + options.edition = Some(Edition::Edition2024); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + } + + #[nightly_only_test] + #[test] + fn version_sets_style_edition_override_correctly() { + let mut options = GetOptsOptions::default(); + options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn version_config_file_sets_style_edition_override_correctly() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/just-version")); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn style_edition_flag_has_correct_precedence_over_edition() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2021); + options.edition = Some(Edition::Edition2024); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2021); + } + + #[nightly_only_test] + #[test] + fn style_edition_flag_has_correct_precedence_over_version() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2018); + options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2018); + } + + #[nightly_only_test] + #[test] + fn style_edition_flag_has_correct_precedence_over_edition_version() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2021); + options.edition = Some(Edition::Edition2018); + options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2021); + } + + #[nightly_only_test] + #[test] + fn style_edition_inline_has_correct_precedence_over_edition_version() { + let mut options = GetOptsOptions::default(); + options.edition = Some(Edition::Edition2018); + options.inline_config = HashMap::from([ + ("version".to_owned(), "One".to_owned()), + ("style_edition".to_owned(), "2024".to_owned()), + ]); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn style_edition_config_file_trumps_edition_flag_version_inline() { + let mut options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/just-style-edition")); + options.edition = Some(Edition::Edition2018); + options.inline_config = HashMap::from([("version".to_owned(), "One".to_owned())]); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + } + + #[nightly_only_test] + #[test] + fn style_edition_config_file_trumps_edition_config_and_version_inline() { + let mut options = GetOptsOptions::default(); + let config_file = Some(Path::new( + "tests/config/style-edition/style-edition-and-edition", + )); + options.inline_config = HashMap::from([("version".to_owned(), "Two".to_owned())]); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2021); + assert_eq!(config.edition(), Edition::Edition2024); + } + + #[nightly_only_test] + #[test] + fn version_config_trumps_edition_config_and_flag() { + let mut options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/version-edition")); + options.edition = Some(Edition::Edition2018); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + } + + #[nightly_only_test] + #[test] + fn style_edition_config_file_trumps_version_config() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new( + "tests/config/style-edition/version-style-edition", + )); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2021); + } + + #[nightly_only_test] + #[test] + fn style_edition_config_file_trumps_edition_version_config() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new( + "tests/config/style-edition/version-style-edition-and-edition", + )); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2021); + } + + #[nightly_only_test] + #[test] + fn correct_defaults_for_style_edition_loaded() { + let mut options = GetOptsOptions::default(); + options.style_edition = Some(StyleEdition::Edition2024); + let config = get_config(None, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), true); + } + + #[nightly_only_test] + #[test] + fn style_edition_defaults_overridden_from_config() { + let options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/overrides")); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), false); + } + + #[nightly_only_test] + #[test] + fn style_edition_defaults_overridden_from_cli() { + let mut options = GetOptsOptions::default(); + let config_file = Some(Path::new("tests/config/style-edition/just-style-edition")); + options.inline_config = + HashMap::from([("overflow_delimited_expr".to_owned(), "false".to_owned())]); + let config = get_config(config_file, Some(options)); + assert_eq!(config.style_edition(), StyleEdition::Edition2024); + assert_eq!(config.overflow_delimited_expr(), false); + } +} diff --git a/src/tools/rustfmt/src/chains.rs b/src/tools/rustfmt/src/chains.rs index 96fbe7a963a..fd2ef9cb1db 100644 --- a/src/tools/rustfmt/src/chains.rs +++ b/src/tools/rustfmt/src/chains.rs @@ -59,15 +59,15 @@ use std::borrow::Cow; use std::cmp::min; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span}; +use rustc_span::{BytePos, Span, symbol}; use tracing::debug; -use crate::comment::{rewrite_comment, CharClasses, FullCodeCharKind, RichChar}; -use crate::config::{IndentStyle, Version}; +use crate::comment::{CharClasses, FullCodeCharKind, RichChar, rewrite_comment}; +use crate::config::{IndentStyle, StyleEdition}; use crate::expr::rewrite_call; use crate::lists::extract_pre_comment; use crate::macros::convert_try_mac; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::utils::{ @@ -80,6 +80,9 @@ use thin_vec::ThinVec; /// Provides the original input contents from the span /// of a chain element with trailing spaces trimmed. fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> { + // TODO(ding-young): Currently returning None when the given span is out of the range + // covered by the snippet provider. If this is a common cause for internal + // rewrite failure, add a new enum variant and return RewriteError instead of None context.snippet_provider.span_to_snippet(span).map(|s| { s.lines() .map(|l| l.trim_end()) @@ -93,12 +96,16 @@ fn format_chain_item( context: &RewriteContext<'_>, rewrite_shape: Shape, allow_overflow: bool, -) -> Option<String> { +) -> RewriteResult { if allow_overflow { - item.rewrite(context, rewrite_shape) - .or_else(|| format_overflow_style(item.span, context)) + // TODO(ding-young): Consider calling format_overflow_style() + // only when item.rewrite_result() returns RewriteError::ExceedsMaxWidth. + // It may be inappropriate to call format_overflow_style on other RewriteError + // since the current approach retries formatting if allow_overflow is true + item.rewrite_result(context, rewrite_shape) + .or_else(|_| format_overflow_style(item.span, context).unknown_error()) } else { - item.rewrite(context, rewrite_shape) + item.rewrite_result(context, rewrite_shape) } } @@ -135,17 +142,17 @@ pub(crate) fn rewrite_chain( expr: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let chain = Chain::from_ast(expr, context); debug!("rewrite_chain {:?} {:?}", chain, shape); // If this is just an expression with some `?`s, then format it trivially and // return early. if chain.children.is_empty() { - return chain.parent.rewrite(context, shape); + return chain.parent.rewrite_result(context, shape); } - chain.rewrite(context, shape) + chain.rewrite_result(context, shape) } #[derive(Debug)] @@ -203,7 +210,7 @@ impl ChainItemKind { fn is_tup_field_access(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Field(_, ref field) => { - field.name.to_string().chars().all(|c| c.is_digit(10)) + field.name.as_str().chars().all(|c| c.is_digit(10)) } _ => false, } @@ -269,7 +276,13 @@ impl ChainItemKind { impl Rewrite for ChainItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let shape = shape.sub_width(self.tries)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let shape = shape + .sub_width(self.tries) + .max_width_error(shape.width, self.span)?; let rewrite = match self.kind { ChainItemKind::Parent { ref expr, @@ -278,14 +291,14 @@ impl Rewrite for ChainItem { ChainItemKind::Parent { ref expr, parens: false, - } => expr.rewrite(context, shape)?, + } => expr.rewrite_result(context, shape)?, ChainItemKind::MethodCall(ref segment, ref types, ref exprs) => { Self::rewrite_method_call(segment.ident, types, exprs, self.span, context, shape)? } ChainItemKind::StructField(ident) => format!(".{}", rewrite_ident(context, ident)), ChainItemKind::TupleField(ident, nested) => format!( "{}.{}", - if nested && context.config.version() == Version::One { + if nested && context.config.style_edition() <= StyleEdition::Edition2021 { " " } else { "" @@ -297,7 +310,7 @@ impl Rewrite for ChainItem { rewrite_comment(comment, false, shape, context.config)? } }; - Some(format!("{rewrite}{}", "?".repeat(self.tries))) + Ok(format!("{rewrite}{}", "?".repeat(self.tries))) } } @@ -327,14 +340,14 @@ impl ChainItem { span: Span, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<String> { + ) -> RewriteResult { let type_str = if types.is_empty() { String::new() } else { let type_list = types .iter() - .map(|ty| ty.rewrite(context, shape)) - .collect::<Option<Vec<_>>>()?; + .map(|ty| ty.rewrite_result(context, shape)) + .collect::<Result<Vec<_>, RewriteError>>()?; format!("::<{}>", type_list.join(", ")) }; @@ -519,6 +532,10 @@ impl Chain { impl Rewrite for Chain { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("rewrite chain {:?} {:?}", self, shape); let mut formatter = match context.config.indent_style() { @@ -532,17 +549,25 @@ impl Rewrite for Chain { formatter.format_root(&self.parent, context, shape)?; if let Some(result) = formatter.pure_root() { - return wrap_str(result, context.config.max_width(), shape); + return wrap_str(result, context.config.max_width(), shape) + .max_width_error(shape.width, self.parent.span); } + let first = self.children.first().unwrap_or(&self.parent); + let last = self.children.last().unwrap_or(&self.parent); + let children_span = mk_sp(first.span.lo(), last.span.hi()); + let full_span = self.parent.span.with_hi(children_span.hi()); + // Decide how to layout the rest of the chain. - let child_shape = formatter.child_shape(context, shape)?; + let child_shape = formatter + .child_shape(context, shape) + .max_width_error(shape.width, children_span)?; formatter.format_children(context, child_shape)?; formatter.format_last_child(context, shape, child_shape)?; let result = formatter.join_rewrites(context, child_shape)?; - wrap_str(result, context.config.max_width(), shape) + wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span) } } @@ -564,16 +589,20 @@ trait ChainFormatter { parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()>; + ) -> Result<(), RewriteError>; fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape>; - fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()>; + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError>; fn format_last_child( &mut self, context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()>; - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String>; + ) -> Result<(), RewriteError>; + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult; // Returns `Some` if the chain is only a root, None otherwise. fn pure_root(&mut self) -> Option<String>; } @@ -616,12 +645,16 @@ impl<'a> ChainFormatterShared<'a> { } } - fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError> { for item in &self.children[..self.children.len() - 1] { let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?; self.rewrites.push(rewrite); } - Some(()) + Ok(()) } // Rewrite the last child. The last child of a chain requires special treatment. We need to @@ -662,8 +695,8 @@ impl<'a> ChainFormatterShared<'a> { context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { - let last = self.children.last()?; + ) -> Result<(), RewriteError> { + let last = self.children.last().unknown_error()?; let extendable = may_extend && last_line_extendable(&self.rewrites[0]); let prev_last_line_width = last_line_width(&self.rewrites[0]); @@ -687,11 +720,17 @@ impl<'a> ChainFormatterShared<'a> { && self.rewrites.iter().all(|s| !s.contains('\n')) && one_line_budget > 0; let last_shape = if all_in_one_line { - shape.sub_width(last.tries)? + shape + .sub_width(last.tries) + .max_width_error(shape.width, last.span)? } else if extendable { - child_shape.sub_width(last.tries)? + child_shape + .sub_width(last.tries) + .max_width_error(child_shape.width, last.span)? } else { - child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? + child_shape + .sub_width(shape.rhs_overhead(context.config) + last.tries) + .max_width_error(child_shape.width, last.span)? }; let mut last_subexpr_str = None; @@ -707,7 +746,7 @@ impl<'a> ChainFormatterShared<'a> { }; if let Some(one_line_shape) = one_line_shape { - if let Some(rw) = last.rewrite(context, one_line_shape) { + if let Ok(rw) = last.rewrite_result(context, one_line_shape) { // We allow overflowing here only if both of the following conditions match: // 1. The entire chain fits in a single line except the last child. // 2. `last_child_str.lines().count() >= 5`. @@ -722,17 +761,18 @@ impl<'a> ChainFormatterShared<'a> { // last child on its own line, and compare two rewrites to choose which is // better. let last_shape = child_shape - .sub_width(shape.rhs_overhead(context.config) + last.tries)?; - match last.rewrite(context, last_shape) { - Some(ref new_rw) if !could_fit_single_line => { + .sub_width(shape.rhs_overhead(context.config) + last.tries) + .max_width_error(child_shape.width, last.span)?; + match last.rewrite_result(context, last_shape) { + Ok(ref new_rw) if !could_fit_single_line => { last_subexpr_str = Some(new_rw.clone()); } - Some(ref new_rw) if new_rw.lines().count() >= line_count => { + Ok(ref new_rw) if new_rw.lines().count() >= line_count => { last_subexpr_str = Some(rw); self.fits_single_line = could_fit_single_line && all_in_one_line; } - new_rw @ Some(..) => { - last_subexpr_str = new_rw; + Ok(new_rw) => { + last_subexpr_str = Some(new_rw); } _ => { last_subexpr_str = Some(rw); @@ -747,22 +787,28 @@ impl<'a> ChainFormatterShared<'a> { let last_shape = if context.use_block_indent() { last_shape } else { - child_shape.sub_width(shape.rhs_overhead(context.config) + last.tries)? + child_shape + .sub_width(shape.rhs_overhead(context.config) + last.tries) + .max_width_error(child_shape.width, last.span)? }; - last_subexpr_str = last_subexpr_str.or_else(|| last.rewrite(context, last_shape)); - self.rewrites.push(last_subexpr_str?); - Some(()) + let last_subexpr_str = + last_subexpr_str.unwrap_or(last.rewrite_result(context, last_shape)?); + self.rewrites.push(last_subexpr_str); + Ok(()) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { let connector = if self.fits_single_line { // Yay, we can put everything on one line. Cow::from("") } else { // Use new lines. if context.force_one_line_chain.get() { - return None; + return Err(RewriteError::ExceedsMaxWidth { + configured_width: child_shape.width, + span: self.children.last().unknown_error()?.span, + }); } child_shape.to_string_with_newline(context.config) }; @@ -781,7 +827,7 @@ impl<'a> ChainFormatterShared<'a> { result.push_str(rewrite); } - Some(result) + Ok(result) } } @@ -806,8 +852,8 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()> { - let mut root_rewrite: String = parent.rewrite(context, shape)?; + ) -> Result<(), RewriteError> { + let mut root_rewrite: String = parent.rewrite_result(context, shape)?; let mut root_ends_with_block = parent.kind.is_block_like(context, &root_rewrite); let tab_width = context.config.tab_spaces().saturating_sub(shape.offset); @@ -817,10 +863,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { if let ChainItemKind::Comment(..) = item.kind { break; } - let shape = shape.offset_left(root_rewrite.len())?; - match &item.rewrite(context, shape) { - Some(rewrite) => root_rewrite.push_str(rewrite), - None => break, + let shape = shape + .offset_left(root_rewrite.len()) + .max_width_error(shape.width, item.span)?; + match &item.rewrite_result(context, shape) { + Ok(rewrite) => root_rewrite.push_str(rewrite), + Err(_) => break, } root_ends_with_block = last_line_extendable(&root_rewrite); @@ -832,7 +880,7 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { } self.shared.rewrites.push(root_rewrite); self.root_ends_with_block = root_ends_with_block; - Some(()) + Ok(()) } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> { @@ -840,7 +888,11 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { Some(get_block_child_shape(block_end, context, shape)) } - fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError> { self.shared.format_children(context, child_shape) } @@ -849,12 +901,12 @@ impl<'a> ChainFormatter for ChainFormatterBlock<'a> { context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { self.shared .format_last_child(true, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { self.shared.join_rewrites(context, child_shape) } @@ -885,9 +937,9 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { parent: &ChainItem, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { let parent_shape = shape.visual_indent(0); - let mut root_rewrite = parent.rewrite(context, parent_shape)?; + let mut root_rewrite = parent.rewrite_result(context, parent_shape)?; let multiline = root_rewrite.contains('\n'); self.offset = if multiline { last_line_width(&root_rewrite).saturating_sub(shape.used_width()) @@ -899,18 +951,19 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { let item = &self.shared.children[0]; if let ChainItemKind::Comment(..) = item.kind { self.shared.rewrites.push(root_rewrite); - return Some(()); + return Ok(()); } let child_shape = parent_shape .visual_indent(self.offset) - .sub_width(self.offset)?; - let rewrite = item.rewrite(context, child_shape)?; + .sub_width(self.offset) + .max_width_error(parent_shape.width, item.span)?; + let rewrite = item.rewrite_result(context, child_shape)?; if filtered_str_fits(&rewrite, context.config.max_width(), shape) { root_rewrite.push_str(&rewrite); } else { // We couldn't fit in at the visual indent, try the last // indent. - let rewrite = item.rewrite(context, parent_shape)?; + let rewrite = item.rewrite_result(context, parent_shape)?; root_rewrite.push_str(&rewrite); self.offset = 0; } @@ -919,7 +972,7 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { } self.shared.rewrites.push(root_rewrite); - Some(()) + Ok(()) } fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> { @@ -932,7 +985,11 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { ) } - fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> { + fn format_children( + &mut self, + context: &RewriteContext<'_>, + child_shape: Shape, + ) -> Result<(), RewriteError> { self.shared.format_children(context, child_shape) } @@ -941,12 +998,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { context: &RewriteContext<'_>, shape: Shape, child_shape: Shape, - ) -> Option<()> { + ) -> Result<(), RewriteError> { self.shared .format_last_child(false, context, shape, child_shape) } - fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<String> { + fn join_rewrites(&self, context: &RewriteContext<'_>, child_shape: Shape) -> RewriteResult { self.shared.join_rewrites(context, child_shape) } diff --git a/src/tools/rustfmt/src/closures.rs b/src/tools/rustfmt/src/closures.rs index b5c26235e77..a37b47e3bc9 100644 --- a/src/tools/rustfmt/src/closures.rs +++ b/src/tools/rustfmt/src/closures.rs @@ -4,17 +4,17 @@ use thin_vec::thin_vec; use tracing::debug; use crate::attr::get_attrs_from_stmt; +use crate::config::StyleEdition; use crate::config::lists::*; -use crate::config::Version; use crate::expr::{block_contains_comment, is_simple_block, is_unsafe_block, rewrite_cond}; use crate::items::{span_hi_for_param, span_lo_for_param}; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; use crate::overflow::OverflowableItem; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::types::rewrite_bound_params; -use crate::utils::{last_line_width, left_most_sub_expr, stmt_expr, NodeIdExt}; +use crate::utils::{NodeIdExt, last_line_width, left_most_sub_expr, stmt_expr}; // This module is pretty messy because of the rules around closures and blocks: // FIXME - the below is probably no longer true in full. @@ -37,7 +37,7 @@ pub(crate) fn rewrite_closure( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_closure {:?}", body); let (prefix, extra_offset) = rewrite_closure_fn_decl( @@ -53,13 +53,15 @@ pub(crate) fn rewrite_closure( shape, )?; // 1 = space between `|...|` and body. - let body_shape = shape.offset_left(extra_offset)?; + let body_shape = shape + .offset_left(extra_offset) + .max_width_error(shape.width, span)?; if let ast::ExprKind::Block(ref block, _) = body.kind { // The body of the closure is an empty block. if block.stmts.is_empty() && !block_contains_comment(context, block) { return body - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{} {}", prefix, s)); } @@ -67,15 +69,15 @@ pub(crate) fn rewrite_closure( ast::FnRetTy::Default(_) if !context.inside_macro() => { try_rewrite_without_block(body, &prefix, context, shape, body_shape) } - _ => None, + _ => Err(RewriteError::Unknown), }; - result.or_else(|| { + result.or_else(|_| { // Either we require a block, or tried without and failed. rewrite_closure_block(block, &prefix, context, body_shape) }) } else { - rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|| { + rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|_| { // The closure originally had a non-block expression, but we can't fit on // one line, so we'll insert a block. rewrite_closure_with_block(body, &prefix, context, body_shape) @@ -89,7 +91,7 @@ fn try_rewrite_without_block( context: &RewriteContext<'_>, shape: Shape, body_shape: Shape, -) -> Option<String> { +) -> RewriteResult { let expr = get_inner_expr(expr, prefix, context); if is_block_closure_forced(context, expr) { @@ -153,11 +155,11 @@ fn rewrite_closure_with_block( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let left_most = left_most_sub_expr(body); let veto_block = veto_block(body) && !expr_requires_semi_to_be_stmt(left_most); if veto_block { - return None; + return Err(RewriteError::Unknown); } let block = ast::Block { @@ -185,7 +187,7 @@ fn rewrite_closure_with_block( shape, false, )?; - Some(format!("{prefix} {block}")) + Ok(format!("{prefix} {block}")) } // Rewrite closure with a single expression without wrapping its body with block. @@ -194,7 +196,7 @@ fn rewrite_closure_expr( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { fn allow_multi_line(expr: &ast::Expr) -> bool { match expr.kind { ast::ExprKind::Match(..) @@ -217,12 +219,12 @@ fn rewrite_closure_expr( // unless it is a block-like expression or we are inside macro call. let veto_multiline = (!allow_multi_line(expr) && !context.inside_macro()) || context.config.force_multiline_blocks(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .and_then(|rw| { if veto_multiline && rw.contains('\n') { - None + Err(RewriteError::Unknown) } else { - Some(rw) + Ok(rw) } }) .map(|rw| format!("{} {}", prefix, rw)) @@ -234,8 +236,12 @@ fn rewrite_closure_block( prefix: &str, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - Some(format!("{} {}", prefix, block.rewrite(context, shape)?)) +) -> RewriteResult { + Ok(format!( + "{} {}", + prefix, + block.rewrite_result(context, shape)? + )) } // Return type is (prefix, extra_offset) @@ -250,13 +256,14 @@ fn rewrite_closure_fn_decl( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<(String, usize)> { +) -> Result<(String, usize), RewriteError> { let binder = match binder { ast::ClosureBinder::For { generic_params, .. } if generic_params.is_empty() => { "for<> ".to_owned() } ast::ClosureBinder::For { generic_params, .. } => { - let lifetime_str = rewrite_bound_params(context, shape, generic_params)?; + let lifetime_str = + rewrite_bound_params(context, shape, generic_params).unknown_error()?; format!("for<{lifetime_str}> ") } ast::ClosureBinder::NotPresent => "".to_owned(), @@ -287,13 +294,17 @@ fn rewrite_closure_fn_decl( // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. let nested_shape = shape - .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())? - .sub_width(4)?; + .shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len()) + .and_then(|shape| shape.sub_width(4)) + .max_width_error(shape.width, span)?; // 1 = | let param_offset = nested_shape.indent + 1; - let param_shape = nested_shape.offset_left(1)?.visual_indent(0); - let ret_str = fn_decl.output.rewrite(context, param_shape)?; + let param_shape = nested_shape + .offset_left(1) + .max_width_error(nested_shape.width, span)? + .visual_indent(0); + let ret_str = fn_decl.output.rewrite_result(context, param_shape)?; let param_items = itemize_list( context.snippet_provider, @@ -302,7 +313,7 @@ fn rewrite_closure_fn_decl( ",", |param| span_lo_for_param(param), |param| span_hi_for_param(context, param), - |param| param.rewrite(context, param_shape), + |param| param.rewrite_result(context, param_shape), context.snippet_provider.span_after(span, "|"), body.span.lo(), false, @@ -317,7 +328,9 @@ fn rewrite_closure_fn_decl( horizontal_budget, ); let param_shape = match tactic { - DefinitiveListTactic::Horizontal => param_shape.sub_width(ret_str.len() + 1)?, + DefinitiveListTactic::Horizontal => param_shape + .sub_width(ret_str.len() + 1) + .max_width_error(param_shape.width, span)?, _ => param_shape, }; @@ -339,7 +352,7 @@ fn rewrite_closure_fn_decl( // 1 = space between `|...|` and body. let extra_offset = last_line_width(&prefix) + 1; - Some((prefix, extra_offset)) + Ok((prefix, extra_offset)) } // Rewriting closure which is placed at the end of the function call's arg. @@ -348,7 +361,7 @@ pub(crate) fn rewrite_last_closure( context: &RewriteContext<'_>, expr: &ast::Expr, shape: Shape, -) -> Option<String> { +) -> RewriteResult { if let ast::ExprKind::Closure(ref closure) = expr.kind { let ast::Closure { ref binder, @@ -385,10 +398,12 @@ pub(crate) fn rewrite_last_closure( )?; // If the closure goes multi line before its body, do not overflow the closure. if prefix.contains('\n') { - return None; + return Err(RewriteError::Unknown); } - let body_shape = shape.offset_left(extra_offset)?; + let body_shape = shape + .offset_left(extra_offset) + .max_width_error(shape.width, expr.span)?; // We force to use block for the body of the closure for certain kinds of expressions. if is_block_closure_forced(context, body) { @@ -400,7 +415,7 @@ pub(crate) fn rewrite_last_closure( // closure. However, if the closure has a return type, then we must // keep the blocks. match rewrite_closure_expr(body, &prefix, context, shape) { - Some(single_line_body_str) + Ok(single_line_body_str) if !single_line_body_str.contains('\n') => { single_line_body_str @@ -424,9 +439,9 @@ pub(crate) fn rewrite_last_closure( } // Seems fine, just format the closure in usual manner. - return expr.rewrite(context, shape); + return expr.rewrite_result(context, shape); } - None + Err(RewriteError::Unknown) } /// Returns `true` if the given vector of arguments has more than one `ast::ExprKind::Closure`. @@ -443,18 +458,18 @@ fn is_block_closure_forced(context: &RewriteContext<'_>, expr: &ast::Expr) -> bo if context.inside_macro() { false } else { - is_block_closure_forced_inner(expr, context.config.version()) + is_block_closure_forced_inner(expr, context.config.style_edition()) } } -fn is_block_closure_forced_inner(expr: &ast::Expr, version: Version) -> bool { +fn is_block_closure_forced_inner(expr: &ast::Expr, style_edition: StyleEdition) -> bool { match expr.kind { ast::ExprKind::If(..) | ast::ExprKind::While(..) | ast::ExprKind::ForLoop { .. } => true, - ast::ExprKind::Loop(..) if version == Version::Two => true, + ast::ExprKind::Loop(..) if style_edition >= StyleEdition::Edition2024 => true, ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, version), + | ast::ExprKind::Cast(ref expr, _) => is_block_closure_forced_inner(expr, style_edition), _ => false, } } diff --git a/src/tools/rustfmt/src/comment.rs b/src/tools/rustfmt/src/comment.rs index e76be0fd162..b7d7a396a67 100644 --- a/src/tools/rustfmt/src/comment.rs +++ b/src/tools/rustfmt/src/comment.rs @@ -2,14 +2,14 @@ use std::{borrow::Cow, iter}; -use itertools::{multipeek, MultiPeek}; +use itertools::{Itertools as _, MultiPeek, multipeek}; use rustc_span::Span; use tracing::{debug, trace}; use crate::config::Config; -use crate::rewrite::RewriteContext; +use crate::rewrite::{RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; -use crate::string::{rewrite_string, StringFormat}; +use crate::string::{StringFormat, rewrite_string}; use crate::utils::{ count_newlines, first_line_width, last_line_width, trim_left_preserve_layout, trimmed_last_line_width, unicode_str_width, @@ -158,7 +158,7 @@ pub(crate) fn combine_strs_with_missing_comments( span: Span, shape: Shape, allow_extend: bool, -) -> Option<String> { +) -> RewriteResult { trace!( "combine_strs_with_missing_comments `{}` `{}` {:?} {:?}", prev_str, next_str, span, shape @@ -188,7 +188,7 @@ pub(crate) fn combine_strs_with_missing_comments( result.push_str(&indent.to_string_with_newline(config)) } result.push_str(next_str); - return Some(result); + return Ok(result); } // We have a missing comment between the first expression and the second expression. @@ -233,10 +233,10 @@ pub(crate) fn combine_strs_with_missing_comments( result.push_str(&second_sep); result.push_str(next_str); - Some(result) + Ok(result) } -pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> Option<String> { +pub(crate) fn rewrite_doc_comment(orig: &str, shape: Shape, config: &Config) -> RewriteResult { identify_comment(orig, false, shape, config, true) } @@ -245,7 +245,7 @@ pub(crate) fn rewrite_comment( block_style: bool, shape: Shape, config: &Config, -) -> Option<String> { +) -> RewriteResult { identify_comment(orig, block_style, shape, config, false) } @@ -255,7 +255,7 @@ fn identify_comment( shape: Shape, config: &Config, is_doc_comment: bool, -) -> Option<String> { +) -> RewriteResult { let style = comment_style(orig, false); // Computes the byte length of line taking into account a newline if the line is part of a @@ -347,7 +347,7 @@ fn identify_comment( let (first_group, rest) = orig.split_at(first_group_ending); let rewritten_first_group = if !config.normalize_comments() && has_bare_lines && style.is_block_comment() { - trim_left_preserve_layout(first_group, shape.indent, config)? + trim_left_preserve_layout(first_group, shape.indent, config).unknown_error()? } else if !config.normalize_comments() && !config.wrap_comments() && !( @@ -368,7 +368,7 @@ fn identify_comment( )? }; if rest.is_empty() { - Some(rewritten_first_group) + Ok(rewritten_first_group) } else { identify_comment( rest.trim_start(), @@ -534,10 +534,11 @@ impl ItemizedBlock { /// Returns the block as a string, with each line trimmed at the start. fn trimmed_block_as_string(&self) -> String { - self.lines - .iter() - .map(|line| format!("{} ", line.trim_start())) - .collect::<String>() + self.lines.iter().fold(String::new(), |mut acc, line| { + acc.push_str(line.trim_start()); + acc.push(' '); + acc + }) } /// Returns the block as a string under its original form. @@ -900,7 +901,7 @@ fn rewrite_comment_inner( shape: Shape, config: &Config, is_doc_comment: bool, -) -> Option<String> { +) -> RewriteResult { let mut rewriter = CommentRewrite::new(orig, block_style, shape, config); let line_breaks = count_newlines(orig.trim_end()); @@ -934,7 +935,7 @@ fn rewrite_comment_inner( } } - Some(rewriter.finish()) + Ok(rewriter.finish()) } const RUSTFMT_CUSTOM_COMMENT_PREFIX: &str = "//#### "; @@ -999,7 +1000,7 @@ pub(crate) fn rewrite_missing_comment( span: Span, shape: Shape, context: &RewriteContext<'_>, -) -> Option<String> { +) -> RewriteResult { let missing_snippet = context.snippet(span); let trimmed_snippet = missing_snippet.trim(); // check the span starts with a comment @@ -1007,7 +1008,7 @@ pub(crate) fn rewrite_missing_comment( if !trimmed_snippet.is_empty() && pos.is_some() { rewrite_comment(trimmed_snippet, false, shape, context.config) } else { - Some(String::new()) + Ok(String::new()) } } @@ -1019,13 +1020,13 @@ pub(crate) fn recover_missing_comment_in_span( shape: Shape, context: &RewriteContext<'_>, used_width: usize, -) -> Option<String> { +) -> RewriteResult { let missing_comment = rewrite_missing_comment(span, shape, context)?; if missing_comment.is_empty() { - Some(String::new()) + Ok(String::new()) } else { let missing_snippet = context.snippet(span); - let pos = missing_snippet.find('/')?; + let pos = missing_snippet.find('/').unknown_error()?; // 1 = ` ` let total_width = missing_comment.len() + used_width + 1; let force_new_line_before_comment = @@ -1035,7 +1036,7 @@ pub(crate) fn recover_missing_comment_in_span( } else { Cow::from(" ") }; - Some(format!("{sep}{missing_comment}")) + Ok(format!("{sep}{missing_comment}")) } } @@ -1055,8 +1056,7 @@ fn light_rewrite_comment( config: &Config, is_doc_comment: bool, ) -> String { - let lines: Vec<&str> = orig - .lines() + orig.lines() .map(|l| { // This is basically just l.trim(), but in the case that a line starts // with `*` we want to leave one space before it, so it aligns with the @@ -1074,8 +1074,7 @@ fn light_rewrite_comment( // Preserve markdown's double-space line break syntax in doc comment. trim_end_unless_two_whitespaces(left_trimmed, is_doc_comment) }) - .collect(); - lines.join(&format!("\n{}", offset.to_string(config))) + .join(&format!("\n{}", offset.to_string(config))) } /// Trims comment characters and possibly a single space from the left of a string. @@ -1704,12 +1703,11 @@ impl<'a> Iterator for CommentCodeSlices<'a> { } /// Checks is `new` didn't miss any comment from `span`, if it removed any, return previous text -/// (if it fits in the width/offset, else return `None`), else return `new` pub(crate) fn recover_comment_removed( new: String, span: Span, context: &RewriteContext<'_>, -) -> Option<String> { +) -> String { let snippet = context.snippet(span); if snippet != new && changed_comment_content(snippet, &new) { // We missed some comments. Warn and keep the original text. @@ -1723,9 +1721,9 @@ pub(crate) fn recover_comment_removed( )], ); } - Some(snippet.to_owned()) + snippet.to_owned() } else { - Some(new) + new } } diff --git a/src/tools/rustfmt/src/config/config_type.rs b/src/tools/rustfmt/src/config/config_type.rs index f7cff1a1729..14217caba0a 100644 --- a/src/tools/rustfmt/src/config/config_type.rs +++ b/src/tools/rustfmt/src/config/config_type.rs @@ -70,15 +70,15 @@ macro_rules! create_config { // // - $i: the ident name of the option // - $ty: the type of the option value - // - $def: the default value of the option // - $stb: true if the option is stable // - $dstring: description of the option - ($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( + ($($i:ident: $ty:ty, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => ( #[cfg(test)] use std::collections::HashSet; use std::io::Write; use serde::{Deserialize, Serialize}; + use $crate::config::style_edition::StyleEditionDefault; #[derive(Clone)] #[allow(unreachable_pub)] @@ -89,7 +89,10 @@ macro_rules! create_config { // - 1: true if the option was manually initialized // - 2: the option value // - 3: true if the option is unstable - $($i: (Cell<bool>, bool, $ty, bool)),+ + // - 4: true if the option was set manually from a CLI flag + // FIXME: 4 is probably unnecessary and duplicative + // https://github.com/rust-lang/rustfmt/issues/6252 + $($i: (Cell<bool>, bool, <$ty as StyleEditionDefault>::ConfigType, bool, bool)),+ } // Just like the Config struct but with each property wrapped @@ -100,7 +103,7 @@ macro_rules! create_config { #[derive(Deserialize, Serialize, Clone)] #[allow(unreachable_pub)] pub struct PartialConfig { - $(pub $i: Option<$ty>),+ + $(pub $i: Option<<$ty as StyleEditionDefault>::ConfigType>),+ } // Macro hygiene won't allow us to make `set_$i()` methods on Config @@ -114,7 +117,7 @@ macro_rules! create_config { impl<'a> ConfigSetter<'a> { $( #[allow(unreachable_pub)] - pub fn $i(&mut self, value: $ty) { + pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) { (self.0).$i.2 = value; match stringify!($i) { "max_width" @@ -130,6 +133,37 @@ macro_rules! create_config { "merge_imports" => self.0.set_merge_imports(), "fn_args_layout" => self.0.set_fn_args_layout(), "hide_parse_errors" => self.0.set_hide_parse_errors(), + "version" => self.0.set_version(), + &_ => (), + } + } + )+ + } + + #[allow(unreachable_pub)] + pub struct CliConfigSetter<'a>(&'a mut Config); + + impl<'a> CliConfigSetter<'a> { + $( + #[allow(unreachable_pub)] + pub fn $i(&mut self, value: <$ty as StyleEditionDefault>::ConfigType) { + (self.0).$i.2 = value; + (self.0).$i.4 = true; + match stringify!($i) { + "max_width" + | "use_small_heuristics" + | "fn_call_width" + | "single_line_if_else_max_width" + | "single_line_let_else_max_width" + | "attr_fn_like_width" + | "struct_lit_width" + | "struct_variant_width" + | "array_width" + | "chain_width" => self.0.set_heuristics(), + "merge_imports" => self.0.set_merge_imports(), + "fn_args_layout" => self.0.set_fn_args_layout(), + "hide_parse_errors" => self.0.set_hide_parse_errors(), + "version" => self.0.set_version(), &_ => (), } } @@ -150,25 +184,66 @@ macro_rules! create_config { )+ } + // Query each option, returns true if the user set the option via a CLI flag, + // false if a default was used. + #[allow(unreachable_pub)] + pub struct CliConfigWasSet<'a>(&'a Config); + + impl<'a> CliConfigWasSet<'a> { + $( + #[allow(unreachable_pub)] + pub fn $i(&self) -> bool { + (self.0).$i.4 + } + )+ + } + impl Config { $( #[allow(unreachable_pub)] - pub fn $i(&self) -> $ty { + pub fn $i(&self) -> <$ty as StyleEditionDefault>::ConfigType { self.$i.0.set(true); self.$i.2.clone() } )+ #[allow(unreachable_pub)] + pub(super) fn default_with_style_edition(style_edition: StyleEdition) -> Config { + Config { + $( + $i: ( + Cell::new(false), + false, + <$ty as StyleEditionDefault>::style_edition_default( + style_edition + ), + $stb, + false, + ), + )+ + } + } + + #[allow(unreachable_pub)] pub fn set(&mut self) -> ConfigSetter<'_> { ConfigSetter(self) } #[allow(unreachable_pub)] + pub fn set_cli(&mut self) -> CliConfigSetter<'_> { + CliConfigSetter(self) + } + + #[allow(unreachable_pub)] pub fn was_set(&self) -> ConfigWasSet<'_> { ConfigWasSet(self) } + #[allow(unreachable_pub)] + pub fn was_set_cli(&self) -> CliConfigWasSet<'_> { + CliConfigWasSet(self) + } + fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config { $( if let Some(option_value) = parsed.$i { @@ -186,6 +261,7 @@ macro_rules! create_config { self.set_merge_imports(); self.set_fn_args_layout(); self.set_hide_parse_errors(); + self.set_version(); self } @@ -212,7 +288,9 @@ macro_rules! create_config { pub fn is_valid_key_val(key: &str, val: &str) -> bool { match key { $( - stringify!($i) => val.parse::<$ty>().is_ok(), + stringify!($i) => { + val.parse::<<$ty as StyleEditionDefault>::ConfigType>().is_ok() + } )+ _ => false, } @@ -246,11 +324,15 @@ macro_rules! create_config { match key { $( stringify!($i) => { - let option_value = val.parse::<$ty>() - .expect(&format!("Failed to parse override for {} (\"{}\") as a {}", - stringify!($i), - val, - stringify!($ty))); + let value = val.parse::<<$ty as StyleEditionDefault>::ConfigType>() + .expect( + &format!( + "Failed to parse override for {} (\"{}\") as a {}", + stringify!($i), + val, + stringify!(<$ty as StyleEditionDefault>::ConfigType) + ) + ); // Users are currently allowed to set unstable // options/variants via the `--config` options override. @@ -261,7 +343,7 @@ macro_rules! create_config { // For now, do not validate whether the option or value is stable, // just always set it. self.$i.1 = true; - self.$i.2 = option_value; + self.$i.2 = value; } )+ _ => panic!("Unknown config key in override: {}", key) @@ -281,6 +363,7 @@ macro_rules! create_config { "merge_imports" => self.set_merge_imports(), "fn_args_layout" => self.set_fn_args_layout(), "hide_parse_errors" => self.set_hide_parse_errors(), + "version" => self.set_version(), &_ => (), } } @@ -301,6 +384,7 @@ macro_rules! create_config { #[allow(unreachable_pub)] pub fn print_docs(out: &mut dyn Write, include_unstable: bool) { + let style_edition = StyleEdition::Edition2015; use std::cmp; let max = 0; $( let max = cmp::max(max, stringify!($i).len()+1); )+ @@ -317,14 +401,17 @@ macro_rules! create_config { } name_out.push_str(name_raw); name_out.push(' '); - let mut default_str = format!("{}", $def); + let default_value = <$ty as StyleEditionDefault>::style_edition_default( + style_edition + ); + let mut default_str = format!("{}", default_value); if default_str.is_empty() { default_str = String::from("\"\""); } writeln!(out, "{}{} Default: {}{}", name_out, - <$ty>::doc_hint(), + <<$ty as StyleEditionDefault>::ConfigType>::doc_hint(), default_str, if !$stb { " (unstable)" } else { "" }).unwrap(); $( @@ -477,12 +564,36 @@ macro_rules! create_config { } } + fn set_version(&mut self) { + if !self.was_set().version() { + return; + } + + eprintln!( + "Warning: the `version` option is deprecated. \ + Use `style_edition` instead." + ); + + if self.was_set().style_edition() || self.was_set_cli().style_edition() { + eprintln!( + "Warning: the deprecated `version` option was \ + used in conjunction with the `style_edition` \ + option which takes precedence. \ + The value of the `version` option will be ignored." + ); + } + } + #[allow(unreachable_pub)] /// Returns `true` if the config key was explicitly set and is the default value. pub fn is_default(&self, key: &str) -> bool { + let style_edition = StyleEdition::Edition2015; $( + let default_value = <$ty as StyleEditionDefault>::style_edition_default( + style_edition + ); if let stringify!($i) = key { - return self.$i.1 && self.$i.2 == $def; + return self.$i.1 && self.$i.2 == default_value; } )+ false @@ -492,11 +603,7 @@ macro_rules! create_config { // Template for the default configuration impl Default for Config { fn default() -> Config { - Config { - $( - $i: (Cell::new(false), false, $def, $stb), - )+ - } + Config::default_with_style_edition(StyleEdition::Edition2015) } } ) diff --git a/src/tools/rustfmt/src/config/file_lines.rs b/src/tools/rustfmt/src/config/file_lines.rs index 224864393d3..c53ec6371e9 100644 --- a/src/tools/rustfmt/src/config/file_lines.rs +++ b/src/tools/rustfmt/src/config/file_lines.rs @@ -7,7 +7,7 @@ use std::{cmp, fmt, iter, str}; use rustc_data_structures::sync::Lrc; use rustc_span::SourceFile; -use serde::{ser, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer, ser}; use serde_json as json; use thiserror::Error; @@ -38,7 +38,7 @@ impl From<rustc_span::FileName> for FileName { impl fmt::Display for FileName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FileName::Real(p) => write!(f, "{}", p.to_str().unwrap()), + FileName::Real(p) => write!(f, "{}", p.display()), FileName::Stdin => write!(f, "<stdin>"), } } @@ -201,7 +201,7 @@ impl FileLines { } /// Returns `true` if this `FileLines` contains all lines in all files. - pub(crate) fn is_all(&self) -> bool { + pub fn is_all(&self) -> bool { self.0.is_none() } diff --git a/src/tools/rustfmt/src/config/mod.rs b/src/tools/rustfmt/src/config/mod.rs index 9484b2e5829..ea257c0a8ba 100644 --- a/src/tools/rustfmt/src/config/mod.rs +++ b/src/tools/rustfmt/src/config/mod.rs @@ -10,9 +10,7 @@ use crate::config::config_type::ConfigType; #[allow(unreachable_pub)] pub use crate::config::file_lines::{FileLines, FileName, Range}; #[allow(unreachable_pub)] -pub use crate::config::lists::*; -#[allow(unreachable_pub)] -pub use crate::config::macro_names::{MacroSelector, MacroSelectors}; +pub use crate::config::macro_names::MacroSelector; #[allow(unreachable_pub)] pub use crate::config::options::*; @@ -34,161 +32,169 @@ pub(crate) mod style_edition; // `name: value type, default value, is stable, description;` create_config! { // Fundamental stuff - max_width: usize, 100, true, "Maximum width of each line"; - hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment"; - tab_spaces: usize, 4, true, "Number of spaces per tab"; - newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings"; - indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; + max_width: MaxWidth, true, "Maximum width of each line"; + hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment"; + tab_spaces: TabSpaces, true, "Number of spaces per tab"; + newline_style: NewlineStyleConfig, true, "Unix or Windows line endings"; + indent_style: IndentStyleConfig, false, "How do we indent expressions or items"; // Width Heuristics - use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \ + use_small_heuristics: UseSmallHeuristics, true, "Whether to use different \ formatting for items and expressions if they satisfy a heuristic notion of 'small'"; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; - fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values"; + fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \ falling back to vertical formatting."; - attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \ attributes before falling back to vertical formatting."; - struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ - falling back to vertical formatting."; - struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \ + struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \ falling back to vertical formatting."; - array_width: usize, 60, true, "Maximum width of an array literal before falling \ + struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \ + before falling back to vertical formatting."; + array_width: ArrayWidth, true, "Maximum width of an array literal before falling \ back to vertical formatting."; - chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; - single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \ - expressions. A value of zero means always break if-else expressions."; - single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ - let-else statements. A value of zero means always format the divergent `else` block \ - over multiple lines."; + chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \ + line if-else expressions. A value of zero means always break if-else expressions."; + single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \ + single line let-else statements. A value of zero means always format the divergent `else` \ + block over multiple lines."; // Comments. macros, and strings - wrap_comments: bool, false, false, "Break comments to fit on the line"; - format_code_in_doc_comments: bool, false, false, "Format the code snippet in doc comments."; - doc_comment_code_block_width: usize, 100, false, "Maximum width for code snippets in doc \ - comments. No effect unless format_code_in_doc_comments = true"; - comment_width: usize, 80, false, + wrap_comments: WrapComments, false, "Break comments to fit on the line"; + format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \ + doc comments."; + doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \ + snippets in doc comments. No effect unless format_code_in_doc_comments = true"; + comment_width: CommentWidth, false, "Maximum length of comments. No effect unless wrap_comments = true"; - normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible"; - normalize_doc_attributes: bool, false, false, "Normalize doc attributes as doc comments"; - format_strings: bool, false, false, "Format string literals where necessary"; - format_macro_matchers: bool, false, false, + normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \ + possible"; + normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \ + comments"; + format_strings: FormatStrings, false, "Format string literals where necessary"; + format_macro_matchers: FormatMacroMatchers, false, "Format the metavariable matching patterns in macros"; - format_macro_bodies: bool, true, false, "Format the bodies of declarative macro definitions"; - skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false, + format_macro_bodies: FormatMacroBodies, false, + "Format the bodies of declarative macro definitions"; + skip_macro_invocations: SkipMacroInvocations, false, "Skip formatting the bodies of macros invoked with the following names."; - hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false, - "Format hexadecimal integer literals"; + hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals"; // Single line expressions and items - empty_item_single_line: bool, true, false, + empty_item_single_line: EmptyItemSingleLine, false, "Put empty-body functions and impls on a single line"; - struct_lit_single_line: bool, true, false, + struct_lit_single_line: StructLitSingleLine, false, "Put small struct literals on a single line"; - fn_single_line: bool, false, false, "Put single-expression functions on a single line"; - where_single_line: bool, false, false, "Force where-clauses to be on a single line"; + fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line"; + where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line"; // Imports - imports_indent: IndentStyle, IndentStyle::Block, false, "Indent of imports"; - imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block"; - imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, + imports_indent: ImportsIndent, false, "Indent of imports"; + imports_layout: ImportsLayout, false, "Item layout inside a import block"; + imports_granularity: ImportsGranularityConfig, false, "Merge or split imports to the provided granularity"; - group_imports: GroupImportsTactic, GroupImportsTactic::Preserve, false, + group_imports: GroupImportsTacticConfig, false, "Controls the strategy for how imports are grouped together"; - merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)"; // Ordering - reorder_imports: bool, true, true, "Reorder import and extern crate statements alphabetically"; - reorder_modules: bool, true, true, "Reorder module statements alphabetically in group"; - reorder_impl_items: bool, false, false, "Reorder impl items"; + reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \ + alphabetically"; + reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group"; + reorder_impl_items: ReorderImplItems, false, "Reorder impl items"; // Spaces around punctuation - type_punctuation_density: TypeDensity, TypeDensity::Wide, false, + type_punctuation_density: TypePunctuationDensity, false, "Determines if '+' or '=' are wrapped in spaces in the punctuation of types"; - space_before_colon: bool, false, false, "Leave a space before the colon"; - space_after_colon: bool, true, false, "Leave a space after the colon"; - spaces_around_ranges: bool, false, false, "Put spaces around the .. and ..= range operators"; - binop_separator: SeparatorPlace, SeparatorPlace::Front, false, + space_before_colon: SpaceBeforeColon, false, "Leave a space before the colon"; + space_after_colon: SpaceAfterColon, false, "Leave a space after the colon"; + spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the .. and ..= range \ + operators"; + binop_separator: BinopSeparator, false, "Where to put a binary operator when a binary expression goes multiline"; // Misc. - remove_nested_parens: bool, true, true, "Remove nested parens"; - combine_control_expr: bool, true, false, "Combine control expressions with function calls"; - short_array_element_width_threshold: usize, 10, true, + remove_nested_parens: RemoveNestedParens, true, "Remove nested parens"; + combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \ + calls"; + short_array_element_width_threshold: ShortArrayElementWidthThreshold, true, "Width threshold for an array element to be considered short"; - overflow_delimited_expr: bool, false, false, + overflow_delimited_expr: OverflowDelimitedExpr, false, "Allow trailing bracket/brace delimited expressions to overflow"; - struct_field_align_threshold: usize, 0, false, + struct_field_align_threshold: StructFieldAlignThreshold, false, "Align struct fields if their diffs fits within threshold"; - enum_discrim_align_threshold: usize, 0, false, + enum_discrim_align_threshold: EnumDiscrimAlignThreshold, false, "Align enum variants discrims, if their diffs fit within threshold"; - match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \ - the same line with the pattern of arms"; - match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true, + match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \ + on the same line with the pattern of arms"; + match_arm_leading_pipes: MatchArmLeadingPipeConfig, true, "Determines whether leading pipes are emitted on match arms"; - force_multiline_blocks: bool, false, false, + force_multiline_blocks: ForceMultilineBlocks, false, "Force multiline closure bodies and match arms to be wrapped in a block"; - fn_args_layout: Density, Density::Tall, true, + fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)"; - fn_params_layout: Density, Density::Tall, true, + fn_params_layout: FnParamsLayout, true, "Control the layout of parameters in function signatures."; - brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items"; - control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false, + brace_style: BraceStyleConfig, false, "Brace style for items"; + control_brace_style: ControlBraceStyleConfig, false, "Brace style for control flow constructs"; - trailing_semicolon: bool, true, false, + trailing_semicolon: TrailingSemicolon, false, "Add trailing semicolon after break, continue and return"; - trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false, + trailing_comma: TrailingComma, false, "How to handle trailing commas for lists"; - match_block_trailing_comma: bool, false, true, + match_block_trailing_comma: MatchBlockTrailingComma, true, "Put a trailing comma after a block based match arm (non-block arms are not affected)"; - blank_lines_upper_bound: usize, 1, false, + blank_lines_upper_bound: BlankLinesUpperBound, false, "Maximum number of blank lines which can be put between items"; - blank_lines_lower_bound: usize, 0, false, + blank_lines_lower_bound: BlankLinesLowerBound, false, "Minimum number of blank lines which must be put between items"; - edition: Edition, Edition::Edition2015, true, "The edition of the parser (RFC 2052)"; - version: Version, Version::One, false, "Version of formatting rules"; - inline_attribute_width: usize, 0, false, + edition: EditionConfig, true, "The edition of the parser (RFC 2052)"; + style_edition: StyleEditionConfig, false, "The edition of the Style Guide (RFC 3338)"; + version: VersionConfig, false, "Version of formatting rules"; + inline_attribute_width: InlineAttributeWidth, false, "Write an item and its attribute on the same line \ if their combined width is below a threshold"; - format_generated_files: bool, true, false, "Format generated files"; - generated_marker_line_search_limit: usize, 5, false, "Number of lines to check for a \ - `@generated` marker when `format_generated_files` is enabled"; + format_generated_files: FormatGeneratedFiles, false, "Format generated files"; + generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \ + check for a `@generated` marker when `format_generated_files` is enabled"; // Options that can change the source code beyond whitespace/blocks (somewhat linty things) - merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one"; - use_try_shorthand: bool, false, true, "Replace uses of the try! macro by the ? shorthand"; - use_field_init_shorthand: bool, false, true, "Use field initialization shorthand if possible"; - force_explicit_abi: bool, true, true, "Always print the abi for extern items"; - condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \ - in tuple patterns"; + merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one"; + use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand"; + use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \ + possible"; + force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items"; + condense_wildcard_suffixes: CondenseWildcardSuffixes, false, "Replace strings of _ wildcards \ + by a single .. in tuple patterns"; // Control options (changes the operation of rustfmt, rather than the formatting) - color: Color, Color::Auto, false, + color: ColorConfig, false, "What Color option to use when none is supplied: Always, Never, Auto"; - required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, + required_version: RequiredVersion, false, "Require a specific version of rustfmt"; - unstable_features: bool, false, false, + unstable_features: UnstableFeatures, false, "Enables unstable features. Only available on nightly channel"; - disable_all_formatting: bool, false, true, "Don't reformat anything"; - skip_children: bool, false, false, "Don't reformat out of line modules"; - hide_parse_errors: bool, false, false, "(deprecated: use show_parse_errors instead)"; - show_parse_errors: bool, true, false, "Show errors from the parser (unstable)"; - error_on_line_overflow: bool, false, false, "Error if unable to get all lines within max_width"; - error_on_unformatted: bool, false, false, + disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything"; + skip_children: SkipChildren, false, "Don't reformat out of line modules"; + hide_parse_errors: HideParseErrors, false, "Hide errors from the parser"; + show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)"; + error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \ + max_width"; + error_on_unformatted: ErrorOnUnformatted, false, "Error if unable to get comments or string literals within max_width, \ or they are left with trailing whitespaces"; - ignore: IgnoreList, IgnoreList::default(), false, + ignore: Ignore, false, "Skip formatting the specified files and directories"; // Not user-facing - verbose: Verbosity, Verbosity::Normal, false, "How much to information to emit to the user"; - file_lines: FileLines, FileLines::all(), false, + verbose: Verbose, false, "How much to information to emit to the user"; + file_lines: FileLinesConfig, false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; - emit_mode: EmitMode, EmitMode::Files, false, + emit_mode: EmitModeConfig, false, "What emit Mode to use when none is supplied"; - make_backup: bool, false, false, "Backup changed files"; - print_misformatted_file_names: bool, false, true, + make_backup: MakeBackup, false, "Backup changed files"; + print_misformatted_file_names: PrintMisformattedFileNames, true, "Prints the names of mismatched files that were formatted. Prints the names of \ files that would be formatted when used with `--check` mode. "; } @@ -211,9 +217,48 @@ impl PartialConfig { ::toml::to_string(&cloned).map_err(ToTomlError) } + + pub(super) fn to_parsed_config( + self, + style_edition_override: Option<StyleEdition>, + edition_override: Option<Edition>, + version_override: Option<Version>, + dir: &Path, + ) -> Config { + Config::default_for_possible_style_edition( + style_edition_override.or(self.style_edition), + edition_override.or(self.edition), + version_override.or(self.version), + ) + .fill_from_parsed_config(self, dir) + } } impl Config { + pub fn default_for_possible_style_edition( + style_edition: Option<StyleEdition>, + edition: Option<Edition>, + version: Option<Version>, + ) -> Config { + // Ensures the configuration defaults associated with Style Editions + // follow the precedence set in + // https://rust-lang.github.io/rfcs/3338-style-evolution.html + // 'version' is a legacy alias for 'style_edition' that we'll support + // for some period of time + // FIXME(calebcartwright) - remove 'version' at some point + match (style_edition, version, edition) { + (Some(se), _, _) => Self::default_with_style_edition(se), + (None, Some(Version::Two), _) => { + Self::default_with_style_edition(StyleEdition::Edition2024) + } + (None, Some(Version::One), _) => { + Self::default_with_style_edition(StyleEdition::Edition2015) + } + (None, None, Some(e)) => Self::default_with_style_edition(e.into()), + (None, None, None) => Config::default(), + } + } + pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { let version = env!("CARGO_PKG_VERSION"); @@ -237,11 +282,16 @@ impl Config { /// /// Returns a `Config` if the config could be read and parsed from /// the file, otherwise errors. - pub(super) fn from_toml_path(file_path: &Path) -> Result<Config, Error> { + pub(super) fn from_toml_path( + file_path: &Path, + edition: Option<Edition>, + style_edition: Option<StyleEdition>, + version: Option<Version>, + ) -> Result<Config, Error> { let mut file = File::open(&file_path)?; let mut toml = String::new(); file.read_to_string(&mut toml)?; - Config::from_toml(&toml, file_path.parent().unwrap()) + Config::from_toml_for_style_edition(&toml, file_path, edition, style_edition, version) .map_err(|err| Error::new(ErrorKind::InvalidData, err)) } @@ -254,9 +304,14 @@ impl Config { /// /// Returns the `Config` to use, and the path of the project file if there was /// one. - pub(super) fn from_resolved_toml_path(dir: &Path) -> Result<(Config, Option<PathBuf>), Error> { + pub(super) fn from_resolved_toml_path( + dir: &Path, + edition: Option<Edition>, + style_edition: Option<StyleEdition>, + version: Option<Version>, + ) -> Result<(Config, Option<PathBuf>), Error> { /// Try to find a project file in the given directory and its parents. - /// Returns the path of a the nearest project file if one exists, + /// Returns the path of the nearest project file if one exists, /// or `None` if no project file was found. fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> { let mut current = if dir.is_relative() { @@ -299,12 +354,27 @@ impl Config { } match resolve_project_file(dir)? { - None => Ok((Config::default(), None)), - Some(path) => Config::from_toml_path(&path).map(|config| (config, Some(path))), + None => Ok(( + Config::default_for_possible_style_edition(style_edition, edition, version), + None, + )), + Some(path) => Config::from_toml_path(&path, edition, style_edition, version) + .map(|config| (config, Some(path))), } } - pub(crate) fn from_toml(toml: &str, dir: &Path) -> Result<Config, String> { + #[allow(dead_code)] + pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result<Config, String> { + Self::from_toml_for_style_edition(toml, file_path, None, None, None) + } + + pub(crate) fn from_toml_for_style_edition( + toml: &str, + file_path: &Path, + edition: Option<Edition>, + style_edition: Option<StyleEdition>, + version: Option<Version>, + ) -> Result<Config, String> { let parsed: ::toml::Value = toml .parse() .map_err(|e| format!("Could not parse TOML: {}", e))?; @@ -318,18 +388,25 @@ impl Config { err.push_str(msg) } } - match parsed.try_into() { + + match parsed.try_into::<PartialConfig>() { Ok(parsed_config) => { if !err.is_empty() { eprint!("{err}"); } - Ok(Config::default().fill_from_parsed_config(parsed_config, dir)) + let dir = file_path.parent().ok_or_else(|| { + format!("failed to get parent directory for {}", file_path.display()) + })?; + + Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir)) } Err(e) => { - err.push_str("Error: Decoding config file failed:\n"); - err.push_str(format!("{e}\n").as_str()); - err.push_str("Please check your config file."); - Err(err) + let err_msg = format!( + "The file `{}` failed to parse.\nError details: {e}", + file_path.display() + ); + err.push_str(&err_msg); + Err(err_msg) } } } @@ -341,17 +418,26 @@ pub fn load_config<O: CliOptions>( file_path: Option<&Path>, options: Option<O>, ) -> Result<(Config, Option<PathBuf>), Error> { - let over_ride = match options { - Some(ref opts) => config_path(opts)?, - None => None, + let (over_ride, edition, style_edition, version) = match options { + Some(ref opts) => ( + config_path(opts)?, + opts.edition(), + opts.style_edition(), + opts.version(), + ), + None => (None, None, None, None), }; let result = if let Some(over_ride) = over_ride { - Config::from_toml_path(over_ride.as_ref()).map(|p| (p, Some(over_ride.to_owned()))) + Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version) + .map(|p| (p, Some(over_ride.to_owned()))) } else if let Some(file_path) = file_path { - Config::from_resolved_toml_path(file_path) + Config::from_resolved_toml_path(file_path, edition, style_edition, version) } else { - Ok((Config::default(), None)) + Ok(( + Config::default_for_possible_style_edition(style_edition, edition, version), + None, + )) }; result.map(|(mut c, p)| { @@ -362,7 +448,7 @@ pub fn load_config<O: CliOptions>( }) } -// Check for the presence of known config file names (`rustfmt.toml, `.rustfmt.toml`) in `dir` +// Check for the presence of known config file names (`rustfmt.toml`, `.rustfmt.toml`) in `dir` // // Return the path if a config file exists, empty if no file exists, and Error for IO errors fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> { @@ -372,7 +458,7 @@ fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> { match fs::metadata(&config_file) { // Only return if it's a file to handle the unlikely situation of a directory named // `rustfmt.toml`. - Ok(ref md) if md.is_file() => return Ok(Some(config_file)), + Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)), // Return the error if it's something other than `NotFound`; otherwise we didn't // find the project file yet, and continue searching. Err(e) => { @@ -411,7 +497,11 @@ fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> { config_path_not_found(path.to_str().unwrap()) } } - path => Ok(path.map(ToOwned::to_owned)), + Some(path) => Ok(Some( + // Canonicalize only after checking above that the `path.exists()`. + path.canonicalize()?, + )), + None => Ok(None), } } @@ -420,12 +510,13 @@ mod test { use super::*; use std::str; - use crate::config::macro_names::MacroName; + use crate::config::macro_names::{MacroName, MacroSelectors}; use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; #[allow(dead_code)] mod mock { use super::super::*; + use crate::config_option_with_style_edition_default; use rustfmt_config_proc_macro::config_type; #[config_type] @@ -436,66 +527,69 @@ mod test { V3, } + config_option_with_style_edition_default!( + StableOption, bool, _ => false; + UnstableOption, bool, _ => false; + PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1; + ); + create_config! { // Options that are used by the generated functions - max_width: usize, 100, true, "Maximum width of each line"; - required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, - "Require a specific version of rustfmt."; - ignore: IgnoreList, IgnoreList::default(), false, - "Skip formatting the specified files and directories."; - verbose: Verbosity, Verbosity::Normal, false, - "How much to information to emit to the user"; - file_lines: FileLines, FileLines::all(), false, + max_width: MaxWidth, true, "Maximum width of each line"; + required_version: RequiredVersion, false, "Require a specific version of rustfmt."; + ignore: Ignore, false, "Skip formatting the specified files and directories."; + verbose: Verbose, false, "How much to information to emit to the user"; + file_lines: FileLinesConfig, false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; // merge_imports deprecation - imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, - "Merge imports"; - merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + imports_granularity: ImportsGranularityConfig, false, "Merge imports"; + merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)"; // fn_args_layout renamed to fn_params_layout - fn_args_layout: Density, Density::Tall, true, - "(deprecated: use fn_params_layout instead)"; - fn_params_layout: Density, Density::Tall, true, + fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)"; + fn_params_layout: FnParamsLayout, true, "Control the layout of parameters in a function signatures."; // hide_parse_errors renamed to show_parse_errors - hide_parse_errors: bool, false, false, + hide_parse_errors: HideParseErrors, false, "(deprecated: use show_parse_errors instead)"; - show_parse_errors: bool, true, false, + show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)"; // Width Heuristics - use_small_heuristics: Heuristics, Heuristics::Default, true, + use_small_heuristics: UseSmallHeuristics, true, "Whether to use different formatting for items and \ expressions if they satisfy a heuristic notion of 'small'."; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; + width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values"; - fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \ falling back to vertical formatting."; - attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ - attributes before falling back to vertical formatting."; - struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ - falling back to vertical formatting."; - struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \ + attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \ + function-like attributes before falling back to vertical formatting."; + struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \ + before falling back to vertical formatting."; + struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \ variant before falling back to vertical formatting."; - array_width: usize, 60, true, "Maximum width of an array literal before falling \ + array_width: ArrayWidth, true, "Maximum width of an array literal before falling \ back to vertical formatting."; - chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; - single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \ - line if-else expressions. A value of zero means always break if-else expressions."; - single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ - line let-else statements. A value of zero means always format the divergent \ - `else` block over multiple lines."; + chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \ + for single line if-else expressions. A value of zero means always break if-else \ + expressions."; + single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \ + for single line let-else statements. A value of zero means always format the \ + divergent `else` block over multiple lines."; // Options that are used by the tests - stable_option: bool, false, true, "A stable option"; - unstable_option: bool, false, false, "An unstable option"; - partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true, - "A partially unstable option"; + stable_option: StableOption, true, "A stable option"; + unstable_option: UnstableOption, false, "An unstable option"; + partially_unstable_option: PartiallyUnstable, true, "A partially unstable option"; + edition: EditionConfig, true, "blah"; + style_edition: StyleEditionConfig, true, "blah"; + version: VersionConfig, false, "blah blah" } #[cfg(test)] @@ -580,7 +674,7 @@ mod test { #[test] fn test_was_set() { - let config = Config::from_toml("hard_tabs = true", Path::new("")).unwrap(); + let config = Config::from_toml("hard_tabs = true", Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.was_set().hard_tabs(), true); assert_eq!(config.was_set().verbose(), false); @@ -679,6 +773,7 @@ match_block_trailing_comma = false blank_lines_upper_bound = 1 blank_lines_lower_bound = 0 edition = "2015" +style_edition = "2015" version = "One" inline_attribute_width = 0 format_generated_files = true @@ -706,6 +801,114 @@ make_backup = false assert_eq!(&toml, &default_config); } + #[test] + fn test_dump_style_edition_2024_config() { + let edition_2024_config = format!( + r#"max_width = 100 +hard_tabs = false +tab_spaces = 4 +newline_style = "Auto" +indent_style = "Block" +use_small_heuristics = "Default" +fn_call_width = 60 +attr_fn_like_width = 70 +struct_lit_width = 18 +struct_variant_width = 35 +array_width = 60 +chain_width = 60 +single_line_if_else_max_width = 50 +single_line_let_else_max_width = 50 +wrap_comments = false +format_code_in_doc_comments = false +doc_comment_code_block_width = 100 +comment_width = 80 +normalize_comments = false +normalize_doc_attributes = false +format_strings = false +format_macro_matchers = false +format_macro_bodies = true +skip_macro_invocations = [] +hex_literal_case = "Preserve" +empty_item_single_line = true +struct_lit_single_line = true +fn_single_line = false +where_single_line = false +imports_indent = "Block" +imports_layout = "Mixed" +imports_granularity = "Preserve" +group_imports = "Preserve" +reorder_imports = true +reorder_modules = true +reorder_impl_items = false +type_punctuation_density = "Wide" +space_before_colon = false +space_after_colon = true +spaces_around_ranges = false +binop_separator = "Front" +remove_nested_parens = true +combine_control_expr = true +short_array_element_width_threshold = 10 +overflow_delimited_expr = true +struct_field_align_threshold = 0 +enum_discrim_align_threshold = 0 +match_arm_blocks = true +match_arm_leading_pipes = "Never" +force_multiline_blocks = false +fn_params_layout = "Tall" +brace_style = "SameLineWhere" +control_brace_style = "AlwaysSameLine" +trailing_semicolon = true +trailing_comma = "Vertical" +match_block_trailing_comma = false +blank_lines_upper_bound = 1 +blank_lines_lower_bound = 0 +edition = "2015" +style_edition = "2024" +version = "Two" +inline_attribute_width = 0 +format_generated_files = true +generated_marker_line_search_limit = 5 +merge_derives = true +use_try_shorthand = false +use_field_init_shorthand = false +force_explicit_abi = true +condense_wildcard_suffixes = false +color = "Auto" +required_version = "{}" +unstable_features = false +disable_all_formatting = false +skip_children = false +show_parse_errors = true +error_on_line_overflow = false +error_on_unformatted = false +ignore = [] +emit_mode = "Files" +make_backup = false +"#, + env!("CARGO_PKG_VERSION") + ); + let toml = Config::default_with_style_edition(StyleEdition::Edition2024) + .all_options() + .to_toml() + .unwrap(); + assert_eq!(&toml, &edition_2024_config); + } + + #[test] + fn test_editions_2015_2018_2021_identical() { + let get_edition_toml = |style_edition: StyleEdition| { + Config::default_with_style_edition(style_edition) + .all_options() + .to_toml() + .unwrap() + }; + let edition2015 = get_edition_toml(StyleEdition::Edition2015); + let edition2018 = get_edition_toml(StyleEdition::Edition2018); + let edition2021 = get_edition_toml(StyleEdition::Edition2021); + assert_eq!(edition2015, edition2018); + assert_eq!(edition2018, edition2021); + } + #[stable_only_test] #[test] fn test_as_not_nightly_channel() { @@ -730,11 +933,26 @@ make_backup = false #[nightly_only_test] #[test] fn test_unstable_from_toml() { - let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap(); + let config = + Config::from_toml("unstable_features = true", Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.was_set().unstable_features(), true); assert_eq!(config.unstable_features(), true); } + #[test] + fn test_set_cli() { + let mut config = Config::default(); + assert_eq!(config.was_set().edition(), false); + assert_eq!(config.was_set_cli().edition(), false); + config.set().edition(Edition::Edition2021); + assert_eq!(config.was_set().edition(), false); + assert_eq!(config.was_set_cli().edition(), false); + config.set_cli().edition(Edition::Edition2021); + assert_eq!(config.was_set().edition(), false); + assert_eq!(config.was_set_cli().edition(), true); + assert_eq!(config.was_set_cli().emit_mode(), false); + } + #[cfg(test)] mod deprecated_option_merge_imports { use super::*; @@ -746,7 +964,7 @@ make_backup = false unstable_features = true merge_imports = true "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.imports_granularity(), ImportGranularity::Crate); } @@ -758,7 +976,7 @@ make_backup = false merge_imports = true imports_granularity = "Preserve" "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } @@ -769,7 +987,7 @@ make_backup = false unstable_features = true merge_imports = true "#; - let mut config = Config::from_toml(toml, Path::new("")).unwrap(); + let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); config.override_value("imports_granularity", "Preserve"); assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } @@ -781,7 +999,7 @@ make_backup = false unstable_features = true imports_granularity = "Module" "#; - let mut config = Config::from_toml(toml, Path::new("")).unwrap(); + let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); config.override_value("merge_imports", "true"); // no effect: the new option always takes precedence assert_eq!(config.imports_granularity(), ImportGranularity::Module); @@ -798,7 +1016,7 @@ make_backup = false use_small_heuristics = "Default" max_width = 200 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 120); assert_eq!(config.attr_fn_like_width(), 140); assert_eq!(config.chain_width(), 120); @@ -814,7 +1032,7 @@ make_backup = false use_small_heuristics = "Max" max_width = 120 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 120); assert_eq!(config.attr_fn_like_width(), 120); assert_eq!(config.chain_width(), 120); @@ -830,11 +1048,11 @@ make_backup = false use_small_heuristics = "Off" max_width = 100 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); - assert_eq!(config.array_width(), usize::max_value()); - assert_eq!(config.attr_fn_like_width(), usize::max_value()); - assert_eq!(config.chain_width(), usize::max_value()); - assert_eq!(config.fn_call_width(), usize::max_value()); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); + assert_eq!(config.array_width(), usize::MAX); + assert_eq!(config.attr_fn_like_width(), usize::MAX); + assert_eq!(config.chain_width(), usize::MAX); + assert_eq!(config.fn_call_width(), usize::MAX); assert_eq!(config.single_line_if_else_max_width(), 0); assert_eq!(config.struct_lit_width(), 0); assert_eq!(config.struct_variant_width(), 0); @@ -852,7 +1070,7 @@ make_backup = false struct_lit_width = 30 struct_variant_width = 34 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 20); assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -874,7 +1092,7 @@ make_backup = false struct_lit_width = 30 struct_variant_width = 34 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 20); assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -896,7 +1114,7 @@ make_backup = false struct_lit_width = 30 struct_variant_width = 34 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 20); assert_eq!(config.attr_fn_like_width(), 40); assert_eq!(config.chain_width(), 20); @@ -912,7 +1130,7 @@ make_backup = false max_width = 90 fn_call_width = 95 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.fn_call_width(), 90); } @@ -922,7 +1140,7 @@ make_backup = false max_width = 80 attr_fn_like_width = 90 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.attr_fn_like_width(), 80); } @@ -932,7 +1150,7 @@ make_backup = false max_width = 78 struct_lit_width = 90 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.struct_lit_width(), 78); } @@ -942,7 +1160,7 @@ make_backup = false max_width = 80 struct_variant_width = 90 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.struct_variant_width(), 80); } @@ -952,7 +1170,7 @@ make_backup = false max_width = 60 array_width = 80 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.array_width(), 60); } @@ -962,7 +1180,7 @@ make_backup = false max_width = 80 chain_width = 90 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.chain_width(), 80); } @@ -972,7 +1190,7 @@ make_backup = false max_width = 70 single_line_if_else_max_width = 90 "#; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert_eq!(config.single_line_if_else_max_width(), 70); } diff --git a/src/tools/rustfmt/src/config/options.rs b/src/tools/rustfmt/src/config/options.rs index 3c5c713a33a..c526b5c678f 100644 --- a/src/tools/rustfmt/src/config/options.rs +++ b/src/tools/rustfmt/src/config/options.rs @@ -1,6 +1,6 @@ #![allow(unused_imports)] -use std::collections::{hash_set, HashSet}; +use std::collections::{HashSet, hash_set}; use std::fmt; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -11,8 +11,10 @@ use serde::de::{SeqAccess, Visitor}; use serde::ser::SerializeSeq; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use crate::config::lists::*; use crate::config::Config; +use crate::config::file_lines::FileLines; +use crate::config::lists::*; +use crate::config::macro_names::MacroSelectors; #[config_type] pub enum NewlineStyle { @@ -253,12 +255,12 @@ impl WidthHeuristics { // Using this WidthHeuristics means we ignore heuristics. pub fn null() -> WidthHeuristics { WidthHeuristics { - fn_call_width: usize::max_value(), - attr_fn_like_width: usize::max_value(), + fn_call_width: usize::MAX, + attr_fn_like_width: usize::MAX, struct_lit_width: 0, struct_variant_width: 0, - array_width: usize::max_value(), - chain_width: usize::max_value(), + array_width: usize::MAX, + chain_width: usize::MAX, single_line_if_else_max_width: 0, single_line_let_else_max_width: 0, } @@ -413,7 +415,13 @@ impl FromStr for IgnoreList { /// values in a config with values from the command line. pub trait CliOptions { fn apply_to(self, config: &mut Config); + + /// It is ok if the returned path doesn't exist or is not canonicalized + /// (i.e. the callers are expected to handle such cases). fn config_path(&self) -> Option<&Path>; + fn edition(&self) -> Option<Edition>; + fn style_edition(&self) -> Option<StyleEdition>; + fn version(&self) -> Option<Version>; } /// The edition of the syntax and semantics of code (RFC 2052). @@ -454,6 +462,17 @@ impl From<Edition> for rustc_span::edition::Edition { } } +impl From<Edition> for StyleEdition { + fn from(edition: Edition) -> Self { + match edition { + Edition::Edition2015 => StyleEdition::Edition2015, + Edition::Edition2018 => StyleEdition::Edition2018, + Edition::Edition2021 => StyleEdition::Edition2021, + Edition::Edition2024 => StyleEdition::Edition2024, + } + } +} + impl PartialOrd for Edition { fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> { rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) @@ -471,10 +490,11 @@ pub enum MatchArmLeadingPipe { Preserve, } -/// Defines the default values for each config according to [the style guide]. -/// rustfmt output may differ between style editions. +/// Defines the default values for each config according to the edition of the +/// [Style Guide] as per [RFC 3338]. Rustfmt output may differ between Style editions. /// -/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/ +/// [Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ +/// [RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html #[config_type] pub enum StyleEdition { #[value = "2015"] @@ -491,6 +511,169 @@ pub enum StyleEdition { Edition2021, #[value = "2024"] #[doc_hint = "2024"] + #[unstable_variant] /// [Edition 2024](). Edition2024, } + +impl From<StyleEdition> for rustc_span::edition::Edition { + fn from(edition: StyleEdition) -> Self { + match edition { + StyleEdition::Edition2015 => Self::Edition2015, + StyleEdition::Edition2018 => Self::Edition2018, + StyleEdition::Edition2021 => Self::Edition2021, + StyleEdition::Edition2024 => Self::Edition2024, + } + } +} + +impl PartialOrd for StyleEdition { + fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> { + rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into()) + } +} + +/// Defines unit structs to implement `StyleEditionDefault` for. +#[macro_export] +macro_rules! config_option_with_style_edition_default { + ($name:ident, $config_ty:ty, _ => $default:expr) => { + #[allow(unreachable_pub)] + pub struct $name; + $crate::style_edition_default!($name, $config_ty, _ => $default); + }; + ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => { + pub struct $name; + $crate::style_edition_default!( + $name, + $config_ty, + Edition2024 => $default_2024, + _ => $default_2015 + ); + }; + ( + $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);* + $(;)* + ) => { + $( + config_option_with_style_edition_default!( + $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default + ); + )* + }; +} + +// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't. +// I chose to add a `Config` suffix in cases where a type for the config option was already +// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion +// about using the `Config` suffix more consistently. +config_option_with_style_edition_default!( + // Fundamental stuff + MaxWidth, usize, _ => 100; + HardTabs, bool, _ => false; + TabSpaces, usize, _ => 4; + NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto; + IndentStyleConfig, IndentStyle, _ => IndentStyle::Block; + + // Width Heuristics + UseSmallHeuristics, Heuristics, _ => Heuristics::Default; + WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100); + FnCallWidth, usize, _ => 60; + AttrFnLikeWidth, usize, _ => 70; + StructLitWidth, usize, _ => 18; + StructVariantWidth, usize, _ => 35; + ArrayWidth, usize, _ => 60; + ChainWidth, usize, _ => 60; + SingleLineIfElseMaxWidth, usize, _ => 50; + SingleLineLetElseMaxWidth, usize, _ => 50; + + // Comments. macros, and strings + WrapComments, bool, _ => false; + FormatCodeInDocComments, bool, _ => false; + DocCommentCodeBlockWidth, usize, _ => 100; + CommentWidth, usize, _ => 80; + NormalizeComments, bool, _ => false; + NormalizeDocAttributes, bool, _ => false; + FormatStrings, bool, _ => false; + FormatMacroMatchers, bool, _ => false; + FormatMacroBodies, bool, _ => true; + SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default(); + HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve; + + // Single line expressions and items + EmptyItemSingleLine, bool, _ => true; + StructLitSingleLine, bool, _ => true; + FnSingleLine, bool, _ => false; + WhereSingleLine, bool, _ => false; + + // Imports + ImportsIndent, IndentStyle, _ => IndentStyle::Block; + ImportsLayout, ListTactic, _ => ListTactic::Mixed; + ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve; + GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve; + MergeImports, bool, _ => false; + + // Ordering + ReorderImports, bool, _ => true; + ReorderModules, bool, _ => true; + ReorderImplItems, bool, _ => false; + + // Spaces around punctuation + TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide; + SpaceBeforeColon, bool, _ => false; + SpaceAfterColon, bool, _ => true; + SpacesAroundRanges, bool, _ => false; + BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front; + + // Misc. + RemoveNestedParens, bool, _ => true; + CombineControlExpr, bool, _ => true; + ShortArrayElementWidthThreshold, usize, _ => 10; + OverflowDelimitedExpr, bool, Edition2024 => true, _ => false; + StructFieldAlignThreshold, usize, _ => 0; + EnumDiscrimAlignThreshold, usize, _ => 0; + MatchArmBlocks, bool, _ => true; + MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never; + ForceMultilineBlocks, bool, _ => false; + FnArgsLayout, Density, _ => Density::Tall; + FnParamsLayout, Density, _ => Density::Tall; + BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere; + ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine; + TrailingSemicolon, bool, _ => true; + TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical; + MatchBlockTrailingComma, bool, _ => false; + BlankLinesUpperBound, usize, _ => 1; + BlankLinesLowerBound, usize, _ => 0; + EditionConfig, Edition, _ => Edition::Edition2015; + StyleEditionConfig, StyleEdition, + Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015; + VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One; + InlineAttributeWidth, usize, _ => 0; + FormatGeneratedFiles, bool, _ => true; + GeneratedMarkerLineSearchLimit, usize, _ => 5; + + // Options that can change the source code beyond whitespace/blocks (somewhat linty things) + MergeDerives, bool, _ => true; + UseTryShorthand, bool, _ => false; + UseFieldInitShorthand, bool, _ => false; + ForceExplicitAbi, bool, _ => true; + CondenseWildcardSuffixes, bool, _ => false; + + // Control options (changes the operation of rustfmt, rather than the formatting) + ColorConfig, Color, _ => Color::Auto; + RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned(); + UnstableFeatures, bool, _ => false; + DisableAllFormatting, bool, _ => false; + SkipChildren, bool, _ => false; + HideParseErrors, bool, _ => false; + ShowParseErrors, bool, _ => true; + ErrorOnLineOverflow, bool, _ => false; + ErrorOnUnformatted, bool, _ => false; + Ignore, IgnoreList, _ => IgnoreList::default(); + + // Not user-facing + Verbose, Verbosity, _ => Verbosity::Normal; + FileLinesConfig, FileLines, _ => FileLines::all(); + EmitModeConfig, EmitMode, _ => EmitMode::Files; + MakeBackup, bool, _ => false; + PrintMisformattedFileNames, bool, _ => false; +); diff --git a/src/tools/rustfmt/src/config/style_edition.rs b/src/tools/rustfmt/src/config/style_edition.rs index 7b3ea3bc119..c34eca9c8e1 100644 --- a/src/tools/rustfmt/src/config/style_edition.rs +++ b/src/tools/rustfmt/src/config/style_edition.rs @@ -2,7 +2,7 @@ use crate::config::StyleEdition; /// Defines the default value for the given style edition #[allow(dead_code)] -pub(crate) trait StyleEditionDefault { +pub trait StyleEditionDefault { type ConfigType; fn style_edition_default(style_edition: StyleEdition) -> Self::ConfigType; } diff --git a/src/tools/rustfmt/src/emitter/checkstyle.rs b/src/tools/rustfmt/src/emitter/checkstyle.rs index 9385ae59a06..c320c16bd1d 100644 --- a/src/tools/rustfmt/src/emitter/checkstyle.rs +++ b/src/tools/rustfmt/src/emitter/checkstyle.rs @@ -1,6 +1,6 @@ use self::xml::XmlEscaped; use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; mod xml; diff --git a/src/tools/rustfmt/src/emitter/json.rs b/src/tools/rustfmt/src/emitter/json.rs index 084f565804c..a99626f783d 100644 --- a/src/tools/rustfmt/src/emitter/json.rs +++ b/src/tools/rustfmt/src/emitter/json.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch}; +use crate::rustfmt_diff::{DiffLine, Mismatch, make_diff}; use serde::Serialize; use serde_json::to_string as to_json_string; diff --git a/src/tools/rustfmt/src/emitter/modified_lines.rs b/src/tools/rustfmt/src/emitter/modified_lines.rs index 81f0a31b974..0d5124bc33e 100644 --- a/src/tools/rustfmt/src/emitter/modified_lines.rs +++ b/src/tools/rustfmt/src/emitter/modified_lines.rs @@ -1,5 +1,5 @@ use super::*; -use crate::rustfmt_diff::{make_diff, ModifiedLines}; +use crate::rustfmt_diff::{ModifiedLines, make_diff}; #[derive(Debug, Default)] pub(crate) struct ModifiedLinesEmitter; diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 75c75c523b0..77c9818b66b 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -3,33 +3,33 @@ use std::cmp::min; use itertools::Itertools; use rustc_ast::token::{Delimiter, Lit, LitKind}; -use rustc_ast::{ast, ptr, token, ForLoopKind, MatchKind}; +use rustc_ast::{ForLoopKind, MatchKind, ast, ptr, token}; use rustc_span::{BytePos, Span}; use tracing::debug; use crate::chains::rewrite_chain; use crate::closures; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, - rewrite_missing_comment, CharClasses, FindUncommented, + CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment, + recover_comment_removed, rewrite_comment, rewrite_missing_comment, }; use crate::config::lists::*; -use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, Version}; +use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, Separator, + ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic, + struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::matches::rewrite_match; use crate::overflow::{self, IntoOverflowableItem, OverflowableItem}; -use crate::pairs::{rewrite_all_pairs, rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_all_pairs, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt; -use crate::string::{rewrite_string, StringFormat}; -use crate::types::{rewrite_path, PathContext}; +use crate::string::{StringFormat, rewrite_string}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{ colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with, inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes, @@ -40,6 +40,10 @@ use crate::visitor::FmtVisitor; impl Rewrite for ast::Expr { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(self, ExprType::SubExpression, context, shape) } } @@ -59,14 +63,14 @@ pub(crate) fn format_expr( expr_type: ExprType, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - skip_out_of_file_lines_range!(context, expr.span); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, expr.span); if contains_skip(&*expr.attrs) { - return Some(context.snippet(expr.span()).to_owned()); + return Ok(context.snippet(expr.span()).to_owned()); } let shape = if expr_type == ExprType::Statement && semicolon_for_expr(context, expr) { - shape.sub_width(1)? + shape.sub_width(1).max_width_error(shape.width, expr.span)? } else { shape }; @@ -82,25 +86,25 @@ pub(crate) fn format_expr( None, ), ast::ExprKind::Lit(token_lit) => { - if let Some(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { - Some(expr_rw) + if let Ok(expr_rw) = rewrite_literal(context, token_lit, expr.span, shape) { + Ok(expr_rw) } else { if let LitKind::StrRaw(_) = token_lit.kind { - Some(context.snippet(expr.span).trim().into()) + Ok(context.snippet(expr.span).trim().into()) } else { - None + Err(RewriteError::Unknown) } } } ast::ExprKind::Call(ref callee, ref args) => { let inner_span = mk_sp(callee.span.hi(), expr.span.hi()); - let callee_str = callee.rewrite(context, shape)?; + let callee_str = callee.rewrite_result(context, shape)?; rewrite_call(context, &callee_str, args, inner_span, shape) } ast::ExprKind::Paren(ref subexpr) => rewrite_paren(context, subexpr, shape, expr.span), ast::ExprKind::Binary(op, ref lhs, ref rhs) => { // FIXME: format comments between operands and operator - rewrite_all_pairs(expr, shape, context).or_else(|| { + rewrite_all_pairs(expr, shape, context).or_else(|_| { rewrite_pair( &**lhs, &**rhs, @@ -138,7 +142,8 @@ pub(crate) fn format_expr( | ast::ExprKind::ForLoop { .. } | ast::ExprKind::Loop(..) | ast::ExprKind::While(..) => to_control_flow(expr, expr_type) - .and_then(|control_flow| control_flow.rewrite(context, shape)), + .unknown_error() + .and_then(|control_flow| control_flow.rewrite_result(context, shape)), ast::ExprKind::ConstBlock(ref anon_const) => { let rewrite = match anon_const.value.kind { ast::ExprKind::Block(ref block, opt_label) => { @@ -148,20 +153,20 @@ pub(crate) fn format_expr( // See https://github.com/rust-lang/rustfmt/issues/6158 rewrite_block(block, Some(&expr.attrs), opt_label, context, shape)? } - _ => anon_const.rewrite(context, shape)?, + _ => anon_const.rewrite_result(context, shape)?, }; - Some(format!("const {}", rewrite)) + Ok(format!("const {}", rewrite)) } ast::ExprKind::Block(ref block, opt_label) => { match expr_type { ExprType::Statement => { if is_unsafe_block(block) { rewrite_block(block, Some(&expr.attrs), opt_label, context, shape) - } else if let rw @ Some(_) = + } else if let Some(rw) = rewrite_empty_block(context, block, Some(&expr.attrs), opt_label, "", shape) { // Rewrite block without trying to put it in a single line. - rw + Ok(rw) } else { let prefix = block_prefix(context, block, shape)?; @@ -198,7 +203,7 @@ pub(crate) fn format_expr( Some(label) => format!(" {}", label.ident), None => String::new(), }; - Some(format!("continue{id_str}")) + Ok(format!("continue{id_str}")) } ast::ExprKind::Break(ref opt_label, ref opt_expr) => { let id_str = match *opt_label { @@ -209,14 +214,14 @@ pub(crate) fn format_expr( if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, &format!("break{id_str} "), &**expr, shape) } else { - Some(format!("break{id_str}")) + Ok(format!("break{id_str}")) } } ast::ExprKind::Yield(ref opt_expr) => { if let Some(ref expr) = *opt_expr { rewrite_unary_prefix(context, "yield ", &**expr, shape) } else { - Some("yield".to_string()) + Ok("yield".to_string()) } } ast::ExprKind::Closure(ref cl) => closures::rewrite_closure( @@ -236,20 +241,21 @@ pub(crate) fn format_expr( | ast::ExprKind::MethodCall(..) | ast::ExprKind::Await(_, _) => rewrite_chain(expr, context, shape), ast::ExprKind::MacCall(ref mac) => { - rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|| { + rewrite_macro(mac, None, context, shape, MacroPosition::Expression).or_else(|_| { wrap_str( context.snippet(expr.span).to_owned(), context.config.max_width(), shape, ) + .max_width_error(shape.width, expr.span) }) } - ast::ExprKind::Ret(None) => Some("return".to_owned()), + ast::ExprKind::Ret(None) => Ok("return".to_owned()), ast::ExprKind::Ret(Some(ref expr)) => { rewrite_unary_prefix(context, "return ", &**expr, shape) } ast::ExprKind::Become(ref expr) => rewrite_unary_prefix(context, "become ", &**expr, shape), - ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()), + ast::ExprKind::Yeet(None) => Ok("do yeet".to_owned()), ast::ExprKind::Yeet(Some(ref expr)) => { rewrite_unary_prefix(context, "do yeet ", &**expr, shape) } @@ -344,23 +350,23 @@ pub(crate) fn format_expr( }; rewrite_unary_suffix(context, &sp_delim, &*lhs, shape) } - (None, None) => Some(delim.to_owned()), + (None, None) => Ok(delim.to_owned()), } } // We do not format these expressions yet, but they should still // satisfy our width restrictions. // Style Guide RFC for InlineAsm variant pending // https://github.com/rust-dev-tools/fmt-rfcs/issues/152 - ast::ExprKind::InlineAsm(..) => Some(context.snippet(expr.span).to_owned()), + ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()), ast::ExprKind::TryBlock(ref block) => { - if let rw @ Some(_) = + if let rw @ Ok(_) = rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape) { rw } else { // 9 = `try ` let budget = shape.width.saturating_sub(9); - Some(format!( + Ok(format!( "{}{}", "try ", rewrite_block( @@ -379,7 +385,7 @@ pub(crate) fn format_expr( } else { "" }; - if let rw @ Some(_) = rewrite_single_line_block( + if let rw @ Ok(_) = rewrite_single_line_block( context, format!("{kind} {mover}").as_str(), block, @@ -391,7 +397,7 @@ pub(crate) fn format_expr( } else { // 6 = `async ` let budget = shape.width.saturating_sub(6); - Some(format!( + Ok(format!( "{kind} {mover}{}", rewrite_block( block, @@ -403,7 +409,7 @@ pub(crate) fn format_expr( )) } } - ast::ExprKind::Underscore => Some("_".to_owned()), + ast::ExprKind::Underscore => Ok("_".to_owned()), ast::ExprKind::FormatArgs(..) | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) @@ -412,16 +418,16 @@ pub(crate) fn format_expr( // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. - None + Err(RewriteError::Unknown) } - ast::ExprKind::Err(_) | ast::ExprKind::Dummy => None, + ast::ExprKind::Err(_) | ast::ExprKind::Dummy => Err(RewriteError::Unknown), }; expr_rw - .and_then(|expr_str| recover_comment_removed(expr_str, expr.span, context)) + .map(|expr_str| recover_comment_removed(expr_str, expr.span, context)) .and_then(|expr_str| { let attrs = outer_attributes(&expr.attrs); - let attrs_str = attrs.rewrite(context, shape)?; + let attrs_str = attrs.rewrite_result(context, shape)?; let span = mk_sp( attrs.last().map_or(expr.span.lo(), |attr| attr.span.hi()), expr.span.lo(), @@ -438,7 +444,7 @@ pub(crate) fn rewrite_array<'a, T: 'a + IntoOverflowableItem<'a>>( shape: Shape, force_separator_tactic: Option<SeparatorTactic>, delim_token: Option<Delimiter>, -) -> Option<String> { +) -> RewriteResult { overflow::rewrite_with_square_brackets( context, name, @@ -488,17 +494,20 @@ fn rewrite_empty_block( None } -fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> Option<String> { - Some(match block.rules { +fn block_prefix(context: &RewriteContext<'_>, block: &ast::Block, shape: Shape) -> RewriteResult { + Ok(match block.rules { ast::BlockCheckMode::Unsafe(..) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; // Extract comment between unsafe and block start. let trimmed = &snippet[6..open_pos].trim(); if !trimmed.is_empty() { // 9 = "unsafe {".len(), 7 = "unsafe ".len() - let budget = shape.width.checked_sub(9)?; + let budget = shape + .width + .checked_sub(9) + .max_width_error(shape.width, block.span)?; format!( "unsafe {} ", rewrite_comment( @@ -523,17 +532,19 @@ fn rewrite_single_line_block( attrs: Option<&[ast::Attribute]>, label: Option<ast::Label>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { - let expr_shape = shape.offset_left(last_line_width(prefix))?; - let expr_str = block_expr.rewrite(context, expr_shape)?; + let expr_shape = shape + .offset_left(last_line_width(prefix)) + .max_width_error(shape.width, block_expr.span())?; + let expr_str = block_expr.rewrite_result(context, expr_shape)?; let label_str = rewrite_label(context, label); let result = format!("{prefix}{label_str}{{ {expr_str} }}"); if result.len() <= shape.width && !result.contains('\n') { - return Some(result); + return Ok(result); } } - None + Err(RewriteError::Unknown) } pub(crate) fn rewrite_block_with_visitor( @@ -544,9 +555,9 @@ pub(crate) fn rewrite_block_with_visitor( label: Option<ast::Label>, shape: Shape, has_braces: bool, -) -> Option<String> { - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { - return rw; +) -> RewriteResult { + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, prefix, shape) { + return Ok(rw_str); } let mut visitor = FmtVisitor::from_context(context); @@ -555,7 +566,7 @@ pub(crate) fn rewrite_block_with_visitor( match (block.rules, label) { (ast::BlockCheckMode::Unsafe(..), _) | (ast::BlockCheckMode::Default, Some(_)) => { let snippet = context.snippet(block.span); - let open_pos = snippet.find_uncommented("{")?; + let open_pos = snippet.find_uncommented("{").unknown_error()?; visitor.last_pos = block.span.lo() + BytePos(open_pos as u32) } (ast::BlockCheckMode::Default, None) => visitor.last_pos = block.span.lo(), @@ -569,11 +580,15 @@ pub(crate) fn rewrite_block_with_visitor( .skipped_range .borrow_mut() .append(&mut visitor_context.skipped_range.borrow_mut()); - Some(format!("{}{}{}", prefix, label_str, visitor.buffer)) + Ok(format!("{}{}{}", prefix, label_str, visitor.buffer)) } impl Rewrite for ast::Block { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_block(self, None, None, context, shape) } } @@ -584,7 +599,7 @@ fn rewrite_block( label: Option<ast::Label>, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_block_inner(block, attrs, label, true, context, shape) } @@ -595,27 +610,24 @@ fn rewrite_block_inner( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let prefix = block_prefix(context, block, shape)?; // shape.width is used only for the single line case: either the empty block `{}`, // or an unsafe expression `unsafe { e }`. - if let rw @ Some(_) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { - return rw; + if let Some(rw_str) = rewrite_empty_block(context, block, attrs, label, &prefix, shape) { + return Ok(rw_str); } - let result = rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true); - if let Some(ref result_str) = result { - if allow_single_line && result_str.lines().count() <= 3 { - if let rw @ Some(_) = - rewrite_single_line_block(context, &prefix, block, attrs, label, shape) - { - return rw; - } + let result_str = + rewrite_block_with_visitor(context, &prefix, block, attrs, label, shape, true)?; + if allow_single_line && result_str.lines().count() <= 3 { + if let rw @ Ok(_) = rewrite_single_line_block(context, &prefix, block, attrs, label, shape) + { + return rw; } } - - result + Ok(result_str) } /// Rewrite the divergent block of a `let-else` statement. @@ -624,7 +636,7 @@ pub(crate) fn rewrite_let_else_block( allow_single_line: bool, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_block_inner(block, None, None, allow_single_line, context, shape) } @@ -648,6 +660,7 @@ pub(crate) fn rewrite_cond( String::from("\n") + &shape.indent.block_only().to_string(context.config); control_flow .rewrite_cond(context, shape, &alt_block_sep) + .ok() .map(|rw| rw.0) }), } @@ -872,10 +885,12 @@ impl<'a> ControlFlow<'a> { expr: &ast::Expr, shape: Shape, offset: usize, - ) -> Option<String> { + ) -> RewriteResult { debug!("rewrite_pat_expr {:?} {:?} {:?}", shape, self.pat, expr); - let cond_shape = shape.offset_left(offset)?; + let cond_shape = shape + .offset_left(offset) + .max_width_error(shape.width, expr.span)?; if let Some(pat) = self.pat { let matcher = if self.matcher.is_empty() { self.matcher.to_owned() @@ -883,9 +898,10 @@ impl<'a> ControlFlow<'a> { format!("{} ", self.matcher) }; let pat_shape = cond_shape - .offset_left(matcher.len())? - .sub_width(self.connector.len())?; - let pat_string = pat.rewrite(context, pat_shape)?; + .offset_left(matcher.len()) + .and_then(|s| s.sub_width(self.connector.len())) + .max_width_error(cond_shape.width, pat.span)?; + let pat_string = pat.rewrite_result(context, pat_shape)?; let comments_lo = context .snippet_provider .span_after(self.span.with_lo(pat.span.hi()), self.connector.trim()); @@ -902,10 +918,10 @@ impl<'a> ControlFlow<'a> { ); } - let expr_rw = expr.rewrite(context, cond_shape); + let expr_rw = expr.rewrite_result(context, cond_shape); // The expression may (partially) fit on the current line. // We do not allow splitting between `if` and condition. - if self.keyword == "if" || expr_rw.is_some() { + if self.keyword == "if" || expr_rw.is_ok() { return expr_rw; } @@ -914,7 +930,7 @@ impl<'a> ControlFlow<'a> { .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let nested_indent_str = nested_shape.indent.to_string_with_newline(context.config); - expr.rewrite(context, nested_shape) + expr.rewrite_result(context, nested_shape) .map(|expr_rw| format!("{}{}", nested_indent_str, expr_rw)) } @@ -923,7 +939,7 @@ impl<'a> ControlFlow<'a> { context: &RewriteContext<'_>, shape: Shape, alt_block_sep: &str, - ) -> Option<(String, usize)> { + ) -> Result<(String, usize), RewriteError> { // Do not take the rhs overhead from the upper expressions into account // when rewriting pattern. let new_width = context.budget(shape.used_width()); @@ -934,7 +950,9 @@ impl<'a> ControlFlow<'a> { let constr_shape = if self.nested_if { // We are part of an if-elseif-else chain. Our constraints are tightened. // 7 = "} else " .len() - fresh_shape.offset_left(7)? + fresh_shape + .offset_left(7) + .max_width_error(fresh_shape.width, self.span)? } else { fresh_shape }; @@ -970,7 +988,7 @@ impl<'a> ControlFlow<'a> { if let Some(cond_str) = trial { if cond_str.len() <= context.config.single_line_if_else_max_width() { - return Some((cond_str, 0)); + return Ok((cond_str, 0)); } } } @@ -1023,7 +1041,7 @@ impl<'a> ControlFlow<'a> { label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 }; - Some(( + Ok(( format!( "{}{}{}{}{}", label_string, @@ -1089,13 +1107,17 @@ pub(crate) fn rewrite_else_kw_with_comments( impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let (cond_str, used_width) = self.rewrite_cond(context, shape, alt_block_sep)?; // If `used_width` is 0, it indicates that whole control flow is written in a single line. if used_width == 0 { - return Some(cond_str); + return Ok(cond_str); } let block_width = shape.width.saturating_sub(used_width); @@ -1139,7 +1161,7 @@ impl<'a> Rewrite for ControlFlow<'a> { true, mk_sp(else_block.span.lo(), self.span.hi()), ) - .rewrite(context, shape) + .rewrite_result(context, shape) } _ => { last_in_chain = true; @@ -1164,7 +1186,7 @@ impl<'a> Rewrite for ControlFlow<'a> { result.push_str(&rewrite?); } - Some(result) + Ok(result) } } @@ -1177,7 +1199,7 @@ fn rewrite_label(context: &RewriteContext<'_>, opt_label: Option<ast::Label>) -> fn extract_comment(span: Span, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { match rewrite_missing_comment(span, shape, context) { - Some(ref comment) if !comment.is_empty() => Some(format!( + Ok(ref comment) if !comment.is_empty() => Some(format!( "{indent}{comment}{indent}", indent = shape.indent.to_string_with_newline(context.config) )), @@ -1248,7 +1270,7 @@ pub(crate) fn rewrite_literal( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { match token_lit.kind { token::LitKind::Str => rewrite_string_lit(context, span, shape), token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), @@ -1256,11 +1278,12 @@ pub(crate) fn rewrite_literal( context.snippet(span).to_owned(), context.config.max_width(), shape, - ), + ) + .max_width_error(shape.width, span), } } -fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> Option<String> { +fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> RewriteResult { let string_lit = context.snippet(span); if !context.config.format_strings() { @@ -1268,11 +1291,12 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> .lines() .dropping_back(1) .all(|line| line.ends_with('\\')) - && context.config.version() == Version::Two + && context.config.style_edition() >= StyleEdition::Edition2024 { - return Some(string_lit.to_owned()); + return Ok(string_lit.to_owned()); } else { - return wrap_str(string_lit.to_owned(), context.config.max_width(), shape); + return wrap_str(string_lit.to_owned(), context.config.max_width(), shape) + .max_width_error(shape.width, span); } } @@ -1284,6 +1308,7 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> &StringFormat::new(shape.visual_indent(0), context.config), shape.width.saturating_sub(2), ) + .max_width_error(shape.width, span) } fn rewrite_int_lit( @@ -1291,7 +1316,7 @@ fn rewrite_int_lit( token_lit: token::Lit, span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let symbol = token_lit.symbol.as_str(); if let Some(symbol_stripped) = symbol.strip_prefix("0x") { @@ -1305,11 +1330,12 @@ fn rewrite_int_lit( format!( "0x{}{}", hex_lit, - token_lit.suffix.map_or(String::new(), |s| s.to_string()) + token_lit.suffix.as_ref().map_or("", |s| s.as_str()) ), context.config.max_width(), shape, - ); + ) + .max_width_error(shape.width, span); } } @@ -1318,6 +1344,7 @@ fn rewrite_int_lit( context.config.max_width(), shape, ) + .max_width_error(shape.width, span) } fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option<SeparatorTactic> { @@ -1338,7 +1365,7 @@ pub(crate) fn rewrite_call( args: &[ptr::P<ast::Expr>], span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { overflow::rewrite_with_parens( context, callee, @@ -1458,7 +1485,7 @@ pub(crate) fn rewrite_paren( mut subexpr: &ast::Expr, shape: Shape, mut span: Span, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_paren, shape: {:?}", shape); // Extract comments within parens. @@ -1487,11 +1514,14 @@ pub(crate) fn rewrite_paren( } // 1 = `(` and `)` - let sub_shape = shape.offset_left(1)?.sub_width(1)?; - let subexpr_str = subexpr.rewrite(context, sub_shape)?; + let sub_shape = shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, span)?; + let subexpr_str = subexpr.rewrite_result(context, sub_shape)?; let fits_single_line = !pre_comment.contains("//") && !post_comment.contains("//"); if fits_single_line { - Some(format!("({pre_comment}{subexpr_str}{post_comment})")) + Ok(format!("({pre_comment}{subexpr_str}{post_comment})")) } else { rewrite_paren_in_multi_line(context, subexpr, shape, pre_span, post_span) } @@ -1503,12 +1533,12 @@ fn rewrite_paren_in_multi_line( shape: Shape, pre_span: Span, post_span: Span, -) -> Option<String> { +) -> RewriteResult { let nested_indent = shape.indent.block_indent(context.config); let nested_shape = Shape::indented(nested_indent, context.config); let pre_comment = rewrite_missing_comment(pre_span, nested_shape, context)?; let post_comment = rewrite_missing_comment(post_span, nested_shape, context)?; - let subexpr_str = subexpr.rewrite(context, nested_shape)?; + let subexpr_str = subexpr.rewrite_result(context, nested_shape)?; let mut result = String::with_capacity(subexpr_str.len() * 2); result.push('('); @@ -1525,7 +1555,7 @@ fn rewrite_paren_in_multi_line( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push(')'); - Some(result) + Ok(result) } fn rewrite_index( @@ -1533,8 +1563,8 @@ fn rewrite_index( index: &ast::Expr, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - let expr_str = expr.rewrite(context, shape)?; +) -> RewriteResult { + let expr_str = expr.rewrite_result(context, shape)?; let offset = last_line_width(&expr_str) + 1; let rhs_overhead = shape.rhs_overhead(context.config); @@ -1549,37 +1579,45 @@ fn rewrite_index( .and_then(|shape| shape.sub_width(1)), IndentStyle::Visual => shape.visual_indent(offset).sub_width(offset + 1), } - }; - let orig_index_rw = index_shape.and_then(|s| index.rewrite(context, s)); + } + .max_width_error(shape.width, index.span()); + let orig_index_rw = index_shape.and_then(|s| index.rewrite_result(context, s)); // Return if index fits in a single line. match orig_index_rw { - Some(ref index_str) if !index_str.contains('\n') => { - return Some(format!("{expr_str}[{index_str}]")); + Ok(ref index_str) if !index_str.contains('\n') => { + return Ok(format!("{expr_str}[{index_str}]")); } _ => (), } // Try putting index on the next line and see if it fits in a single line. let indent = shape.indent.block_indent(context.config); - let index_shape = Shape::indented(indent, context.config).offset_left(1)?; - let index_shape = index_shape.sub_width(1 + rhs_overhead)?; - let new_index_rw = index.rewrite(context, index_shape); + let index_shape = Shape::indented(indent, context.config) + .offset_left(1) + .max_width_error(shape.width, index.span())?; + let index_shape = index_shape + .sub_width(1 + rhs_overhead) + .max_width_error(index_shape.width, index.span())?; + let new_index_rw = index.rewrite_result(context, index_shape); match (orig_index_rw, new_index_rw) { - (_, Some(ref new_index_str)) if !new_index_str.contains('\n') => Some(format!( + (_, Ok(ref new_index_str)) if !new_index_str.contains('\n') => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (None, Some(ref new_index_str)) => Some(format!( + (Err(_), Ok(ref new_index_str)) => Ok(format!( "{}{}[{}]", expr_str, indent.to_string_with_newline(context.config), new_index_str, )), - (Some(ref index_str), _) => Some(format!("{expr_str}[{index_str}]")), - _ => None, + (Ok(ref index_str), _) => Ok(format!("{expr_str}[{index_str}]")), + // When both orig_index_rw and new_index_rw result in errors, we currently propagate the + // error from the second attempt since it is more generous with width constraints. + // This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_index_rw_err)) => Err(new_index_rw_err), } } @@ -1596,7 +1634,7 @@ fn rewrite_struct_lit<'a>( attrs: &[ast::Attribute], span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_struct_lit: shape {:?}", shape); enum StructLitField<'a> { @@ -1606,20 +1644,21 @@ fn rewrite_struct_lit<'a>( } // 2 = " {".len() - let path_shape = shape.sub_width(2)?; + let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?; let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; let has_base_or_rest = match struct_rest { - ast::StructRest::None if fields.is_empty() => return Some(format!("{path_str} {{}}")), + ast::StructRest::None if fields.is_empty() => return Ok(format!("{path_str} {{}}")), ast::StructRest::Rest(_) if fields.is_empty() => { - return Some(format!("{path_str} {{ .. }}")); + return Ok(format!("{path_str} {{ .. }}")); } ast::StructRest::Rest(_) | ast::StructRest::Base(_) => true, _ => false, }; // Foo { a: Foo } - indent is +3, width is -5. - let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2)?; + let (h_shape, v_shape) = struct_lit_shape(shape, context, path_str.len() + 3, 2) + .max_width_error(shape.width, span)?; let one_line_width = h_shape.map_or(0, |shape| shape.width); let body_lo = context.snippet_provider.span_after(span, "{"); @@ -1632,7 +1671,8 @@ fn rewrite_struct_lit<'a>( v_shape, mk_sp(body_lo, span.hi()), one_line_width, - )? + ) + .unknown_error()? } else { let field_iter = fields.iter().map(StructLitField::Regular).chain( match struct_rest { @@ -1661,14 +1701,24 @@ fn rewrite_struct_lit<'a>( let rewrite = |item: &StructLitField<'_>| match *item { StructLitField::Regular(field) => { // The 1 taken from the v_budget is for the comma. - rewrite_field(context, field, v_shape.sub_width(1)?, 0) + rewrite_field( + context, + field, + v_shape.sub_width(1).max_width_error(v_shape.width, span)?, + 0, + ) } StructLitField::Base(expr) => { // 2 = .. - expr.rewrite(context, v_shape.offset_left(2)?) - .map(|s| format!("..{}", s)) + expr.rewrite_result( + context, + v_shape + .offset_left(2) + .max_width_error(v_shape.width, span)?, + ) + .map(|s| format!("..{}", s)) } - StructLitField::Rest(_) => Some("..".to_owned()), + StructLitField::Rest(_) => Ok("..".to_owned()), }; let items = itemize_list( @@ -1703,7 +1753,7 @@ fn rewrite_struct_lit<'a>( let fields_str = wrap_struct_field(context, attrs, &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{path_str} {{{fields_str}}}")) + Ok(format!("{path_str} {{{fields_str}}}")) // FIXME if context.config.indent_style() == Visual, but we run out // of space, we should fall back to BlockIndent. @@ -1716,7 +1766,7 @@ pub(crate) fn wrap_struct_field( shape: Shape, nested_shape: Shape, one_line_width: usize, -) -> Option<String> { +) -> RewriteResult { let should_vertical = context.config.indent_style() == IndentStyle::Block && (fields_str.contains('\n') || !context.config.struct_lit_single_line() @@ -1725,7 +1775,7 @@ pub(crate) fn wrap_struct_field( let inner_attrs = &inner_attributes(attrs); if inner_attrs.is_empty() { if should_vertical { - Some(format!( + Ok(format!( "{}{}{}", nested_shape.indent.to_string_with_newline(context.config), fields_str, @@ -1733,13 +1783,13 @@ pub(crate) fn wrap_struct_field( )) } else { // One liner or visual indent. - Some(format!(" {fields_str} ")) + Ok(format!(" {fields_str} ")) } } else { - Some(format!( + Ok(format!( "{}{}{}{}{}", nested_shape.indent.to_string_with_newline(context.config), - inner_attrs.rewrite(context, shape)?, + inner_attrs.rewrite_result(context, shape)?, nested_shape.indent.to_string_with_newline(context.config), fields_str, shape.indent.to_string_with_newline(context.config) @@ -1756,38 +1806,40 @@ pub(crate) fn rewrite_field( field: &ast::ExprField, shape: Shape, prefix_max_width: usize, -) -> Option<String> { +) -> RewriteResult { if contains_skip(&field.attrs) { - return Some(context.snippet(field.span()).to_owned()); + return Ok(context.snippet(field.span()).to_owned()); } - let mut attrs_str = field.attrs.rewrite(context, shape)?; + let mut attrs_str = field.attrs.rewrite_result(context, shape)?; if !attrs_str.is_empty() { attrs_str.push_str(&shape.indent.to_string_with_newline(context.config)); }; let name = context.snippet(field.ident.span); if field.is_shorthand { - Some(attrs_str + name) + Ok(attrs_str + name) } else { let mut separator = String::from(struct_lit_field_separator(context.config)); for _ in 0..prefix_max_width.saturating_sub(name.len()) { separator.push(' '); } let overhead = name.len() + separator.len(); - let expr_shape = shape.offset_left(overhead)?; - let expr = field.expr.rewrite(context, expr_shape); + let expr_shape = shape + .offset_left(overhead) + .max_width_error(shape.width, field.span)?; + let expr = field.expr.rewrite_result(context, expr_shape); let is_lit = matches!(field.expr.kind, ast::ExprKind::Lit(_)); match expr { - Some(ref e) + Ok(ref e) if !is_lit && e.as_str() == name && context.config.use_field_init_shorthand() => { - Some(attrs_str + name) + Ok(attrs_str + name) } - Some(e) => Some(format!("{attrs_str}{name}{separator}{e}")), - None => { + Ok(e) => Ok(format!("{attrs_str}{name}{separator}{e}")), + Err(_) => { let expr_offset = shape.indent.block_indent(context.config); let expr = field .expr - .rewrite(context, Shape::indented(expr_offset, context.config)); + .rewrite_result(context, Shape::indented(expr_offset, context.config)); expr.map(|s| { format!( "{}{}:\n{}{}", @@ -1808,21 +1860,27 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option<String> { +) -> RewriteResult { // In case of length 1, need a trailing comma debug!("rewrite_tuple_in_visual_indent_style {:?}", shape); if is_singleton_tuple { // 3 = "(" + ",)" - let nested_shape = shape.sub_width(3)?.visual_indent(1); + let nested_shape = shape + .sub_width(3) + .max_width_error(shape.width, span)? + .visual_indent(1); return items .next() .unwrap() - .rewrite(context, nested_shape) + .rewrite_result(context, nested_shape) .map(|s| format!("({},)", s)); } let list_lo = context.snippet_provider.span_after(span, "("); - let nested_shape = shape.sub_width(2)?.visual_indent(1); + let nested_shape = shape + .sub_width(2) + .max_width_error(shape.width, span)? + .visual_indent(1); let items = itemize_list( context.snippet_provider, items, @@ -1830,7 +1888,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(context, nested_shape), + |item| item.rewrite_result(context, nested_shape), list_lo, span.hi() - BytePos(1), false, @@ -1847,7 +1905,7 @@ fn rewrite_tuple_in_visual_indent_style<'a, T: 'a + IntoOverflowableItem<'a>>( .ends_with_newline(false); let list_str = write_list(&item_vec, &fmt)?; - Some(format!("({list_str})")) + Ok(format!("({list_str})")) } fn rewrite_let( @@ -1855,14 +1913,16 @@ fn rewrite_let( shape: Shape, pat: &ast::Pat, expr: &ast::Expr, -) -> Option<String> { +) -> RewriteResult { let mut result = "let ".to_owned(); // TODO(ytmimi) comments could appear between `let` and the `pat` // 4 = "let ".len() - let pat_shape = shape.offset_left(4)?; - let pat_str = pat.rewrite(context, pat_shape)?; + let pat_shape = shape + .offset_left(4) + .max_width_error(shape.width, pat.span)?; + let pat_str = pat.rewrite_result(context, pat_shape)?; result.push_str(&pat_str); // TODO(ytmimi) comments could appear between `pat` and `=` @@ -1890,7 +1950,7 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, shape: Shape, is_singleton_tuple: bool, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_tuple {:?}", shape); if context.use_block_indent() { // We use the same rule as function calls for rewriting tuples. @@ -1919,31 +1979,35 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( } } -pub(crate) fn rewrite_unary_prefix<R: Rewrite>( +pub(crate) fn rewrite_unary_prefix<R: Rewrite + Spanned>( context: &RewriteContext<'_>, prefix: &str, rewrite: &R, shape: Shape, -) -> Option<String> { +) -> RewriteResult { + let shape = shape + .offset_left(prefix.len()) + .max_width_error(shape.width, rewrite.span())?; rewrite - .rewrite(context, shape.offset_left(prefix.len())?) + .rewrite_result(context, shape) .map(|r| format!("{}{}", prefix, r)) } // FIXME: this is probably not correct for multi-line Rewrites. we should // subtract suffix.len() from the last line budget, not the first! -pub(crate) fn rewrite_unary_suffix<R: Rewrite>( +pub(crate) fn rewrite_unary_suffix<R: Rewrite + Spanned>( context: &RewriteContext<'_>, suffix: &str, rewrite: &R, shape: Shape, -) -> Option<String> { - rewrite - .rewrite(context, shape.sub_width(suffix.len())?) - .map(|mut r| { - r.push_str(suffix); - r - }) +) -> RewriteResult { + let shape = shape + .sub_width(suffix.len()) + .max_width_error(shape.width, rewrite.span())?; + rewrite.rewrite_result(context, shape).map(|mut r| { + r.push_str(suffix); + r + }) } fn rewrite_unary_op( @@ -1951,7 +2015,7 @@ fn rewrite_unary_op( op: ast::UnOp, expr: &ast::Expr, shape: Shape, -) -> Option<String> { +) -> RewriteResult { // For some reason, an UnOp is not spanned like BinOp! rewrite_unary_prefix(context, op.as_str(), expr, shape) } @@ -1990,15 +2054,21 @@ fn rewrite_assignment( rhs: &ast::Expr, op: Option<&ast::BinOp>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let operator_str = match op { Some(op) => context.snippet(op.span), None => "=", }; // 1 = space between lhs and operator. - let lhs_shape = shape.sub_width(operator_str.len() + 1)?; - let lhs_str = format!("{} {}", lhs.rewrite(context, lhs_shape)?, operator_str); + let lhs_shape = shape + .sub_width(operator_str.len() + 1) + .max_width_error(shape.width, lhs.span())?; + let lhs_str = format!( + "{} {}", + lhs.rewrite_result(context, lhs_shape)?, + operator_str + ); rewrite_assign_rhs( context, @@ -2029,7 +2099,7 @@ pub(crate) fn rewrite_assign_rhs<S: Into<String>, R: Rewrite>( ex: &R, rhs_kind: &RhsAssignKind<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { rewrite_assign_rhs_with(context, lhs, ex, shape, rhs_kind, RhsTactics::Default) } @@ -2040,7 +2110,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option<String> { +) -> RewriteResult { let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { shape.indent.width() } else { @@ -2062,7 +2132,7 @@ pub(crate) fn rewrite_assign_rhs_expr<R: Rewrite>( context, ex, orig_shape, - ex.rewrite(context, orig_shape), + ex.rewrite_result(context, orig_shape), rhs_kind, rhs_tactics, has_rhs_comment, @@ -2076,13 +2146,13 @@ pub(crate) fn rewrite_assign_rhs_with<S: Into<String>, R: Rewrite>( shape: Shape, rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, -) -> Option<String> { +) -> RewriteResult { let lhs = lhs.into(); let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - Some(lhs + &rhs) + Ok(lhs + &rhs) } -pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>( +pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite + Spanned>( context: &RewriteContext<'_>, lhs: S, ex: &R, @@ -2091,21 +2161,22 @@ pub(crate) fn rewrite_assign_rhs_with_comments<S: Into<String>, R: Rewrite>( rhs_tactics: RhsTactics, between_span: Span, allow_extend: bool, -) -> Option<String> { +) -> RewriteResult { let lhs = lhs.into(); let contains_comment = contains_comment(context.snippet(between_span)); let shape = if contains_comment { - shape.block_left(context.config.tab_spaces())? + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, between_span.with_hi(ex.span().hi()))? } else { shape }; let rhs = rewrite_assign_rhs_expr(context, &lhs, ex, shape, rhs_kind, rhs_tactics)?; - if contains_comment { let rhs = rhs.trim_start(); combine_strs_with_missing_comments(context, &lhs, rhs, between_span, shape, allow_extend) } else { - Some(lhs + &rhs) + Ok(lhs + &rhs) } } @@ -2113,23 +2184,25 @@ fn choose_rhs<R: Rewrite>( context: &RewriteContext<'_>, expr: &R, shape: Shape, - orig_rhs: Option<String>, + orig_rhs: RewriteResult, _rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, has_rhs_comment: bool, -) -> Option<String> { +) -> RewriteResult { match orig_rhs { - Some(ref new_str) if new_str.is_empty() => Some(String::new()), - Some(ref new_str) - if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => - { - Some(format!(" {new_str}")) + Ok(ref new_str) if new_str.is_empty() => Ok(String::new()), + Ok(ref new_str) if !new_str.contains('\n') && unicode_str_width(new_str) <= shape.width => { + Ok(format!(" {new_str}")) } _ => { // Expression did not fit on the same line as the identifier. // Try splitting the line and see if that works better. - let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics)?; - let new_rhs = expr.rewrite(context, new_shape); + let new_shape = shape_from_rhs_tactic(context, shape, rhs_tactics) + // TODO(ding-young) Ideally, we can replace unknown_error() with max_width_error(), + // but this requires either implementing the Spanned trait for ast::GenericBounds + // or grabbing the span from the call site. + .unknown_error()?; + let new_rhs = expr.rewrite_result(context, new_shape); let new_indent_str = &shape .indent .block_indent(context.config) @@ -2137,24 +2210,27 @@ fn choose_rhs<R: Rewrite>( let before_space_str = if has_rhs_comment { "" } else { " " }; match (orig_rhs, new_rhs) { - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => { - Some(format!("{before_space_str}{orig_rhs}")) + Ok(format!("{before_space_str}{orig_rhs}")) } - (Some(ref orig_rhs), Some(ref new_rhs)) + (Ok(ref orig_rhs), Ok(ref new_rhs)) if prefer_next_line(orig_rhs, new_rhs, rhs_tactics) => { - Some(format!("{new_indent_str}{new_rhs}")) + Ok(format!("{new_indent_str}{new_rhs}")) } - (None, Some(ref new_rhs)) => Some(format!("{new_indent_str}{new_rhs}")), - (None, None) if rhs_tactics == RhsTactics::AllowOverflow => { + (Err(_), Ok(ref new_rhs)) => Ok(format!("{new_indent_str}{new_rhs}")), + (Err(_), Err(_)) if rhs_tactics == RhsTactics::AllowOverflow => { let shape = shape.infinite_width(); - expr.rewrite(context, shape) + expr.rewrite_result(context, shape) .map(|s| format!("{}{}", before_space_str, s)) } - (None, None) => None, - (Some(orig_rhs), _) => Some(format!("{before_space_str}{orig_rhs}")), + // When both orig_rhs and new_rhs result in errors, we currently propagate + // the error from the second attempt since it is more generous with + // width constraints. This decision is somewhat arbitrary and is open to change. + (Err(_), Err(new_rhs_err)) => Err(new_rhs_err), + (Ok(orig_rhs), _) => Ok(format!("{before_space_str}{orig_rhs}")), } } } @@ -2204,7 +2280,7 @@ fn rewrite_expr_addrof( mutability: ast::Mutability, expr: &ast::Expr, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let operator_str = match (mutability, borrow_kind) { (ast::Mutability::Not, ast::BorrowKind::Ref) => "&", (ast::Mutability::Not, ast::BorrowKind::Raw) => "&raw const ", diff --git a/src/tools/rustfmt/src/formatting.rs b/src/tools/rustfmt/src/formatting.rs index 5e71fe107eb..1e1e329f624 100644 --- a/src/tools/rustfmt/src/formatting.rs +++ b/src/tools/rustfmt/src/formatting.rs @@ -17,7 +17,7 @@ use crate::parse::parser::{DirectoryOwnership, Parser, ParserError}; use crate::parse::session::ParseSess; use crate::utils::{contains_skip, count_newlines}; use crate::visitor::FmtVisitor; -use crate::{modules, source_file, ErrorKind, FormatReport, Input, Session}; +use crate::{ErrorKind, FormatReport, Input, Session, modules, source_file}; mod generated; mod newline_style; diff --git a/src/tools/rustfmt/src/git-rustfmt/main.rs b/src/tools/rustfmt/src/git-rustfmt/main.rs index b8b0432aa95..64458420f59 100644 --- a/src/tools/rustfmt/src/git-rustfmt/main.rs +++ b/src/tools/rustfmt/src/git-rustfmt/main.rs @@ -13,7 +13,9 @@ use rustfmt_nightly as rustfmt; use tracing::debug; use tracing_subscriber::EnvFilter; -use crate::rustfmt::{load_config, CliOptions, FormatReportFormatterBuilder, Input, Session}; +use crate::rustfmt::{ + CliOptions, FormatReportFormatterBuilder, Input, Session, Version, load_config, +}; fn prune_files(files: Vec<&str>) -> Vec<&str> { let prefixes: Vec<_> = files @@ -87,6 +89,15 @@ impl CliOptions for NullOptions { fn config_path(&self) -> Option<&Path> { unreachable!(); } + fn edition(&self) -> Option<rustfmt_nightly::Edition> { + unreachable!(); + } + fn style_edition(&self) -> Option<rustfmt_nightly::StyleEdition> { + unreachable!(); + } + fn version(&self) -> Option<Version> { + unreachable!(); + } } fn uncommitted_files() -> Vec<String> { diff --git a/src/tools/rustfmt/src/ignore_path.rs b/src/tools/rustfmt/src/ignore_path.rs index 5c25f233ce3..2f804ef5a25 100644 --- a/src/tools/rustfmt/src/ignore_path.rs +++ b/src/tools/rustfmt/src/ignore_path.rs @@ -41,8 +41,11 @@ mod test { use crate::ignore_path::IgnorePathSet; use std::path::{Path, PathBuf}; - let config = - Config::from_toml(r#"ignore = ["foo.rs", "bar_dir/*"]"#, Path::new("")).unwrap(); + let config = Config::from_toml( + r#"ignore = ["foo.rs", "bar_dir/*"]"#, + Path::new("./rustfmt.toml"), + ) + .unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); assert!(ignore_path_set.is_match(&FileName::Real(PathBuf::from("src/foo.rs")))); @@ -59,7 +62,7 @@ mod test { let config = Config::from_toml( r#"ignore = ["foo.rs", "bar_dir/*", "!bar_dir/*/what.rs"]"#, - Path::new(""), + Path::new("./rustfmt.toml"), ) .unwrap(); let ignore_path_set = IgnorePathSet::from_ignore_list(&config.ignore()).unwrap(); diff --git a/src/tools/rustfmt/src/imports.rs b/src/tools/rustfmt/src/imports.rs index 12c178e136f..b741dd9b5da 100644 --- a/src/tools/rustfmt/src/imports.rs +++ b/src/tools/rustfmt/src/imports.rs @@ -8,19 +8,20 @@ use itertools::Itertools; use rustc_ast::ast::{self, UseTreeKind}; use rustc_span::{ + BytePos, DUMMY_SP, Span, symbol::{self, sym}, - BytePos, Span, DUMMY_SP, }; use crate::comment::combine_strs_with_missing_comments; -use crate::config::lists::*; use crate::config::ImportGranularity; -use crate::config::{Edition, IndentStyle, Version}; +use crate::config::lists::*; +use crate::config::{Edition, IndentStyle, StyleEdition}; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; +use crate::sort::version_sort; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{is_same_visibility, mk_sp, rewrite_ident}; @@ -44,7 +45,8 @@ impl<'a> FmtVisitor<'a> { Some(item.span.lo()), Some(item.attrs.clone()), ) - .rewrite_top_level(&self.get_context(), shape); + .rewrite_top_level(&self.get_context(), shape) + .ok(); match rw { Some(ref s) if s.is_empty() => { // Format up to last newline @@ -104,7 +106,7 @@ pub(crate) enum UseSegmentKind { #[derive(Clone, Eq, PartialEq)] pub(crate) struct UseSegment { pub(crate) kind: UseSegmentKind, - pub(crate) version: Version, + pub(crate) style_edition: StyleEdition, } #[derive(Clone)] @@ -149,7 +151,7 @@ impl UseSegment { }; UseSegment { kind, - version: self.version, + style_edition: self.style_edition, } } @@ -197,7 +199,7 @@ impl UseSegment { Some(UseSegment { kind, - version: context.config.version(), + style_edition: context.config.style_edition(), }) } @@ -331,12 +333,17 @@ impl UseTree { &self, context: &RewriteContext<'_>, shape: Shape, - ) -> Option<String> { + ) -> RewriteResult { let vis = self.visibility.as_ref().map_or(Cow::from(""), |vis| { crate::utils::format_visibility(context, vis) }); let use_str = self - .rewrite(context, shape.offset_left(vis.len())?) + .rewrite_result( + context, + shape + .offset_left(vis.len()) + .max_width_error(shape.width, self.span())?, + ) .map(|s| { if s.is_empty() { s @@ -346,8 +353,8 @@ impl UseTree { })?; match self.attrs { Some(ref attrs) if !attrs.is_empty() => { - let attr_str = attrs.rewrite(context, shape)?; - let lo = attrs.last().as_ref()?.span.hi(); + let attr_str = attrs.rewrite_result(context, shape)?; + let lo = attrs.last().unknown_error()?.span.hi(); let hi = self.span.lo(); let span = mk_sp(lo, hi); @@ -368,7 +375,7 @@ impl UseTree { allow_extend, ) } - _ => Some(use_str), + _ => Ok(use_str), } } @@ -444,18 +451,21 @@ impl UseTree { } } - let version = context.config.version(); + let style_edition = context.config.style_edition(); match a.kind { UseTreeKind::Glob => { // in case of a global path and the glob starts at the root, e.g., "::*" if a.prefix.segments.len() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } result.path.push(UseSegment { kind: UseSegmentKind::Glob, - version, + style_edition, }); } UseTreeKind::Nested { @@ -470,7 +480,7 @@ impl UseTree { ",", |tree| tree.span.lo(), |tree| tree.span.hi(), - |_| Some("".to_owned()), // We only need comments for now. + |_| Ok("".to_owned()), // We only need comments for now. context.snippet_provider.span_after(a.span, "{"), a.span.hi(), false, @@ -480,7 +490,10 @@ impl UseTree { // e.g., "::{foo, bar}" if a.prefix.segments.len() == 1 && leading_modsep { let kind = UseSegmentKind::Ident("".to_owned(), None); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } let kind = UseSegmentKind::List( list.iter() @@ -490,7 +503,10 @@ impl UseTree { }) .collect(), ); - result.path.push(UseSegment { kind, version }); + result.path.push(UseSegment { + kind, + style_edition, + }); } UseTreeKind::Simple(ref rename) => { // If the path has leading double colons and is composed of only 2 segments, then we @@ -519,7 +535,10 @@ impl UseTree { _ => UseSegmentKind::Ident(name, alias), }; - let segment = UseSegment { kind, version }; + let segment = UseSegment { + kind, + style_edition, + }; // `name` is already in result. result.path.pop(); @@ -614,7 +633,7 @@ impl UseTree { list.sort(); last = UseSegment { kind: UseSegmentKind::List(list), - version: last.version, + style_edition: last.style_edition, }; } @@ -732,9 +751,12 @@ impl UseTree { }) = self.path.last() { let self_segment = self.path.pop().unwrap(); - let version = self_segment.version; + let style_edition = self_segment.style_edition; let kind = UseSegmentKind::List(vec![UseTree::from_path(vec![self_segment], DUMMY_SP)]); - self.path.push(UseSegment { kind, version }); + self.path.push(UseSegment { + kind, + style_edition, + }); } self } @@ -750,7 +772,7 @@ fn merge_rest( return None; } if a.len() != len && b.len() != len { - let version = a[len].version; + let style_edition = a[len].style_edition; if let UseSegmentKind::List(ref list) = a[len].kind { let mut list = list.clone(); merge_use_trees_inner( @@ -760,7 +782,10 @@ fn merge_rest( ); let mut new_path = b[..len].to_vec(); let kind = UseSegmentKind::List(list); - new_path.push(UseSegment { kind, version }); + new_path.push(UseSegment { + kind, + style_edition, + }); return Some(new_path); } } else if len == 1 { @@ -770,9 +795,12 @@ fn merge_rest( (&b[0], &a[1..]) }; let kind = UseSegmentKind::Slf(common.get_alias().map(ToString::to_string)); - let version = a[0].version; + let style_edition = a[0].style_edition; let mut list = vec![UseTree::from_path( - vec![UseSegment { kind, version }], + vec![UseSegment { + kind, + style_edition, + }], DUMMY_SP, )]; match rest { @@ -788,7 +816,7 @@ fn merge_rest( b[0].clone(), UseSegment { kind: UseSegmentKind::List(list), - version, + style_edition, }, ]); } else { @@ -801,8 +829,11 @@ fn merge_rest( list.sort(); let mut new_path = b[..len].to_vec(); let kind = UseSegmentKind::List(list); - let version = a[0].version; - new_path.push(UseSegment { kind, version }); + let style_edition = a[0].style_edition; + new_path.push(UseSegment { + kind, + style_edition, + }); Some(new_path) } @@ -892,8 +923,8 @@ impl Ord for UseSegment { | (Super(ref a), Super(ref b)) | (Crate(ref a), Crate(ref b)) => match (a, b) { (Some(sa), Some(sb)) => { - if self.version == Version::Two { - sa.trim_start_matches("r#").cmp(sb.trim_start_matches("r#")) + if self.style_edition >= StyleEdition::Edition2024 { + version_sort(sa.trim_start_matches("r#"), sb.trim_start_matches("r#")) } else { a.cmp(b) } @@ -902,25 +933,31 @@ impl Ord for UseSegment { }, (Glob, Glob) => Ordering::Equal, (Ident(ref pia, ref aa), Ident(ref pib, ref ab)) => { - let (ia, ib) = if self.version == Version::Two { + let (ia, ib) = if self.style_edition >= StyleEdition::Edition2024 { (pia.trim_start_matches("r#"), pib.trim_start_matches("r#")) } else { (pia.as_str(), pib.as_str()) }; - // snake_case < CamelCase < UPPER_SNAKE_CASE - if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { - return Ordering::Greater; - } - if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { - return Ordering::Less; - } - if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { - return Ordering::Greater; - } - if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { - return Ordering::Less; - } - let ident_ord = ia.cmp(ib); + + let ident_ord = if self.style_edition >= StyleEdition::Edition2024 { + version_sort(ia, ib) + } else { + // snake_case < CamelCase < UPPER_SNAKE_CASE + if ia.starts_with(char::is_uppercase) && ib.starts_with(char::is_lowercase) { + return Ordering::Greater; + } + if ia.starts_with(char::is_lowercase) && ib.starts_with(char::is_uppercase) { + return Ordering::Less; + } + if is_upper_snake_case(ia) && !is_upper_snake_case(ib) { + return Ordering::Greater; + } + if !is_upper_snake_case(ia) && is_upper_snake_case(ib) { + return Ordering::Less; + } + ia.cmp(ib) + }; + if ident_ord != Ordering::Equal { return ident_ord; } @@ -928,9 +965,8 @@ impl Ord for UseSegment { (None, Some(_)) => Ordering::Less, (Some(_), None) => Ordering::Greater, (Some(aas), Some(abs)) => { - if self.version == Version::Two { - aas.trim_start_matches("r#") - .cmp(abs.trim_start_matches("r#")) + if self.style_edition >= StyleEdition::Edition2024 { + version_sort(aas.trim_start_matches("r#"), abs.trim_start_matches("r#")) } else { aas.cmp(abs) } @@ -982,21 +1018,24 @@ fn rewrite_nested_use_tree( context: &RewriteContext<'_>, use_tree_list: &[UseTree], shape: Shape, -) -> Option<String> { +) -> RewriteResult { let mut list_items = Vec::with_capacity(use_tree_list.len()); let nested_shape = match context.config.imports_indent() { IndentStyle::Block => shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config) - .sub_width(1)?, + .sub_width(1) + .unknown_error()?, IndentStyle::Visual => shape.visual_indent(0), }; for use_tree in use_tree_list { if let Some(mut list_item) = use_tree.list_item.clone() { - list_item.item = use_tree.rewrite(context, nested_shape); + list_item.item = use_tree.rewrite_result(context, nested_shape); list_items.push(list_item); } else { - list_items.push(ListItem::from_str(use_tree.rewrite(context, nested_shape)?)); + list_items.push(ListItem::from_str( + use_tree.rewrite_result(context, nested_shape)?, + )); } } let has_nested_list = use_tree_list.iter().any(|use_segment| { @@ -1049,12 +1088,16 @@ fn rewrite_nested_use_tree( format!("{{{list_str}}}") }; - Some(result) + Ok(result) } impl Rewrite for UseSegment { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - Some(match self.kind { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + Ok(match self.kind { UseSegmentKind::Ident(ref ident, Some(ref rename)) => { format!("{ident} as {rename}") } @@ -1066,31 +1109,42 @@ impl Rewrite for UseSegment { UseSegmentKind::Crate(Some(ref rename)) => format!("crate as {rename}"), UseSegmentKind::Crate(None) => "crate".to_owned(), UseSegmentKind::Glob => "*".to_owned(), - UseSegmentKind::List(ref use_tree_list) => rewrite_nested_use_tree( - context, - use_tree_list, - // 1 = "{" and "}" - shape.offset_left(1)?.sub_width(1)?, - )?, + UseSegmentKind::List(ref use_tree_list) => { + rewrite_nested_use_tree( + context, + use_tree_list, + // 1 = "{" and "}" + shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .unknown_error()?, + )? + } }) } } impl Rewrite for UseTree { + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + // This does NOT format attributes and visibility or add a trailing `;`. - fn rewrite(&self, context: &RewriteContext<'_>, mut shape: Shape) -> Option<String> { + fn rewrite_result(&self, context: &RewriteContext<'_>, mut shape: Shape) -> RewriteResult { let mut result = String::with_capacity(256); let mut iter = self.path.iter().peekable(); while let Some(segment) = iter.next() { - let segment_str = segment.rewrite(context, shape)?; + let segment_str = segment.rewrite_result(context, shape)?; result.push_str(&segment_str); if iter.peek().is_some() { result.push_str("::"); // 2 = "::" - shape = shape.offset_left(2 + segment_str.len())?; + shape = shape + .offset_left(2 + segment_str.len()) + .max_width_error(shape.width, self.span())?; } } - Some(result) + Ok(result) } } @@ -1114,7 +1168,7 @@ mod test { struct Parser<'a> { input: Peekable<Chars<'a>>, - version: Version, + style_edition: StyleEdition, } impl<'a> Parser<'a> { @@ -1132,7 +1186,7 @@ mod test { buf: &mut String, alias_buf: &mut Option<String>, ) { - let version = self.version; + let style_edition = self.style_edition; if !buf.is_empty() { let mut alias = None; swap(alias_buf, &mut alias); @@ -1140,19 +1194,28 @@ mod test { match buf.as_ref() { "self" => { let kind = UseSegmentKind::Slf(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } "super" => { let kind = UseSegmentKind::Super(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } "crate" => { let kind = UseSegmentKind::Crate(alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); *buf = String::new(); *alias_buf = None; } @@ -1160,7 +1223,10 @@ mod test { let mut name = String::new(); swap(buf, &mut name); let kind = UseSegmentKind::Ident(name, alias); - result.push(UseSegment { kind, version }); + result.push(UseSegment { + kind, + style_edition, + }); } } } @@ -1178,7 +1244,7 @@ mod test { let kind = UseSegmentKind::List(self.parse_list()); result.push(UseSegment { kind, - version: self.version, + style_edition: self.style_edition, }); self.eat('}'); } @@ -1188,7 +1254,7 @@ mod test { let kind = UseSegmentKind::Glob; result.push(UseSegment { kind, - version: self.version, + style_edition: self.style_edition, }); } ':' => { @@ -1249,7 +1315,7 @@ mod test { let mut parser = Parser { input: s.chars().peekable(), - version: Version::One, + style_edition: StyleEdition::Edition2015, }; parser.parse_in_list() } diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index cbf7ce90e37..fc043a697e0 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1,31 +1,30 @@ // Formatting top-level items - functions, structs, enums, traits, impls. use std::borrow::Cow; -use std::cmp::{max, min, Ordering}; +use std::cmp::{Ordering, max, min}; use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, DUMMY_SP, Span, symbol}; use tracing::debug; use crate::attr::filter_inline_attrs; use crate::comment::{ - combine_strs_with_missing_comments, contains_comment, is_last_comment_block, + FindUncommented, combine_strs_with_missing_comments, contains_comment, is_last_comment_block, recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, - FindUncommented, }; use crate::config::lists::*; -use crate::config::{BraceStyle, Config, IndentStyle, Version}; +use crate::config::{BraceStyle, Config, IndentStyle, StyleEdition}; use crate::expr::{ - is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, - rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, rewrite_let_else_block, - RhsAssignKind, RhsTactics, + RhsAssignKind, RhsTactics, is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, + rewrite_assign_rhs_with, rewrite_assign_rhs_with_comments, rewrite_else_kw_with_comments, + rewrite_let_else_block, }; -use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::lists::{ListFormatting, Separator, definitive_tactic, itemize_list, write_list}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; @@ -49,18 +48,22 @@ fn type_annotation_separator(config: &Config) -> &str { // let pat: ty = init; or let pat: ty = init else { .. }; impl Rewrite for ast::Local { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { debug!( "Local::rewrite {:?} {} {:?}", self, shape.width, shape.indent ); - skip_out_of_file_lines_range!(context, self.span); + skip_out_of_file_lines_range_err!(context, self.span); if contains_skip(&self.attrs) { - return None; + return Err(RewriteError::SkipFormatting); } - let attrs_str = self.attrs.rewrite(context, shape)?; + let attrs_str = self.attrs.rewrite_result(context, shape)?; let mut result = if attrs_str.is_empty() { "let ".to_owned() } else { @@ -79,10 +82,15 @@ impl Rewrite for ast::Local { let let_kw_offset = result.len() - "let ".len(); // 4 = "let ".len() - let pat_shape = shape.offset_left(4)?; + let pat_shape = shape + .offset_left(4) + .max_width_error(shape.width, self.span())?; // 1 = ; - let pat_shape = pat_shape.sub_width(1)?; - let pat_str = self.pat.rewrite(context, pat_shape)?; + let pat_shape = pat_shape + .sub_width(1) + .max_width_error(shape.width, self.span())?; + let pat_str = self.pat.rewrite_result(context, pat_shape)?; + result.push_str(&pat_str); // String that is placed within the assignment pattern and expression. @@ -96,11 +104,13 @@ impl Rewrite for ast::Local { } else { shape } - .offset_left(last_line_width(&result) + separator.len())? + .offset_left(last_line_width(&result) + separator.len()) + .max_width_error(shape.width, self.span())? // 2 = ` =` - .sub_width(2)?; + .sub_width(2) + .max_width_error(shape.width, self.span())?; - let rewrite = ty.rewrite(context, ty_shape)?; + let rewrite = ty.rewrite_result(context, ty_shape)?; infix.push_str(separator); infix.push_str(&rewrite); @@ -117,7 +127,9 @@ impl Rewrite for ast::Local { if let Some((init, else_block)) = self.kind.init_else_opt() { // 1 = trailing semicolon; - let nested_shape = shape.sub_width(1)?; + let nested_shape = shape + .sub_width(1) + .max_width_error(shape.width, self.span())?; result = rewrite_assign_rhs( context, @@ -131,7 +143,8 @@ impl Rewrite for ast::Local { let else_kw_span = init.span.between(block.span); // Strip attributes and comments to check if newline is needed before the else // keyword from the initializer part. (#5901) - let init_str = if context.config.version() == Version::Two { + let style_edition = context.config.style_edition(); + let init_str = if style_edition >= StyleEdition::Edition2024 { &result[let_kw_offset..] } else { result.as_str() @@ -155,7 +168,8 @@ impl Rewrite for ast::Local { std::cmp::min(shape.width, context.config.single_line_let_else_max_width()); // If available_space hits zero we know for sure this will be a multi-lined block - let assign_str_with_else_kw = if context.config.version() == Version::Two { + let style_edition = context.config.style_edition(); + let assign_str_with_else_kw = if style_edition >= StyleEdition::Edition2024 { &result[let_kw_offset..] } else { result.as_str() @@ -184,7 +198,7 @@ impl Rewrite for ast::Local { } result.push(';'); - Some(result) + Ok(result) } } @@ -420,7 +434,7 @@ impl<'a> FmtVisitor<'a> { let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause); let (result, _, force_newline_brace) = - rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?; + rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style).ok()?; // 2 = ` {` if self.config.brace_style() == BraceStyle::AlwaysNextLine @@ -441,7 +455,7 @@ impl<'a> FmtVisitor<'a> { vis: &ast::Visibility, generics: &ast::Generics, span: Span, - ) -> Option<String> { + ) -> RewriteResult { // Drop semicolon or it will be interpreted as comment. let span = mk_sp(span.lo(), span.hi() - BytePos(1)); let context = self.get_context(); @@ -463,7 +477,7 @@ impl<'a> FmtVisitor<'a> { // Re-attach semicolon result.push(';'); - Some(result) + Ok(result) } pub(crate) fn single_line_fn( @@ -615,7 +629,10 @@ impl<'a> FmtVisitor<'a> { } }, |f| f.span.hi(), - |f| self.format_variant(f, one_line_width, pad_discrim_ident_to), + |f| { + self.format_variant(f, one_line_width, pad_discrim_ident_to) + .unknown_error() + }, body_lo, body_hi, false, @@ -636,7 +653,7 @@ impl<'a> FmtVisitor<'a> { .trailing_separator(self.config.trailing_comma()) .preserve_newline(true); - let list = write_list(&items, &fmt)?; + let list = write_list(&items, &fmt).ok()?; result.push_str(&list); result.push_str(&original_offset.to_string_with_newline(self.config)); result.push('}'); @@ -658,10 +675,10 @@ impl<'a> FmtVisitor<'a> { let context = self.get_context(); let shape = self.shape(); - let attrs_str = if context.config.version() == Version::Two { + let attrs_str = if context.config.style_edition() >= StyleEdition::Edition2024 { field.attrs.rewrite(&context, shape)? } else { - // Version::One formatting that was off by 1. See issue #5801 + // StyleEdition::Edition20{15|18|21} formatting that was off by 1. See issue #5801 field.attrs.rewrite(&context, shape.sub_width(1)?)? }; // sub_width(1) to take the trailing comma into account @@ -693,12 +710,14 @@ impl<'a> FmtVisitor<'a> { shape, &RhsAssignKind::Expr(&ex.kind, ex.span), RhsTactics::AllowOverflow, - )? + ) + .ok()? } else { variant_body }; combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false) + .ok() } fn visit_impl_items(&mut self, items: &[ptr::P<ast::AssocItem>]) { @@ -822,7 +841,8 @@ pub(crate) fn format_impl( where_span_end, self_ty.span.hi(), option, - )?; + ) + .ok()?; // If there is no where-clause, we may have missing comments between the trait name and // the opening brace. @@ -834,7 +854,7 @@ pub(crate) fn format_impl( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); } _ => (), @@ -949,7 +969,7 @@ fn format_impl_ref_and_type( result.push_str(format_defaultness(defaultness)); result.push_str(format_safety(safety)); - let shape = if context.config.version() == Version::Two { + let shape = if context.config.style_edition() >= StyleEdition::Edition2024 { Shape::indented(offset + last_line_width(&result), context.config) } else { generics_shape_from_config( @@ -958,7 +978,7 @@ fn format_impl_ref_and_type( 0, )? }; - let generics_str = rewrite_generics(context, "impl", generics, shape)?; + let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?; result.push_str(&generics_str); result.push_str(format_constness_right(constness)); @@ -1166,7 +1186,7 @@ pub(crate) fn format_trait( let shape = Shape::indented(offset, context.config).offset_left(result.len())?; let generics_str = - rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; + rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape).ok()?; result.push_str(&generics_str); // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. @@ -1187,7 +1207,8 @@ pub(crate) fn format_trait( shape, &RhsAssignKind::Bounds, RhsTactics::ForceNextLineWithoutIndent, - )?; + ) + .ok()?; } // Rewrite where-clause. @@ -1212,7 +1233,8 @@ pub(crate) fn format_trait( None, pos_before_where, option, - )?; + ) + .ok()?; // If the where-clause cannot fit on the same line, // put the where-clause on a new line if !where_clause_str.contains('\n') @@ -1241,7 +1263,7 @@ pub(crate) fn format_trait( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); } _ => (), @@ -1317,7 +1339,11 @@ pub(crate) struct TraitAliasBounds<'a> { impl<'a> Rewrite for TraitAliasBounds<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let generic_bounds_str = self.generic_bounds.rewrite_result(context, shape)?; let mut option = WhereClauseOption::new(true, WhereClauseSpace::None); option.allow_single_line(); @@ -1346,7 +1372,7 @@ impl<'a> Rewrite for TraitAliasBounds<'a> { shape.indent.to_string_with_newline(context.config) }; - Some(format!("{generic_bounds_str}{space}{where_str}")) + Ok(format!("{generic_bounds_str}{space}{where_str}")) } } @@ -1361,7 +1387,7 @@ pub(crate) fn format_trait_alias( let alias = rewrite_ident(context, ident); // 6 = "trait ", 2 = " =" let g_shape = shape.offset_left(6)?.sub_width(2)?; - let generics_str = rewrite_generics(context, alias, generics, g_shape)?; + let generics_str = rewrite_generics(context, alias, generics, g_shape).ok()?; let vis_str = format_visibility(context, vis); let lhs = format!("{vis_str}trait {generics_str} ="); // 1 = ";" @@ -1377,6 +1403,7 @@ pub(crate) fn format_trait_alias( shape.sub_width(1)?, ) .map(|s| s + ";") + .ok() } fn format_unit_struct( @@ -1531,8 +1558,8 @@ fn format_empty_struct_or_tuple( // indented shape for proper indenting of multi-line comments let shape = Shape::indented(offset.block_indent(context.config), context.config); match rewrite_missing_comment(span, shape, context) { - Some(ref s) if s.is_empty() => (), - Some(ref s) => { + Ok(ref s) if s.is_empty() => (), + Ok(ref s) => { let is_multi_line = !is_single_line(s); if is_multi_line || first_line_contains_single_line_comment(s) { let nested_indent_str = offset @@ -1545,7 +1572,7 @@ fn format_empty_struct_or_tuple( result.push_str(&offset.to_string_with_newline(context.config)); } } - None => result.push_str(context.snippet(span)), + Err(_) => result.push_str(context.snippet(span)), } result.push_str(closer); } @@ -1587,7 +1614,7 @@ fn format_tuple_struct( Some(generics) => { let budget = context.budget(last_line_width(&header_str)); let shape = Shape::legacy(budget, offset); - let generics_str = rewrite_generics(context, "", generics, shape)?; + let generics_str = rewrite_generics(context, "", generics, shape).ok()?; result.push_str(&generics_str); let where_budget = context.budget(last_line_width(&result)); @@ -1603,7 +1630,8 @@ fn format_tuple_struct( None, body_hi, option, - )? + ) + .ok()? } None => "".to_owned(), }; @@ -1629,7 +1657,8 @@ fn format_tuple_struct( mk_sp(lo, span.hi()), context.config.fn_call_width(), None, - )?; + ) + .ok()?; } if !where_clause_str.is_empty() @@ -1672,7 +1701,7 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( indent: Indent, visitor_kind: &ItemVisitorKind<'b>, span: Span, -) -> Option<String> { +) -> RewriteResult { use ItemVisitorKind::*; let ast::TyAlias { @@ -1683,6 +1712,9 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( where_clauses, } = *ty_alias_kind; let ty_opt = ty.as_ref(); + let rhs_hi = ty + .as_ref() + .map_or(where_clauses.before.span.hi(), |ty| ty.span.hi()); let (ident, vis) = match visitor_kind { Item(i) => (i.ident, &i.vis), AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), @@ -1697,21 +1729,27 @@ pub(crate) fn rewrite_type_alias<'a, 'b>( match (visitor_kind, &op_ty) { (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { let op = OpaqueType { bounds: op_bounds }; - rewrite_ty(rw_info, Some(bounds), Some(&op), vis) + rewrite_ty(rw_info, Some(bounds), Some(&op), rhs_hi, vis) } (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => { - rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis) } (AssocImplItem(_), _) => { let result = if let Some(op_bounds) = op_ty { let op = OpaqueType { bounds: op_bounds }; - rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) + rewrite_ty( + rw_info, + Some(bounds), + Some(&op), + rhs_hi, + &DEFAULT_VISIBILITY, + ) } else { - rewrite_ty(rw_info, Some(bounds), ty_opt, vis) + rewrite_ty(rw_info, Some(bounds), ty_opt, rhs_hi, vis) }?; match defaultness { - ast::Defaultness::Default(..) => Some(format!("default {result}")), - _ => Some(result), + ast::Defaultness::Default(..) => Ok(format!("default {result}")), + _ => Ok(result), } } } @@ -1721,17 +1759,16 @@ fn rewrite_ty<R: Rewrite>( rw_info: &TyAliasRewriteInfo<'_, '_>, generic_bounds_opt: Option<&ast::GenericBounds>, rhs: Option<&R>, + // the span of the end of the RHS (or the end of the generics, if there is no RHS) + rhs_hi: BytePos, vis: &ast::Visibility, -) -> Option<String> { +) -> RewriteResult { let mut result = String::with_capacity(128); let TyAliasRewriteInfo(context, indent, generics, where_clauses, ident, span) = *rw_info; let (before_where_predicates, after_where_predicates) = generics .where_clause .predicates .split_at(where_clauses.split); - if !after_where_predicates.is_empty() { - return None; - } result.push_str(&format!("{}type ", format_visibility(context, vis))); let ident_str = rewrite_ident(context, ident); @@ -1739,9 +1776,11 @@ fn rewrite_ty<R: Rewrite>( result.push_str(ident_str) } else { // 2 = `= ` - let g_shape = Shape::indented(indent, context.config) - .offset_left(result.len())? - .sub_width(2)?; + let g_shape = Shape::indented(indent, context.config); + let g_shape = g_shape + .offset_left(result.len()) + .and_then(|s| s.sub_width(2)) + .max_width_error(g_shape.width, span)?; let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?; result.push_str(&generics_str); } @@ -1749,8 +1788,13 @@ fn rewrite_ty<R: Rewrite>( if let Some(bounds) = generic_bounds_opt { if !bounds.is_empty() { // 2 = `: ` - let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?; - let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?; + let shape = Shape::indented(indent, context.config); + let shape = shape + .offset_left(result.len() + 2) + .max_width_error(shape.width, span)?; + let type_bounds = bounds + .rewrite_result(context, shape) + .map(|s| format!(": {}", s))?; result.push_str(&type_bounds); } } @@ -1760,7 +1804,7 @@ fn rewrite_ty<R: Rewrite>( if rhs.is_none() { option.suppress_comma(); } - let where_clause_str = rewrite_where_clause( + let before_where_clause_str = rewrite_where_clause( context, before_where_predicates, where_clauses.before.span, @@ -1772,14 +1816,20 @@ fn rewrite_ty<R: Rewrite>( generics.span.hi(), option, )?; - result.push_str(&where_clause_str); + result.push_str(&before_where_clause_str); - if let Some(ty) = rhs { - // If there's a where clause, add a newline before the assignment. Otherwise just add a - // space. - let has_where = !before_where_predicates.is_empty(); - if has_where { + let mut result = if let Some(ty) = rhs { + // If there are any where clauses, add a newline before the assignment. + // If there is a before where clause, do not indent, but if there is + // only an after where clause, additionally indent the type. + if !before_where_predicates.is_empty() { result.push_str(&indent.to_string_with_newline(context.config)); + } else if !after_where_predicates.is_empty() { + result.push_str( + &indent + .block_indent(context.config) + .to_string_with_newline(context.config), + ); } else { result.push(' '); } @@ -1791,13 +1841,20 @@ fn rewrite_ty<R: Rewrite>( let lhs = match comment_span { Some(comment_span) - if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) => + if contains_comment( + context + .snippet_provider + .span_to_snippet(comment_span) + .unknown_error()?, + ) => { - let comment_shape = if has_where { + let comment_shape = if !before_where_predicates.is_empty() { Shape::indented(indent, context.config) } else { - Shape::indented(indent, context.config) - .block_left(context.config.tab_spaces())? + let shape = Shape::indented(indent, context.config); + shape + .block_left(context.config.tab_spaces()) + .max_width_error(shape.width, span)? }; combine_strs_with_missing_comments( @@ -1812,12 +1869,39 @@ fn rewrite_ty<R: Rewrite>( _ => format!("{result}="), }; - // 1 = `;` - let shape = Shape::indented(indent, context.config).sub_width(1)?; - rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") + // 1 = `;` unless there's a trailing where clause + let shape = Shape::indented(indent, context.config); + let shape = if after_where_predicates.is_empty() { + Shape::indented(indent, context.config) + .sub_width(1) + .max_width_error(shape.width, span)? + } else { + shape + }; + rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape)? } else { - Some(format!("{result};")) + result + }; + + if !after_where_predicates.is_empty() { + let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); + let after_where_clause_str = rewrite_where_clause( + context, + after_where_predicates, + where_clauses.after.span, + context.config.brace_style(), + Shape::indented(indent, context.config), + false, + ";", + None, + rhs_hi, + option, + )?; + result.push_str(&after_where_clause_str); } + + result += ";"; + Ok(result) } fn type_annotation_spacing(config: &Config) -> (&str, &str) { @@ -1830,10 +1914,10 @@ fn type_annotation_spacing(config: &Config) -> (&str, &str) { pub(crate) fn rewrite_struct_field_prefix( context: &RewriteContext<'_>, field: &ast::FieldDef, -) -> Option<String> { +) -> RewriteResult { let vis = format_visibility(context, &field.vis); let type_annotation_spacing = type_annotation_spacing(context.config); - Some(match field.ident { + Ok(match field.ident { Some(name) => format!( "{}{}{}:", vis, @@ -1846,6 +1930,10 @@ pub(crate) fn rewrite_struct_field_prefix( impl Rewrite for ast::FieldDef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_struct_field(context, self, shape, 0) } } @@ -1855,15 +1943,15 @@ pub(crate) fn rewrite_struct_field( field: &ast::FieldDef, shape: Shape, lhs_max_width: usize, -) -> Option<String> { +) -> RewriteResult { if contains_skip(&field.attrs) { - return Some(context.snippet(field.span()).to_owned()); + return Ok(context.snippet(field.span()).to_owned()); } let type_annotation_spacing = type_annotation_spacing(context.config); let prefix = rewrite_struct_field_prefix(context, field)?; - let attrs_str = field.attrs.rewrite(context, shape)?; + let attrs_str = field.attrs.rewrite_result(context, shape)?; let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str); let missing_span = if field.attrs.is_empty() { mk_sp(field.span.lo(), field.span.lo()) @@ -1893,12 +1981,14 @@ pub(crate) fn rewrite_struct_field( if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() { spacing.push(' '); } + let orig_ty = shape .offset_left(overhead + spacing.len()) - .and_then(|ty_shape| field.ty.rewrite(context, ty_shape)); + .and_then(|ty_shape| field.ty.rewrite_result(context, ty_shape).ok()); + if let Some(ref ty) = orig_ty { if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) { - return Some(attr_prefix + &spacing + ty); + return Ok(attr_prefix + &spacing + ty); } } @@ -2045,7 +2135,8 @@ fn rewrite_static( comments_span, true, ) - .and_then(|res| recover_comment_removed(res, static_parts.span, context)) + .ok() + .map(|res| recover_comment_removed(res, static_parts.span, context)) .map(|s| if s.ends_with(';') { s } else { s + ";" }) } else { Some(format!("{prefix}{ty_str};")) @@ -2072,19 +2163,34 @@ impl<'a> Rewrite for OpaqueType<'a> { impl Rewrite for ast::FnRetTy { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - ast::FnRetTy::Default(_) => Some(String::new()), + ast::FnRetTy::Default(_) => Ok(String::new()), ast::FnRetTy::Ty(ref ty) => { - if context.config.version() == Version::One + let arrow_width = "-> ".len(); + if context.config.style_edition() <= StyleEdition::Edition2021 || context.config.indent_style() == IndentStyle::Visual { - let inner_width = shape.width.checked_sub(3)?; + let inner_width = shape + .width + .checked_sub(arrow_width) + .max_width_error(shape.width, self.span())?; return ty - .rewrite(context, Shape::legacy(inner_width, shape.indent + 3)) + .rewrite_result( + context, + Shape::legacy(inner_width, shape.indent + arrow_width), + ) .map(|r| format!("-> {}", r)); } - ty.rewrite(context, shape.offset_left(3)?) + let shape = shape + .offset_left(arrow_width) + .max_width_error(shape.width, self.span())?; + + ty.rewrite_result(context, shape) .map(|s| format!("-> {}", s)) } } @@ -2126,9 +2232,11 @@ fn get_missing_param_comments( }; let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context) + .ok() .filter(|comment| !comment.is_empty()) .map_or(String::new(), |comment| format!(" {}", comment)); let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context) + .ok() .filter(|comment| !comment.is_empty()) .map_or(String::new(), |comment| format!("{} ", comment)); (comment_before_colon, comment_after_colon) @@ -2136,9 +2244,13 @@ fn get_missing_param_comments( impl Rewrite for ast::Param { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let param_attrs_result = self .attrs - .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?; // N.B. Doc comments aren't typically valid syntax, but could appear // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936 let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() { @@ -2164,7 +2276,7 @@ impl Rewrite for ast::Param { } else if is_named_param(self) { let param_name = &self .pat - .rewrite(context, Shape::legacy(shape.width, shape.indent))?; + .rewrite_result(context, Shape::legacy(shape.width, shape.indent))?; let mut result = combine_strs_with_missing_comments( context, ¶m_attrs_result, @@ -2181,10 +2293,13 @@ impl Rewrite for ast::Param { result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); let overhead = last_line_width(&result); - let max_width = shape.width.checked_sub(overhead)?; - if let Some(ty_str) = self + let max_width = shape + .width + .checked_sub(overhead) + .max_width_error(shape.width, self.span())?; + if let Ok(ty_str) = self .ty - .rewrite(context, Shape::legacy(max_width, shape.indent)) + .rewrite_result(context, Shape::legacy(max_width, shape.indent)) { result.push_str(&ty_str); } else { @@ -2206,17 +2321,20 @@ impl Rewrite for ast::Param { result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); let overhead = last_line_width(&result); - let max_width = shape.width.checked_sub(overhead)?; + let max_width = shape + .width + .checked_sub(overhead) + .max_width_error(shape.width, self.span())?; let ty_str = self .ty - .rewrite(context, Shape::legacy(max_width, shape.indent))?; + .rewrite_result(context, Shape::legacy(max_width, shape.indent))?; result.push_str(&ty_str); } } - Some(result) + Ok(result) } else { - self.ty.rewrite(context, shape) + self.ty.rewrite_result(context, shape) } } } @@ -2228,17 +2346,17 @@ fn rewrite_explicit_self( span: Span, shape: Shape, has_multiple_attr_lines: bool, -) -> Option<String> { +) -> RewriteResult { match explicit_self.node { ast::SelfKind::Region(lt, m) => { let mut_str = format_mutability(m); match lt { Some(ref l) => { - let lifetime_str = l.rewrite( + let lifetime_str = l.rewrite_result( context, Shape::legacy(context.config.max_width(), Indent::empty()), )?; - Some(combine_strs_with_missing_comments( + Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("&{lifetime_str} {mut_str}self"), @@ -2247,7 +2365,7 @@ fn rewrite_explicit_self( !has_multiple_attr_lines, )?) } - None => Some(combine_strs_with_missing_comments( + None => Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("&{mut_str}self"), @@ -2258,12 +2376,12 @@ fn rewrite_explicit_self( } } ast::SelfKind::Explicit(ref ty, mutability) => { - let type_str = ty.rewrite( + let type_str = ty.rewrite_result( context, Shape::legacy(context.config.max_width(), Indent::empty()), )?; - Some(combine_strs_with_missing_comments( + Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("{}self: {}", format_mutability(mutability), type_str), @@ -2272,7 +2390,7 @@ fn rewrite_explicit_self( !has_multiple_attr_lines, )?) } - ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments( + ast::SelfKind::Value(mutability) => Ok(combine_strs_with_missing_comments( context, param_attrs, &format!("{}self", format_mutability(mutability)), @@ -2326,7 +2444,7 @@ fn rewrite_fn_base( fn_sig: &FnSig<'_>, span: Span, fn_brace_style: FnBraceStyle, -) -> Option<(String, bool, bool)> { +) -> Result<(String, bool, bool), RewriteError> { let mut force_new_line_for_brace = false; let where_clause = &fn_sig.generics.where_clause; @@ -2370,7 +2488,7 @@ fn rewrite_fn_base( // return type later anyway. let ret_str = fd .output - .rewrite(context, Shape::indented(indent, context.config))?; + .rewrite_result(context, Shape::indented(indent, context.config))?; let multi_line_ret_str = ret_str.contains('\n'); let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() }; @@ -2383,7 +2501,7 @@ fn rewrite_fn_base( ret_str_len, fn_brace_style, multi_line_ret_str, - )?; + ); debug!( "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}", @@ -2452,7 +2570,7 @@ fn rewrite_fn_base( .last() .map_or(false, |last_line| last_line.contains("//")); - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if closing_paren_overflow_max_width { result.push(')'); result.push_str(&indent.to_string_with_newline(context.config)); @@ -2495,7 +2613,7 @@ fn rewrite_fn_base( } }; let ret_shape = if ret_should_indent { - if context.config.version() == Version::One + if context.config.style_edition() <= StyleEdition::Edition2021 || context.config.indent_style() == IndentStyle::Visual { let indent = if param_str.is_empty() { @@ -2528,7 +2646,7 @@ fn rewrite_fn_base( ret_shape } } else { - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if !param_str.is_empty() || !no_params_and_over_max_width { result.push(' '); } @@ -2545,7 +2663,7 @@ fn rewrite_fn_base( if multi_line_ret_str || ret_should_indent { // Now that we know the proper indent and width, we need to // re-layout the return type. - let ret_str = fd.output.rewrite(context, ret_shape)?; + let ret_str = fd.output.rewrite_result(context, ret_shape)?; result.push_str(&ret_str); } else { result.push_str(&ret_str); @@ -2617,7 +2735,7 @@ fn rewrite_fn_base( context, last_line_width(&result), ) { - Some(ref missing_comment) if !missing_comment.is_empty() => { + Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); force_new_line_for_brace = true; } @@ -2632,7 +2750,7 @@ fn rewrite_fn_base( force_new_line_for_brace |= ends_with_comment; force_new_line_for_brace |= is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty(); - Some((result, ends_with_comment, force_new_line_for_brace)) + Ok((result, ends_with_comment, force_new_line_for_brace)) } /// Kind of spaces to put before `where`. @@ -2703,7 +2821,7 @@ fn rewrite_params( param_indent: Indent, span: Span, variadic: bool, -) -> Option<String> { +) -> RewriteResult { if params.is_empty() { let comment = context .snippet(mk_sp( @@ -2712,7 +2830,7 @@ fn rewrite_params( span.hi() - BytePos(1), )) .trim(); - return Some(comment.to_owned()); + return Ok(comment.to_owned()); } let param_items: Vec<_> = itemize_list( context.snippet_provider, @@ -2723,8 +2841,8 @@ fn rewrite_params( |param| param.ty.span.hi(), |param| { param - .rewrite(context, Shape::legacy(multi_line_budget, param_indent)) - .or_else(|| Some(context.snippet(param.span()).to_owned())) + .rewrite_result(context, Shape::legacy(multi_line_budget, param_indent)) + .or_else(|_| Ok(context.snippet(param.span()).to_owned())) }, span.lo(), span.hi(), @@ -2772,7 +2890,7 @@ fn compute_budgets_for_params( ret_str_len: usize, fn_brace_style: FnBraceStyle, force_vertical_layout: bool, -) -> Option<(usize, usize, Indent)> { +) -> (usize, usize, Indent) { debug!( "compute_budgets_for_params {} {:?}, {}, {:?}", result.len(), @@ -2809,7 +2927,7 @@ fn compute_budgets_for_params( } }; - return Some((one_line_budget, multi_line_budget, indent)); + return (one_line_budget, multi_line_budget, indent); } } @@ -2821,7 +2939,7 @@ fn compute_budgets_for_params( // Account for `)` and possibly ` {`. IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 }, }; - Some((0, context.budget(used_space), new_indent)) + (0, context.budget(used_space), new_indent) } fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle { @@ -2846,12 +2964,12 @@ fn rewrite_generics( ident: &str, generics: &ast::Generics, shape: Shape, -) -> Option<String> { +) -> RewriteResult { // FIXME: convert bounds to where-clauses where they get too big or if // there is a where-clause at all. if generics.params.is_empty() { - return Some(ident.to_owned()); + return Ok(ident.to_owned()); } let params = generics.params.iter(); @@ -2881,7 +2999,7 @@ fn rewrite_where_clause_rfc_style( span_end: Option<BytePos>, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option<String> { +) -> RewriteResult { let (where_keyword, allow_single_line) = rewrite_where_keyword( context, predicates, @@ -2895,8 +3013,9 @@ fn rewrite_where_clause_rfc_style( let clause_shape = shape .block() .with_max_width(context.config) - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, where_span)?; let force_single_line = context.config.where_single_line() && predicates.len() == 1 && !where_clause_option.veto_single_line; @@ -2921,7 +3040,7 @@ fn rewrite_where_clause_rfc_style( clause_shape.indent.to_string_with_newline(context.config) }; - Some(format!("{where_keyword}{clause_sep}{preds_str}")) + Ok(format!("{where_keyword}{clause_sep}{preds_str}")) } /// Rewrite `where` and comment around it. @@ -2932,12 +3051,13 @@ fn rewrite_where_keyword( shape: Shape, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option<(String, bool)> { +) -> Result<(String, bool), RewriteError> { let block_shape = shape.block().with_max_width(context.config); // 1 = `,` let clause_shape = block_shape - .block_left(context.config.tab_spaces())? - .sub_width(1)?; + .block_left(context.config.tab_spaces()) + .and_then(|s| s.sub_width(1)) + .max_width_error(block_shape.width, where_span)?; let comment_separator = |comment: &str, shape: Shape| { if comment.is_empty() { @@ -2968,7 +3088,7 @@ fn rewrite_where_keyword( && comment_before.is_empty() && comment_after.is_empty(); - Some((result, allow_single_line)) + Ok((result, allow_single_line)) } /// Rewrite bounds on a where clause. @@ -2980,7 +3100,7 @@ fn rewrite_bounds_on_where_clause( span_end: Option<BytePos>, where_clause_option: WhereClauseOption, force_single_line: bool, -) -> Option<String> { +) -> RewriteResult { let span_start = predicates[0].span().lo(); // If we don't have the start of the next span, then use the end of the // predicates, but that means we miss comments. @@ -2994,7 +3114,7 @@ fn rewrite_bounds_on_where_clause( ",", |pred| pred.span().lo(), |pred| pred.span().hi(), - |pred| pred.rewrite(context, shape), + |pred| pred.rewrite_result(context, shape), span_start, span_end, false, @@ -3013,7 +3133,7 @@ fn rewrite_bounds_on_where_clause( DefinitiveListTactic::Vertical }; - let preserve_newline = context.config.version() == Version::One; + let preserve_newline = context.config.style_edition() <= StyleEdition::Edition2021; let fmt = ListFormatting::new(shape, context.config) .tactic(shape_tactic) @@ -3033,9 +3153,9 @@ fn rewrite_where_clause( span_end: Option<BytePos>, span_end_before_where: BytePos, where_clause_option: WhereClauseOption, -) -> Option<String> { +) -> RewriteResult { if predicates.is_empty() { - return Some(String::new()); + return Ok(String::new()); } if context.config.indent_style() == IndentStyle::Block { @@ -3075,7 +3195,7 @@ fn rewrite_where_clause( ",", |pred| pred.span().lo(), |pred| pred.span().hi(), - |pred| pred.rewrite(context, Shape::legacy(budget, offset)), + |pred| pred.rewrite_result(context, Shape::legacy(budget, offset)), span_start, span_end, false, @@ -3113,13 +3233,13 @@ fn rewrite_where_clause( || preds_str.contains('\n') || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width { - Some(format!( + Ok(format!( "\n{}where {}", (shape.indent + extra_indent).to_string(context.config), preds_str )) } else { - Some(format!(" where {preds_str}")) + Ok(format!(" where {preds_str}")) } } @@ -3140,14 +3260,14 @@ fn rewrite_comments_before_after_where( span_before_where: Span, span_after_where: Span, shape: Shape, -) -> Option<(String, String)> { +) -> Result<(String, String), RewriteError> { let before_comment = rewrite_missing_comment(span_before_where, shape, context)?; let after_comment = rewrite_missing_comment( span_after_where, shape.block_indent(context.config.tab_spaces()), context, )?; - Some((before_comment, after_comment)) + Ok((before_comment, after_comment)) } fn format_header( @@ -3169,7 +3289,7 @@ fn format_header( .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim()) { let missing_span = mk_sp(after_vis, before_item_name); - if let Some(result_with_comment) = combine_strs_with_missing_comments( + if let Ok(result_with_comment) = combine_strs_with_missing_comments( context, &result, item_name, @@ -3203,7 +3323,7 @@ fn format_generics( used_width: usize, ) -> Option<String> { let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); - let mut result = rewrite_generics(context, "", generics, shape)?; + let mut result = rewrite_generics(context, "", generics, shape).ok()?; // If the generics are not parameterized then generics.span.hi() == 0, // so we use span.lo(), which is the position after `struct Foo`. @@ -3229,7 +3349,8 @@ fn format_generics( Some(span.hi()), span_end_before_where, option, - )?; + ) + .ok()?; result.push_str(&where_clause_str); ( brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, @@ -3253,7 +3374,8 @@ fn format_generics( ), shape, context, - ), + ) + .ok(), ) }; // add missing comments @@ -3295,7 +3417,11 @@ fn format_generics( impl Rewrite for ast::ForeignItem { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let attrs_str = self.attrs.rewrite(context, shape)?; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; // Drop semicolon or it will be interpreted as comment. // FIXME: this may be a faulty span from libsyntax. let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1)); @@ -3328,7 +3454,7 @@ impl Rewrite for ast::ForeignItem { defaultness, Some(&inner_attrs), ); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } else { rewrite_fn_base( context, @@ -3360,7 +3486,9 @@ impl Rewrite for ast::ForeignItem { prefix, &static_foreign_item.ty, &RhsAssignKind::Ty, - shape.sub_width(1)?, + shape + .sub_width(1) + .max_width_error(shape.width, static_foreign_item.ty.span)?, ) .map(|s| s + ";") } @@ -3421,6 +3549,7 @@ fn rewrite_attrs( shape, allow_extend, ) + .ok() } /// Rewrite an inline mod. diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 4263a49fd72..08cda6913b9 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -5,9 +5,6 @@ #![allow(clippy::match_like_matches_macro)] #![allow(unreachable_pub)] -// #[macro_use] -// extern crate tracing; - // N.B. these crates are loaded from the sysroot, so they need extern crate. extern crate rustc_ast; extern crate rustc_ast_pretty; @@ -48,8 +45,8 @@ use crate::shape::Indent; use crate::utils::indent_next_line; pub use crate::config::{ - load_config, CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, - Range, Verbosity, + CliOptions, Color, Config, Edition, EmitMode, FileLines, FileName, NewlineStyle, Range, + StyleEdition, Verbosity, Version, load_config, }; pub use crate::format_report_formatter::{FormatReportFormatter, FormatReportFormatterBuilder}; @@ -94,6 +91,7 @@ mod rewrite; pub(crate) mod rustfmt_diff; mod shape; mod skip; +mod sort; pub(crate) mod source_file; pub(crate) mod source_map; mod spanned; diff --git a/src/tools/rustfmt/src/lists.rs b/src/tools/rustfmt/src/lists.rs index 41afef279e9..415144a8abc 100644 --- a/src/tools/rustfmt/src/lists.rs +++ b/src/tools/rustfmt/src/lists.rs @@ -5,10 +5,10 @@ use std::iter::Peekable; use rustc_span::BytePos; -use crate::comment::{find_comment_end, rewrite_comment, FindUncommented}; +use crate::comment::{FindUncommented, find_comment_end, rewrite_comment}; use crate::config::lists::*; use crate::config::{Config, IndentStyle}; -use crate::rewrite::RewriteContext; +use crate::rewrite::{RewriteContext, RewriteError, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::utils::{ count_newlines, first_line_width, last_line_width, mk_sp, starts_with_newline, @@ -125,18 +125,18 @@ pub(crate) struct ListItem { pub(crate) pre_comment_style: ListItemCommentStyle, // Item should include attributes and doc comments. None indicates a failed // rewrite. - pub(crate) item: Option<String>, + pub(crate) item: RewriteResult, pub(crate) post_comment: Option<String>, // Whether there is extra whitespace before this item. pub(crate) new_lines: bool, } impl ListItem { - pub(crate) fn empty() -> ListItem { + pub(crate) fn from_item(item: RewriteResult) -> ListItem { ListItem { pre_comment: None, pre_comment_style: ListItemCommentStyle::None, - item: None, + item: item, post_comment: None, new_lines: false, } @@ -185,7 +185,7 @@ impl ListItem { ListItem { pre_comment: None, pre_comment_style: ListItemCommentStyle::None, - item: Some(s.into()), + item: Ok(s.into()), post_comment: None, new_lines: false, } @@ -197,7 +197,11 @@ impl ListItem { !matches!(*s, Some(ref s) if !s.is_empty()) } - !(empty(&self.pre_comment) && empty(&self.item) && empty(&self.post_comment)) + fn empty_result(s: &RewriteResult) -> bool { + !matches!(*s, Ok(ref s) if !s.is_empty()) + } + + !(empty(&self.pre_comment) && empty_result(&self.item) && empty(&self.post_comment)) } } @@ -257,7 +261,7 @@ where } // Format a list of commented items into a string. -pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> Option<String> +pub(crate) fn write_list<I, T>(items: I, formatting: &ListFormatting<'_>) -> RewriteResult where I: IntoIterator<Item = T> + Clone, T: AsRef<ListItem>, @@ -281,7 +285,7 @@ where let indent_str = &formatting.shape.indent.to_string(formatting.config); while let Some((i, item)) = iter.next() { let item = item.as_ref(); - let inner_item = item.item.as_ref()?; + let inner_item = item.item.as_ref().or_else(|err| Err(err.clone()))?; let first = i == 0; let last = iter.peek().is_none(); let mut separate = match sep_place { @@ -516,7 +520,7 @@ where prev_item_is_nested_import = inner_item.contains("::"); } - Some(result) + Ok(result) } fn max_width_of_item_with_post_comment<I, T>( @@ -741,7 +745,7 @@ where I: Iterator<Item = T>, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> Option<String>, + F3: Fn(&T) -> RewriteResult, { type Item = ListItem; @@ -775,8 +779,9 @@ where ListItem { pre_comment, pre_comment_style, + // leave_last is set to true only for rewrite_items item: if self.inner.peek().is_none() && self.leave_last { - None + Err(RewriteError::SkipFormatting) } else { (self.get_item_string)(&item) }, @@ -805,7 +810,7 @@ where I: Iterator<Item = T>, F1: Fn(&T) -> BytePos, F2: Fn(&T) -> BytePos, - F3: Fn(&T) -> Option<String>, + F3: Fn(&T) -> RewriteResult, { ListItems { snippet_provider, diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 524fc666fae..5a35e115d8f 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -10,35 +10,37 @@ // and those with brackets will be formatted as array literals. use std::collections::HashMap; -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{ast, ptr}; use rustc_ast_pretty::pprust; use rustc_span::{ + BytePos, DUMMY_SP, Span, Symbol, symbol::{self, kw}, - BytePos, Span, Symbol, DUMMY_SP, }; use tracing::debug; use crate::comment::{ - contains_comment, CharClasses, FindUncommented, FullCodeCharKind, LineClasses, + CharClasses, FindUncommented, FullCodeCharKind, LineClasses, contains_comment, }; +use crate::config::StyleEdition; use crate::config::lists::*; -use crate::config::Version; -use crate::expr::{rewrite_array, rewrite_assign_rhs, RhsAssignKind}; -use crate::lists::{itemize_list, write_list, ListFormatting}; +use crate::expr::{RhsAssignKind, rewrite_array, rewrite_assign_rhs}; +use crate::lists::{ListFormatting, itemize_list, write_list}; use crate::overflow; use crate::parse::macros::lazy_static::parse_lazy_static; -use crate::parse::macros::{parse_expr, parse_macro_args, ParsedMacroArgs}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::parse::macros::{ParsedMacroArgs, parse_expr, parse_macro_args}; +use crate::rewrite::{ + MacroErrorKind, Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult, +}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; use crate::utils::{ - filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, - remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt, + NodeIdExt, filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp, + remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, }; use crate::visitor::FmtVisitor; @@ -72,22 +74,30 @@ impl MacroArg { impl Rewrite for ast::Item { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let mut visitor = crate::visitor::FmtVisitor::from_context(context); visitor.block_indent = shape.indent; visitor.last_pos = self.span().lo(); visitor.visit_item(self); - Some(visitor.buffer.to_owned()) + Ok(visitor.buffer.to_owned()) } } impl Rewrite for MacroArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - MacroArg::Expr(ref expr) => expr.rewrite(context, shape), - MacroArg::Ty(ref ty) => ty.rewrite(context, shape), - MacroArg::Pat(ref pat) => pat.rewrite(context, shape), - MacroArg::Item(ref item) => item.rewrite(context, shape), - MacroArg::Keyword(ident, _) => Some(ident.name.to_string()), + MacroArg::Expr(ref expr) => expr.rewrite_result(context, shape), + MacroArg::Ty(ref ty) => ty.rewrite_result(context, shape), + MacroArg::Pat(ref pat) => pat.rewrite_result(context, shape), + MacroArg::Item(ref item) => item.rewrite_result(context, shape), + MacroArg::Keyword(ident, _) => Ok(ident.name.to_string()), } } } @@ -111,12 +121,14 @@ fn rewrite_macro_name( } // Use this on failing to format the macro call. +// TODO(ding-young) We should also report macro parse failure to tell users why given snippet +// is left unformatted. One possible improvement is appending formatting error to context.report fn return_macro_parse_failure_fallback( context: &RewriteContext<'_>, indent: Indent, position: MacroPosition, span: Span, -) -> Option<String> { +) -> RewriteResult { // Mark this as a failure however we format it context.macro_rewrite_failure.replace(true); @@ -134,7 +146,8 @@ fn return_macro_parse_failure_fallback( }) .unwrap_or(false); if is_like_block_indent_style { - return trim_left_preserve_layout(context.snippet(span), indent, context.config); + return trim_left_preserve_layout(context.snippet(span), indent, context.config) + .macro_error(MacroErrorKind::Unknown, span); } context.skipped_range.borrow_mut().push(( @@ -147,7 +160,7 @@ fn return_macro_parse_failure_fallback( if position == MacroPosition::Item { snippet.push(';'); } - Some(snippet) + Ok(snippet) } pub(crate) fn rewrite_macro( @@ -156,13 +169,13 @@ pub(crate) fn rewrite_macro( context: &RewriteContext<'_>, shape: Shape, position: MacroPosition, -) -> Option<String> { +) -> RewriteResult { let should_skip = context .skip_context .macros .skip(context.snippet(mac.path.span)); if should_skip { - None + Err(RewriteError::SkipFormatting) } else { let guard = context.enter_macro(); let result = catch_unwind(AssertUnwindSafe(|| { @@ -176,9 +189,16 @@ pub(crate) fn rewrite_macro( ) })); match result { - Err(..) | Ok(None) => { + Err(..) => { context.macro_rewrite_failure.replace(true); - None + Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: mac.span(), + }) + } + Ok(Err(e)) => { + context.macro_rewrite_failure.replace(true); + Err(e) } Ok(rw) => rw, } @@ -192,11 +212,11 @@ fn rewrite_macro_inner( shape: Shape, position: MacroPosition, is_nested_macro: bool, -) -> Option<String> { +) -> RewriteResult { if context.config.use_try_shorthand() { if let Some(expr) = convert_try_mac(mac, context) { context.leave_macro(); - return expr.rewrite(context, shape); + return expr.rewrite_result(context, shape); } } @@ -216,21 +236,27 @@ fn rewrite_macro_inner( if ts.is_empty() && !has_comment { return match style { Delimiter::Parenthesis if position == MacroPosition::Item => { - Some(format!("{macro_name}();")) + Ok(format!("{macro_name}();")) } - Delimiter::Bracket if position == MacroPosition::Item => { - Some(format!("{macro_name}[];")) - } - Delimiter::Parenthesis => Some(format!("{macro_name}()")), - Delimiter::Bracket => Some(format!("{macro_name}[]")), - Delimiter::Brace => Some(format!("{macro_name} {{}}")), + Delimiter::Bracket if position == MacroPosition::Item => Ok(format!("{macro_name}[];")), + Delimiter::Parenthesis => Ok(format!("{macro_name}()")), + Delimiter::Bracket => Ok(format!("{macro_name}[]")), + Delimiter::Brace => Ok(format!("{macro_name} {{}}")), _ => unreachable!(), }; } // Format well-known macros which cannot be parsed as a valid AST. if macro_name == "lazy_static!" && !has_comment { - if let success @ Some(..) = format_lazy_static(context, shape, ts.clone()) { - return success; + match format_lazy_static(context, shape, ts.clone(), mac.span()) { + Ok(rw) => return Ok(rw), + Err(err) => match err { + // We will move on to parsing macro args just like other macros + // if we could not parse lazy_static! with known syntax + RewriteError::MacroFailure { kind, span: _ } + if kind == MacroErrorKind::ParseFailure => {} + // If formatting fails even though parsing succeeds, return the err early + _ => return Err(err), + }, } } @@ -267,7 +293,7 @@ fn rewrite_macro_inner( Delimiter::Parenthesis => { // Handle special case: `vec!(expr; expr)` if vec_with_semi { - handle_vec_semi(context, shape, arg_vec, macro_name, style) + handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span()) } else { // Format macro invocation as function call, preserve the trailing // comma because not all macros support them. @@ -293,7 +319,7 @@ fn rewrite_macro_inner( Delimiter::Bracket => { // Handle special case: `vec![expr; expr]` if vec_with_semi { - handle_vec_semi(context, shape, arg_vec, macro_name, style) + handle_vec_semi(context, shape, arg_vec, macro_name, style, mac.span()) } else { // If we are rewriting `vec!` macro or other special macros, // then we can rewrite this as a usual array literal. @@ -323,7 +349,7 @@ fn rewrite_macro_inner( _ => "", }; - Some(format!("{rewrite}{comma}")) + Ok(format!("{rewrite}{comma}")) } } Delimiter::Brace => { @@ -332,8 +358,8 @@ fn rewrite_macro_inner( // anything in between the braces (for now). let snippet = context.snippet(mac.span()).trim_start_matches(|c| c != '{'); match trim_left_preserve_layout(snippet, shape.indent, context.config) { - Some(macro_body) => Some(format!("{macro_name} {macro_body}")), - None => Some(format!("{macro_name} {snippet}")), + Some(macro_body) => Ok(format!("{macro_name} {macro_body}")), + None => Ok(format!("{macro_name} {snippet}")), } } _ => unreachable!(), @@ -346,28 +372,32 @@ fn handle_vec_semi( arg_vec: Vec<MacroArg>, macro_name: String, delim_token: Delimiter, -) -> Option<String> { + span: Span, +) -> RewriteResult { let (left, right) = match delim_token { Delimiter::Parenthesis => ("(", ")"), Delimiter::Bracket => ("[", "]"), _ => unreachable!(), }; - let mac_shape = shape.offset_left(macro_name.len())?; + // Should we return MaxWidthError, Or Macro failure + let mac_shape = shape + .offset_left(macro_name.len()) + .max_width_error(shape.width, span)?; // 8 = `vec![]` + `; ` or `vec!()` + `; ` let total_overhead = 8; let nested_shape = mac_shape.block_indent(context.config.tab_spaces()); - let lhs = arg_vec[0].rewrite(context, nested_shape)?; - let rhs = arg_vec[1].rewrite(context, nested_shape)?; + let lhs = arg_vec[0].rewrite_result(context, nested_shape)?; + let rhs = arg_vec[1].rewrite_result(context, nested_shape)?; if !lhs.contains('\n') && !rhs.contains('\n') && lhs.len() + rhs.len() + total_overhead <= shape.width { // macro_name(lhs; rhs) or macro_name[lhs; rhs] - Some(format!("{macro_name}{left}{lhs}; {rhs}{right}")) + Ok(format!("{macro_name}{left}{lhs}; {rhs}{right}")) } else { // macro_name(\nlhs;\nrhs\n) or macro_name[\nlhs;\nrhs\n] - Some(format!( + Ok(format!( "{}{}{}{};{}{}{}{}", macro_name, left, @@ -385,7 +415,7 @@ fn rewrite_empty_macro_def_body( context: &RewriteContext<'_>, span: Span, shape: Shape, -) -> Option<String> { +) -> RewriteResult { // Create an empty, dummy `ast::Block` representing an empty macro body let block = ast::Block { stmts: vec![].into(), @@ -395,7 +425,7 @@ fn rewrite_empty_macro_def_body( tokens: None, could_be_bare_literal: false, }; - block.rewrite(context, shape) + block.rewrite_result(context, shape) } pub(crate) fn rewrite_macro_def( @@ -406,8 +436,8 @@ pub(crate) fn rewrite_macro_def( ident: symbol::Ident, vis: &ast::Visibility, span: Span, -) -> Option<String> { - let snippet = Some(remove_trailing_white_spaces(context.snippet(span))); +) -> RewriteResult { + let snippet = Ok(remove_trailing_white_spaces(context.snippet(span))); if snippet.as_ref().map_or(true, |s| s.ends_with(';')) { return snippet; } @@ -442,7 +472,7 @@ pub(crate) fn rewrite_macro_def( let lo = context.snippet_provider.span_before(span, "{"); result += " "; result += &rewrite_empty_macro_def_body(context, span.with_lo(lo), shape)?; - return Some(result); + return Ok(result); } let branch_items = itemize_list( @@ -453,13 +483,14 @@ pub(crate) fn rewrite_macro_def( |branch| branch.span.lo(), |branch| branch.span.hi(), |branch| match branch.rewrite(context, arm_shape, multi_branch_style) { - Some(v) => Some(v), + Ok(v) => Ok(v), // if the rewrite returned None because a macro could not be rewritten, then return the // original body - None if context.macro_rewrite_failure.get() => { - Some(context.snippet(branch.body).trim().to_string()) + // TODO(ding-young) report rewrite error even if we return Ok with original snippet + Err(_) if context.macro_rewrite_failure.get() => { + Ok(context.snippet(branch.body).trim().to_string()) } - None => None, + Err(e) => Err(e), }, context.snippet_provider.span_after(span, "{"), span.hi(), @@ -478,8 +509,8 @@ pub(crate) fn rewrite_macro_def( } match write_list(&branch_items, &fmt) { - Some(ref s) => result += s, - None => return snippet, + Ok(ref s) => result += s, + Err(_) => return snippet, } if multi_branch_style { @@ -487,7 +518,7 @@ pub(crate) fn rewrite_macro_def( result += "}"; } - Some(result) + Ok(result) } fn register_metavariable( @@ -639,12 +670,13 @@ impl MacroArgKind { context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option<String> { - let rewrite_delimited_inner = |delim_tok, args| -> Option<(String, String, String)> { + ) -> RewriteResult { + type DelimitedArgsRewrite = Result<(String, String, String), RewriteError>; + let rewrite_delimited_inner = |delim_tok, args| -> DelimitedArgsRewrite { let inner = wrap_macro_args(context, args, shape)?; let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, false, inner.is_empty()); if lhs.len() + inner.len() + rhs.len() <= shape.width { - return Some((lhs, inner, rhs)); + return Ok((lhs, inner, rhs)); } let (lhs, rhs) = delim_token_to_str(context, delim_tok, shape, true, false); @@ -652,27 +684,27 @@ impl MacroArgKind { .block_indent(context.config.tab_spaces()) .with_max_width(context.config); let inner = wrap_macro_args(context, args, nested_shape)?; - Some((lhs, inner, rhs)) + Ok((lhs, inner, rhs)) }; match *self { - MacroArgKind::MetaVariable(ty, ref name) => Some(format!("${name}:{ty}")), + MacroArgKind::MetaVariable(ty, ref name) => Ok(format!("${name}:{ty}")), MacroArgKind::Repeat(delim_tok, ref args, ref another, ref tok) => { let (lhs, inner, rhs) = rewrite_delimited_inner(delim_tok, args)?; let another = another .as_ref() - .and_then(|a| a.rewrite(context, shape, use_multiple_lines)) + .and_then(|a| a.rewrite(context, shape, use_multiple_lines).ok()) .unwrap_or_else(|| "".to_owned()); let repeat_tok = pprust::token_to_string(tok); - Some(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) + Ok(format!("${lhs}{inner}{rhs}{another}{repeat_tok}")) } MacroArgKind::Delimited(delim_tok, ref args) => { rewrite_delimited_inner(delim_tok, args) .map(|(lhs, inner, rhs)| format!("{}{}{}", lhs, inner, rhs)) } - MacroArgKind::Separator(ref sep, ref prefix) => Some(format!("{prefix}{sep} ")), - MacroArgKind::Other(ref inner, ref prefix) => Some(format!("{prefix}{inner}")), + MacroArgKind::Separator(ref sep, ref prefix) => Ok(format!("{prefix}{sep} ")), + MacroArgKind::Other(ref inner, ref prefix) => Ok(format!("{prefix}{inner}")), } } } @@ -688,7 +720,7 @@ impl ParsedMacroArg { context: &RewriteContext<'_>, shape: Shape, use_multiple_lines: bool, - ) -> Option<String> { + ) -> RewriteResult { self.kind.rewrite(context, shape, use_multiple_lines) } } @@ -966,9 +998,9 @@ fn wrap_macro_args( context: &RewriteContext<'_>, args: &[ParsedMacroArg], shape: Shape, -) -> Option<String> { +) -> RewriteResult { wrap_macro_args_inner(context, args, shape, false) - .or_else(|| wrap_macro_args_inner(context, args, shape, true)) + .or_else(|_| wrap_macro_args_inner(context, args, shape, true)) } fn wrap_macro_args_inner( @@ -976,7 +1008,7 @@ fn wrap_macro_args_inner( args: &[ParsedMacroArg], shape: Shape, use_multiple_lines: bool, -) -> Option<String> { +) -> RewriteResult { let mut result = String::with_capacity(128); let mut iter = args.iter().peekable(); let indent_str = shape.indent.to_string_with_newline(context.config); @@ -1002,9 +1034,9 @@ fn wrap_macro_args_inner( } if !use_multiple_lines && result.len() >= shape.width { - None + Err(RewriteError::Unknown) } else { - Some(result) + Ok(result) } } @@ -1012,22 +1044,21 @@ fn wrap_macro_args_inner( // for some common cases. I hope the basic logic is sufficient. Note that the // meaning of some tokens is a bit different here from usual Rust, e.g., `*` // and `(`/`)` have special meaning. -// -// We always try and format on one line. -// FIXME: Use multi-line when every thing does not fit on one line. fn format_macro_args( context: &RewriteContext<'_>, token_stream: TokenStream, shape: Shape, -) -> Option<String> { +) -> RewriteResult { + let span = span_for_token_stream(&token_stream); if !context.config.format_macro_matchers() { - let span = span_for_token_stream(&token_stream); - return Some(match span { + return Ok(match span { Some(span) => context.snippet(span).to_owned(), None => String::new(), }); } - let parsed_args = MacroArgParser::new().parse(token_stream)?; + let parsed_args = MacroArgParser::new() + .parse(token_stream) + .macro_error(MacroErrorKind::ParseFailure, span.unwrap())?; wrap_macro_args(context, &parsed_args, shape) } @@ -1132,9 +1163,9 @@ pub(crate) fn convert_try_mac( pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter { let snippet = context.snippet(mac.span()); - let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value()); - let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value()); - let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value()); + let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::MAX); + let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::MAX); + let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::MAX); if paren_pos < bracket_pos && paren_pos < brace_pos { Delimiter::Parenthesis @@ -1242,23 +1273,31 @@ impl MacroBranch { context: &RewriteContext<'_>, shape: Shape, multi_branch_style: bool, - ) -> Option<String> { + ) -> RewriteResult { // Only attempt to format function-like macros. if self.args_paren_kind != Delimiter::Parenthesis { // FIXME(#1539): implement for non-sugared macros. - return None; + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: self.span, + }); } let old_body = context.snippet(self.body).trim(); let has_block_body = old_body.starts_with('{'); let mut prefix_width = 5; // 5 = " => {" - if context.config.version() == Version::Two { + if context.config.style_edition() >= StyleEdition::Edition2024 { if has_block_body { prefix_width = 6; // 6 = " => {{" } } - let mut result = - format_macro_args(context, self.args.clone(), shape.sub_width(prefix_width)?)?; + let mut result = format_macro_args( + context, + self.args.clone(), + shape + .sub_width(prefix_width) + .max_width_error(shape.width, self.span)?, + )?; if multi_branch_style { result += " =>"; @@ -1267,7 +1306,7 @@ impl MacroBranch { if !context.config.format_macro_bodies() { result += " "; result += context.snippet(self.whole_body); - return Some(result); + return Ok(result); } // The macro body is the most interesting part. It might end up as various @@ -1276,7 +1315,8 @@ impl MacroBranch { // `$$`). We'll try and format like an AST node, but we'll substitute // variables for new names with the same length first. - let (body_str, substs) = replace_names(old_body)?; + let (body_str, substs) = + replace_names(old_body).macro_error(MacroErrorKind::ReplaceMacroVariable, self.span)?; let mut config = context.config.clone(); config.set().show_parse_errors(false); @@ -1299,13 +1339,21 @@ impl MacroBranch { config.set().max_width(new_width); match crate::format_code_block(&body_str, &config, true) { Some(new_body) => new_body, - None => return None, + None => { + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::Unknown, + span: self.span, + }); + } } } }; if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { - return None; + return Err(RewriteError::ExceedsMaxWidth { + configured_width: shape.width, + span: self.span, + }); } // Indent the body since it is in a block. @@ -1331,7 +1379,10 @@ impl MacroBranch { for (old, new) in &substs { if old_body.contains(new) { debug!("rewrite_macro_def: bailing matching variable: `{}`", new); - return None; + return Err(RewriteError::MacroFailure { + kind: MacroErrorKind::ReplaceMacroVariable, + span: self.span, + }); } new_body = new_body.replace(new, old); } @@ -1346,7 +1397,7 @@ impl MacroBranch { result += "}"; - Some(result) + Ok(result) } } @@ -1366,7 +1417,8 @@ fn format_lazy_static( context: &RewriteContext<'_>, shape: Shape, ts: TokenStream, -) -> Option<String> { + span: Span, +) -> RewriteResult { let mut result = String::with_capacity(1024); let nested_shape = shape .block_indent(context.config.tab_spaces()) @@ -1375,7 +1427,8 @@ fn format_lazy_static( result.push_str("lazy_static! {"); result.push_str(&nested_shape.indent.to_string_with_newline(context.config)); - let parsed_elems = parse_lazy_static(context, ts)?; + let parsed_elems = + parse_lazy_static(context, ts).macro_error(MacroErrorKind::ParseFailure, span)?; let last = parsed_elems.len() - 1; for (i, (vis, id, ty, expr)) in parsed_elems.iter().enumerate() { // Rewrite as a static item. @@ -1385,14 +1438,16 @@ fn format_lazy_static( "{}static ref {}: {} =", vis, id, - ty.rewrite(context, nested_shape)? + ty.rewrite_result(context, nested_shape)? )); result.push_str(&rewrite_assign_rhs( context, stmt, &*expr, &RhsAssignKind::Expr(&expr.kind, expr.span), - nested_shape.sub_width(1)?, + nested_shape + .sub_width(1) + .max_width_error(nested_shape.width, expr.span)?, )?); result.push(';'); if i != last { @@ -1403,7 +1458,7 @@ fn format_lazy_static( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push('}'); - Some(result) + Ok(result) } fn rewrite_macro_with_items( @@ -1415,12 +1470,12 @@ fn rewrite_macro_with_items( original_style: Delimiter, position: MacroPosition, span: Span, -) -> Option<String> { +) -> RewriteResult { let style_to_delims = |style| match style { - Delimiter::Parenthesis => Some(("(", ")")), - Delimiter::Bracket => Some(("[", "]")), - Delimiter::Brace => Some((" {", "}")), - _ => None, + Delimiter::Parenthesis => Ok(("(", ")")), + Delimiter::Bracket => Ok(("[", "]")), + Delimiter::Brace => Ok((" {", "}")), + _ => Err(RewriteError::Unknown), }; let (opener, closer) = style_to_delims(style)?; @@ -1442,7 +1497,7 @@ fn rewrite_macro_with_items( for item in items { let item = match item { MacroArg::Item(item) => item, - _ => return None, + _ => return Err(RewriteError::Unknown), }; visitor.visit_item(item); } @@ -1455,5 +1510,5 @@ fn rewrite_macro_with_items( result.push_str(&shape.indent.to_string_with_newline(context.config)); result.push_str(closer); result.push_str(trailing_semicolon); - Some(result) + Ok(result) } diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index 30bf6271b2e..8f62648e576 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -2,19 +2,19 @@ use std::iter::repeat; -use rustc_ast::{ast, ptr, MatchKind}; +use rustc_ast::{MatchKind, ast, ptr}; use rustc_span::{BytePos, Span}; use tracing::debug; -use crate::comment::{combine_strs_with_missing_comments, rewrite_comment, FindUncommented}; +use crate::comment::{FindUncommented, combine_strs_with_missing_comments, rewrite_comment}; use crate::config::lists::*; -use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, Version}; +use crate::config::{Config, ControlBraceStyle, IndentStyle, MatchArmLeadingPipe, StyleEdition}; use crate::expr::{ - format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_cond, - ExprType, RhsTactics, + ExprType, RhsTactics, format_expr, is_empty_block, is_simple_block, is_unsafe_block, + prefer_next_line, rewrite_cond, }; -use crate::lists::{itemize_list, write_list, ListFormatting}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::lists::{ListFormatting, itemize_list, write_list}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -56,6 +56,10 @@ impl<'a> Spanned for ArmWrapper<'a> { impl<'a> Rewrite for ArmWrapper<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_match_arm( context, self.arm, @@ -74,7 +78,7 @@ pub(crate) fn rewrite_match( span: Span, attrs: &[ast::Attribute], match_kind: MatchKind, -) -> Option<String> { +) -> RewriteResult { // Do not take the rhs overhead from the upper expressions into account // when rewriting match condition. let cond_shape = Shape { @@ -83,10 +87,14 @@ pub(crate) fn rewrite_match( }; // 6 = `match ` let cond_shape = match context.config.indent_style() { - IndentStyle::Visual => cond_shape.shrink_left(6)?, - IndentStyle::Block => cond_shape.offset_left(6)?, + IndentStyle::Visual => cond_shape + .shrink_left(6) + .max_width_error(shape.width, span)?, + IndentStyle::Block => cond_shape + .offset_left(6) + .max_width_error(shape.width, span)?, }; - let cond_str = cond.rewrite(context, cond_shape)?; + let cond_str = cond.rewrite_result(context, cond_shape)?; let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let block_sep = match context.config.control_brace_style() { ControlBraceStyle::AlwaysNextLine => alt_block_sep, @@ -105,12 +113,13 @@ pub(crate) fn rewrite_match( let inner_attrs_str = if inner_attrs.is_empty() { String::new() } else { - let shape = match context.config.version() { - Version::One => shape, - _ => shape.block_indent(context.config.tab_spaces()), + let shape = if context.config.style_edition() <= StyleEdition::Edition2021 { + shape + } else { + shape.block_indent(context.config.tab_spaces()) }; inner_attrs - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{}{}\n", nested_indent_str, s))? }; @@ -130,16 +139,16 @@ pub(crate) fn rewrite_match( if arms.is_empty() { let snippet = context.snippet(mk_sp(open_brace_pos, span.hi() - BytePos(1))); if snippet.trim().is_empty() { - Some(format!("match {cond_str} {{}}")) + Ok(format!("match {cond_str} {{}}")) } else { // Empty match with comments or inner attributes? We are not going to bother, sorry ;) - Some(context.snippet(span).to_owned()) + Ok(context.snippet(span).to_owned()) } } else { let span_after_cond = mk_sp(cond.span.hi(), span.hi()); match match_kind { - MatchKind::Prefix => Some(format!( + MatchKind::Prefix => Ok(format!( "match {}{}{{\n{}{}{}\n{}}}", cond_str, block_sep, @@ -148,7 +157,7 @@ pub(crate) fn rewrite_match( rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, shape.indent.to_string(context.config), )), - MatchKind::Postfix => Some(format!( + MatchKind::Postfix => Ok(format!( "{}.match{}{{\n{}{}{}\n{}}}", cond_str, block_sep, @@ -198,7 +207,7 @@ fn rewrite_match_arms( shape: Shape, span: Span, open_brace_pos: BytePos, -) -> Option<String> { +) -> RewriteResult { let arm_shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); @@ -218,7 +227,7 @@ fn rewrite_match_arms( "|", |arm| arm.span().lo(), |arm| arm.span().hi(), - |arm| arm.rewrite(context, arm_shape), + |arm| arm.rewrite_result(context, arm_shape), open_brace_pos, span.hi(), false, @@ -238,19 +247,19 @@ fn rewrite_match_arm( shape: Shape, is_last: bool, has_leading_pipe: bool, -) -> Option<String> { +) -> RewriteResult { let (missing_span, attrs_str) = if !arm.attrs.is_empty() { if contains_skip(&arm.attrs) { - let (_, body) = flatten_arm_body(context, arm.body.as_deref()?, None); + let (_, body) = flatten_arm_body(context, arm.body.as_deref().unknown_error()?, None); // `arm.span()` does not include trailing comma, add it manually. - return Some(format!( + return Ok(format!( "{}{}", context.snippet(arm.span()), arm_comma(context.config, body, is_last), )); } let missing_span = mk_sp(arm.attrs[arm.attrs.len() - 1].span.hi(), arm.pat.span.lo()); - (missing_span, arm.attrs.rewrite(context, shape)?) + (missing_span, arm.attrs.rewrite_result(context, shape)?) } else { (mk_sp(arm.span().lo(), arm.span().lo()), String::new()) }; @@ -264,19 +273,25 @@ fn rewrite_match_arm( }; // Patterns - let pat_shape = match &arm.body.as_ref()?.kind { + let pat_shape = match &arm.body.as_ref().unknown_error()?.kind { ast::ExprKind::Block(_, Some(label)) => { // Some block with a label ` => 'label: {` // 7 = ` => : {` let label_len = label.ident.as_str().len(); - shape.sub_width(7 + label_len)?.offset_left(pipe_offset)? + shape + .sub_width(7 + label_len) + .and_then(|s| s.offset_left(pipe_offset)) + .max_width_error(shape.width, arm.span)? } _ => { // 5 = ` => {` - shape.sub_width(5)?.offset_left(pipe_offset)? + shape + .sub_width(5) + .and_then(|s| s.offset_left(pipe_offset)) + .max_width_error(shape.width, arm.span)? } }; - let pats_str = arm.pat.rewrite(context, pat_shape)?; + let pats_str = arm.pat.rewrite_result(context, pat_shape)?; // Guard let block_like_pat = trimmed_last_line_width(&pats_str) <= context.config.tab_spaces(); @@ -298,10 +313,13 @@ fn rewrite_match_arm( false, )?; - let arrow_span = mk_sp(arm.pat.span.hi(), arm.body.as_ref()?.span().lo()); + let arrow_span = mk_sp( + arm.pat.span.hi(), + arm.body.as_ref().unknown_error()?.span().lo(), + ); rewrite_match_body( context, - arm.body.as_ref()?, + arm.body.as_ref().unknown_error()?, &lhs_str, shape, guard_str.contains('\n'), @@ -382,7 +400,7 @@ fn rewrite_match_body( has_guard: bool, arrow_span: Span, is_last: bool, -) -> Option<String> { +) -> RewriteResult { let (extend, body) = flatten_arm_body( context, body, @@ -403,7 +421,7 @@ fn rewrite_match_body( _ => " ", }; - Some(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma)) + Ok(format!("{} =>{}{}{}", pats_str, block_sep, body_str, comma)) }; let next_line_indent = if !is_block || is_empty_block { @@ -420,7 +438,7 @@ fn rewrite_match_body( let arrow_snippet = context.snippet(arrow_span).trim(); // search for the arrow starting from the end of the snippet since there may be a match // expression within the guard - let arrow_index = if context.config.version() == Version::One { + let arrow_index = if context.config.style_edition() <= StyleEdition::Edition2021 { arrow_snippet.rfind("=>").unwrap() } else { arrow_snippet.find_last_uncommented("=>").unwrap() @@ -447,7 +465,7 @@ fn rewrite_match_body( result.push_str(&nested_indent_str); result.push_str(body_str); result.push_str(comma); - return Some(result); + return Ok(result); } let indent_str = shape.indent.to_string_with_newline(context.config); @@ -458,7 +476,7 @@ fn rewrite_match_body( } else { "" }; - let semicolon = if context.config.version() == Version::One { + let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 { "" } else { if semicolon_for_expr(context, body) { @@ -490,7 +508,7 @@ fn rewrite_match_body( result.push_str(&block_sep); result.push_str(body_str); result.push_str(&body_suffix); - Some(result) + Ok(result) }; // Let's try and get the arm body on the same line as the condition. @@ -499,7 +517,7 @@ fn rewrite_match_body( .offset_left(extra_offset(pats_str, shape) + 4) .and_then(|shape| shape.sub_width(comma.len())); let orig_body = if forbid_same_line || !arrow_comment.is_empty() { - None + Err(RewriteError::Unknown) } else if let Some(body_shape) = orig_body_shape { let rewrite = nop_block_collapse( format_expr(body, ExprType::Statement, context, body_shape), @@ -507,7 +525,7 @@ fn rewrite_match_body( ); match rewrite { - Some(ref body_str) + Ok(ref body_str) if is_block || (!body_str.contains('\n') && unicode_str_width(body_str) <= body_shape.width) => @@ -517,7 +535,7 @@ fn rewrite_match_body( _ => rewrite, } } else { - None + Err(RewriteError::Unknown) }; let orig_budget = orig_body_shape.map_or(0, |shape| shape.width); @@ -528,20 +546,23 @@ fn rewrite_match_body( next_line_body_shape.width, ); match (orig_body, next_line_body) { - (Some(ref orig_str), Some(ref next_line_str)) + (Ok(ref orig_str), Ok(ref next_line_str)) if prefer_next_line(orig_str, next_line_str, RhsTactics::Default) => { combine_next_line_body(next_line_str) } - (Some(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { + (Ok(ref orig_str), _) if extend && first_line_width(orig_str) <= orig_budget => { combine_orig_body(orig_str) } - (Some(ref orig_str), Some(ref next_line_str)) if orig_str.contains('\n') => { + (Ok(ref orig_str), Ok(ref next_line_str)) if orig_str.contains('\n') => { combine_next_line_body(next_line_str) } - (None, Some(ref next_line_str)) => combine_next_line_body(next_line_str), - (None, None) => None, - (Some(ref orig_str), _) => combine_orig_body(orig_str), + (Err(_), Ok(ref next_line_str)) => combine_next_line_body(next_line_str), + // When both orig_body and next_line_body result in errors, we currently propagate the + // error from the second attempt since it is more generous with width constraints. + // This decision is somewhat arbitrary and is open to change. + (Err(_), Err(next_line_err)) => Err(next_line_err), + (Ok(ref orig_str), _) => combine_orig_body(orig_str), } } @@ -554,7 +575,7 @@ fn rewrite_guard( // the arm (excludes offset). pattern_width: usize, multiline_pattern: bool, -) -> Option<String> { +) -> RewriteResult { if let Some(ref guard) = *guard { // First try to fit the guard string on the same line as the pattern. // 4 = ` if `, 5 = ` => {` @@ -563,9 +584,9 @@ fn rewrite_guard( .and_then(|s| s.sub_width(5)); if !multiline_pattern { if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { + if let Ok(cond_str) = guard.rewrite_result(context, cond_shape) { if !cond_str.contains('\n') || pattern_width <= context.config.tab_spaces() { - return Some(format!(" if {cond_str}")); + return Ok(format!(" if {cond_str}")); } } } @@ -575,24 +596,20 @@ fn rewrite_guard( // 3 = `if `, 5 = ` => {` let cond_shape = Shape::indented(shape.indent.block_indent(context.config), context.config) .offset_left(3) - .and_then(|s| s.sub_width(5)); - if let Some(cond_shape) = cond_shape { - if let Some(cond_str) = guard.rewrite(context, cond_shape) { - return Some(format!( - "{}if {}", - cond_shape.indent.to_string_with_newline(context.config), - cond_str - )); - } - } - - None + .and_then(|s| s.sub_width(5)) + .max_width_error(shape.width, guard.span)?; + let cond_str = guard.rewrite_result(context, cond_shape)?; + Ok(format!( + "{}if {}", + cond_shape.indent.to_string_with_newline(context.config), + cond_str + )) } else { - Some(String::new()) + Ok(String::new()) } } -fn nop_block_collapse(block_str: Option<String>, budget: usize) -> Option<String> { +fn nop_block_collapse(block_str: RewriteResult, budget: usize) -> RewriteResult { debug!("nop_block_collapse {:?} {}", block_str, budget); block_str.map(|block_str| { if block_str.starts_with('{') diff --git a/src/tools/rustfmt/src/missed_spans.rs b/src/tools/rustfmt/src/missed_spans.rs index 56adb2e5e69..384de1ce9ae 100644 --- a/src/tools/rustfmt/src/missed_spans.rs +++ b/src/tools/rustfmt/src/missed_spans.rs @@ -1,10 +1,10 @@ use rustc_span::{BytePos, Pos, Span}; use tracing::debug; -use crate::comment::{is_last_comment_block, rewrite_comment, CodeCharKind, CommentCodeSlices}; -use crate::config::file_lines::FileLines; +use crate::comment::{CodeCharKind, CommentCodeSlices, is_last_comment_block, rewrite_comment}; use crate::config::FileName; -use crate::config::Version; +use crate::config::StyleEdition; +use crate::config::file_lines::FileLines; use crate::coverage::transform_missing_snippet; use crate::shape::{Indent, Shape}; use crate::source_map::LineRangeUtils; @@ -247,7 +247,9 @@ impl<'a> FmtVisitor<'a> { let indent_str = self.block_indent.to_string(self.config); self.push_str(&indent_str); self.block_indent - } else if self.config.version() == Version::Two && !snippet.starts_with('\n') { + } else if self.config.style_edition() >= StyleEdition::Edition2024 + && !snippet.starts_with('\n') + { // The comment appears on the same line as the previous formatted code. // Assuming that comment is logically associated with that code, we want to keep it on // the same level and avoid mixing it with possible other comment. @@ -283,13 +285,13 @@ impl<'a> FmtVisitor<'a> { let other_lines = &subslice[offset + 1..]; let comment_str = rewrite_comment(other_lines, false, comment_shape, self.config) - .unwrap_or_else(|| String::from(other_lines)); + .unwrap_or_else(|_| String::from(other_lines)); self.push_str(&comment_str); } } } else { let comment_str = rewrite_comment(subslice, false, comment_shape, self.config) - .unwrap_or_else(|| String::from(subslice)); + .unwrap_or_else(|_| String::from(subslice)); self.push_str(&comment_str); } diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs index 0590f28ee05..493b04f16c6 100644 --- a/src/tools/rustfmt/src/modules.rs +++ b/src/tools/rustfmt/src/modules.rs @@ -4,8 +4,8 @@ use std::path::{Path, PathBuf}; use rustc_ast::ast; use rustc_ast::visit::Visitor; -use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{self, Symbol, sym}; use thin_vec::ThinVec; use thiserror::Error; diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs index cdb735be8a4..fdc8e23e72b 100644 --- a/src/tools/rustfmt/src/overflow.rs +++ b/src/tools/rustfmt/src/overflow.rs @@ -9,22 +9,22 @@ use rustc_span::Span; use tracing::debug; use crate::closures; -use crate::config::Version; -use crate::config::{lists::*, Config}; +use crate::config::StyleEdition; +use crate::config::{Config, lists::*}; use crate::expr::{ can_be_overflowed_expr, is_every_expr_simple, is_method_call, is_nested_call, is_simple_expr, rewrite_cond, }; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; use crate::macros::MacroArg; -use crate::patterns::{can_be_overflowed_pat, TuplePatField}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::patterns::{TuplePatField, can_be_overflowed_pat}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; -use crate::types::{can_be_overflowed_type, SegmentParam}; +use crate::types::{SegmentParam, can_be_overflowed_type}; use crate::utils::{count_newlines, extra_offset, first_line_width, last_line_width, mk_sp}; /// A list of `format!`-like macros, that take a long format string and a list of arguments to @@ -91,6 +91,10 @@ impl<'a> Rewrite for OverflowableItem<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { self.map(|item| item.rewrite(context, shape)) } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + self.map(|item| item.rewrite_result(context, shape)) + } } impl<'a> Spanned for OverflowableItem<'a> { @@ -201,8 +205,12 @@ impl<'a> OverflowableItem<'a> { OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR, _ => &[], }; - let additional_cases = match (self, config.version()) { - (OverflowableItem::MacroArg(..), Version::Two) => SPECIAL_CASE_MACROS_V2, + let additional_cases = match self { + OverflowableItem::MacroArg(..) + if config.style_edition() >= StyleEdition::Edition2024 => + { + SPECIAL_CASE_MACROS_V2 + } _ => &[], }; base_cases.iter().chain(additional_cases) @@ -278,7 +286,7 @@ pub(crate) fn rewrite_with_parens<'a, T: 'a + IntoOverflowableItem<'a>>( span: Span, item_max_width: usize, force_separator_tactic: Option<SeparatorTactic>, -) -> Option<String> { +) -> RewriteResult { Context::new( context, items, @@ -300,7 +308,7 @@ pub(crate) fn rewrite_with_angle_brackets<'a, T: 'a + IntoOverflowableItem<'a>>( items: impl Iterator<Item = &'a T>, shape: Shape, span: Span, -) -> Option<String> { +) -> RewriteResult { Context::new( context, items, @@ -324,7 +332,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>> span: Span, force_separator_tactic: Option<SeparatorTactic>, delim_token: Option<Delimiter>, -) -> Option<String> { +) -> RewriteResult { let (lhs, rhs) = match delim_token { Some(Delimiter::Parenthesis) => ("(", ")"), Some(Delimiter::Brace) => ("{", "}"), @@ -428,7 +436,7 @@ impl<'a> Context<'a> { if closures::args_have_many_closure(&self.items) { None } else { - closures::rewrite_last_closure(self.context, expr, shape) + closures::rewrite_last_closure(self.context, expr, shape).ok() } } @@ -457,7 +465,7 @@ impl<'a> Context<'a> { if let Some(rewrite) = rewrite { // splitn(2, *).next().unwrap() is always safe. - let rewrite_first_line = Some(rewrite.splitn(2, '\n').next().unwrap().to_owned()); + let rewrite_first_line = Ok(rewrite.splitn(2, '\n').next().unwrap().to_owned()); last_list_item.item = rewrite_first_line; Some(rewrite) } else { @@ -495,7 +503,7 @@ impl<'a> Context<'a> { Some(OverflowableItem::MacroArg(MacroArg::Expr(expr))) if !combine_arg_with_callee && is_method_call(expr) - && self.context.config.version() == Version::Two => + && self.context.config.style_edition() >= StyleEdition::Edition2024 => { self.context.force_one_line_chain.replace(true); } @@ -545,22 +553,23 @@ impl<'a> Context<'a> { .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); let no_newline = rw.as_ref().map_or(false, |s| !s.contains('\n')); if no_newline { - list_items[self.items.len() - 1].item = rw; + list_items[self.items.len() - 1].item = rw.unknown_error(); } else { - list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); + list_items[self.items.len() - 1].item = Ok(overflowed.to_owned()); } } else { - list_items[self.items.len() - 1].item = Some(overflowed.to_owned()); + list_items[self.items.len() - 1].item = Ok(overflowed.to_owned()); } } (true, DefinitiveListTactic::Horizontal, placeholder @ Some(..)) => { - list_items[self.items.len() - 1].item = placeholder; + list_items[self.items.len() - 1].item = placeholder.unknown_error(); } _ if !self.items.is_empty() => { list_items[self.items.len() - 1].item = self .items .last() - .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)); + .and_then(|last_item| last_item.rewrite(self.context, self.nested_shape)) + .unknown_error(); // Use horizontal layout for a function with a single argument as long as // everything fits in a single line. @@ -613,7 +622,7 @@ impl<'a> Context<'a> { tactic } - fn rewrite_items(&self) -> Option<(bool, String)> { + fn rewrite_items(&self) -> Result<(bool, String), RewriteError> { let span = self.items_span(); debug!("items: {:?}", self.items); @@ -624,7 +633,7 @@ impl<'a> Context<'a> { ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(self.context, self.nested_shape), + |item| item.rewrite_result(self.context, self.nested_shape), span.lo(), span.hi(), true, @@ -689,7 +698,8 @@ impl<'a> Context<'a> { ); result.push_str(self.ident); result.push_str(prefix); - let force_single_line = if self.context.config.version() == Version::Two { + let force_single_line = if self.context.config.style_edition() >= StyleEdition::Edition2024 + { !self.context.use_block_indent() || (is_extendable && extend_width <= shape.width) } else { // 2 = `()` @@ -711,7 +721,7 @@ impl<'a> Context<'a> { result } - fn rewrite(&self, shape: Shape) -> Option<String> { + fn rewrite(&self, shape: Shape) -> RewriteResult { let (extendable, items_str) = self.rewrite_items()?; // If we are using visual indent style and failed to format, retry with block indent. @@ -725,7 +735,7 @@ impl<'a> Context<'a> { return result; } - Some(self.wrap_items(&items_str, shape, extendable)) + Ok(self.wrap_items(&items_str, shape, extendable)) } } diff --git a/src/tools/rustfmt/src/pairs.rs b/src/tools/rustfmt/src/pairs.rs index bfc2ffed383..9c51298416b 100644 --- a/src/tools/rustfmt/src/pairs.rs +++ b/src/tools/rustfmt/src/pairs.rs @@ -1,9 +1,11 @@ use rustc_ast::ast; +use rustc_span::Span; -use crate::config::lists::*; use crate::config::IndentStyle; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::config::lists::*; +use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; +use crate::spanned::Spanned; use crate::utils::{ first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str, }; @@ -40,16 +42,19 @@ pub(crate) fn rewrite_all_pairs( expr: &ast::Expr, shape: Shape, context: &RewriteContext<'_>, -) -> Option<String> { - expr.flatten(context, shape).and_then(|list| { - if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { - rewrite_pairs_multiline(&list, shape, context) - } else { - // First we try formatting on one line. - rewrite_pairs_one_line(&list, shape, context) - .or_else(|| rewrite_pairs_multiline(&list, shape, context)) - } - }) +) -> RewriteResult { + expr.flatten(context, shape) + .unknown_error() + .and_then(|list| { + if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() { + rewrite_pairs_multiline(&list, shape, context) + } else { + // First we try formatting on one line. + rewrite_pairs_one_line(&list, shape, context) + .unknown_error() + .or_else(|_| rewrite_pairs_multiline(&list, shape, context)) + } + }) } // This may return a multi-line result since we allow the last expression to go @@ -65,7 +70,7 @@ fn rewrite_pairs_one_line<T: Rewrite>( let base_shape = shape.block(); for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) { - if let Some(rewrite) = rewrite { + if let Ok(rewrite) = rewrite { if !is_single_line(rewrite) || result.len() > shape.width { return None; } @@ -104,19 +109,20 @@ fn rewrite_pairs_multiline<T: Rewrite>( list: &PairList<'_, '_, T>, shape: Shape, context: &RewriteContext<'_>, -) -> Option<String> { +) -> RewriteResult { let rhs_offset = shape.rhs_overhead(context.config); let nested_shape = (match context.config.indent_style() { IndentStyle::Visual => shape.visual_indent(0), IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) .with_max_width(context.config) - .sub_width(rhs_offset)?; + .sub_width(rhs_offset) + .max_width_error(shape.width, list.span)?; let indent_str = nested_shape.indent.to_string_with_newline(context.config); let mut result = String::new(); - result.push_str(list.list[0].1.as_ref()?); + result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?); for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) { // The following test checks if we should keep two subexprs on the same @@ -132,7 +138,7 @@ fn rewrite_pairs_multiline<T: Rewrite>( if let Some(line_shape) = shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result)) { - if let Some(rewrite) = e.rewrite(context, line_shape) { + if let Ok(rewrite) = e.rewrite_result(context, line_shape) { result.push(' '); result.push_str(s); result.push(' '); @@ -155,9 +161,9 @@ fn rewrite_pairs_multiline<T: Rewrite>( } } - result.push_str(default_rw.as_ref()?); + result.push_str(default_rw.as_ref().map_err(|err| err.clone())?); } - Some(result) + Ok(result) } // Rewrites a single pair. @@ -168,10 +174,10 @@ pub(crate) fn rewrite_pair<LHS, RHS>( context: &RewriteContext<'_>, shape: Shape, separator_place: SeparatorPlace, -) -> Option<String> +) -> RewriteResult where - LHS: Rewrite, - RHS: Rewrite, + LHS: Rewrite + Spanned, + RHS: Rewrite + Spanned, { let tab_spaces = context.config.tab_spaces(); let lhs_overhead = match separator_place { @@ -183,15 +189,17 @@ where ..shape }; let lhs_result = lhs - .rewrite(context, lhs_shape) + .rewrite_result(context, lhs_shape) .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?; // Try to put both lhs and rhs on the same line. let rhs_orig_result = shape .offset_left(last_line_width(&lhs_result) + pp.infix.len()) .and_then(|s| s.sub_width(pp.suffix.len())) - .and_then(|rhs_shape| rhs.rewrite(context, rhs_shape)); - if let Some(ref rhs_result) = rhs_orig_result { + .max_width_error(shape.width, rhs.span()) + .and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape)); + + if let Ok(ref rhs_result) = rhs_orig_result { // If the length of the lhs is equal to or shorter than the tab width or // the rhs looks like block expression, we put the rhs on the same // line with the lhs even if the rhs is multi-lined. @@ -207,7 +215,7 @@ where + first_line_width(rhs_result) + pp.suffix.len(); if one_line_width <= shape.width { - return Some(format!( + return Ok(format!( "{}{}{}{}", lhs_result, pp.infix, rhs_result, pp.suffix )); @@ -219,13 +227,15 @@ where // Re-evaluate the rhs because we have more space now: let mut rhs_shape = match context.config.indent_style() { IndentStyle::Visual => shape - .sub_width(pp.suffix.len() + pp.prefix.len())? + .sub_width(pp.suffix.len() + pp.prefix.len()) + .max_width_error(shape.width, rhs.span())? .visual_indent(pp.prefix.len()), IndentStyle::Block => { // Try to calculate the initial constraint on the right hand side. let rhs_overhead = shape.rhs_overhead(context.config); Shape::indented(shape.indent.block_indent(context.config), context.config) - .sub_width(rhs_overhead)? + .sub_width(rhs_overhead) + .max_width_error(shape.width, rhs.span())? } }; let infix = match separator_place { @@ -233,15 +243,17 @@ where SeparatorPlace::Front => pp.infix.trim_start(), }; if separator_place == SeparatorPlace::Front { - rhs_shape = rhs_shape.offset_left(infix.len())?; + rhs_shape = rhs_shape + .offset_left(infix.len()) + .max_width_error(rhs_shape.width, rhs.span())?; } - let rhs_result = rhs.rewrite(context, rhs_shape)?; + let rhs_result = rhs.rewrite_result(context, rhs_shape)?; let indent_str = rhs_shape.indent.to_string_with_newline(context.config); let infix_with_sep = match separator_place { SeparatorPlace::Back => format!("{infix}{indent_str}"), SeparatorPlace::Front => format!("{indent_str}{infix}"), }; - Some(format!( + Ok(format!( "{}{}{}{}", lhs_result, infix_with_sep, rhs_result, pp.suffix )) @@ -255,8 +267,9 @@ trait FlattenPair: Rewrite + Sized { } struct PairList<'a, 'b, T: Rewrite> { - list: Vec<(&'b T, Option<String>)>, + list: Vec<(&'b T, RewriteResult)>, separators: Vec<&'a str>, + span: Span, } fn is_ident(expr: &ast::Expr) -> bool { @@ -303,7 +316,7 @@ impl FlattenPair for ast::Expr { let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| { if is_first { - return node.rewrite(context, shape); + return node.rewrite_result(context, shape); } let nested_overhead = sep + 1; let rhs_offset = shape.rhs_overhead(context.config); @@ -312,12 +325,17 @@ impl FlattenPair for ast::Expr { IndentStyle::Block => shape.block_indent(context.config.tab_spaces()), }) .with_max_width(context.config) - .sub_width(rhs_offset)?; + .sub_width(rhs_offset) + .max_width_error(shape.width, node.span)?; let default_shape = match context.config.binop_separator() { - SeparatorPlace::Back => nested_shape.sub_width(nested_overhead)?, - SeparatorPlace::Front => nested_shape.offset_left(nested_overhead)?, + SeparatorPlace::Back => nested_shape + .sub_width(nested_overhead) + .max_width_error(nested_shape.width, node.span)?, + SeparatorPlace::Front => nested_shape + .offset_left(nested_overhead) + .max_width_error(nested_shape.width, node.span)?, }; - node.rewrite(context, default_shape) + node.rewrite_result(context, default_shape) }; // Turn a tree of binop expressions into a list using a depth-first, @@ -326,6 +344,7 @@ impl FlattenPair for ast::Expr { let mut list = vec![]; let mut separators = vec![]; let mut node = self; + let span = self.span(); loop { match node.kind { ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => { @@ -352,7 +371,11 @@ impl FlattenPair for ast::Expr { } assert_eq!(list.len() - 1, separators.len()); - Some(PairList { list, separators }) + Some(PairList { + list, + separators, + span, + }) } } diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs index 6373d0251f8..5ee083da539 100644 --- a/src/tools/rustfmt/src/parse/macros/asm.rs +++ b/src/tools/rustfmt/src/parse/macros/asm.rs @@ -1,5 +1,5 @@ use rustc_ast::ast; -use rustc_builtin_macros::asm::{parse_asm_args, AsmArgs}; +use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args}; use crate::rewrite::RewriteContext; diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs index b91d203d531..ec771f96a3f 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use rustc_ast::ast; use rustc_ast::token::{Delimiter, TokenKind}; diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs index 8d5f7f90958..7271e73db8d 100644 --- a/src/tools/rustfmt/src/parse/macros/mod.rs +++ b/src/tools/rustfmt/src/parse/macros/mod.rs @@ -1,11 +1,11 @@ use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{ast, ptr}; -use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::MACRO_ARGUMENTS; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_session::parse::ParseSess; -use rustc_span::symbol::{self, kw}; use rustc_span::Symbol; +use rustc_span::symbol::{self, kw}; use crate::macros::MacroArg; use crate::rewrite::RewriteContext; diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 6051241309d..28b4c2b612f 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -1,4 +1,4 @@ -use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::panic::{AssertUnwindSafe, catch_unwind}; use std::path::{Path, PathBuf}; use rustc_ast::token::TokenKind; @@ -6,11 +6,11 @@ use rustc_ast::{ast, attr, ptr}; use rustc_errors::Diag; use rustc_parse::parser::Parser as RawParser; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use thin_vec::ThinVec; -use crate::parse::session::ParseSess; use crate::Input; +use crate::parse::session::ParseSess; pub(crate) type DirectoryOwnership = rustc_expand::module::DirOwnership; pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess; diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index 05cf467167c..9f27a05939e 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -2,13 +2,14 @@ use std::path::Path; use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{IntoDynSyncSend, Lrc}; -use rustc_errors::emitter::{stderr_destination, DynEmitter, Emitter, HumanEmitter, SilentEmitter}; +use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::translation::Translate; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ + BytePos, Span, source_map::{FilePathMapping, SourceMap}, - symbol, BytePos, Span, + symbol, }; use crate::config::file_lines::LineRange; @@ -394,7 +395,9 @@ mod tests { } fn get_ignore_list(config: &str) -> IgnoreList { - Config::from_toml(config, Path::new("")).unwrap().ignore() + Config::from_toml(config, Path::new("./rustfmt.toml")) + .unwrap() + .ignore() } #[test] diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index d8cb26a20f1..6fe2d4a8520 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -2,22 +2,22 @@ use rustc_ast::ast::{self, BindingMode, ByRef, Pat, PatField, PatKind, RangeEnd, use rustc_ast::ptr; use rustc_span::{BytePos, Span}; -use crate::comment::{combine_strs_with_missing_comments, FindUncommented}; +use crate::comment::{FindUncommented, combine_strs_with_missing_comments}; +use crate::config::StyleEdition; use crate::config::lists::*; -use crate::config::Version; use crate::expr::{can_be_overflowed_expr, rewrite_unary_prefix, wrap_struct_field}; use crate::lists::{ - definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, - struct_lit_tactic, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, shape_for_tactic, + struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::pairs::{rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; -use crate::types::{rewrite_path, PathContext}; +use crate::types::{PathContext, rewrite_path}; use crate::utils::{format_mutability, mk_sp, mk_sp_lo_plus_one, rewrite_ident}; /// Returns `true` if the given pattern is "short". @@ -61,25 +61,36 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { } } -struct RangeOperand<'a>(&'a Option<ptr::P<ast::Expr>>); +pub(crate) struct RangeOperand<'a> { + operand: &'a Option<ptr::P<ast::Expr>>, + pub(crate) span: Span, +} impl<'a> Rewrite for RangeOperand<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - match &self.0 { - None => Some("".to_owned()), - Some(ref exp) => exp.rewrite(context, shape), + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + match &self.operand { + None => Ok("".to_owned()), + Some(ref exp) => exp.rewrite_result(context, shape), } } } impl Rewrite for Pat { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self.kind { PatKind::Or(ref pats) => { let pat_strs = pats .iter() - .map(|p| p.rewrite(context, shape)) - .collect::<Option<Vec<_>>>()?; + .map(|p| p.rewrite_result(context, shape)) + .collect::<Result<Vec<_>, RewriteError>>()?; let use_mixed_layout = pats .iter() @@ -115,14 +126,21 @@ impl Rewrite for Pat { let sub_pat = match *sub_pat { Some(ref p) => { // 2 - `@ `. - let width = shape.width.checked_sub( - mut_prefix.len() + ref_kw.len() + mut_infix.len() + id_str.len() + 2, - )?; + let width = shape + .width + .checked_sub( + mut_prefix.len() + + ref_kw.len() + + mut_infix.len() + + id_str.len() + + 2, + ) + .max_width_error(shape.width, p.span())?; let lo = context.snippet_provider.span_after(self.span, "@"); combine_strs_with_missing_comments( context, "@", - &p.rewrite(context, Shape::legacy(width, shape.indent))?, + &p.rewrite_result(context, Shape::legacy(width, shape.indent))?, mk_sp(lo, p.span.lo()), shape, true, @@ -207,19 +225,25 @@ impl Rewrite for Pat { } PatKind::Wild => { if 1 <= shape.width { - Some("_".to_owned()) + Ok("_".to_owned()) } else { - None + Err(RewriteError::ExceedsMaxWidth { + configured_width: 1, + span: self.span, + }) } } PatKind::Rest => { if 1 <= shape.width { - Some("..".to_owned()) + Ok("..".to_owned()) } else { - None + Err(RewriteError::ExceedsMaxWidth { + configured_width: 1, + span: self.span, + }) } } - PatKind::Never => None, + PatKind::Never => Err(RewriteError::Unknown), PatKind::Range(ref lhs, ref rhs, ref end_kind) => { let infix = match end_kind.node { RangeEnd::Included(RangeSyntax::DotDotDot) => "...", @@ -239,9 +263,17 @@ impl Rewrite for Pat { } else { infix.to_owned() }; + let lspan = self.span.with_hi(end_kind.span.lo()); + let rspan = self.span.with_lo(end_kind.span.hi()); rewrite_pair( - &RangeOperand(lhs), - &RangeOperand(rhs), + &RangeOperand { + operand: lhs, + span: lspan, + }, + &RangeOperand { + operand: rhs, + span: rspan, + }, PairParts::infix(&infix), context, shape, @@ -260,19 +292,21 @@ impl Rewrite for Pat { let path_str = rewrite_path(context, PathContext::Expr, q_self, path, shape)?; rewrite_tuple_pat(pat_vec, Some(path_str), self.span, context, shape) } - PatKind::Lit(ref expr) => expr.rewrite(context, shape), - PatKind::Slice(ref slice_pat) if context.config.version() == Version::One => { + PatKind::Lit(ref expr) => expr.rewrite_result(context, shape), + PatKind::Slice(ref slice_pat) + if context.config.style_edition() <= StyleEdition::Edition2021 => + { let rw: Vec<String> = slice_pat .iter() .map(|p| { - if let Some(rw) = p.rewrite(context, shape) { + if let Ok(rw) = p.rewrite_result(context, shape) { rw } else { context.snippet(p.span).to_string() } }) .collect(); - Some(format!("[{}]", rw.join(", "))) + Ok(format!("[{}]", rw.join(", "))) } PatKind::Slice(ref slice_pat) => overflow::rewrite_with_square_brackets( context, @@ -296,10 +330,16 @@ impl Rewrite for Pat { rewrite_macro(mac, None, context, shape, MacroPosition::Pat) } PatKind::Paren(ref pat) => pat - .rewrite(context, shape.offset_left(1)?.sub_width(1)?) + .rewrite_result( + context, + shape + .offset_left(1) + .and_then(|s| s.sub_width(1)) + .max_width_error(shape.width, self.span)?, + ) .map(|inner_pat| format!("({})", inner_pat)), - PatKind::Err(_) => None, - PatKind::Deref(_) => None, + PatKind::Err(_) => Err(RewriteError::Unknown), + PatKind::Deref(_) => Err(RewriteError::Unknown), } } } @@ -312,20 +352,21 @@ fn rewrite_struct_pat( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { // 2 = ` {` - let path_shape = shape.sub_width(2)?; + let path_shape = shape.sub_width(2).max_width_error(shape.width, span)?; let path_str = rewrite_path(context, PathContext::Expr, qself, path, path_shape)?; if fields.is_empty() && !ellipsis { - return Some(format!("{path_str} {{}}")); + return Ok(format!("{path_str} {{}}")); } let (ellipsis_str, terminator) = if ellipsis { (", ..", "..") } else { ("", "}") }; // 3 = ` { `, 2 = ` }`. let (h_shape, v_shape) = - struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2)?; + struct_lit_shape(shape, context, path_str.len() + 3, ellipsis_str.len() + 2) + .max_width_error(shape.width, span)?; let items = itemize_list( context.snippet_provider, @@ -340,7 +381,7 @@ fn rewrite_struct_pat( } }, |f| f.span.hi(), - |f| f.rewrite(context, v_shape), + |f| f.rewrite_result(context, v_shape), context.snippet_provider.span_after(span, "{"), span.hi(), false, @@ -379,11 +420,15 @@ fn rewrite_struct_pat( // ast::Pat doesn't have attrs so use &[] let fields_str = wrap_struct_field(context, &[], &fields_str, shape, v_shape, one_line_width)?; - Some(format!("{path_str} {{{fields_str}}}")) + Ok(format!("{path_str} {{{fields_str}}}")) } impl Rewrite for PatField { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { let hi_pos = if let Some(last) = self.attrs.last() { last.span.hi() } else { @@ -393,10 +438,10 @@ impl Rewrite for PatField { let attrs_str = if self.attrs.is_empty() { String::from("") } else { - self.attrs.rewrite(context, shape)? + self.attrs.rewrite_result(context, shape)? }; - let pat_str = self.pat.rewrite(context, shape)?; + let pat_str = self.pat.rewrite_result(context, shape)?; if self.is_shorthand { combine_strs_with_missing_comments( context, @@ -417,7 +462,7 @@ impl Rewrite for PatField { "{}:\n{}{}", id_str, nested_shape.indent.to_string(context.config), - self.pat.rewrite(context, nested_shape)? + self.pat.rewrite_result(context, nested_shape)? ) }; combine_strs_with_missing_comments( @@ -440,9 +485,13 @@ pub(crate) enum TuplePatField<'a> { impl<'a> Rewrite for TuplePatField<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - TuplePatField::Pat(p) => p.rewrite(context, shape), - TuplePatField::Dotdot(_) => Some("..".to_string()), + TuplePatField::Pat(p) => p.rewrite_result(context, shape), + TuplePatField::Dotdot(_) => Ok("..".to_string()), } } } @@ -492,9 +541,9 @@ fn rewrite_tuple_pat( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { if pats.is_empty() { - return Some(format!("{}()", path_str.unwrap_or_default())); + return Ok(format!("{}()", path_str.unwrap_or_default())); } let mut pat_vec: Vec<_> = pats.iter().map(TuplePatField::Pat).collect(); @@ -548,7 +597,7 @@ fn count_wildcard_suffix_len( ",", |item| item.span().lo(), |item| item.span().hi(), - |item| item.rewrite(context, shape), + |item| item.rewrite_result(context, shape), context.snippet_provider.span_after(span, "("), span.hi() - BytePos(1), false, @@ -558,7 +607,7 @@ fn count_wildcard_suffix_len( for item in items .iter() .rev() - .take_while(|i| matches!(i.item, Some(ref internal_string) if internal_string == "_")) + .take_while(|i| matches!(i.item, Ok(ref internal_string) if internal_string == "_")) { suffix_len += 1; diff --git a/src/tools/rustfmt/src/reorder.rs b/src/tools/rustfmt/src/reorder.rs index fdbed939af5..8a31e0ac816 100644 --- a/src/tools/rustfmt/src/reorder.rs +++ b/src/tools/rustfmt/src/reorder.rs @@ -9,13 +9,13 @@ use std::cmp::Ordering; use rustc_ast::{ast, attr}; -use rustc_span::{symbol::sym, Span}; +use rustc_span::{Span, symbol::sym}; use crate::config::{Config, GroupImportsTactic}; -use crate::imports::{normalize_use_trees_with_granularity, UseSegmentKind, UseTree}; +use crate::imports::{UseSegmentKind, UseTree, normalize_use_trees_with_granularity}; use crate::items::{is_mod_decl, rewrite_extern_crate, rewrite_mod}; -use crate::lists::{itemize_list, write_list, ListFormatting, ListItem}; -use crate::rewrite::RewriteContext; +use crate::lists::{ListFormatting, ListItem, itemize_list, write_list}; +use crate::rewrite::{RewriteContext, RewriteErrorExt}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; use crate::spanned::Spanned; @@ -59,7 +59,7 @@ fn wrap_reorderable_items( let fmt = ListFormatting::new(shape, context.config) .separator("") .align_comments(false); - write_list(list_items, &fmt) + write_list(list_items, &fmt).ok() } fn rewrite_reorderable_item( @@ -99,7 +99,7 @@ fn rewrite_reorderable_or_regroupable_items( ";", |item| item.span().lo(), |item| item.span().hi(), - |_item| Some("".to_owned()), + |_item| Ok("".to_owned()), span.lo(), span.hi(), false, @@ -131,9 +131,16 @@ fn rewrite_reorderable_or_regroupable_items( .map(|use_group| { let item_vec: Vec<_> = use_group .into_iter() - .map(|use_tree| ListItem { - item: use_tree.rewrite_top_level(context, nested_shape), - ..use_tree.list_item.unwrap_or_else(ListItem::empty) + .map(|use_tree| { + let item = use_tree.rewrite_top_level(context, nested_shape); + if let Some(list_item) = use_tree.list_item { + ListItem { + item: item, + ..list_item + } + } else { + ListItem::from_item(item) + } }) .collect(); wrap_reorderable_items(context, &item_vec, nested_shape) @@ -151,7 +158,7 @@ fn rewrite_reorderable_or_regroupable_items( ";", |item| item.span().lo(), |item| item.span().hi(), - |item| rewrite_reorderable_item(context, item, shape), + |item| rewrite_reorderable_item(context, item, shape).unknown_error(), span.lo(), span.hi(), false, diff --git a/src/tools/rustfmt/src/rewrite.rs b/src/tools/rustfmt/src/rewrite.rs index e2498a3500a..83020709797 100644 --- a/src/tools/rustfmt/src/rewrite.rs +++ b/src/tools/rustfmt/src/rewrite.rs @@ -5,17 +5,23 @@ use std::rc::Rc; use rustc_ast::ptr; use rustc_span::Span; +use thiserror::Error; +use crate::FormatReport; use crate::config::{Config, IndentStyle}; use crate::parse::session::ParseSess; use crate::shape::Shape; use crate::skip::SkipContext; use crate::visitor::SnippetProvider; -use crate::FormatReport; +pub(crate) type RewriteResult = Result<String, RewriteError>; pub(crate) trait Rewrite { /// Rewrite self into shape. fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>; + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + self.rewrite(context, shape).unknown_error() + } } impl<T: Rewrite> Rewrite for ptr::P<T> { @@ -24,6 +30,66 @@ impl<T: Rewrite> Rewrite for ptr::P<T> { } } +#[derive(Clone, Debug, PartialEq)] +pub(crate) enum MacroErrorKind { + ParseFailure, + ReplaceMacroVariable, + Unknown, +} + +impl std::fmt::Display for MacroErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MacroErrorKind::ParseFailure => write!(f, "(parse failure)"), + MacroErrorKind::ReplaceMacroVariable => write!(f, "(replacing macro variables with $)"), + MacroErrorKind::Unknown => write!(f, ""), + } + } +} + +#[derive(Clone, Error, Debug)] +pub(crate) enum RewriteError { + #[error("Formatting was skipped due to skip attribute or out of file range.")] + SkipFormatting, + + #[error("It exceeds the required width of {configured_width} for the span: {span:?}")] + ExceedsMaxWidth { configured_width: usize, span: Span }, + + #[error("Failed to format given macro{} at: {span:?}", kind)] + MacroFailure { kind: MacroErrorKind, span: Span }, + + /// Format failure that does not fit to above categories. + #[error("An unknown error occurred during formatting.")] + Unknown, +} + +/// Extension trait used to conveniently convert to RewriteError +pub(crate) trait RewriteErrorExt<T> { + fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError>; + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError>; + fn unknown_error(self) -> Result<T, RewriteError>; +} + +impl<T> RewriteErrorExt<T> for Option<T> { + fn max_width_error(self, width: usize, span: Span) -> Result<T, RewriteError> { + self.ok_or_else(|| RewriteError::ExceedsMaxWidth { + configured_width: width, + span: span, + }) + } + + fn macro_error(self, kind: MacroErrorKind, span: Span) -> Result<T, RewriteError> { + self.ok_or_else(|| RewriteError::MacroFailure { + kind: kind, + span: span, + }) + } + + fn unknown_error(self) -> Result<T, RewriteError> { + self.ok_or_else(|| RewriteError::Unknown) + } +} + #[derive(Clone)] pub(crate) struct RewriteContext<'a> { pub(crate) psess: &'a ParseSess, diff --git a/src/tools/rustfmt/src/rustfmt_diff.rs b/src/tools/rustfmt/src/rustfmt_diff.rs index c9883452185..4624683fa05 100644 --- a/src/tools/rustfmt/src/rustfmt_diff.rs +++ b/src/tools/rustfmt/src/rustfmt_diff.rs @@ -282,7 +282,7 @@ where #[cfg(test)] mod test { use super::DiffLine::*; - use super::{make_diff, Mismatch}; + use super::{Mismatch, make_diff}; use super::{ModifiedChunk, ModifiedLines}; #[test] diff --git a/src/tools/rustfmt/src/sort.rs b/src/tools/rustfmt/src/sort.rs new file mode 100644 index 00000000000..670f664a119 --- /dev/null +++ b/src/tools/rustfmt/src/sort.rs @@ -0,0 +1,368 @@ +use itertools::EitherOrBoth; +use itertools::Itertools; + +/// Iterator which breaks an identifier into various [VersionChunk]s. +struct VersionChunkIter<'a> { + ident: &'a str, + start: usize, +} + +impl<'a> VersionChunkIter<'a> { + pub(crate) fn new(ident: &'a str) -> Self { + Self { ident, start: 0 } + } + + fn parse_numeric_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option<VersionChunk<'a>> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + let zeros = source.chars().take_while(|c| *c == '0').count(); + let value = source.parse::<usize>().ok()?; + + Some(VersionChunk::Number { + value, + zeros, + source, + }) + } + + fn parse_str_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option<VersionChunk<'a>> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c == '_' { + is_end_of_chunk = true; + break; + } + + if !c.is_numeric() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + Some(VersionChunk::Str(source)) + } +} + +impl<'a> Iterator for VersionChunkIter<'a> { + type Item = VersionChunk<'a>; + + fn next(&mut self) -> Option<Self::Item> { + let mut chars = self.ident[self.start..].char_indices(); + let (_, next) = chars.next()?; + + if next == '_' { + self.start = self.start + next.len_utf8(); + return Some(VersionChunk::Underscore); + } + + if next.is_ascii_digit() { + return self.parse_numeric_chunk(chars); + } + + self.parse_str_chunk(chars) + } +} + +/// Represents a chunk in the version-sort algorithm +#[derive(Debug, PartialEq, Eq)] +enum VersionChunk<'a> { + /// A single `_` in an identifier. Underscores are sorted before all other characters. + Underscore, + /// A &str chunk in the version sort. + Str(&'a str), + /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros. + Number { + value: usize, + zeros: usize, + source: &'a str, + }, +} + +/// Determine which side of the version-sort comparison had more leading zeros. +#[derive(Debug, PartialEq, Eq)] +enum MoreLeadingZeros { + Left, + Right, + Equal, +} + +/// Compare two identifiers based on the version sorting algorithm described in [the style guide] +/// +/// [the style guide]: https://doc.rust-lang.org/nightly/style-guide/#sorting +pub(crate) fn version_sort(a: &str, b: &str) -> std::cmp::Ordering { + let iter_a = VersionChunkIter::new(a); + let iter_b = VersionChunkIter::new(b); + let mut more_leading_zeros = MoreLeadingZeros::Equal; + + for either_or_both in iter_a.zip_longest(iter_b) { + match either_or_both { + EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater, + EitherOrBoth::Right(_) => return std::cmp::Ordering::Less, + EitherOrBoth::Both(a, b) => match (a, b) { + (VersionChunk::Underscore, VersionChunk::Underscore) => { + continue; + } + (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less, + (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater, + (VersionChunk::Str(ca), VersionChunk::Str(cb)) + | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. }) + | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => { + match ca.cmp(&cb) { + std::cmp::Ordering::Equal => { + continue; + } + order @ _ => return order, + } + } + ( + VersionChunk::Number { + value: va, + zeros: lza, + .. + }, + VersionChunk::Number { + value: vb, + zeros: lzb, + .. + }, + ) => match va.cmp(&vb) { + std::cmp::Ordering::Equal => { + if lza == lzb { + continue; + } + + if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb { + more_leading_zeros = MoreLeadingZeros::Left; + } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb { + more_leading_zeros = MoreLeadingZeros::Right; + } + continue; + } + order @ _ => return order, + }, + }, + } + } + + match more_leading_zeros { + MoreLeadingZeros::Equal => std::cmp::Ordering::Equal, + MoreLeadingZeros::Left => std::cmp::Ordering::Less, + MoreLeadingZeros::Right => std::cmp::Ordering::Greater, + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_chunks() { + let mut iter = VersionChunkIter::new("x86_128"); + assert_eq!(iter.next(), Some(VersionChunk::Str("x"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 86, + zeros: 0, + source: "86" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 128, + zeros: 0, + source: "128" + }) + ); + assert_eq!(iter.next(), None); + + let mut iter = VersionChunkIter::new("w005s09t"); + assert_eq!(iter.next(), Some(VersionChunk::Str("w"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 5, + zeros: 2, + source: "005" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("s"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 9, + zeros: 1, + source: "09" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("t"))); + assert_eq!(iter.next(), None); + + let mut iter = VersionChunkIter::new("ZY_WX"); + assert_eq!(iter.next(), Some(VersionChunk::Str("ZY"))); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!(iter.next(), Some(VersionChunk::Str("WX"))); + + let mut iter = VersionChunkIter::new("_v1"); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 1, + zeros: 0, + source: "1" + }) + ); + + let mut iter = VersionChunkIter::new("_1v"); + assert_eq!(iter.next(), Some(VersionChunk::Underscore)); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 1, + zeros: 0, + source: "1" + }) + ); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + + let mut iter = VersionChunkIter::new("v009"); + assert_eq!(iter.next(), Some(VersionChunk::Str("v"))); + assert_eq!( + iter.next(), + Some(VersionChunk::Number { + value: 9, + zeros: 2, + source: "009" + }) + ); + } + + #[test] + fn test_version_sort() { + let mut input = vec!["", "b", "a"]; + let expected = vec!["", "a", "b"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["x7x", "xxx"]; + let expected = vec!["x7x", "xxx"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["applesauce", "apple"]; + let expected = vec!["apple", "applesauce"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["aaaaa", "aaa_a"]; + let expected = vec!["aaa_a", "aaaaa"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["AAAAA", "AAA1A", "BBBBB", "BB_BB", "C3CCC"]; + let expected = vec!["AAA1A", "AAAAA", "BB_BB", "BBBBB", "C3CCC"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["1_000_000", "1_010_001"]; + let expected = vec!["1_000_000", "1_010_001"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec![ + "5", "50", "500", "5_000", "5_005", "5_050", "5_500", "50_000", "50_005", "50_050", + "50_500", + ]; + let expected = vec![ + "5", "5_000", "5_005", "5_050", "5_500", "50", "50_000", "50_005", "50_050", "50_500", + "500", + ]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["X86_64", "x86_64", "X86_128", "x86_128"]; + let expected = vec!["X86_64", "X86_128", "x86_64", "x86_128"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["__", "_"]; + let expected = vec!["_", "__"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["foo_", "foo"]; + let expected = vec!["foo", "foo_"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec!["A", "AA", "B", "a", "aA", "aa", "b"]; + let expected = vec!["A", "AA", "B", "a", "aA", "aa", "b"]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected); + + let mut input = vec![ + "x86_128", "usize", "uz", "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", + "v1", "v009", "x87", "zyxw", "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", + "ZYXW", "v09", "v9", "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", + "ua", "x86_64", "ZYXW_", "a1", "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", + "u256", + ]; + let expected = vec![ + "_ZYXW", "_abcd", "A2", "ABCD", "Z_YXW", "ZY_XW", "ZY_XW", "ZYXW", "ZYXW_", "a1", + "abcd", "u_zzz", "u8", "u16", "u32", "u64", "u128", "u256", "ua", "usize", "uz", + "v000", "v00", "v0", "v0s", "v00t", "v0u", "v001", "v01", "v1", "v009", "v09", "v9", + "v010", "v10", "w005s09t", "w5s009t", "x64", "x86", "x86_32", "x86_64", "x86_128", + "x87", "zyxw", + ]; + input.sort_by(|a, b| version_sort(a, b)); + assert_eq!(input, expected) + } +} diff --git a/src/tools/rustfmt/src/source_file.rs b/src/tools/rustfmt/src/source_file.rs index 5eea8021b32..73f8ecb5529 100644 --- a/src/tools/rustfmt/src/source_file.rs +++ b/src/tools/rustfmt/src/source_file.rs @@ -2,10 +2,10 @@ use std::fs; use std::io::{self, Write}; use std::path::Path; +use crate::NewlineStyle; use crate::config::FileName; use crate::emitter::{self, Emitter}; use crate::parse::session::ParseSess; -use crate::NewlineStyle; #[cfg(test)] use crate::config::Config; diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 1ee691b4ade..b4424e476ee 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -1,9 +1,10 @@ use std::cmp::max; use rustc_ast::{ast, ptr}; -use rustc_span::{source_map, Span}; +use rustc_span::{Span, source_map}; use crate::macros::MacroArg; +use crate::patterns::RangeOperand; use crate::utils::{mk_sp, outer_attributes}; /// Spanned returns a span including attributes, if available. @@ -212,3 +213,9 @@ impl Spanned for ast::PreciseCapturingArg { } } } + +impl<'a> Spanned for RangeOperand<'a> { + fn span(&self) -> Span { + self.span + } +} diff --git a/src/tools/rustfmt/src/stmt.rs b/src/tools/rustfmt/src/stmt.rs index 73a9cce416c..2788159018d 100644 --- a/src/tools/rustfmt/src/stmt.rs +++ b/src/tools/rustfmt/src/stmt.rs @@ -2,9 +2,9 @@ use rustc_ast::ast; use rustc_span::Span; use crate::comment::recover_comment_removed; -use crate::config::Version; -use crate::expr::{format_expr, is_simple_block, ExprType}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::config::StyleEdition; +use crate::expr::{ExprType, format_expr, is_simple_block}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::LineRangeUtils; use crate::spanned::Spanned; @@ -90,11 +90,20 @@ impl<'a> Stmt<'a> { impl<'a> Rewrite for Stmt<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let expr_type = if context.config.version() == Version::Two && self.is_last_expr() { - ExprType::SubExpression - } else { - ExprType::Statement - }; + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result( + &self, + context: &RewriteContext<'_>, + shape: Shape, + ) -> crate::rewrite::RewriteResult { + let expr_type = + if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() { + ExprType::SubExpression + } else { + ExprType::Statement + }; format_stmt( context, shape, @@ -111,11 +120,11 @@ fn format_stmt( stmt: &ast::Stmt, expr_type: ExprType, is_last_expr: bool, -) -> Option<String> { - skip_out_of_file_lines_range!(context, stmt.span()); +) -> RewriteResult { + skip_out_of_file_lines_range_err!(context, stmt.span()); let result = match stmt.kind { - ast::StmtKind::Let(ref local) => local.rewrite(context, shape), + ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape), ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => { let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) { ";" @@ -123,10 +132,14 @@ fn format_stmt( "" }; - let shape = shape.sub_width(suffix.len())?; + let shape = shape + .sub_width(suffix.len()) + .max_width_error(shape.width, ex.span())?; format_expr(ex, expr_type, context, shape).map(|s| s + suffix) } - ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => None, + ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => { + Err(RewriteError::Unknown) + } }; - result.and_then(|res| recover_comment_removed(res, stmt.span(), context)) + result.map(|res| recover_comment_removed(res, stmt.span(), context)) } diff --git a/src/tools/rustfmt/src/string.rs b/src/tools/rustfmt/src/string.rs index cb666fff695..3b971188cd5 100644 --- a/src/tools/rustfmt/src/string.rs +++ b/src/tools/rustfmt/src/string.rs @@ -84,7 +84,7 @@ pub(crate) fn rewrite_string<'a>( stripped_str .len() .checked_next_power_of_two() - .unwrap_or(usize::max_value()), + .unwrap_or(usize::MAX), ); result.push_str(fmt.opener); @@ -375,7 +375,7 @@ fn graphemes_width(graphemes: &[&str]) -> usize { #[cfg(test)] mod test { - use super::{break_string, detect_url, rewrite_string, SnippetState, StringFormat}; + use super::{SnippetState, StringFormat, break_string, detect_url, rewrite_string}; use crate::config::Config; use crate::shape::{Indent, Shape}; use unicode_segmentation::UnicodeSegmentation; diff --git a/src/tools/rustfmt/src/test/configuration_snippet.rs b/src/tools/rustfmt/src/test/configuration_snippet.rs index e4a390ada66..5b93703de0b 100644 --- a/src/tools/rustfmt/src/test/configuration_snippet.rs +++ b/src/tools/rustfmt/src/test/configuration_snippet.rs @@ -4,9 +4,9 @@ use std::io::{BufRead, BufReader, Write}; use std::iter::Enumerate; use std::path::{Path, PathBuf}; -use super::{print_mismatches, write_message, DIFF_CONTEXT_SIZE}; +use super::{DIFF_CONTEXT_SIZE, print_mismatches, write_message}; use crate::config::{Config, EmitMode, Verbosity}; -use crate::rustfmt_diff::{make_diff, Mismatch}; +use crate::rustfmt_diff::{Mismatch, make_diff}; use crate::{Input, Session}; const CONFIGURATIONS_FILE_NAME: &str = "Configurations.md"; diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index 286e8b8760a..d62da08fff8 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -6,14 +6,17 @@ use std::iter::Peekable; use std::mem; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; -use std::str::Chars; +use std::str::{Chars, FromStr}; use std::thread; use crate::config::{Color, Config, EmitMode, FileName, NewlineStyle}; use crate::formatting::{ReportedErrors, SourceFile}; -use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChunk, OutputWriter}; +use crate::rustfmt_diff::{DiffLine, Mismatch, ModifiedChunk, OutputWriter, make_diff, print_diff}; use crate::source_file; -use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; +use crate::{ + Edition, FormatReport, FormatReportFormatterBuilder, Input, Session, StyleEdition, Version, + is_nightly_channel, +}; use rustfmt_config_proc_macro::nightly_only_test; use tracing::{debug, warn}; @@ -102,10 +105,9 @@ fn is_file_skip(path: &Path) -> bool { fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> { let mut files = vec![]; if path.is_dir() { - for entry in fs::read_dir(path).expect(&format!( - "couldn't read directory {}", - path.to_str().unwrap() - )) { + for entry in + fs::read_dir(path).expect(&format!("couldn't read directory {}", path.display())) + { let entry = entry.expect("couldn't get `DirEntry`"); let path = entry.path(); if path.is_dir() && recursive { @@ -119,10 +121,7 @@ fn get_test_files(path: &Path, recursive: bool) -> Vec<PathBuf> { } fn verify_config_used(path: &Path, config_name: &str) { - for entry in fs::read_dir(path).expect(&format!( - "couldn't read {} directory", - path.to_str().unwrap() - )) { + for entry in fs::read_dir(path).expect(&format!("couldn't read {} directory", path.display())) { let entry = entry.expect("couldn't get directory entry"); let path = entry.path(); if path.extension().map_or(false, |f| f == "rs") { @@ -711,13 +710,24 @@ fn print_mismatches<T: Fn(u32) -> String>( fn read_config(filename: &Path) -> Config { let sig_comments = read_significant_comments(filename); + let (edition, style_edition, version) = get_editions_from_comments(&sig_comments); // Look for a config file. If there is a 'config' property in the significant comments, use // that. Otherwise, if there are no significant comments at all, look for a config file with // the same name as the test file. let mut config = if !sig_comments.is_empty() { - get_config(sig_comments.get("config").map(Path::new)) + get_config( + sig_comments.get("config").map(Path::new), + edition, + style_edition, + version, + ) } else { - get_config(filename.with_extension("toml").file_name().map(Path::new)) + get_config( + filename.with_extension("toml").file_name().map(Path::new), + edition, + style_edition, + version, + ) }; for (key, val) in &sig_comments { @@ -748,13 +758,31 @@ enum IdempotentCheckError { Parse, } +fn get_editions_from_comments( + comments: &HashMap<String, String>, +) -> (Option<Edition>, Option<StyleEdition>, Option<Version>) { + ( + comments + .get("edition") + .map(|e| Edition::from_str(e).expect(&format!("invalid edition value: '{}'", e))), + comments.get("style_edition").map(|se| { + StyleEdition::from_str(se).expect(&format!("invalid style_edition value: '{}'", se)) + }), + comments + .get("version") + .map(|v| Version::from_str(v).expect(&format!("invalid version value: '{}'", v))), + ) +} + fn idempotent_check( filename: &PathBuf, opt_config: &Option<PathBuf>, ) -> Result<FormatReport, IdempotentCheckError> { let sig_comments = read_significant_comments(filename); let config = if let Some(ref config_file_path) = opt_config { - Config::from_toml_path(config_file_path).expect("`rustfmt.toml` not found") + let (edition, style_edition, version) = get_editions_from_comments(&sig_comments); + Config::from_toml_path(config_file_path, edition, style_edition, version) + .expect("`rustfmt.toml` not found") } else { read_config(filename) }; @@ -778,14 +806,19 @@ fn idempotent_check( // Reads test config file using the supplied (optional) file name. If there's no file name or the // file doesn't exist, just return the default config. Otherwise, the file must be read // successfully. -fn get_config(config_file: Option<&Path>) -> Config { +fn get_config( + config_file: Option<&Path>, + edition: Option<Edition>, + style_edition: Option<StyleEdition>, + version: Option<Version>, +) -> Config { let config_file_name = match config_file { - None => return Default::default(), + None => return Config::default_for_possible_style_edition(style_edition, edition, version), Some(file_name) => { let mut full_path = PathBuf::from("tests/config/"); full_path.push(file_name); if !full_path.exists() { - return Default::default(); + return Config::default_for_possible_style_edition(style_edition, edition, version); }; full_path } @@ -797,7 +830,14 @@ fn get_config(config_file: Option<&Path>) -> Config { .read_to_string(&mut def_config) .expect("Couldn't read config"); - Config::from_toml(&def_config, Path::new("tests/config/")).expect("invalid TOML") + Config::from_toml_for_style_edition( + &def_config, + Path::new("tests/config/"), + edition, + style_edition, + version, + ) + .expect("invalid TOML") } // Reads significant comments of the form: `// rustfmt-key: value` into a hash map. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 7730aa467ce..07b483b2b37 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -2,23 +2,23 @@ use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability, Term}; use rustc_ast::ptr; -use rustc_span::{symbol::kw, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol::kw}; use tracing::debug; use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::config::lists::*; -use crate::config::{IndentStyle, TypeDensity, Version}; +use crate::config::{IndentStyle, StyleEdition, TypeDensity}; use crate::expr::{ - format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, - RhsAssignKind, + ExprType, RhsAssignKind, format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, + rewrite_unary_prefix, }; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; -use crate::macros::{rewrite_macro, MacroPosition}; +use crate::macros::{MacroPosition, rewrite_macro}; use crate::overflow; -use crate::pairs::{rewrite_pair, PairParts}; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::pairs::{PairParts, rewrite_pair}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult}; use crate::shape::Shape; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -41,7 +41,7 @@ pub(crate) fn rewrite_path( qself: &Option<ptr::P<ast::QSelf>>, path: &ast::Path, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let skip_count = qself.as_ref().map_or(0, |x| x.position); // 32 covers almost all path lengths measured when compiling core, and there isn't a big @@ -57,7 +57,7 @@ pub(crate) fn rewrite_path( if let Some(qself) = qself { result.push('<'); - let fmt_ty = qself.ty.rewrite(context, shape)?; + let fmt_ty = qself.ty.rewrite_result(context, shape)?; result.push_str(&fmt_ty); if skip_count > 0 { @@ -67,7 +67,7 @@ pub(crate) fn rewrite_path( } // 3 = ">::".len() - let shape = shape.sub_width(3)?; + let shape = shape.sub_width(3).max_width_error(shape.width, path.span)?; result = rewrite_path_segments( PathContext::Type, @@ -103,7 +103,7 @@ fn rewrite_path_segments<'a, I>( span_hi: BytePos, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> +) -> RewriteResult where I: Iterator<Item = &'a ast::PathSegment>, { @@ -122,7 +122,9 @@ where } let extra_offset = extra_offset(&buffer, shape); - let new_shape = shape.shrink_left(extra_offset)?; + let new_shape = shape + .shrink_left(extra_offset) + .max_width_error(shape.width, mk_sp(span_lo, span_hi))?; let segment_string = rewrite_segment( path_context, segment, @@ -135,7 +137,7 @@ where buffer.push_str(&segment_string); } - Some(buffer) + Ok(buffer) } #[derive(Debug)] @@ -169,19 +171,27 @@ impl<'a> Spanned for SegmentParam<'a> { impl<'a> Rewrite for SegmentParam<'a> { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - SegmentParam::Const(const_) => const_.rewrite(context, shape), - SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), - SegmentParam::Type(ty) => ty.rewrite(context, shape), - SegmentParam::Binding(atc) => atc.rewrite(context, shape), + SegmentParam::Const(const_) => const_.rewrite_result(context, shape), + SegmentParam::LifeTime(lt) => lt.rewrite_result(context, shape), + SegmentParam::Type(ty) => ty.rewrite_result(context, shape), + SegmentParam::Binding(atc) => atc.rewrite_result(context, shape), } } } impl Rewrite for ast::PreciseCapturingArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self { - ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite(context, shape), + ast::PreciseCapturingArg::Lifetime(lt) => lt.rewrite_result(context, shape), ast::PreciseCapturingArg::Arg(p, _) => { rewrite_path(context, PathContext::Type, &None, p, shape) } @@ -191,13 +201,20 @@ impl Rewrite for ast::PreciseCapturingArg { impl Rewrite for ast::AssocItemConstraint { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { use ast::AssocItemConstraintKind::{Bound, Equality}; let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, self.ident)); if let Some(ref gen_args) = self.gen_args { - let budget = shape.width.checked_sub(result.len())?; + let budget = shape + .width + .checked_sub(result.len()) + .max_width_error(shape.width, self.span)?; let shape = Shape::legacy(budget, shape.indent + result.len()); let gen_str = rewrite_generic_args(gen_args, context, shape, gen_args.span())?; result.push_str(&gen_str); @@ -210,23 +227,30 @@ impl Rewrite for ast::AssocItemConstraint { }; result.push_str(infix); - let budget = shape.width.checked_sub(result.len())?; + let budget = shape + .width + .checked_sub(result.len()) + .max_width_error(shape.width, self.span)?; let shape = Shape::legacy(budget, shape.indent + result.len()); - let rewrite = self.kind.rewrite(context, shape)?; + let rewrite = self.kind.rewrite_result(context, shape)?; result.push_str(&rewrite); - Some(result) + Ok(result) } } impl Rewrite for ast::AssocItemConstraintKind { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self { ast::AssocItemConstraintKind::Equality { term } => match term { - Term::Ty(ty) => ty.rewrite(context, shape), - Term::Const(c) => c.rewrite(context, shape), + Term::Ty(ty) => ty.rewrite_result(context, shape), + Term::Const(c) => c.rewrite_result(context, shape), }, - ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), + ast::AssocItemConstraintKind::Bound { bounds } => bounds.rewrite_result(context, shape), } } } @@ -248,16 +272,17 @@ fn rewrite_segment( span_hi: BytePos, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { let mut result = String::with_capacity(128); result.push_str(rewrite_ident(context, segment.ident)); let ident_len = result.len(); let shape = if context.use_block_indent() { - shape.offset_left(ident_len)? + shape.offset_left(ident_len) } else { - shape.shrink_left(ident_len)? - }; + shape.shrink_left(ident_len) + } + .max_width_error(shape.width, mk_sp(*span_lo, span_hi))?; if let Some(ref args) = segment.args { let generics_str = rewrite_generic_args(args, context, shape, mk_sp(*span_lo, span_hi))?; @@ -288,7 +313,7 @@ fn rewrite_segment( result.push_str(&generics_str) } - Some(result) + Ok(result) } fn format_function_type<'a, I>( @@ -298,7 +323,7 @@ fn format_function_type<'a, I>( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> +) -> RewriteResult where I: ExactSizeIterator, <I as Iterator>::Item: Deref, @@ -308,12 +333,12 @@ where let ty_shape = match context.config.indent_style() { // 4 = " -> " - IndentStyle::Block => shape.offset_left(4)?, - IndentStyle::Visual => shape.block_left(4)?, + IndentStyle::Block => shape.offset_left(4).max_width_error(shape.width, span)?, + IndentStyle::Visual => shape.block_left(4).max_width_error(shape.width, span)?, }; let output = match *output { FnRetTy::Ty(ref ty) => { - let type_str = ty.rewrite(context, ty_shape)?; + let type_str = ty.rewrite_result(context, ty_shape)?; format!(" -> {type_str}") } FnRetTy::Default(..) => String::new(), @@ -326,7 +351,10 @@ where ) } else { // 2 for () - let budget = shape.width.checked_sub(2)?; + let budget = shape + .width + .checked_sub(2) + .max_width_error(shape.width, span)?; // 1 for ( let offset = shape.indent + 1; Shape::legacy(budget, offset) @@ -339,7 +367,8 @@ where let list_hi = context.snippet_provider.span_before(span, ")"); let comment = context .snippet_provider - .span_to_snippet(mk_sp(list_lo, list_hi))? + .span_to_snippet(mk_sp(list_lo, list_hi)) + .unknown_error()? .trim(); let comment = if comment.starts_with("//") { format!( @@ -360,7 +389,7 @@ where ",", |arg| arg.span().lo(), |arg| arg.span().hi(), - |arg| arg.rewrite(context, list_shape), + |arg| arg.rewrite_result(context, list_shape), list_lo, span.hi(), false, @@ -396,9 +425,9 @@ where ) }; if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { - Some(format!("{args}{output}")) + Ok(format!("{args}{output}")) } else { - Some(format!( + Ok(format!( "{}\n{}{}", args, list_shape.indent.to_string(context.config), @@ -429,6 +458,10 @@ fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveL impl Rewrite for ast::WherePredicate { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { // FIXME: dead spans? let result = match *self { ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { @@ -437,7 +470,7 @@ impl Rewrite for ast::WherePredicate { ref bounds, .. }) => { - let type_str = bounded_ty.rewrite(context, shape)?; + let type_str = bounded_ty.rewrite_result(context, shape)?; let colon = type_bound_colon(context).trim_end(); let lhs = if let Some(binder_str) = rewrite_bound_params(context, shape, bound_generic_params) @@ -452,28 +485,34 @@ impl Rewrite for ast::WherePredicate { ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, - .. - }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, + span, + }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?, ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => { - let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; + let lhs_ty_str = lhs_ty + .rewrite_result(context, shape) + .map(|lhs| lhs + " =")?; rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, &RhsAssignKind::Ty, shape)? } }; - Some(result) + Ok(result) } } impl Rewrite for ast::GenericArg { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { - ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape), - ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape), - ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape), + ast::GenericArg::Lifetime(ref lt) => lt.rewrite_result(context, shape), + ast::GenericArg::Type(ref ty) => ty.rewrite_result(context, shape), + ast::GenericArg::Const(ref const_) => const_.rewrite_result(context, shape), } } } @@ -483,11 +522,11 @@ fn rewrite_generic_args( context: &RewriteContext<'_>, shape: Shape, span: Span, -) -> Option<String> { +) -> RewriteResult { match gen_args { ast::GenericArgs::AngleBracketed(ref data) => { if data.args.is_empty() { - Some("".to_owned()) + Ok("".to_owned()) } else { let args = data .args @@ -513,47 +552,63 @@ fn rewrite_generic_args( context, shape, ), - ast::GenericArgs::ParenthesizedElided(..) => Some("(..)".to_owned()), + ast::GenericArgs::ParenthesizedElided(..) => Ok("(..)".to_owned()), } } fn rewrite_bounded_lifetime( lt: &ast::Lifetime, bounds: &[ast::GenericBound], + span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { - let result = lt.rewrite(context, shape)?; +) -> RewriteResult { + let result = lt.rewrite_result(context, shape)?; if bounds.is_empty() { - Some(result) + Ok(result) } else { let colon = type_bound_colon(context); let overhead = last_line_width(&result) + colon.len(); + let shape = shape + .sub_width(overhead) + .max_width_error(shape.width, span)?; let result = format!( "{}{}{}", result, colon, - join_bounds(context, shape.sub_width(overhead)?, bounds, true)? + join_bounds(context, shape, bounds, true)? ); - Some(result) + Ok(result) } } impl Rewrite for ast::AnonConst { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { format_expr(&self.value, ExprType::SubExpression, context, shape) } } impl Rewrite for ast::Lifetime { - fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> { - Some(context.snippet(self.ident.span).to_owned()) + fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, _: Shape) -> RewriteResult { + Ok(context.snippet(self.ident.span).to_owned()) } } impl Rewrite for ast::GenericBound { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match *self { ast::GenericBound::Trait( ref poly_trait_ref, @@ -574,24 +629,30 @@ impl Rewrite for ast::GenericBound { asyncness.push(' '); } let polarity = polarity.as_str(); - let shape = shape.offset_left(constness.len() + polarity.len())?; + let shape = shape + .offset_left(constness.len() + polarity.len()) + .max_width_error(shape.width, self.span())?; poly_trait_ref - .rewrite(context, shape) + .rewrite_result(context, shape) .map(|s| format!("{constness}{asyncness}{polarity}{s}")) .map(|s| if has_paren { format!("({})", s) } else { s }) } ast::GenericBound::Use(ref args, span) => { overflow::rewrite_with_angle_brackets(context, "use", args.iter(), shape, span) } - ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), + ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite_result(context, shape), } } } impl Rewrite for ast::GenericBounds { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { if self.is_empty() { - return Some(String::new()); + return Ok(String::new()); } join_bounds(context, shape, self, true) @@ -600,8 +661,15 @@ impl Rewrite for ast::GenericBounds { impl Rewrite for ast::GenericParam { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { // FIXME: If there are more than one attributes, this will force multiline. - let mut result = self.attrs.rewrite(context, shape).unwrap_or(String::new()); + let mut result = self + .attrs + .rewrite_result(context, shape) + .unwrap_or(String::new()); let has_attrs = !result.is_empty(); let mut param = String::with_capacity(128); @@ -615,15 +683,19 @@ impl Rewrite for ast::GenericParam { param.push_str("const "); param.push_str(rewrite_ident(context, self.ident)); param.push_str(": "); - param.push_str(&ty.rewrite(context, shape)?); + param.push_str(&ty.rewrite_result(context, shape)?); if let Some(default) = default { let eq_str = match context.config.type_punctuation_density() { TypeDensity::Compressed => "=", TypeDensity::Wide => " = ", }; param.push_str(eq_str); - let budget = shape.width.checked_sub(param.len())?; - let rewrite = default.rewrite(context, Shape::legacy(budget, shape.indent))?; + let budget = shape + .width + .checked_sub(param.len()) + .max_width_error(shape.width, self.span())?; + let rewrite = + default.rewrite_result(context, Shape::legacy(budget, shape.indent))?; param.push_str(&rewrite); } kw_span.lo() @@ -634,7 +706,7 @@ impl Rewrite for ast::GenericParam { if !self.bounds.is_empty() { param.push_str(type_bound_colon(context)); - param.push_str(&self.bounds.rewrite(context, shape)?) + param.push_str(&self.bounds.rewrite_result(context, shape)?) } if let ast::GenericParamKind::Type { default: Some(ref def), @@ -645,9 +717,12 @@ impl Rewrite for ast::GenericParam { TypeDensity::Wide => " = ", }; param.push_str(eq_str); - let budget = shape.width.checked_sub(param.len())?; + let budget = shape + .width + .checked_sub(param.len()) + .max_width_error(shape.width, self.span())?; let rewrite = - def.rewrite(context, Shape::legacy(budget, shape.indent + param.len()))?; + def.rewrite_result(context, Shape::legacy(budget, shape.indent + param.len()))?; param.push_str(&rewrite); } @@ -673,44 +748,67 @@ impl Rewrite for ast::GenericParam { result.push_str(¶m); } - Some(result) + Ok(result) } } impl Rewrite for ast::PolyTraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { if let Some(lifetime_str) = rewrite_bound_params(context, shape, &self.bound_generic_params) { // 6 is "for<> ".len() let extra_offset = lifetime_str.len() + 6; - let path_str = self - .trait_ref - .rewrite(context, shape.offset_left(extra_offset)?)?; + let shape = shape + .offset_left(extra_offset) + .max_width_error(shape.width, self.span)?; + let path_str = self.trait_ref.rewrite_result(context, shape)?; - Some(format!("for<{lifetime_str}> {path_str}")) + Ok(format!("for<{lifetime_str}> {path_str}")) } else { - self.trait_ref.rewrite(context, shape) + self.trait_ref.rewrite_result(context, shape) } } } impl Rewrite for ast::TraitRef { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { rewrite_path(context, PathContext::Type, &None, &self.path, shape) } } impl Rewrite for ast::Ty { fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { + self.rewrite_result(context, shape).ok() + } + + fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { match self.kind { ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { // we have to consider 'dyn' keyword is used or not!!! let (shape, prefix) = match tobj_syntax { - ast::TraitObjectSyntax::Dyn => (shape.offset_left(4)?, "dyn "), - ast::TraitObjectSyntax::DynStar => (shape.offset_left(5)?, "dyn* "), + ast::TraitObjectSyntax::Dyn => { + let shape = shape + .offset_left(4) + .max_width_error(shape.width, self.span())?; + (shape, "dyn ") + } + ast::TraitObjectSyntax::DynStar => { + let shape = shape + .offset_left(5) + .max_width_error(shape.width, self.span())?; + (shape, "dyn* ") + } ast::TraitObjectSyntax::None => (shape, ""), }; - let mut res = bounds.rewrite(context, shape)?; + let mut res = bounds.rewrite_result(context, shape)?; // We may have falsely removed a trailing `+` inside macro call. if context.inside_macro() && bounds.len() == 1 @@ -719,7 +817,7 @@ impl Rewrite for ast::Ty { { res.push('+'); } - Some(format!("{prefix}{res}")) + Ok(format!("{prefix}{res}")) } ast::TyKind::Ptr(ref mt) => { let prefix = match mt.mutbl { @@ -738,8 +836,11 @@ impl Rewrite for ast::Ty { let mut cmnt_lo = ref_hi; if let Some(ref lifetime) = *lifetime { - let lt_budget = shape.width.checked_sub(2 + mut_len)?; - let lt_str = lifetime.rewrite( + let lt_budget = shape + .width + .checked_sub(2 + mut_len) + .max_width_error(shape.width, self.span())?; + let lt_str = lifetime.rewrite_result( context, Shape::legacy(lt_budget, shape.indent + 2 + mut_len), )?; @@ -783,39 +884,46 @@ impl Rewrite for ast::Ty { result = combine_strs_with_missing_comments( context, result.trim_end(), - &mt.ty.rewrite(context, shape)?, + &mt.ty.rewrite_result(context, shape)?, before_ty_span, shape, true, )?; } else { let used_width = last_line_width(&result); - let budget = shape.width.checked_sub(used_width)?; - let ty_str = mt - .ty - .rewrite(context, Shape::legacy(budget, shape.indent + used_width))?; + let budget = shape + .width + .checked_sub(used_width) + .max_width_error(shape.width, self.span())?; + let ty_str = mt.ty.rewrite_result( + context, + Shape::legacy(budget, shape.indent + used_width), + )?; result.push_str(&ty_str); } - Some(result) + Ok(result) } // FIXME: we drop any comments here, even though it's a silly place to put // comments. ast::TyKind::Paren(ref ty) => { - if context.config.version() == Version::One + if context.config.style_edition() <= StyleEdition::Edition2021 || context.config.indent_style() == IndentStyle::Visual { - let budget = shape.width.checked_sub(2)?; + let budget = shape + .width + .checked_sub(2) + .max_width_error(shape.width, self.span())?; return ty - .rewrite(context, Shape::legacy(budget, shape.indent + 1)) + .rewrite_result(context, Shape::legacy(budget, shape.indent + 1)) .map(|ty_str| format!("({})", ty_str)); } // 2 = () if let Some(sh) = shape.sub_width(2) { - if let Some(ref s) = ty.rewrite(context, sh) { + if let Ok(ref s) = ty.rewrite_result(context, sh) { if !s.contains('\n') { - return Some(format!("({s})")); + return Ok(format!("({s})")); } } } @@ -824,8 +932,8 @@ impl Rewrite for ast::Ty { let shape = shape .block_indent(context.config.tab_spaces()) .with_max_width(context.config); - let rw = ty.rewrite(context, shape)?; - Some(format!( + let rw = ty.rewrite_result(context, shape)?; + Ok(format!( "({}{}{})", shape.to_string_with_newline(context.config), rw, @@ -833,15 +941,18 @@ impl Rewrite for ast::Ty { )) } ast::TyKind::Slice(ref ty) => { - let budget = shape.width.checked_sub(4)?; - ty.rewrite(context, Shape::legacy(budget, shape.indent + 1)) + let budget = shape + .width + .checked_sub(4) + .max_width_error(shape.width, self.span())?; + ty.rewrite_result(context, Shape::legacy(budget, shape.indent + 1)) .map(|ty_str| format!("[{}]", ty_str)) } ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } - ast::TyKind::AnonStruct(..) => Some(context.snippet(self.span).to_owned()), - ast::TyKind::AnonUnion(..) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()), + ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } @@ -855,24 +966,27 @@ impl Rewrite for ast::Ty { ), ast::TyKind::Infer => { if shape.width >= 1 { - Some("_".to_owned()) + Ok("_".to_owned()) } else { - None + Err(RewriteError::ExceedsMaxWidth { + configured_width: shape.width, + span: self.span(), + }) } } ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape), - ast::TyKind::Never => Some(String::from("!")), + ast::TyKind::Never => Ok(String::from("!")), ast::TyKind::MacCall(ref mac) => { rewrite_macro(mac, None, context, shape, MacroPosition::Expression) } - ast::TyKind::ImplicitSelf => Some(String::from("")), + ast::TyKind::ImplicitSelf => Ok(String::from("")), ast::TyKind::ImplTrait(_, ref it) => { // Empty trait is not a parser error. if it.is_empty() { - return Some("impl".to_owned()); + return Ok("impl".to_owned()); } - let rw = if context.config.version() == Version::One { - it.rewrite(context, shape) + let rw = if context.config.style_edition() <= StyleEdition::Edition2021 { + it.rewrite_result(context, shape) } else { join_bounds(context, shape, it, false) }; @@ -881,8 +995,8 @@ impl Rewrite for ast::Ty { format!("impl{}{}", space, it_str) }) } - ast::TyKind::CVarArgs => Some("...".to_owned()), - ast::TyKind::Dummy | ast::TyKind::Err(_) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::CVarArgs => Ok("...".to_owned()), + ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::Typeof(ref anon_const) => rewrite_call( context, "typeof", @@ -891,9 +1005,9 @@ impl Rewrite for ast::Ty { shape, ), ast::TyKind::Pat(ref ty, ref pat) => { - let ty = ty.rewrite(context, shape)?; - let pat = pat.rewrite(context, shape)?; - Some(format!("{ty} is {pat}")) + let ty = ty.rewrite_result(context, shape)?; + let pat = pat.rewrite_result(context, shape)?; + Ok(format!("{ty} is {pat}")) } } } @@ -904,7 +1018,7 @@ fn rewrite_bare_fn( span: Span, context: &RewriteContext<'_>, shape: Shape, -) -> Option<String> { +) -> RewriteResult { debug!("rewrite_bare_fn {:#?}", shape); let mut result = String::with_capacity(128); @@ -928,9 +1042,14 @@ fn rewrite_bare_fn( result.push_str("fn"); let func_ty_shape = if context.use_block_indent() { - shape.offset_left(result.len())? + shape + .offset_left(result.len()) + .max_width_error(shape.width, span)? } else { - shape.visual_indent(result.len()).sub_width(result.len())? + shape + .visual_indent(result.len()) + .sub_width(result.len()) + .max_width_error(shape.width, span)? }; let rewrite = format_function_type( @@ -944,7 +1063,7 @@ fn rewrite_bare_fn( result.push_str(&rewrite); - Some(result) + Ok(result) } fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { @@ -968,7 +1087,7 @@ fn join_bounds( shape: Shape, items: &[ast::GenericBound], need_indent: bool, -) -> Option<String> { +) -> RewriteResult { join_bounds_inner(context, shape, items, need_indent, false) } @@ -978,7 +1097,7 @@ fn join_bounds_inner( items: &[ast::GenericBound], need_indent: bool, force_newline: bool, -) -> Option<String> { +) -> RewriteResult { debug_assert!(!items.is_empty()); let generic_bounds_in_order = is_generic_bounds_in_order(items); @@ -1073,10 +1192,10 @@ fn join_bounds_inner( }; let (extendable, trailing_str) = if i == 0 { - let bound_str = item.rewrite(context, shape)?; + let bound_str = item.rewrite_result(context, shape)?; (is_bound_extendable(&bound_str, item), bound_str) } else { - let bound_str = &item.rewrite(context, shape)?; + let bound_str = &item.rewrite_result(context, shape)?; match leading_span { Some(ls) if has_leading_comment => ( is_bound_extendable(bound_str, item), @@ -1100,7 +1219,7 @@ fn join_bounds_inner( true, ) .map(|v| (v, trailing_span, extendable)), - _ => Some((strs + &trailing_str, trailing_span, extendable)), + _ => Ok((strs + &trailing_str, trailing_span, extendable)), } }, )?; @@ -1110,22 +1229,22 @@ fn join_bounds_inner( // and either there is more than one item; // or the single item is of type `Trait`, // and any of the internal arrays contains more than one item; - let retry_with_force_newline = match context.config.version() { - Version::One => { + let retry_with_force_newline = match context.config.style_edition() { + style_edition @ _ if style_edition <= StyleEdition::Edition2021 => { !force_newline && items.len() > 1 && (result.0.contains('\n') || result.0.len() > shape.width) } - Version::Two if force_newline => false, - Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, - Version::Two if items.len() > 1 => true, - Version::Two => is_item_with_multi_items_array(&items[0]), + _ if force_newline => false, + _ if (!result.0.contains('\n') && result.0.len() <= shape.width) => false, + _ if items.len() > 1 => true, + _ => is_item_with_multi_items_array(&items[0]), }; if retry_with_force_newline { join_bounds_inner(context, shape, items, need_indent, true) } else { - Some(result.0) + Ok(result.0) } } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index fd59aedadfe..d1cfc6acc49 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -6,11 +6,11 @@ use rustc_ast::ast::{ }; use rustc_ast::ptr; use rustc_ast_pretty::pprust; -use rustc_span::{sym, symbol, BytePos, LocalExpnId, Span, Symbol, SyntaxContext}; +use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; use unicode_width::UnicodeWidthStr; -use crate::comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses}; -use crate::config::{Config, Version}; +use crate::comment::{CharClasses, FullCodeCharKind, LineClasses, filter_normal_code}; +use crate::config::{Config, StyleEdition}; use crate::rewrite::RewriteContext; use crate::shape::{Indent, Shape}; @@ -367,10 +367,10 @@ macro_rules! out_of_file_lines_range { }; } -macro_rules! skip_out_of_file_lines_range { +macro_rules! skip_out_of_file_lines_range_err { ($self:ident, $span:expr) => { if out_of_file_lines_range!($self, $span) { - return None; + return Err(RewriteError::SkipFormatting); } }; } @@ -596,7 +596,7 @@ pub(crate) fn trim_left_preserve_layout( // just InString{Commented} in order to allow the start of a string to be indented let new_veto_trim_value = (kind == FullCodeCharKind::InString - || (config.version() == Version::Two + || (config.style_edition() >= StyleEdition::Edition2024 && kind == FullCodeCharKind::InStringCommented)) && !line.ends_with('\\'); let line = if veto_trim || new_veto_trim_value { @@ -612,7 +612,7 @@ pub(crate) fn trim_left_preserve_layout( // such lines should not be taken into account when computing the minimum. match kind { FullCodeCharKind::InStringCommented | FullCodeCharKind::EndStringCommented - if config.version() == Version::Two => + if config.style_edition() >= StyleEdition::Edition2024 => { None } @@ -656,7 +656,7 @@ pub(crate) fn indent_next_line(kind: FullCodeCharKind, line: &str, config: &Conf // formatting the code block, therefore the string's indentation needs // to be adjusted for the code surrounding the code block. config.format_strings() && line.ends_with('\\') - } else if config.version() == Version::Two { + } else if config.style_edition() >= StyleEdition::Edition2024 { !kind.is_commented_string() } else { true diff --git a/src/tools/rustfmt/src/vertical.rs b/src/tools/rustfmt/src/vertical.rs index a06bc995aa5..1ec239c3821 100644 --- a/src/tools/rustfmt/src/vertical.rs +++ b/src/tools/rustfmt/src/vertical.rs @@ -11,9 +11,9 @@ use crate::config::lists::*; use crate::expr::rewrite_field; use crate::items::{rewrite_struct_field, rewrite_struct_field_prefix}; use crate::lists::{ - definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, + ListFormatting, ListItem, Separator, definitive_tactic, itemize_list, write_list, }; -use crate::rewrite::{Rewrite, RewriteContext}; +use crate::rewrite::{Rewrite, RewriteContext, RewriteResult}; use crate::shape::{Indent, Shape}; use crate::source_map::SpanUtils; use crate::spanned::Spanned; @@ -24,13 +24,13 @@ use crate::utils::{ pub(crate) trait AlignedItem { fn skip(&self) -> bool; fn get_span(&self) -> Span; - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String>; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult; fn rewrite_aligned_item( &self, context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option<String>; + ) -> RewriteResult; } impl AlignedItem for ast::FieldDef { @@ -42,24 +42,23 @@ impl AlignedItem for ast::FieldDef { self.span() } - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let attrs_str = self.attrs.rewrite(context, shape)?; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; let missing_span = if self.attrs.is_empty() { mk_sp(self.span.lo(), self.span.lo()) } else { mk_sp(self.attrs.last().unwrap().span.hi(), self.span.lo()) }; let attrs_extendable = self.ident.is_none() && is_attributes_extendable(&attrs_str); - rewrite_struct_field_prefix(context, self).and_then(|field_str| { - combine_strs_with_missing_comments( - context, - &attrs_str, - &field_str, - missing_span, - shape, - attrs_extendable, - ) - }) + let field_str = rewrite_struct_field_prefix(context, self)?; + combine_strs_with_missing_comments( + context, + &attrs_str, + &field_str, + missing_span, + shape, + attrs_extendable, + ) } fn rewrite_aligned_item( @@ -67,7 +66,7 @@ impl AlignedItem for ast::FieldDef { context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option<String> { + ) -> RewriteResult { rewrite_struct_field(context, self, shape, prefix_max_width) } } @@ -81,8 +80,8 @@ impl AlignedItem for ast::ExprField { self.span() } - fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { - let attrs_str = self.attrs.rewrite(context, shape)?; + fn rewrite_prefix(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { + let attrs_str = self.attrs.rewrite_result(context, shape)?; let name = rewrite_ident(context, self.ident); let missing_span = if self.attrs.is_empty() { mk_sp(self.span.lo(), self.span.lo()) @@ -104,7 +103,7 @@ impl AlignedItem for ast::ExprField { context: &RewriteContext<'_>, shape: Shape, prefix_max_width: usize, - ) -> Option<String> { + ) -> RewriteResult { rewrite_field(context, self, shape, prefix_max_width) } } @@ -199,7 +198,7 @@ fn struct_field_prefix_max_min_width<T: AlignedItem>( .rewrite_prefix(context, shape) .map(|field_str| trimmed_last_line_width(&field_str)) }) - .fold_options((0, ::std::usize::MAX), |(max_len, min_len), len| { + .fold_ok((0, ::std::usize::MAX), |(max_len, min_len), len| { (cmp::max(max_len, len), cmp::min(min_len, len)) }) .unwrap_or((0, 0)) @@ -246,12 +245,12 @@ fn rewrite_aligned_items_inner<T: AlignedItem>( if tactic == DefinitiveListTactic::Horizontal { // since the items fits on a line, there is no need to align them let do_rewrite = - |field: &T| -> Option<String> { field.rewrite_aligned_item(context, item_shape, 0) }; + |field: &T| -> RewriteResult { field.rewrite_aligned_item(context, item_shape, 0) }; fields .iter() .zip(items.iter_mut()) .for_each(|(field, list_item): (&T, &mut ListItem)| { - if list_item.item.is_some() { + if list_item.item.is_ok() { list_item.item = do_rewrite(field); } }); @@ -267,7 +266,7 @@ fn rewrite_aligned_items_inner<T: AlignedItem>( .tactic(tactic) .trailing_separator(separator_tactic) .preserve_newline(true); - write_list(&items, &fmt) + write_list(&items, &fmt).ok() } /// Returns the index in `fields` up to which a field belongs to the current group. diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index ac68fc5fecf..8102fe7ad8f 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -3,24 +3,23 @@ use std::rc::Rc; use rustc_ast::{ast, token::Delimiter, visit}; use rustc_data_structures::sync::Lrc; -use rustc_span::{symbol, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, symbol}; use tracing::debug; use crate::attr::*; -use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; -use crate::config::Version; -use crate::config::{BraceStyle, Config, MacroSelector}; +use crate::comment::{CodeCharKind, CommentCodeSlices, contains_comment, rewrite_comment}; +use crate::config::{BraceStyle, Config, MacroSelector, StyleEdition}; use crate::coverage::transform_missing_snippet; use crate::items::{ - format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, - rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, + FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, format_impl, format_trait, + format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, rewrite_type_alias, }; -use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; +use crate::macros::{MacroPosition, macro_style, rewrite_macro, rewrite_macro_def}; use crate::modules::Module; use crate::parse::session::ParseSess; use crate::rewrite::{Rewrite, RewriteContext}; use crate::shape::{Indent, Shape}; -use crate::skip::{is_skip_attr, SkipContext}; +use crate::skip::{SkipContext, is_skip_attr}; use crate::source_map::{LineRangeUtils, SpanUtils}; use crate::spanned::Spanned; use crate::stmt::Stmt; @@ -291,7 +290,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let mut comment_shape = Shape::indented(self.block_indent, config).comment(config); - if self.config.version() == Version::Two && comment_on_same_line { + if self.config.style_edition() >= StyleEdition::Edition2024 + && comment_on_same_line + { self.push_str(" "); // put the first line of the comment on the same line as the // block's last line @@ -312,8 +313,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let comment_str = rewrite_comment(other_lines, false, comment_shape, config); match comment_str { - Some(ref s) => self.push_str(s), - None => self.push_str(other_lines), + Ok(ref s) => self.push_str(s), + Err(_) => self.push_str(other_lines), } } } @@ -342,8 +343,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config); match comment_str { - Some(ref s) => self.push_str(s), - None => self.push_str(&sub_slice), + Ok(ref s) => self.push_str(s), + Err(_) => self.push_str(&sub_slice), } } } @@ -561,9 +562,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ) } else { let indent = self.block_indent; - let rewrite = self.rewrite_required_fn( - indent, item.ident, sig, &item.vis, generics, item.span, - ); + let rewrite = self + .rewrite_required_fn( + indent, item.ident, sig, &item.vis, generics, item.span, + ) + .ok(); self.push_rewrite(item.span, rewrite); } } @@ -584,7 +587,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { item.ident, &item.vis, item.span, - ); + ) + .ok(); self.push_rewrite(item.span, rewrite); } ast::ItemKind::Delegation(..) | ast::ItemKind::DelegationMac(..) => { @@ -609,7 +613,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { self.block_indent, visitor_kind, span, - ); + ) + .ok(); self.push_rewrite(span, rewrite); } @@ -655,8 +660,9 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ); } else { let indent = self.block_indent; - let rewrite = - self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); + let rewrite = self + .rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span) + .ok(); self.push_rewrite(ai.span, rewrite); } } @@ -683,7 +689,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { // 1 = ; let shape = self.shape().saturating_sub_width(1); - let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); + let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos).ok()); // As of v638 of the rustc-ap-* crates, the associated span no longer includes // the trailing semicolon. This determines the correct span to ensure scenarios // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) diff --git a/src/tools/rustfmt/tests/config/issue-5801-v2.toml b/src/tools/rustfmt/tests/config/issue-5801-v2.toml index 948f4fb66bf..98e89d37916 100644 --- a/src/tools/rustfmt/tests/config/issue-5801-v2.toml +++ b/src/tools/rustfmt/tests/config/issue-5801-v2.toml @@ -1,3 +1,3 @@ max_width = 120 version = "Two" -attr_fn_like_width = 120 \ No newline at end of file +attr_fn_like_width = 120 diff --git a/src/tools/rustfmt/tests/config/issue-6302.toml b/src/tools/rustfmt/tests/config/issue-6302.toml new file mode 100644 index 00000000000..8148b37b1f6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/issue-6302.toml @@ -0,0 +1 @@ +edition = 2019 diff --git a/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml new file mode 100644 index 00000000000..3501136812c --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/just-style-edition/rustfmt.toml @@ -0,0 +1 @@ +style_edition = "2024" diff --git a/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml new file mode 100644 index 00000000000..1082fd88889 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/just-version/rustfmt.toml @@ -0,0 +1 @@ +version = "Two" diff --git a/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml new file mode 100644 index 00000000000..24205692b1f --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/overrides/rustfmt.toml @@ -0,0 +1,2 @@ +style_edition = "2024" +overflow_delimited_expr = false diff --git a/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml new file mode 100644 index 00000000000..92844e03ab6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/style-edition-and-edition/rustfmt.toml @@ -0,0 +1,2 @@ +style_edition = "2021" +edition = "2024" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml new file mode 100644 index 00000000000..16ea9a13f36 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-edition/rustfmt.toml @@ -0,0 +1,2 @@ +version = "Two" +edition = "2018" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml new file mode 100644 index 00000000000..187ba13cfb6 --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition-and-edition/rustfmt.toml @@ -0,0 +1,3 @@ +version = "Two" +edition = "2018" +style_edition = "2021" diff --git a/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml new file mode 100644 index 00000000000..c894bd981bc --- /dev/null +++ b/src/tools/rustfmt/tests/config/style-edition/version-style-edition/rustfmt.toml @@ -0,0 +1,2 @@ +version = "Two" +style_edition = "2021" diff --git a/src/tools/rustfmt/tests/rustfmt/main.rs b/src/tools/rustfmt/tests/rustfmt/main.rs index 58cf0e5e4db..a9f58b9328e 100644 --- a/src/tools/rustfmt/tests/rustfmt/main.rs +++ b/src/tools/rustfmt/tests/rustfmt/main.rs @@ -5,7 +5,7 @@ use std::fs::remove_file; use std::path::Path; use std::process::Command; -use rustfmt_config_proc_macro::rustfmt_only_ci_test; +use rustfmt_config_proc_macro::{nightly_only_test, rustfmt_only_ci_test}; /// Run the rustfmt executable and return its output. fn rustfmt(args: &[&str]) -> (String, String) { @@ -207,3 +207,28 @@ fn rustfmt_emits_error_when_control_brace_style_is_always_next_line() { let (_stdout, stderr) = rustfmt(&args); assert!(!stderr.contains("error[internal]: left behind trailing whitespace")) } + +#[nightly_only_test] +#[test] +fn rustfmt_generates_no_error_if_failed_format_code_in_doc_comments() { + // See also https://github.com/rust-lang/rustfmt/issues/6109 + + let file = "tests/target/issue-6109.rs"; + let args = ["--config", "format_code_in_doc_comments=true", file]; + let (stdout, stderr) = rustfmt(&args); + assert!(stderr.is_empty()); + assert!(stdout.is_empty()); +} + +#[test] +fn rustfmt_error_improvement_regarding_invalid_toml() { + // See also https://github.com/rust-lang/rustfmt/issues/6302 + let invalid_toml_config = "tests/config/issue-6302.toml"; + let args = ["--config-path", invalid_toml_config]; + let (_stdout, stderr) = rustfmt(&args); + + let toml_path = Path::new(invalid_toml_config).canonicalize().unwrap(); + let expected_error_message = format!("The file `{}` failed to parse", toml_path.display()); + + assert!(stderr.contains(&expected_error_message)); +} diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs index fb0576a4822..38e8967e7e6 100644 --- a/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs +++ b/src/tools/rustfmt/tests/source/arrow_in_comments/arrow_in_single_comment.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs index fc696b309f2..98ef919450f 100644 --- a/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs +++ b/src/tools/rustfmt/tests/source/arrow_in_comments/multiple_arrows.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => // comment with => diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs index 6d48ea742fc..66e2f538415 100644 --- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs +++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs index 7a62d722c6e..51aa6376e23 100644 --- a/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs +++ b/src/tools/rustfmt/tests/source/configs/indent_style/block_trailing_comma_call/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 00000000000..5cb4a870fc1 --- /dev/null +++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2015.rs @@ -0,0 +1,155 @@ +// rustfmt-style_edition: 2015 + +fn combine_blocklike() { + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing( + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 00000000000..66c95e71aa9 --- /dev/null +++ b/src/tools/rustfmt/tests/source/configs/style_edition/overflow_delim_expr_2024.rs @@ -0,0 +1,155 @@ +// rustfmt-style_edition: 2024 + +fn combine_blocklike() { + do_thing( + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + |param| { + action(); + foo(param) + }, + ); + + do_thing( + x, + + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing( + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + ( + 1, + 2, + 3, + |param| { + action(); + foo(param) + }, + ), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs index 469ab621567..85957e3fdcc 100644 --- a/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs +++ b/src/tools/rustfmt/tests/source/fn-single-line/version_one.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Test single-line functions. fn foo_expr() { diff --git a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs index bf381ff1065..f119e581217 100644 --- a/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs +++ b/src/tools/rustfmt/tests/source/fn-single-line/version_two.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Test single-line functions. fn foo_expr() { diff --git a/src/tools/rustfmt/tests/source/issue-2179/one.rs b/src/tools/rustfmt/tests/source/issue-2179/one.rs index d23947931ff..8bbd56f0521 100644 --- a/src/tools/rustfmt/tests/source/issue-2179/one.rs +++ b/src/tools/rustfmt/tests/source/issue-2179/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/source/issue-2179/two.rs b/src/tools/rustfmt/tests/source/issue-2179/two.rs index f4cc9cc488b..631b0f3c86e 100644 --- a/src/tools/rustfmt/tests/source/issue-2179/two.rs +++ b/src/tools/rustfmt/tests/source/issue-2179/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs index f9f4cab55e3..ed7d5145150 100644 --- a/src/tools/rustfmt/tests/source/issue-3213/version_one.rs +++ b/src/tools/rustfmt/tests/source/issue-3213/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs index 0f068c19d74..c6d04aced8d 100644 --- a/src/tools/rustfmt/tests/source/issue-3213/version_two.rs +++ b/src/tools/rustfmt/tests/source/issue-3213/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/source/issue-3227/two.rs b/src/tools/rustfmt/tests/source/issue-3227/two.rs index c1572c00d57..50c0ad47dc1 100644 --- a/src/tools/rustfmt/tests/source/issue-3227/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3227/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/source/issue-3270/one.rs b/src/tools/rustfmt/tests/source/issue-3270/one.rs index 3c2e27e2293..64861176b91 100644 --- a/src/tools/rustfmt/tests/source/issue-3270/one.rs +++ b/src/tools/rustfmt/tests/source/issue-3270/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/source/issue-3270/two.rs b/src/tools/rustfmt/tests/source/issue-3270/two.rs index 0eb756471e7..1342cf03c39 100644 --- a/src/tools/rustfmt/tests/source/issue-3270/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3270/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/source/issue-3272/v1.rs b/src/tools/rustfmt/tests/source/issue-3272/v1.rs index f4c1b7c992b..56dc048bf8a 100644 --- a/src/tools/rustfmt/tests/source/issue-3272/v1.rs +++ b/src/tools/rustfmt/tests/source/issue-3272/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/source/issue-3272/v2.rs b/src/tools/rustfmt/tests/source/issue-3272/v2.rs index 0148368edc8..f3adbe37c76 100644 --- a/src/tools/rustfmt/tests/source/issue-3272/v2.rs +++ b/src/tools/rustfmt/tests/source/issue-3272/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs index 580679fbae3..718a32b4c7e 100644 --- a/src/tools/rustfmt/tests/source/issue-3278/version_one.rs +++ b/src/tools/rustfmt/tests/source/issue-3278/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn parse_conditional<'a, I: 'a>( ) -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a diff --git a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs index c17b1742d39..eb605e509f9 100644 --- a/src/tools/rustfmt/tests/source/issue-3278/version_two.rs +++ b/src/tools/rustfmt/tests/source/issue-3278/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn parse_conditional<'a, I: 'a>() -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a diff --git a/src/tools/rustfmt/tests/source/issue-3295/two.rs b/src/tools/rustfmt/tests/source/issue-3295/two.rs index 0eaf022249b..ae3d2ec28c0 100644 --- a/src/tools/rustfmt/tests/source/issue-3295/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3295/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub enum TestEnum { a, b, diff --git a/src/tools/rustfmt/tests/source/issue-3302.rs b/src/tools/rustfmt/tests/source/issue-3302.rs index c037584fd71..5e0862cb399 100644 --- a/src/tools/rustfmt/tests/source/issue-3302.rs +++ b/src/tools/rustfmt/tests/source/issue-3302.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 macro_rules! moo1 { () => { diff --git a/src/tools/rustfmt/tests/source/issue-3701/one.rs b/src/tools/rustfmt/tests/source/issue-3701/one.rs index a7f0bd3aa17..4e8518b6f18 100644 --- a/src/tools/rustfmt/tests/source/issue-3701/one.rs +++ b/src/tools/rustfmt/tests/source/issue-3701/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/source/issue-3701/two.rs b/src/tools/rustfmt/tests/source/issue-3701/two.rs index 8e15c58b8b2..d7cb790a754 100644 --- a/src/tools/rustfmt/tests/source/issue-3701/two.rs +++ b/src/tools/rustfmt/tests/source/issue-3701/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/source/issue-3805.rs b/src/tools/rustfmt/tests/source/issue-3805.rs index a0289b57974..aadc4a9dddc 100644 --- a/src/tools/rustfmt/tests/source/issue-3805.rs +++ b/src/tools/rustfmt/tests/source/issue-3805.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-format_macro_matchers: true // From original issue example - Line length 101 diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs index 7b505fda87c..8d009eabdec 100644 --- a/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs +++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_hard-tabs.rs @@ -1,5 +1,5 @@ // rustfmt-hard_tabs: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl<Target: FromEvent<A> + FromEvent<B>, A: Widget2<Ctx = C>, B: Widget2<Ctx = C>, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter<Target, A, B> { diff --git a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs index 39c8ef31292..9e283d3be0a 100644 --- a/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs +++ b/src/tools/rustfmt/tests/source/issue-3840/version-two_soft-tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl<Target: FromEvent<A> + FromEvent<B>, A: Widget2<Ctx = C>, B: Widget2<Ctx = C>, C: for<'a> CtxFamily<'a>> Widget2 for WidgetEventLifter<Target, A, B> { diff --git a/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs new file mode 100644 index 00000000000..bb4c9feae1d --- /dev/null +++ b/src/tools/rustfmt/tests/source/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; diff --git a/src/tools/rustfmt/tests/source/issue-4530.rs b/src/tools/rustfmt/tests/source/issue-4530.rs index 9d2882abb3c..6b92122f0c0 100644 --- a/src/tools/rustfmt/tests/source/issue-4530.rs +++ b/src/tools/rustfmt/tests/source/issue-4530.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let [aaaaaaaaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, cccccccccccccccccccccccccc, ddddddddddddddddddddddddd] = panic!(); } diff --git a/src/tools/rustfmt/tests/source/issue-4689/one.rs b/src/tools/rustfmt/tests/source/issue-4689/one.rs index d048eb10fb1..bff090a3525 100644 --- a/src/tools/rustfmt/tests/source/issue-4689/one.rs +++ b/src/tools/rustfmt/tests/source/issue-4689/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/source/issue-4689/two.rs b/src/tools/rustfmt/tests/source/issue-4689/two.rs index ea7feda825d..217535c046e 100644 --- a/src/tools/rustfmt/tests/source/issue-4689/two.rs +++ b/src/tools/rustfmt/tests/source/issue-4689/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/source/issue-5586.rs b/src/tools/rustfmt/tests/source/issue-5586.rs index 9cf6c1d58dd..061ad4bdaa4 100644 --- a/src/tools/rustfmt/tests/source/issue-5586.rs +++ b/src/tools/rustfmt/tests/source/issue-5586.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { // sample 1 { diff --git a/src/tools/rustfmt/tests/source/issue-5655/one.rs b/src/tools/rustfmt/tests/source/issue-5655/one.rs index 1758ec56f8b..62df2655c29 100644 --- a/src/tools/rustfmt/tests/source/issue-5655/one.rs +++ b/src/tools/rustfmt/tests/source/issue-5655/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo<T>(_: T) where diff --git a/src/tools/rustfmt/tests/source/issue-5655/two.rs b/src/tools/rustfmt/tests/source/issue-5655/two.rs index e37ebbea8af..bfe1d3813bb 100644 --- a/src/tools/rustfmt/tests/source/issue-5655/two.rs +++ b/src/tools/rustfmt/tests/source/issue-5655/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo<T>(_: T) where diff --git a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs index 5847afd9560..e61d34604a1 100644 --- a/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs +++ b/src/tools/rustfmt/tests/source/issue-5801/comment_unexpectedly_wraps_before_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: One +// rustfmt-style_edition: 2015 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/source/issue-5987/two.rs b/src/tools/rustfmt/tests/source/issue-5987/two.rs index e20026b5565..98ed35c4f9a 100644 --- a/src/tools/rustfmt/tests/source/issue-5987/two.rs +++ b/src/tools/rustfmt/tests/source/issue-5987/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs index 2ac2e0361c3..bcae86aaff2 100644 --- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs +++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs index c1bf1ad4bf8..da612b213fc 100644 --- a/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs +++ b/src/tools/rustfmt/tests/source/issue-6147/case_rustfmt_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/source/issue_5027.rs b/src/tools/rustfmt/tests/source/issue_5027.rs index 67beeb23b71..a47d6df6f0f 100644 --- a/src/tools/rustfmt/tests/source/issue_5027.rs +++ b/src/tools/rustfmt/tests/source/issue_5027.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)>+ ExactSizeIterator+ 'a; diff --git a/src/tools/rustfmt/tests/source/let_else_v2.rs b/src/tools/rustfmt/tests/source/let_else_v2.rs index a420fbcf95b..23be32d629a 100644 --- a/src/tools/rustfmt/tests/source/let_else_v2.rs +++ b/src/tools/rustfmt/tests/source/let_else_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-single_line_let_else_max_width: 100 fn issue5901() { diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs index d6832c2af09..60083024810 100644 --- a/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs +++ b/src/tools/rustfmt/tests/source/long-fn-1/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs index f402a26e8b6..bce2c551c6a 100644 --- a/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs +++ b/src/tools/rustfmt/tests/source/long-fn-1/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/source/one_line_if_v1.rs b/src/tools/rustfmt/tests/source/one_line_if_v1.rs index d3dcbe6787a..bf7bc75fb88 100644 --- a/src/tools/rustfmt/tests/source/one_line_if_v1.rs +++ b/src/tools/rustfmt/tests/source/one_line_if_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/source/one_line_if_v2.rs b/src/tools/rustfmt/tests/source/one_line_if_v2.rs index 40c834959f9..f3c974b12db 100644 --- a/src/tools/rustfmt/tests/source/one_line_if_v2.rs +++ b/src/tools/rustfmt/tests/source/one_line_if_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs index a3aa631ed4a..fea146d8d33 100644 --- a/src/tools/rustfmt/tests/source/single-line-macro/v1.rs +++ b/src/tools/rustfmt/tests/source/single-line-macro/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs index 51a665f7560..d9fba64ac70 100644 --- a/src/tools/rustfmt/tests/source/single-line-macro/v2.rs +++ b/src/tools/rustfmt/tests/source/single-line-macro/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs index 88249aa5fb9..c6d1b3f0ab7 100644 --- a/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs +++ b/src/tools/rustfmt/tests/source/trailing_comments/hard_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true // rustfmt-hard_tabs: true diff --git a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs index 7845f713b8a..431875989f3 100644 --- a/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs +++ b/src/tools/rustfmt/tests/source/trailing_comments/soft_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast diff --git a/src/tools/rustfmt/tests/source/tuple_v2.rs b/src/tools/rustfmt/tests/source/tuple_v2.rs index 9223033832b..6dc18e00d7e 100644 --- a/src/tools/rustfmt/tests/source/tuple_v2.rs +++ b/src/tools/rustfmt/tests/source/tuple_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn issue_4355() { let _ = ((1,),).0 .0; diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs new file mode 100644 index 00000000000..722ebac47ac --- /dev/null +++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses-with-comments.rs @@ -0,0 +1,31 @@ +type Foo // comment1 + // interlinear1 +where // comment2 + // interlinear2 +A: B, // comment3 +C: D, // comment4 + // interlinear3 += E; // comment5 + +type Foo // comment6 + // interlinear4 +where// comment7 + // interlinear5 +A: B, // comment8 +C: D, // comment9 + // interlinear6 += E // comment10 + // interlinear7 +where // comment11 + // interlinear8 +F: G, // comment12 +H: I; // comment13 + +type Foo // comment14 + // interlinear9 += E // comment15 + // interlinear10 +where // comment16 + // interlinear11 +F: G,// comment17 +H: I;// comment18 diff --git a/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs new file mode 100644 index 00000000000..ad998caf308 --- /dev/null +++ b/src/tools/rustfmt/tests/source/type-alias-where-clauses.rs @@ -0,0 +1,20 @@ + type Foo + where + A: B, + C: D, + = E; + + type Foo + where + A: B, + C: D, + = E + where + F: G, + H: I; + + type Foo + = E + where + F: G, + H: I; \ No newline at end of file diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs index deffdbeeaaf..d638a6b4ef0 100644 --- a/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs +++ b/src/tools/rustfmt/tests/target/arrow_in_comments/arrow_in_single_comment.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs index b0443411816..f6f4a60eec3 100644 --- a/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs +++ b/src/tools/rustfmt/tests/target/arrow_in_comments/multiple_arrows.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { match a { _ => diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs index 6b9489bef55..204dce6d655 100644 --- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs +++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs index 4f4292e5f48..887e8328ccc 100644 --- a/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs +++ b/src/tools/rustfmt/tests/target/configs/indent_style/block_trailing_comma_call/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false // rustfmt-indent_style: Block diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs new file mode 100644 index 00000000000..05d4b8b6d33 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2015.rs @@ -0,0 +1,135 @@ +// rustfmt-style_edition: 2015 + +fn combine_blocklike() { + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing(Bar { + x: value, + y: value2, + }); + + do_thing( + x, + Bar { + x: value, + y: value2, + }, + ); + + do_thing( + x, + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing(&[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing(vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); +} + +fn combine_struct_sample() { + let identity = verify( + &ctx, + VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + }, + )?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount( + "/", + routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ], + ) + .launch(); +} diff --git a/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs new file mode 100644 index 00000000000..ecd2e8ca797 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/style_edition/overflow_delim_expr_2024.rs @@ -0,0 +1,120 @@ +// rustfmt-style_edition: 2024 + +fn combine_blocklike() { + do_thing(|param| { + action(); + foo(param) + }); + + do_thing(x, |param| { + action(); + foo(param) + }); + + do_thing( + x, + // I'll be discussing the `action` with your para(m)legal counsel + |param| { + action(); + foo(param) + }, + ); + + do_thing(Bar { + x: value, + y: value2, + }); + + do_thing(x, Bar { + x: value, + y: value2, + }); + + do_thing( + x, + // Let me tell you about that one time at the `Bar` + Bar { + x: value, + y: value2, + }, + ); + + do_thing(&[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + &[ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing(vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing(x, vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ]); + + do_thing( + x, + // Just admit it; my list is longer than can be folded on to one line + vec![ + value_with_longer_name, + value2_with_longer_name, + value3_with_longer_name, + value4_with_longer_name, + ], + ); + + do_thing( + x, + (1, 2, 3, |param| { + action(); + foo(param) + }), + ); +} + +fn combine_struct_sample() { + let identity = verify(&ctx, VerifyLogin { + type_: LoginType::Username, + username: args.username.clone(), + password: Some(args.password.clone()), + domain: None, + })?; +} + +fn combine_macro_sample() { + rocket::ignite() + .mount("/", routes![ + http::auth::login, + http::auth::logout, + http::cors::options, + http::action::dance, + http::action::sleep, + ]) + .launch(); +} diff --git a/src/tools/rustfmt/tests/target/configs/version/mapped.rs b/src/tools/rustfmt/tests/target/configs/version/mapped.rs new file mode 100644 index 00000000000..296dc559a93 --- /dev/null +++ b/src/tools/rustfmt/tests/target/configs/version/mapped.rs @@ -0,0 +1,9 @@ +// rustfmt-version: Two +fn main() { + let [ + aaaaaaaaaaaaaaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, + cccccccccccccccccccccccccc, + ddddddddddddddddddddddddd, + ] = panic!(); +} diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs index 013b2cd7216..5b704474d6d 100644 --- a/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs +++ b/src/tools/rustfmt/tests/target/fn-single-line/version_one.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Test single-line functions. fn foo_expr() { 1 } diff --git a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs index b8053d4c2f5..aedd1d481b6 100644 --- a/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs +++ b/src/tools/rustfmt/tests/target/fn-single-line/version_two.rs @@ -1,5 +1,5 @@ // rustfmt-fn_single_line: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Test single-line functions. fn foo_expr() { 1 } diff --git a/src/tools/rustfmt/tests/target/issue-2179/one.rs b/src/tools/rustfmt/tests/target/issue-2179/one.rs index 3f98acc8dcd..6593624e589 100644 --- a/src/tools/rustfmt/tests/target/issue-2179/one.rs +++ b/src/tools/rustfmt/tests/target/issue-2179/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/target/issue-2179/two.rs b/src/tools/rustfmt/tests/target/issue-2179/two.rs index 96531509ea2..088518705fe 100644 --- a/src/tools/rustfmt/tests/target/issue-2179/two.rs +++ b/src/tools/rustfmt/tests/target/issue-2179/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-error_on_line_overflow: false fn issue_2179() { diff --git a/src/tools/rustfmt/tests/target/issue-3132.rs b/src/tools/rustfmt/tests/target/issue-3132.rs index 4dffe0ab836..4d61baa7507 100644 --- a/src/tools/rustfmt/tests/target/issue-3132.rs +++ b/src/tools/rustfmt/tests/target/issue-3132.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn test() { /* diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs index 307903b128b..a20915df761 100644 --- a/src/tools/rustfmt/tests/target/issue-3213/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3213/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs index de93d04ba95..fb609f6a67a 100644 --- a/src/tools/rustfmt/tests/target/issue-3213/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3213/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo() { match 0 { diff --git a/src/tools/rustfmt/tests/target/issue-3227/one.rs b/src/tools/rustfmt/tests/target/issue-3227/one.rs index fcc8331000d..2922bfdfef7 100644 --- a/src/tools/rustfmt/tests/target/issue-3227/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3227/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/target/issue-3227/two.rs b/src/tools/rustfmt/tests/target/issue-3227/two.rs index 374ab54305d..ae7eee47194 100644 --- a/src/tools/rustfmt/tests/target/issue-3227/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3227/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { thread::spawn(|| { diff --git a/src/tools/rustfmt/tests/target/issue-3270/one.rs b/src/tools/rustfmt/tests/target/issue-3270/one.rs index 78de9473243..a31a0ff5742 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/target/issue-3270/two.rs b/src/tools/rustfmt/tests/target/issue-3270/two.rs index e48b5921329..8e26f8ac23d 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { /* let s = String::from( diff --git a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs index 7435c5f0866..967bfb5534c 100644 --- a/src/tools/rustfmt/tests/target/issue-3270/wrap.rs +++ b/src/tools/rustfmt/tests/target/issue-3270/wrap.rs @@ -1,5 +1,5 @@ // rustfmt-wrap_comments: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // check that a line below max_width does not get over the limit when wrapping // it in a block comment diff --git a/src/tools/rustfmt/tests/target/issue-3272/v1.rs b/src/tools/rustfmt/tests/target/issue-3272/v1.rs index aab201027d5..7cca7ea7896 100644 --- a/src/tools/rustfmt/tests/target/issue-3272/v1.rs +++ b/src/tools/rustfmt/tests/target/issue-3272/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { assert!(HAYSTACK diff --git a/src/tools/rustfmt/tests/target/issue-3272/v2.rs b/src/tools/rustfmt/tests/target/issue-3272/v2.rs index a42a2fccd5b..ca7718c5a61 100644 --- a/src/tools/rustfmt/tests/target/issue-3272/v2.rs +++ b/src/tools/rustfmt/tests/target/issue-3272/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { assert!( diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs index 580679fbae3..718a32b4c7e 100644 --- a/src/tools/rustfmt/tests/target/issue-3278/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3278/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn parse_conditional<'a, I: 'a>( ) -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a diff --git a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs index c17b1742d39..eb605e509f9 100644 --- a/src/tools/rustfmt/tests/target/issue-3278/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3278/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn parse_conditional<'a, I: 'a>() -> impl Parser<Input = I, Output = Expr, PartialState = ()> + 'a diff --git a/src/tools/rustfmt/tests/target/issue-3295/two.rs b/src/tools/rustfmt/tests/target/issue-3295/two.rs index 3e669a0bb75..4f685172cfa 100644 --- a/src/tools/rustfmt/tests/target/issue-3295/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3295/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub enum TestEnum { a, b, diff --git a/src/tools/rustfmt/tests/target/issue-3302.rs b/src/tools/rustfmt/tests/target/issue-3302.rs index 146cb983819..504bfd987f5 100644 --- a/src/tools/rustfmt/tests/target/issue-3302.rs +++ b/src/tools/rustfmt/tests/target/issue-3302.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 macro_rules! moo1 { () => { diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs index 8ab28304732..4bd972aa87a 100644 --- a/src/tools/rustfmt/tests/target/issue-3614/version_one.rs +++ b/src/tools/rustfmt/tests/target/issue-3614/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { let toto = || { diff --git a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs index 5d6f8e7a313..7b9534caa02 100644 --- a/src/tools/rustfmt/tests/target/issue-3614/version_two.rs +++ b/src/tools/rustfmt/tests/target/issue-3614/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let toto = || { diff --git a/src/tools/rustfmt/tests/target/issue-3701/one.rs b/src/tools/rustfmt/tests/target/issue-3701/one.rs index 9d1ef9eed9a..b154724d341 100644 --- a/src/tools/rustfmt/tests/target/issue-3701/one.rs +++ b/src/tools/rustfmt/tests/target/issue-3701/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/target/issue-3701/two.rs b/src/tools/rustfmt/tests/target/issue-3701/two.rs index 62ffc9d823d..995763ef291 100644 --- a/src/tools/rustfmt/tests/target/issue-3701/two.rs +++ b/src/tools/rustfmt/tests/target/issue-3701/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn build_sorted_static_get_entry_names( mut entries: Vec<(u8, &'static str)>, diff --git a/src/tools/rustfmt/tests/target/issue-3805.rs b/src/tools/rustfmt/tests/target/issue-3805.rs index a247a43fe6d..a1eda832d76 100644 --- a/src/tools/rustfmt/tests/target/issue-3805.rs +++ b/src/tools/rustfmt/tests/target/issue-3805.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-format_macro_matchers: true // From original issue example - Line length 101 diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs index 084db3d1465..78a2f921735 100644 --- a/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs +++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_hard-tabs.rs @@ -1,5 +1,5 @@ // rustfmt-hard_tabs: true -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl< Target: FromEvent<A> + FromEvent<B>, diff --git a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs index bc59b0baa56..c76dd4dafbb 100644 --- a/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs +++ b/src/tools/rustfmt/tests/target/issue-3840/version-two_soft-tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 impl< Target: FromEvent<A> + FromEvent<B>, diff --git a/src/tools/rustfmt/tests/target/issue-3882.rs b/src/tools/rustfmt/tests/target/issue-3882.rs index 5eb442af974..8e617635b7c 100644 --- a/src/tools/rustfmt/tests/target/issue-3882.rs +++ b/src/tools/rustfmt/tests/target/issue-3882.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn bar(_t: T, // bar ) { } diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs new file mode 100644 index 00000000000..1fc1613083e --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2015.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2015 + +use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; diff --git a/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs new file mode 100644 index 00000000000..df85636ad57 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-4381/style_edition_2024.rs @@ -0,0 +1,3 @@ +// rustfmt-style_edition: 2024 + +use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64}; diff --git a/src/tools/rustfmt/tests/target/issue-4530.rs b/src/tools/rustfmt/tests/target/issue-4530.rs index 296dc559a93..9e9fbf95c2d 100644 --- a/src/tools/rustfmt/tests/target/issue-4530.rs +++ b/src/tools/rustfmt/tests/target/issue-4530.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { let [ aaaaaaaaaaaaaaaaaaaaaaaaaa, diff --git a/src/tools/rustfmt/tests/target/issue-4689/one.rs b/src/tools/rustfmt/tests/target/issue-4689/one.rs index 7735e34f3b5..fb523f38501 100644 --- a/src/tools/rustfmt/tests/target/issue-4689/one.rs +++ b/src/tools/rustfmt/tests/target/issue-4689/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/target/issue-4689/two.rs b/src/tools/rustfmt/tests/target/issue-4689/two.rs index e3b5cd22810..30303490129 100644 --- a/src/tools/rustfmt/tests/target/issue-4689/two.rs +++ b/src/tools/rustfmt/tests/target/issue-4689/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Based on the issue description pub trait PrettyPrinter<'tcx>: diff --git a/src/tools/rustfmt/tests/target/issue-5586.rs b/src/tools/rustfmt/tests/target/issue-5586.rs index 7033ae975b3..afe13d1fb1c 100644 --- a/src/tools/rustfmt/tests/target/issue-5586.rs +++ b/src/tools/rustfmt/tests/target/issue-5586.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { // sample 1 { diff --git a/src/tools/rustfmt/tests/target/issue-5655/one.rs b/src/tools/rustfmt/tests/target/issue-5655/one.rs index 1758ec56f8b..62df2655c29 100644 --- a/src/tools/rustfmt/tests/target/issue-5655/one.rs +++ b/src/tools/rustfmt/tests/target/issue-5655/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn foo<T>(_: T) where diff --git a/src/tools/rustfmt/tests/target/issue-5655/two.rs b/src/tools/rustfmt/tests/target/issue-5655/two.rs index 14fbc3d1321..4a7eb4f4bc2 100644 --- a/src/tools/rustfmt/tests/target/issue-5655/two.rs +++ b/src/tools/rustfmt/tests/target/issue-5655/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn foo<T>(_: T) where diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs index 9f294751108..461b5873a68 100644 --- a/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs +++ b/src/tools/rustfmt/tests/target/issue-5801/comment_does_not_wrap_within_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: Two +// rustfmt-style_edition: 2024 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs index dd839dd4548..58bb8a1c2e3 100644 --- a/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs +++ b/src/tools/rustfmt/tests/target/issue-5801/comment_unexpectedly_wraps_before_max_width.rs @@ -1,7 +1,7 @@ // rustfmt-comment_width: 120 // rustfmt-wrap_comments: true // rustfmt-max_width: 120 -// rustfmt-version: One +// rustfmt-style_edition: 2015 /// This function is 120 columns wide and is left alone. This comment is 120 columns wide and the formatter is also fine fn my_super_cool_function_name(my_very_cool_argument_name: String, my_other_very_cool_argument_name: String) -> String { diff --git a/src/tools/rustfmt/tests/target/issue-5987/one.rs b/src/tools/rustfmt/tests/target/issue-5987/one.rs index 3c995ed28ba..17327feead5 100644 --- a/src/tools/rustfmt/tests/target/issue-5987/one.rs +++ b/src/tools/rustfmt/tests/target/issue-5987/one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/target/issue-5987/two.rs b/src/tools/rustfmt/tests/target/issue-5987/two.rs index 8fd92fc179e..41ecb13e3db 100644 --- a/src/tools/rustfmt/tests/target/issue-5987/two.rs +++ b/src/tools/rustfmt/tests/target/issue-5987/two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn main() { trace!( diff --git a/src/tools/rustfmt/tests/target/issue-6109.rs b/src/tools/rustfmt/tests/target/issue-6109.rs new file mode 100644 index 00000000000..7d2bbf75691 --- /dev/null +++ b/src/tools/rustfmt/tests/target/issue-6109.rs @@ -0,0 +1,7 @@ +/// Some doc comment with code snippet: +///``` +/// '\u{1F} +/// ``` +pub struct Code {} + +fn main() {} diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs index 75800012c63..d8e67655e4c 100644 --- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs +++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs index 5e4220e7306..9d0b12d686b 100644 --- a/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs +++ b/src/tools/rustfmt/tests/target/issue-6147/case_rustfmt_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub fn main() { let a = Some(12); diff --git a/src/tools/rustfmt/tests/target/issue_5027.rs b/src/tools/rustfmt/tests/target/issue_5027.rs index 26d771720b6..1de3e0d4f15 100644 --- a/src/tools/rustfmt/tests/target/issue_5027.rs +++ b/src/tools/rustfmt/tests/target/issue_5027.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 pub type Iter<'a, D> = impl DoubleEndedIterator<Item = (SomethingSomethingSomethingLongType<D>)> + ExactSizeIterator diff --git a/src/tools/rustfmt/tests/target/let_else_v2.rs b/src/tools/rustfmt/tests/target/let_else_v2.rs index b25ac1609d8..6e886299cb0 100644 --- a/src/tools/rustfmt/tests/target/let_else_v2.rs +++ b/src/tools/rustfmt/tests/target/let_else_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-single_line_let_else_max_width: 100 fn issue5901() { diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs index 05f69953c26..60f672c1837 100644 --- a/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs +++ b/src/tools/rustfmt/tests/target/long-fn-1/version_one.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs index 32794bccde2..f6007398bcc 100644 --- a/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs +++ b/src/tools/rustfmt/tests/target/long-fn-1/version_two.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // Tests that a function which is almost short enough, but not quite, gets // formatted correctly. diff --git a/src/tools/rustfmt/tests/target/one_line_if_v1.rs b/src/tools/rustfmt/tests/target/one_line_if_v1.rs index b3c6c4cbeb2..5160ea0264c 100644 --- a/src/tools/rustfmt/tests/target/one_line_if_v1.rs +++ b/src/tools/rustfmt/tests/target/one_line_if_v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 fn plain_if(x: bool) -> u8 { if x { diff --git a/src/tools/rustfmt/tests/target/one_line_if_v2.rs b/src/tools/rustfmt/tests/target/one_line_if_v2.rs index 81ca4c8b8b4..a9610ec9749 100644 --- a/src/tools/rustfmt/tests/target/one_line_if_v2.rs +++ b/src/tools/rustfmt/tests/target/one_line_if_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn plain_if(x: bool) -> u8 { if x { 0 } else { 1 } diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs index a3aa631ed4a..fea146d8d33 100644 --- a/src/tools/rustfmt/tests/target/single-line-macro/v1.rs +++ b/src/tools/rustfmt/tests/target/single-line-macro/v1.rs @@ -1,4 +1,4 @@ -// rustfmt-version: One +// rustfmt-style_edition: 2015 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs index 9c6bcf33ad5..6fcacb70ba3 100644 --- a/src/tools/rustfmt/tests/target/single-line-macro/v2.rs +++ b/src/tools/rustfmt/tests/target/single-line-macro/v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // #2652 // Preserve trailing comma inside macro, even if it looks an array. diff --git a/src/tools/rustfmt/tests/target/style_edition/default.rs b/src/tools/rustfmt/tests/target/style_edition/default.rs new file mode 100644 index 00000000000..17442df6c49 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/default.rs @@ -0,0 +1,10 @@ +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> (impl Fn( + AlphabeticalTraversal, + Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>, +) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>> + + Send + + Sync + + 'static) { +} diff --git a/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs new file mode 100644 index 00000000000..c36a993d842 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/follows_edition.rs @@ -0,0 +1,14 @@ +// rustfmt-edition: 2024 + +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> ( + impl Fn( + AlphabeticalTraversal, + Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>, + ) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>> + + Send + + Sync + + 'static +) { +} diff --git a/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs new file mode 100644 index 00000000000..6d0eaac8970 --- /dev/null +++ b/src/tools/rustfmt/tests/target/style_edition/overrides_edition_when_set.rs @@ -0,0 +1,14 @@ +// rustfmt-edition: 2018 +// rustfmt-style_edition: 2024 +fn build_sorted_static_get_entry_names( + mut entries: Vec<(u8, &'static str)>, +) -> ( + impl Fn( + AlphabeticalTraversal, + Box<dyn dirents_sink::Sink<AlphabeticalTraversal>>, + ) -> BoxFuture<'static, Result<Box<dyn dirents_sink::Sealed>, Status>> + + Send + + Sync + + 'static +) { +} diff --git a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs index 35e72f1affd..e7009ac00c0 100644 --- a/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs +++ b/src/tools/rustfmt/tests/target/trailing_comments/hard_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true // rustfmt-hard_tabs: true diff --git a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs index eba943042ad..34cfed1a229 100644 --- a/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs +++ b/src/tools/rustfmt/tests/target/trailing_comments/soft_tabs.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 // rustfmt-wrap_comments: true pub const IFF_MULTICAST: ::c_int = 0x0000000800; // Supports multicast diff --git a/src/tools/rustfmt/tests/target/tuple_v2.rs b/src/tools/rustfmt/tests/target/tuple_v2.rs index ba653291c2f..fa508332be5 100644 --- a/src/tools/rustfmt/tests/target/tuple_v2.rs +++ b/src/tools/rustfmt/tests/target/tuple_v2.rs @@ -1,4 +1,4 @@ -// rustfmt-version: Two +// rustfmt-style_edition: 2024 fn issue_4355() { let _ = ((1,),).0.0; diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs new file mode 100644 index 00000000000..25c38916376 --- /dev/null +++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses-with-comments.rs @@ -0,0 +1,39 @@ +type Foo +// comment1 +// interlinear1 +where + // comment2 + // interlinear2 + A: B, // comment3 + C: D, // comment4 +// interlinear3 += E; // comment5 + +type Foo +// comment6 +// interlinear4 +where + // comment7 + // interlinear5 + A: B, // comment8 + C: D, // comment9 +// interlinear6 += E +// comment10 +// interlinear7 +where + // comment11 + // interlinear8 + F: G, // comment12 + H: I; // comment13 + +type Foo // comment14 + // interlinear9 + = E +// comment15 +// interlinear10 +where + // comment16 + // interlinear11 + F: G, // comment17 + H: I; // comment18 diff --git a/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs new file mode 100644 index 00000000000..96d2d63bbb8 --- /dev/null +++ b/src/tools/rustfmt/tests/target/type-alias-where-clauses.rs @@ -0,0 +1,20 @@ +type Foo +where + A: B, + C: D, += E; + +type Foo +where + A: B, + C: D, += E +where + F: G, + H: I; + +type Foo + = E +where + F: G, + H: I; diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 8197a1bd859..b2de3457dec 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1649,7 +1649,6 @@ ui/issues/issue-17068.rs ui/issues/issue-17121.rs ui/issues/issue-17216.rs ui/issues/issue-17252.rs -ui/issues/issue-17302.rs ui/issues/issue-17322.rs ui/issues/issue-17336.rs ui/issues/issue-17337.rs diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 3215896091d..1e7eb82b83e 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -82,6 +82,7 @@ pub mod pal; pub mod run_make_tests; pub mod rustdoc_css_themes; pub mod rustdoc_gui_tests; +pub mod rustdoc_templates; pub mod style; pub mod target_policy; pub mod target_specific_tests; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index e6d21bfb245..38b3f3f6417 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -108,6 +108,7 @@ fn main() { check!(mir_opt_tests, &tests_path, bless); check!(rustdoc_gui_tests, &tests_path); check!(rustdoc_css_themes, &librustdoc_path); + check!(rustdoc_templates, &librustdoc_path); check!(known_bug, &crashes_path); check!(unknown_revision, &tests_path); diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs new file mode 100644 index 00000000000..6c8530e6366 --- /dev/null +++ b/src/tools/tidy/src/rustdoc_templates.rs @@ -0,0 +1,58 @@ +//! Tidy check to ensure that rustdoc templates didn't forget a `{# #}` to strip extra whitespace +//! characters. + +use std::ffi::OsStr; +use std::path::Path; + +use ignore::DirEntry; + +use crate::walk::walk; + +// Array containing `("beginning of tag", "end of tag")`. +const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")]; + +pub fn check(librustdoc_path: &Path, bad: &mut bool) { + walk( + &librustdoc_path.join("html/templates"), + |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("html")), + &mut |path: &DirEntry, file_content: &str| { + let mut lines = file_content.lines().enumerate().peekable(); + + while let Some((pos, line)) = lines.next() { + let line = line.trim(); + if TAGS.iter().any(|(_, tag)| line.ends_with(tag)) { + continue; + } + let Some(next_line) = lines.peek().map(|(_, next_line)| next_line.trim()) else { + continue; + }; + if TAGS.iter().any(|(tag, _)| next_line.starts_with(tag)) { + continue; + } + // Maybe this is a multi-line tag, let's filter it out then. + match TAGS.iter().find_map(|(tag, end_tag)| { + if line.rfind(tag).is_some() { Some(end_tag) } else { None } + }) { + None => { + // No it's not, let's error. + tidy_error!( + bad, + "`{}` at line {}: missing `{{# #}}` at the end of the line", + path.path().display(), + pos + 1, + ); + } + Some(end_tag) => { + // We skip the tag. + while let Some((_, next_line)) = lines.peek() { + if next_line.contains(end_tag) { + break; + } + lines.next(); + } + } + } + } + }, + ); +} diff --git a/tests/assembly/simd-bitmask.rs b/tests/assembly/simd-bitmask.rs index cd22ca06706..9a355cc162f 100644 --- a/tests/assembly/simd-bitmask.rs +++ b/tests/assembly/simd-bitmask.rs @@ -9,7 +9,6 @@ //@ [x86-avx512] needs-llvm-components: x86 //@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 -//@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -O -C panic=abort diff --git a/tests/assembly/simd-intrinsic-gather.rs b/tests/assembly/simd-intrinsic-gather.rs index 83015f05ab3..2cbb6cfbb50 100644 --- a/tests/assembly/simd-intrinsic-gather.rs +++ b/tests/assembly/simd-intrinsic-gather.rs @@ -2,7 +2,6 @@ //@ [x86-avx512] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel //@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq //@ [x86-avx512] needs-llvm-components: x86 -//@ [x86-avx512] min-llvm-version: 18.0 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -O -C panic=abort diff --git a/tests/assembly/simd-intrinsic-mask-reduce.rs b/tests/assembly/simd-intrinsic-mask-reduce.rs index dd4dbaeda76..61d7aa59093 100644 --- a/tests/assembly/simd-intrinsic-mask-reduce.rs +++ b/tests/assembly/simd-intrinsic-mask-reduce.rs @@ -6,7 +6,6 @@ //@ [x86] needs-llvm-components: x86 //@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 -//@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -O -C panic=abort diff --git a/tests/assembly/simd-intrinsic-scatter.rs b/tests/assembly/simd-intrinsic-scatter.rs index 55095e4cb68..679972d9b86 100644 --- a/tests/assembly/simd-intrinsic-scatter.rs +++ b/tests/assembly/simd-intrinsic-scatter.rs @@ -2,7 +2,6 @@ //@ [x86-avx512] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel //@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq //@ [x86-avx512] needs-llvm-components: x86 -//@ [x86-avx512] min-llvm-version: 18.0 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -O -C panic=abort diff --git a/tests/assembly/simd-intrinsic-select.rs b/tests/assembly/simd-intrinsic-select.rs index 4dfc2f9ed1f..57fd36fd9e3 100644 --- a/tests/assembly/simd-intrinsic-select.rs +++ b/tests/assembly/simd-intrinsic-select.rs @@ -7,7 +7,6 @@ //@ [x86-avx512] needs-llvm-components: x86 //@ [aarch64] compile-flags: --target=aarch64-unknown-linux-gnu //@ [aarch64] needs-llvm-components: aarch64 -//@ [aarch64] min-llvm-version: 18.0 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -O -C panic=abort diff --git a/tests/assembly/stack-probes.rs b/tests/assembly/stack-probes.rs index ddabd4b1632..e0931157ce1 100644 --- a/tests/assembly/stack-probes.rs +++ b/tests/assembly/stack-probes.rs @@ -6,7 +6,6 @@ //@[i686] needs-llvm-components: x86 //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu //@[aarch64] needs-llvm-components: aarch64 -//@[aarch64] min-llvm-version: 18 #![feature(no_core, lang_items)] #![crate_type = "lib"] diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs index 5ed0b6c50a7..57fc601a2e0 100644 --- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs +++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs @@ -9,7 +9,6 @@ //@ [basic] compile-flags: -Z stack-protector=basic //@ [none] compile-flags: -Z stack-protector=none //@ compile-flags: -C opt-level=2 -Z merge-functions=disabled -//@ min-llvm-version: 17.0.2 // NOTE: the heuristics for stack smash protection inappropriately rely on types in LLVM IR, // despite those types having no semantic meaning. This means that the `basic` and `strong` diff --git a/tests/assembly/targets/targets-macho.rs b/tests/assembly/targets/targets-macho.rs index 713129b692c..8095ae9029b 100644 --- a/tests/assembly/targets/targets-macho.rs +++ b/tests/assembly/targets/targets-macho.rs @@ -18,6 +18,9 @@ //@ revisions: aarch64_apple_tvos_sim //@ [aarch64_apple_tvos_sim] compile-flags: --target aarch64-apple-tvos-sim //@ [aarch64_apple_tvos_sim] needs-llvm-components: aarch64 +//@ revisions: arm64e_apple_tvos +//@ [arm64e_apple_tvos] compile-flags: --target arm64e-apple-tvos +//@ [arm64e_apple_tvos] needs-llvm-components: aarch64 //@ revisions: aarch64_apple_watchos //@ [aarch64_apple_watchos] compile-flags: --target aarch64-apple-watchos //@ [aarch64_apple_watchos] needs-llvm-components: aarch64 @@ -28,11 +31,9 @@ //@ [arm64_32_apple_watchos] compile-flags: --target arm64_32-apple-watchos //@ [arm64_32_apple_watchos] needs-llvm-components: aarch64 //@ revisions: aarch64_apple_visionos -//@ [aarch64_apple_visionos] min-llvm-version: 18 //@ [aarch64_apple_visionos] compile-flags: --target aarch64-apple-visionos //@ [aarch64_apple_visionos] needs-llvm-components: aarch64 //@ revisions: aarch64_apple_visionos_sim -//@ [aarch64_apple_visionos_sim] min-llvm-version: 18 //@ [aarch64_apple_visionos_sim] compile-flags: --target aarch64-apple-visionos-sim //@ [aarch64_apple_visionos_sim] needs-llvm-components: aarch64 //@ revisions: arm64e_apple_darwin diff --git a/tests/codegen/align-byval.rs b/tests/codegen/align-byval.rs index 223696229cb..b057147ab13 100644 --- a/tests/codegen/align-byval.rs +++ b/tests/codegen/align-byval.rs @@ -1,10 +1,8 @@ // ignore-tidy-linelength -//@ revisions:m68k wasm x86_64-linux x86_64-windows i686-linux i686-windows +//@ revisions:m68k x86_64-linux x86_64-windows i686-linux i686-windows //@[m68k] compile-flags: --target m68k-unknown-linux-gnu //@[m68k] needs-llvm-components: m68k -//@[wasm] compile-flags: --target wasm32-unknown-emscripten -//@[wasm] needs-llvm-components: webassembly //@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64-linux] needs-llvm-components: x86 //@[x86_64-windows] compile-flags: --target x86_64-pc-windows-msvc @@ -15,7 +13,7 @@ //@[i686-windows] needs-llvm-components: x86 // Tests that `byval` alignment is properly specified (#80127). -// The only targets that use `byval` are m68k, wasm, x86-64, and x86. +// The only targets that use `byval` are m68k, x86-64, and x86. // Note also that Windows mandates a by-ref ABI here, so it does not use byval. #![feature(no_core, lang_items)] @@ -112,9 +110,6 @@ pub unsafe fn call_na1(x: NaturalAlign1) { // m68k: [[ALLOCA:%[a-z0-9+]]] = alloca [2 x i8], align 1 // m68k: call void @natural_align_1({{.*}}byval([2 x i8]) align 1{{.*}} [[ALLOCA]]) - // wasm: [[ALLOCA:%[a-z0-9+]]] = alloca [2 x i8], align 1 - // wasm: call void @natural_align_1({{.*}}byval([2 x i8]) align 1{{.*}} [[ALLOCA]]) - // x86_64-linux: call void @natural_align_1(i16 // x86_64-windows: call void @natural_align_1(i16 @@ -133,7 +128,6 @@ pub unsafe fn call_na2(x: NaturalAlign2) { // CHECK: start: // m68k-NEXT: call void @natural_align_2 - // wasm-NEXT: call void @natural_align_2 // x86_64-linux-NEXT: call void @natural_align_2 // x86_64-windows-NEXT: call void @natural_align_2 @@ -204,8 +198,6 @@ pub unsafe fn call_fa16(x: ForceAlign16) { extern "C" { // m68k: declare void @natural_align_1({{.*}}byval([2 x i8]) align 1{{.*}}) - // wasm: declare void @natural_align_1({{.*}}byval([2 x i8]) align 1{{.*}}) - // x86_64-linux: declare void @natural_align_1(i16) // x86_64-windows: declare void @natural_align_1(i16) @@ -217,8 +209,6 @@ extern "C" { // m68k: declare void @natural_align_2({{.*}}byval([34 x i8]) align 2{{.*}}) - // wasm: declare void @natural_align_2({{.*}}byval([34 x i8]) align 2{{.*}}) - // x86_64-linux: declare void @natural_align_2({{.*}}byval([34 x i8]) align 2{{.*}}) // x86_64-windows: declare void @natural_align_2( @@ -232,8 +222,6 @@ extern "C" { // m68k: declare void @force_align_4({{.*}}byval([20 x i8]) align 4{{.*}}) - // wasm: declare void @force_align_4({{.*}}byval([20 x i8]) align 4{{.*}}) - // x86_64-linux: declare void @force_align_4({{.*}}byval([20 x i8]) align 4{{.*}}) // x86_64-windows: declare void @force_align_4( @@ -247,8 +235,6 @@ extern "C" { // m68k: declare void @natural_align_8({{.*}}byval([24 x i8]) align 4{{.*}}) - // wasm: declare void @natural_align_8({{.*}}byval([24 x i8]) align 8{{.*}}) - // x86_64-linux: declare void @natural_align_8({{.*}}byval([24 x i8]) align 8{{.*}}) // x86_64-windows: declare void @natural_align_8( @@ -262,8 +248,6 @@ extern "C" { // m68k: declare void @force_align_8({{.*}}byval([24 x i8]) align 8{{.*}}) - // wasm: declare void @force_align_8({{.*}}byval([24 x i8]) align 8{{.*}}) - // x86_64-linux: declare void @force_align_8({{.*}}byval([24 x i8]) align 8{{.*}}) // x86_64-windows: declare void @force_align_8( @@ -279,8 +263,6 @@ extern "C" { // m68k: declare void @lower_fa8({{.*}}byval([24 x i8]) align 4{{.*}}) - // wasm: declare void @lower_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) - // x86_64-linux: declare void @lower_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) // x86_64-windows: declare void @lower_fa8( @@ -294,8 +276,6 @@ extern "C" { // m68k: declare void @wrapped_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) - // wasm: declare void @wrapped_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) - // x86_64-linux: declare void @wrapped_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) // x86_64-windows: declare void @wrapped_fa8( @@ -311,8 +291,6 @@ extern "C" { // m68k: declare void @transparent_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) - // wasm: declare void @transparent_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) - // x86_64-linux: declare void @transparent_fa8({{.*}}byval([24 x i8]) align 8{{.*}}) // x86_64-windows: declare void @transparent_fa8( @@ -328,8 +306,6 @@ extern "C" { // m68k: declare void @force_align_16({{.*}}byval([80 x i8]) align 16{{.*}}) - // wasm: declare void @force_align_16({{.*}}byval([80 x i8]) align 16{{.*}}) - // x86_64-linux: declare void @force_align_16({{.*}}byval([80 x i8]) align 16{{.*}}) // x86_64-windows: declare void @force_align_16( diff --git a/tests/codegen/enum/enum-early-otherwise-branch.rs b/tests/codegen/enum/enum-early-otherwise-branch.rs index 6c7548912da..07c8aed2624 100644 --- a/tests/codegen/enum/enum-early-otherwise-branch.rs +++ b/tests/codegen/enum/enum-early-otherwise-branch.rs @@ -1,5 +1,4 @@ //@ compile-flags: -O -//@ min-llvm-version: 18 #![crate_type = "lib"] diff --git a/tests/codegen/issue-97217.rs b/tests/codegen/issue-97217.rs index ecf1fa1ddb3..ef9acc5fc93 100644 --- a/tests/codegen/issue-97217.rs +++ b/tests/codegen/issue-97217.rs @@ -1,5 +1,4 @@ //@ compile-flags: -C opt-level=3 -//@ min-llvm-version: 17.0.2 #![crate_type = "lib"] // Regression test for issue 97217 (the following should result in no allocations) diff --git a/tests/codegen/issues/issue-110797-enum-jump-same.rs b/tests/codegen/issues/issue-110797-enum-jump-same.rs index f34b191ac70..f114e0e260e 100644 --- a/tests/codegen/issues/issue-110797-enum-jump-same.rs +++ b/tests/codegen/issues/issue-110797-enum-jump-same.rs @@ -1,5 +1,4 @@ //@ compile-flags: -O -//@ min-llvm-version: 18 #![crate_type = "lib"] diff --git a/tests/codegen/issues/issue-118392.rs b/tests/codegen/issues/issue-118392.rs index 2cbb1f8b204..ce2332b4c3c 100644 --- a/tests/codegen/issues/issue-118392.rs +++ b/tests/codegen/issues/issue-118392.rs @@ -1,5 +1,4 @@ //@ compile-flags: -O -//@ min-llvm-version: 18 #![crate_type = "lib"] // CHECK-LABEL: @div2 diff --git a/tests/codegen/issues/issue-44056-macos-tls-align.rs b/tests/codegen/issues/issue-44056-macos-tls-align.rs deleted file mode 100644 index 972b8490d18..00000000000 --- a/tests/codegen/issues/issue-44056-macos-tls-align.rs +++ /dev/null @@ -1,27 +0,0 @@ -// -//@ only-apple -//@ compile-flags: -O - -#![crate_type = "rlib"] -#![feature(thread_local)] - -// local_unnamed_addr does not appear when std is built with debug assertions. -// CHECK: @STATIC_VAR_1 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> zeroinitializer, section "__DATA,__thread_bss", align 4 -#[no_mangle] -#[thread_local] -static mut STATIC_VAR_1: [u32; 8] = [0; 8]; - -// CHECK: @STATIC_VAR_2 = thread_local {{(local_unnamed_addr )?}}global <{ [32 x i8] }> <{{[^>]*}}>, section "__DATA,__thread_data", align 4 -#[no_mangle] -#[thread_local] -static mut STATIC_VAR_2: [u32; 8] = [4; 8]; - -#[no_mangle] -pub unsafe fn f(x: &mut [u32; 8]) { - std::mem::swap(x, &mut STATIC_VAR_1) -} - -#[no_mangle] -pub unsafe fn g(x: &mut [u32; 8]) { - std::mem::swap(x, &mut STATIC_VAR_2) -} diff --git a/tests/codegen/maybeuninit-rvo.rs b/tests/codegen/maybeuninit-rvo.rs index cc5da39a9ca..db2e33c34bd 100644 --- a/tests/codegen/maybeuninit-rvo.rs +++ b/tests/codegen/maybeuninit-rvo.rs @@ -1,6 +1,5 @@ //@ compile-flags: -O //@ needs-unwind -//@ min-llvm-version: 18 #![feature(c_unwind)] #![crate_type = "lib"] diff --git a/tests/codegen/option-niche-eq.rs b/tests/codegen/option-niche-eq.rs index 4d3a7ce3764..caef0598b4b 100644 --- a/tests/codegen/option-niche-eq.rs +++ b/tests/codegen/option-niche-eq.rs @@ -1,5 +1,4 @@ //@ compile-flags: -O -Zmerge-functions=disabled -//@ min-llvm-version: 18 #![crate_type = "lib"] extern crate core; diff --git a/tests/codegen/repr/transparent-struct-ptr.rs b/tests/codegen/repr/transparent-byval-struct-ptr.rs index 9cffd6c7f73..92ef937d734 100644 --- a/tests/codegen/repr/transparent-struct-ptr.rs +++ b/tests/codegen/repr/transparent-byval-struct-ptr.rs @@ -1,4 +1,4 @@ -//@ revisions: i686-linux i686-freebsd x64-linux x64-apple wasm32 +//@ revisions: i686-linux i686-freebsd x64-linux x64-apple //@ compile-flags: -O -C no-prepopulate-passes //@[i686-linux] compile-flags: --target i686-unknown-linux-gnu @@ -9,13 +9,12 @@ //@[x64-linux] needs-llvm-components: x86 //@[x64-apple] compile-flags: --target x86_64-apple-darwin //@[x64-apple] needs-llvm-components: x86 -//@[wasm32] compile-flags: --target wasm32-wasi -//@[wasm32] needs-llvm-components: webassembly // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR -// Other platforms pass large aggregates using struct pointer in LLVMIR -// This covers the "struct pointer" case. +// Other platforms pass large aggregates using by-value struct pointer in LLVMIR +// Yet more platforms pass large aggregates using opaque pointer in LLVMIR +// This covers the "by-value struct pointer" case. #![feature(no_core, lang_items, transparent_unions)] #![crate_type = "lib"] diff --git a/tests/codegen/repr/transparent-imm-array.rs b/tests/codegen/repr/transparent-imm-array.rs index 1acd4742d35..99828e4e80a 100644 --- a/tests/codegen/repr/transparent-imm-array.rs +++ b/tests/codegen/repr/transparent-imm-array.rs @@ -18,7 +18,8 @@ // See ./transparent.rs // Some platforms pass large aggregates using immediate arrays in LLVMIR -// Other platforms pass large aggregates using struct pointer in LLVMIR +// Other platforms pass large aggregates using by-value struct pointer in LLVMIR +// Yet more platforms pass large aggregates using opaque pointer in LLVMIR // This covers the "immediate array" case. #![feature(no_core, lang_items, transparent_unions)] diff --git a/tests/codegen/repr/transparent-opaque-ptr.rs b/tests/codegen/repr/transparent-opaque-ptr.rs new file mode 100644 index 00000000000..4e7b38bca39 --- /dev/null +++ b/tests/codegen/repr/transparent-opaque-ptr.rs @@ -0,0 +1,113 @@ +//@ revisions: aarch64-linux aarch64-darwin wasm32-wasi +//@ compile-flags: -O -C no-prepopulate-passes + +//@[aarch64-linux] compile-flags: --target aarch64-unknown-linux-gnu +//@[aarch64-linux] needs-llvm-components: aarch64 +//@[aarch64-darwin] compile-flags: --target aarch64-apple-darwin +//@[aarch64-darwin] needs-llvm-components: aarch64 +//@[wasm32-wasi] compile-flags: --target wasm32-wasi +//@[wasm32-wasi] needs-llvm-components: webassembly + +// See ./transparent.rs +// Some platforms pass large aggregates using immediate arrays in LLVMIR +// Other platforms pass large aggregates using by-value struct pointer in LLVMIR +// Yet more platforms pass large aggregates using opaque pointer in LLVMIR +// This covers the "opaque pointer" case. + +#![feature(no_core, lang_items, transparent_unions)] +#![crate_type = "lib"] +#![no_std] +#![no_core] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +impl Copy for [u32; 16] {} +impl Copy for BigS {} +impl Copy for BigU {} + +#[repr(C)] +pub struct BigS([u32; 16]); + +#[repr(transparent)] +pub struct TsBigS(BigS); + +#[repr(transparent)] +pub union TuBigS { + field: BigS, +} + +#[repr(transparent)] +pub enum TeBigS { + Variant(BigS), +} + +// CHECK: define{{.*}}void @test_BigS(ptr [[BIGS_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGS_RET_ATTRS2:.*]], ptr [[BIGS_ARG_ATTRS1:.*]]) +#[no_mangle] +pub extern "C" fn test_BigS(_: BigS) -> BigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TsBigS(_: TsBigS) -> TsBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TuBigS(_: TuBigS) -> TuBigS { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigS(ptr [[BIGS_RET_ATTRS1]] sret([64 x i8]) [[BIGS_RET_ATTRS2]], ptr [[BIGS_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TeBigS(_: TeBigS) -> TeBigS { + loop {} +} + +#[repr(C)] +pub union BigU { + foo: [u32; 16], +} + +#[repr(transparent)] +pub struct TsBigU(BigU); + +#[repr(transparent)] +pub union TuBigU { + field: BigU, +} + +#[repr(transparent)] +pub enum TeBigU { + Variant(BigU), +} + +// CHECK: define{{.*}}void @test_BigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1:.*]]) +#[no_mangle] +pub extern "C" fn test_BigU(_: BigU) -> BigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TsBigU(ptr [[BIGU_RET_ATTRS1:.*]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TsBigU(_: TsBigU) -> TsBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TuBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TuBigU(_: TuBigU) -> TuBigU { + loop {} +} + +// CHECK: define{{.*}}void @test_TeBigU(ptr [[BIGU_RET_ATTRS1]] sret([64 x i8]) [[BIGU_RET_ATTRS2:.*]], ptr [[BIGU_ARG_ATTRS1]]) +#[no_mangle] +pub extern "C" fn test_TeBigU(_: TeBigU) -> TeBigU { + loop {} +} diff --git a/tests/codegen/slice-pointer-nonnull-unwrap.rs b/tests/codegen/slice-pointer-nonnull-unwrap.rs index 2c4a959685f..202edb98c73 100644 --- a/tests/codegen/slice-pointer-nonnull-unwrap.rs +++ b/tests/codegen/slice-pointer-nonnull-unwrap.rs @@ -1,5 +1,4 @@ //@ compile-flags: -O -//@ min-llvm-version: 18 #![crate_type = "lib"] use std::ptr::NonNull; diff --git a/tests/codegen/try_question_mark_nop.rs b/tests/codegen/try_question_mark_nop.rs index 321067d1b90..65167f5c5af 100644 --- a/tests/codegen/try_question_mark_nop.rs +++ b/tests/codegen/try_question_mark_nop.rs @@ -1,7 +1,10 @@ //@ compile-flags: -O -Z merge-functions=disabled --edition=2021 //@ only-x86_64 // FIXME: Remove the `min-llvm-version`. -//@ min-llvm-version: 19 +//@ revisions: NINETEEN TWENTY +//@[NINETEEN] min-llvm-version: 19 +//@[NINETEEN] ignore-llvm-version: 20-99 +//@[TWENTY] min-llvm-version: 20 #![crate_type = "lib"] #![feature(try_blocks)] @@ -9,14 +12,14 @@ use std::ops::ControlFlow::{self, Break, Continue}; use std::ptr::NonNull; -// FIXME: The `trunc` and `select` instructions can be eliminated. // CHECK-LABEL: @option_nop_match_32 #[no_mangle] pub fn option_nop_match_32(x: Option<u32>) -> Option<u32> { // CHECK: start: - // CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1 - // CHECK-NEXT: [[FIRST:%.*]] = select i1 [[TRUNC]], i32 %0 - // CHECK-NEXT: insertvalue { i32, i32 } poison, i32 [[FIRST]] + // NINETEEN-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1 + // NINETEEN-NEXT: [[FIRST:%.*]] = select i1 [[TRUNC]], i32 %0 + // NINETEEN-NEXT: insertvalue { i32, i32 } poison, i32 [[FIRST]], 0 + // TWENTY-NEXT: insertvalue { i32, i32 } poison, i32 %0, 0 // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: ret { i32, i32 } match x { diff --git a/tests/codegen/unwind-landingpad-cold.rs b/tests/codegen/unwind-landingpad-cold.rs index fa200bc300c..fb095e04650 100644 --- a/tests/codegen/unwind-landingpad-cold.rs +++ b/tests/codegen/unwind-landingpad-cold.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Cno-prepopulate-passes //@ needs-unwind -//@ min-llvm-version: 17.0.2 #![crate_type = "lib"] // This test checks that drop calls in unwind landing pads diff --git a/tests/codegen/unwind-landingpad-inline.rs b/tests/codegen/unwind-landingpad-inline.rs index 77ef8d2a5fe..920774b3402 100644 --- a/tests/codegen/unwind-landingpad-inline.rs +++ b/tests/codegen/unwind-landingpad-inline.rs @@ -1,4 +1,3 @@ -//@ min-llvm-version: 17.0.2 //@ compile-flags: -Copt-level=3 #![crate_type = "lib"] diff --git a/tests/coverage/mcdc/condition-limit.cov-map b/tests/coverage/mcdc/condition-limit.cov-map index b565353572a..b4447a33691 100644 --- a/tests/coverage/mcdc/condition-limit.cov-map +++ b/tests/coverage/mcdc/condition-limit.cov-map @@ -1,5 +1,5 @@ Function name: condition_limit::bad -Raw bytes (204): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 11, 01, 15, 01, 03, 09, 20, 05, 02, 03, 08, 00, 09, 05, 00, 0d, 00, 0e, 20, 7a, 1d, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 20, 76, 19, 00, 12, 00, 13, 76, 00, 17, 00, 18, 20, 72, 15, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 20, 6e, 11, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 20, 6a, 0d, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 20, 21, 09, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02] +Raw bytes (204): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 11, 01, 14, 01, 03, 09, 20, 05, 02, 03, 08, 00, 09, 05, 00, 0d, 00, 0e, 20, 7a, 1d, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 20, 76, 19, 00, 12, 00, 13, 76, 00, 17, 00, 18, 20, 72, 15, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 20, 6e, 11, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 20, 6a, 0d, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 20, 21, 09, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 44 @@ -48,7 +48,7 @@ Number of expressions: 44 - expression 42 operands: lhs = Expression(43, Add), rhs = Counter(4) - expression 43 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 17 -- Code(Counter(0)) at (prev + 21, 1) to (start + 3, 9) +- Code(Counter(0)) at (prev + 20, 1) to (start + 3, 9) - Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 3, 8) to (start + 0, 9) true = c1 false = (c0 - c1) @@ -88,7 +88,7 @@ Number of file 0 mappings: 17 = (c8 + ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1))) Function name: condition_limit::good -Raw bytes (180): 0x[01, 01, 20, 01, 05, 05, 19, 05, 19, 52, 15, 05, 19, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 1d, 6f, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 10, 01, 0d, 01, 03, 09, 28, 00, 06, 03, 08, 00, 22, 30, 05, 02, 01, 06, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 52, 19, 06, 05, 00, 00, 0d, 00, 0e, 52, 00, 12, 00, 13, 30, 4e, 15, 05, 04, 00, 00, 12, 00, 13, 4e, 00, 17, 00, 18, 30, 4a, 11, 04, 03, 00, 00, 17, 00, 18, 4a, 00, 1c, 00, 1d, 30, 46, 0d, 03, 02, 00, 00, 1c, 00, 1d, 46, 00, 21, 00, 22, 30, 1d, 09, 02, 00, 00, 00, 21, 00, 22, 1d, 00, 23, 02, 06, 6f, 02, 06, 00, 07, 6b, 01, 01, 00, 02] +Raw bytes (180): 0x[01, 01, 20, 01, 05, 05, 19, 05, 19, 52, 15, 05, 19, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 1d, 6f, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 10, 01, 0c, 01, 03, 09, 28, 00, 06, 03, 08, 00, 22, 30, 05, 02, 01, 06, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 52, 19, 06, 05, 00, 00, 0d, 00, 0e, 52, 00, 12, 00, 13, 30, 4e, 15, 05, 04, 00, 00, 12, 00, 13, 4e, 00, 17, 00, 18, 30, 4a, 11, 04, 03, 00, 00, 17, 00, 18, 4a, 00, 1c, 00, 1d, 30, 46, 0d, 03, 02, 00, 00, 1c, 00, 1d, 46, 00, 21, 00, 22, 30, 1d, 09, 02, 00, 00, 00, 21, 00, 22, 1d, 00, 23, 02, 06, 6f, 02, 06, 00, 07, 6b, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 32 @@ -125,7 +125,7 @@ Number of expressions: 32 - expression 30 operands: lhs = Expression(31, Add), rhs = Counter(4) - expression 31 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 16 -- Code(Counter(0)) at (prev + 13, 1) to (start + 3, 9) +- Code(Counter(0)) at (prev + 12, 1) to (start + 3, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 6 } at (prev + 3, 8) to (start + 0, 34) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 6, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 diff --git a/tests/coverage/mcdc/condition-limit.coverage b/tests/coverage/mcdc/condition-limit.coverage index 81e832d6a49..ae8596bb961 100644 --- a/tests/coverage/mcdc/condition-limit.coverage +++ b/tests/coverage/mcdc/condition-limit.coverage @@ -1,6 +1,5 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| |//@ min-llvm-version: 18 LL| |//@ ignore-llvm-version: 19 - 99 LL| |//@ compile-flags: -Zcoverage-options=mcdc LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/condition-limit.rs b/tests/coverage/mcdc/condition-limit.rs index 2ff46b11a16..0d8546b01cd 100644 --- a/tests/coverage/mcdc/condition-limit.rs +++ b/tests/coverage/mcdc/condition-limit.rs @@ -1,6 +1,5 @@ #![feature(coverage_attribute)] //@ edition: 2021 -//@ min-llvm-version: 18 //@ ignore-llvm-version: 19 - 99 //@ compile-flags: -Zcoverage-options=mcdc //@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/if.cov-map b/tests/coverage/mcdc/if.cov-map index ea8dedb0ac3..9a7d15f700d 100644 --- a/tests/coverage/mcdc/if.cov-map +++ b/tests/coverage/mcdc/if.cov-map @@ -1,5 +1,5 @@ Function name: if::mcdc_check_a -Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 10, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 0f, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -8,7 +8,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 16, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 15, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -24,7 +24,7 @@ Number of file 0 mappings: 8 = (c3 + (c2 + (c0 - c1))) Function name: if::mcdc_check_b -Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 18, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 17, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -33,7 +33,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 24, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 23, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -49,7 +49,7 @@ Number of file 0 mappings: 8 = (c3 + (c2 + (c0 - c1))) Function name: if::mcdc_check_both -Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 20, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 1f, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -58,7 +58,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 32, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 31, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -74,7 +74,7 @@ Number of file 0 mappings: 8 = (c3 + (c2 + (c0 - c1))) Function name: if::mcdc_check_neither -Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 08, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Raw bytes (64): 0x[01, 01, 04, 01, 05, 09, 02, 0d, 0f, 09, 02, 08, 01, 07, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 0e, 0d, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -83,7 +83,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(3), rhs = Expression(3, Add) - expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 8, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 7, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -99,7 +99,7 @@ Number of file 0 mappings: 8 = (c3 + (c2 + (c0 - c1))) Function name: if::mcdc_check_not_tree_decision -Raw bytes (87): 0x[01, 01, 08, 01, 05, 02, 09, 05, 09, 0d, 1e, 02, 09, 11, 1b, 0d, 1e, 02, 09, 0a, 01, 32, 01, 03, 0a, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 03, 00, 09, 00, 0a, 02, 00, 0e, 00, 0f, 30, 09, 1e, 03, 02, 00, 00, 0e, 00, 0f, 0b, 00, 14, 00, 15, 30, 11, 0d, 02, 00, 00, 00, 14, 00, 15, 11, 00, 16, 02, 06, 1b, 02, 0c, 02, 06, 17, 03, 01, 00, 02] +Raw bytes (87): 0x[01, 01, 08, 01, 05, 02, 09, 05, 09, 0d, 1e, 02, 09, 11, 1b, 0d, 1e, 02, 09, 0a, 01, 31, 01, 03, 0a, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 03, 00, 09, 00, 0a, 02, 00, 0e, 00, 0f, 30, 09, 1e, 03, 02, 00, 00, 0e, 00, 0f, 0b, 00, 14, 00, 15, 30, 11, 0d, 02, 00, 00, 00, 14, 00, 15, 11, 00, 16, 02, 06, 1b, 02, 0c, 02, 06, 17, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 8 @@ -112,7 +112,7 @@ Number of expressions: 8 - expression 6 operands: lhs = Counter(3), rhs = Expression(7, Sub) - expression 7 operands: lhs = Expression(0, Sub), rhs = Counter(2) Number of file 0 mappings: 10 -- Code(Counter(0)) at (prev + 50, 1) to (start + 3, 10) +- Code(Counter(0)) at (prev + 49, 1) to (start + 3, 10) - MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 3, 8) to (start + 0, 21) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 3 } at (prev + 0, 9) to (start + 0, 10) true = c1 @@ -134,7 +134,7 @@ Number of file 0 mappings: 10 = (c4 + (c3 + ((c0 - c1) - c2))) Function name: if::mcdc_check_tree_decision -Raw bytes (87): 0x[01, 01, 08, 01, 05, 05, 0d, 05, 0d, 0d, 11, 09, 02, 1b, 1f, 0d, 11, 09, 02, 0a, 01, 28, 01, 03, 09, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0e, 00, 0f, 30, 0d, 0a, 02, 00, 03, 00, 0e, 00, 0f, 0a, 00, 13, 00, 14, 30, 11, 09, 03, 00, 00, 00, 13, 00, 14, 1b, 00, 16, 02, 06, 1f, 02, 0c, 02, 06, 17, 03, 01, 00, 02] +Raw bytes (87): 0x[01, 01, 08, 01, 05, 05, 0d, 05, 0d, 0d, 11, 09, 02, 1b, 1f, 0d, 11, 09, 02, 0a, 01, 27, 01, 03, 09, 28, 00, 03, 03, 08, 00, 15, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 05, 00, 0e, 00, 0f, 30, 0d, 0a, 02, 00, 03, 00, 0e, 00, 0f, 0a, 00, 13, 00, 14, 30, 11, 09, 03, 00, 00, 00, 13, 00, 14, 1b, 00, 16, 02, 06, 1f, 02, 0c, 02, 06, 17, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 8 @@ -147,7 +147,7 @@ Number of expressions: 8 - expression 6 operands: lhs = Counter(3), rhs = Counter(4) - expression 7 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 10 -- Code(Counter(0)) at (prev + 40, 1) to (start + 3, 9) +- Code(Counter(0)) at (prev + 39, 1) to (start + 3, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 3 } at (prev + 3, 8) to (start + 0, 21) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -169,7 +169,7 @@ Number of file 0 mappings: 10 = ((c3 + c4) + (c2 + (c0 - c1))) Function name: if::mcdc_nested_if -Raw bytes (124): 0x[01, 01, 0d, 01, 05, 02, 09, 05, 09, 1b, 15, 05, 09, 1b, 15, 05, 09, 11, 15, 02, 09, 2b, 32, 0d, 2f, 11, 15, 02, 09, 0e, 01, 3c, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 00, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 30, 09, 32, 02, 00, 00, 00, 0d, 00, 0e, 1b, 01, 09, 01, 0d, 28, 01, 02, 01, 0c, 00, 12, 30, 16, 15, 01, 02, 00, 00, 0c, 00, 0d, 16, 00, 11, 00, 12, 30, 0d, 11, 02, 00, 00, 00, 11, 00, 12, 0d, 00, 13, 02, 0a, 2f, 02, 0a, 00, 0b, 32, 01, 0c, 02, 06, 27, 03, 01, 00, 02] +Raw bytes (124): 0x[01, 01, 0d, 01, 05, 02, 09, 05, 09, 1b, 15, 05, 09, 1b, 15, 05, 09, 11, 15, 02, 09, 2b, 32, 0d, 2f, 11, 15, 02, 09, 0e, 01, 3b, 01, 01, 09, 28, 00, 02, 01, 08, 00, 0e, 30, 05, 02, 01, 00, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 30, 09, 32, 02, 00, 00, 00, 0d, 00, 0e, 1b, 01, 09, 01, 0d, 28, 01, 02, 01, 0c, 00, 12, 30, 16, 15, 01, 02, 00, 00, 0c, 00, 0d, 16, 00, 11, 00, 12, 30, 0d, 11, 02, 00, 00, 00, 11, 00, 12, 0d, 00, 13, 02, 0a, 2f, 02, 0a, 00, 0b, 32, 01, 0c, 02, 06, 27, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 13 @@ -187,7 +187,7 @@ Number of expressions: 13 - expression 11 operands: lhs = Counter(4), rhs = Counter(5) - expression 12 operands: lhs = Expression(0, Sub), rhs = Counter(2) Number of file 0 mappings: 14 -- Code(Counter(0)) at (prev + 60, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 59, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 14) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 0, false_next_id: 2 } at (prev + 0, 8) to (start + 0, 9) true = c1 diff --git a/tests/coverage/mcdc/if.coverage b/tests/coverage/mcdc/if.coverage index dc93319950b..d71de28c6f6 100644 --- a/tests/coverage/mcdc/if.coverage +++ b/tests/coverage/mcdc/if.coverage @@ -1,6 +1,5 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| |//@ min-llvm-version: 18 LL| |//@ ignore-llvm-version: 19 - 99 LL| |//@ compile-flags: -Zcoverage-options=mcdc LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/if.rs b/tests/coverage/mcdc/if.rs index 6f589659a3d..17247f5e0c1 100644 --- a/tests/coverage/mcdc/if.rs +++ b/tests/coverage/mcdc/if.rs @@ -1,6 +1,5 @@ #![feature(coverage_attribute)] //@ edition: 2021 -//@ min-llvm-version: 18 //@ ignore-llvm-version: 19 - 99 //@ compile-flags: -Zcoverage-options=mcdc //@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/inlined_expressions.cov-map b/tests/coverage/mcdc/inlined_expressions.cov-map index 8bb488c0dc0..09b7291c964 100644 --- a/tests/coverage/mcdc/inlined_expressions.cov-map +++ b/tests/coverage/mcdc/inlined_expressions.cov-map @@ -1,5 +1,5 @@ Function name: inlined_expressions::inlined_instance -Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 09, 01, 01, 06, 28, 00, 02, 01, 05, 00, 0b, 30, 05, 02, 01, 02, 00, 00, 05, 00, 06, 05, 00, 0a, 00, 0b, 30, 09, 0d, 02, 00, 00, 00, 0a, 00, 0b, 07, 01, 01, 00, 02] +Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 08, 01, 01, 06, 28, 00, 02, 01, 05, 00, 0b, 30, 05, 02, 01, 02, 00, 00, 05, 00, 06, 05, 00, 0a, 00, 0b, 30, 09, 0d, 02, 00, 00, 00, 0a, 00, 0b, 07, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 @@ -7,7 +7,7 @@ Number of expressions: 3 - expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) - expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 6 -- Code(Counter(0)) at (prev + 9, 1) to (start + 1, 6) +- Code(Counter(0)) at (prev + 8, 1) to (start + 1, 6) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 5) to (start + 0, 11) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 5) to (start + 0, 6) true = c1 diff --git a/tests/coverage/mcdc/inlined_expressions.coverage b/tests/coverage/mcdc/inlined_expressions.coverage index 4e4800310c9..af0b78477d4 100644 --- a/tests/coverage/mcdc/inlined_expressions.coverage +++ b/tests/coverage/mcdc/inlined_expressions.coverage @@ -1,6 +1,5 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| |//@ min-llvm-version: 18 LL| |//@ ignore-llvm-version: 19 - 99 LL| |//@ compile-flags: -Zcoverage-options=mcdc -Copt-level=z -Cllvm-args=--inline-threshold=0 LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/inlined_expressions.rs b/tests/coverage/mcdc/inlined_expressions.rs index fc1e4dae37c..5c1fde6681a 100644 --- a/tests/coverage/mcdc/inlined_expressions.rs +++ b/tests/coverage/mcdc/inlined_expressions.rs @@ -1,6 +1,5 @@ #![feature(coverage_attribute)] //@ edition: 2021 -//@ min-llvm-version: 18 //@ ignore-llvm-version: 19 - 99 //@ compile-flags: -Zcoverage-options=mcdc -Copt-level=z -Cllvm-args=--inline-threshold=0 //@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/nested_if.cov-map b/tests/coverage/mcdc/nested_if.cov-map index 0bd2aef814c..adeb6cbc1fb 100644 --- a/tests/coverage/mcdc/nested_if.cov-map +++ b/tests/coverage/mcdc/nested_if.cov-map @@ -1,5 +1,5 @@ Function name: nested_if::doubly_nested_if_in_condition -Raw bytes (168): 0x[01, 01, 0e, 01, 05, 05, 11, 05, 11, 26, 19, 05, 11, 19, 1d, 19, 1d, 1d, 22, 26, 19, 05, 11, 11, 15, 09, 02, 0d, 37, 09, 02, 14, 01, 10, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4e, 05, 00, 10, 00, 11, 28, 01, 02, 00, 10, 00, 36, 30, 11, 26, 01, 00, 02, 00, 10, 00, 11, 30, 15, 21, 02, 00, 00, 00, 15, 00, 36, 26, 00, 18, 00, 19, 28, 00, 02, 00, 18, 00, 1e, 30, 19, 22, 01, 02, 00, 00, 18, 00, 19, 19, 00, 1d, 00, 1e, 30, 1a, 1d, 02, 00, 00, 00, 1d, 00, 1e, 1a, 00, 21, 00, 25, 1f, 00, 2f, 00, 34, 2b, 00, 39, 00, 3e, 21, 00, 48, 00, 4c, 0d, 00, 4f, 02, 06, 37, 02, 0c, 02, 06, 33, 03, 01, 00, 02] +Raw bytes (168): 0x[01, 01, 0e, 01, 05, 05, 11, 05, 11, 26, 19, 05, 11, 19, 1d, 19, 1d, 1d, 22, 26, 19, 05, 11, 11, 15, 09, 02, 0d, 37, 09, 02, 14, 01, 0f, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4e, 05, 00, 10, 00, 11, 28, 01, 02, 00, 10, 00, 36, 30, 11, 26, 01, 00, 02, 00, 10, 00, 11, 30, 15, 21, 02, 00, 00, 00, 15, 00, 36, 26, 00, 18, 00, 19, 28, 00, 02, 00, 18, 00, 1e, 30, 19, 22, 01, 02, 00, 00, 18, 00, 19, 19, 00, 1d, 00, 1e, 30, 1a, 1d, 02, 00, 00, 00, 1d, 00, 1e, 1a, 00, 21, 00, 25, 1f, 00, 2f, 00, 34, 2b, 00, 39, 00, 3e, 21, 00, 48, 00, 4c, 0d, 00, 4f, 02, 06, 37, 02, 0c, 02, 06, 33, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 14 @@ -18,7 +18,7 @@ Number of expressions: 14 - expression 12 operands: lhs = Counter(3), rhs = Expression(13, Add) - expression 13 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 20 -- Code(Counter(0)) at (prev + 16, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 15, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 78) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -58,7 +58,7 @@ Number of file 0 mappings: 20 = (c3 + (c2 + (c0 - c1))) Function name: nested_if::nested_if_in_condition -Raw bytes (120): 0x[01, 01, 0b, 01, 05, 05, 11, 05, 11, 1e, 15, 05, 11, 11, 15, 1e, 15, 05, 11, 09, 02, 0d, 2b, 09, 02, 0e, 01, 08, 01, 01, 09, 28, 01, 02, 01, 08, 00, 2e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 2e, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 1e, 01, 00, 02, 00, 10, 00, 11, 1e, 00, 15, 00, 16, 30, 15, 1a, 02, 00, 00, 00, 15, 00, 16, 17, 00, 19, 00, 1d, 1a, 00, 27, 00, 2c, 0d, 00, 2f, 02, 06, 2b, 02, 0c, 02, 06, 27, 03, 01, 00, 02] +Raw bytes (120): 0x[01, 01, 0b, 01, 05, 05, 11, 05, 11, 1e, 15, 05, 11, 11, 15, 1e, 15, 05, 11, 09, 02, 0d, 2b, 09, 02, 0e, 01, 07, 01, 01, 09, 28, 01, 02, 01, 08, 00, 2e, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 2e, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 1e, 01, 00, 02, 00, 10, 00, 11, 1e, 00, 15, 00, 16, 30, 15, 1a, 02, 00, 00, 00, 15, 00, 16, 17, 00, 19, 00, 1d, 1a, 00, 27, 00, 2c, 0d, 00, 2f, 02, 06, 2b, 02, 0c, 02, 06, 27, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 11 @@ -74,7 +74,7 @@ Number of expressions: 11 - expression 9 operands: lhs = Counter(3), rhs = Expression(10, Add) - expression 10 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 14 -- Code(Counter(0)) at (prev + 8, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 7, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 1, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 46) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -103,7 +103,7 @@ Number of file 0 mappings: 14 = (c3 + (c2 + (c0 - c1))) Function name: nested_if::nested_in_then_block_in_condition -Raw bytes (176): 0x[01, 01, 12, 01, 05, 05, 11, 05, 11, 3a, 15, 05, 11, 11, 15, 33, 19, 11, 15, 19, 1d, 19, 1d, 1d, 2e, 33, 19, 11, 15, 3a, 15, 05, 11, 09, 02, 0d, 47, 09, 02, 14, 01, 23, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4b, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4b, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 3a, 01, 00, 02, 00, 10, 00, 11, 3a, 00, 15, 00, 16, 30, 15, 36, 02, 00, 00, 00, 15, 00, 16, 33, 00, 1c, 00, 1d, 28, 01, 02, 00, 1c, 00, 22, 30, 19, 2e, 01, 02, 00, 00, 1c, 00, 1d, 19, 00, 21, 00, 22, 30, 26, 1d, 02, 00, 00, 00, 21, 00, 22, 26, 00, 25, 00, 29, 2b, 00, 33, 00, 38, 36, 00, 44, 00, 49, 0d, 00, 4c, 02, 06, 47, 02, 0c, 02, 06, 43, 03, 01, 00, 02] +Raw bytes (176): 0x[01, 01, 12, 01, 05, 05, 11, 05, 11, 3a, 15, 05, 11, 11, 15, 33, 19, 11, 15, 19, 1d, 19, 1d, 1d, 2e, 33, 19, 11, 15, 3a, 15, 05, 11, 09, 02, 0d, 47, 09, 02, 14, 01, 22, 01, 01, 09, 28, 02, 02, 01, 08, 00, 4b, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 4b, 05, 00, 10, 00, 11, 28, 00, 02, 00, 10, 00, 16, 30, 11, 3a, 01, 00, 02, 00, 10, 00, 11, 3a, 00, 15, 00, 16, 30, 15, 36, 02, 00, 00, 00, 15, 00, 16, 33, 00, 1c, 00, 1d, 28, 01, 02, 00, 1c, 00, 22, 30, 19, 2e, 01, 02, 00, 00, 1c, 00, 1d, 19, 00, 21, 00, 22, 30, 26, 1d, 02, 00, 00, 00, 21, 00, 22, 26, 00, 25, 00, 29, 2b, 00, 33, 00, 38, 36, 00, 44, 00, 49, 0d, 00, 4c, 02, 06, 47, 02, 0c, 02, 06, 43, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 18 @@ -126,7 +126,7 @@ Number of expressions: 18 - expression 16 operands: lhs = Counter(3), rhs = Expression(17, Add) - expression 17 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 20 -- Code(Counter(0)) at (prev + 35, 1) to (start + 1, 9) +- Code(Counter(0)) at (prev + 34, 1) to (start + 1, 9) - MCDCDecision { bitmap_idx: 2, conditions_num: 2 } at (prev + 1, 8) to (start + 0, 75) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 @@ -167,7 +167,7 @@ Number of file 0 mappings: 20 = (c3 + (c2 + (c0 - c1))) Function name: nested_if::nested_single_condition_decision -Raw bytes (85): 0x[01, 01, 06, 01, 05, 05, 11, 05, 11, 09, 02, 0d, 17, 09, 02, 0b, 01, 18, 01, 04, 09, 28, 00, 02, 04, 08, 00, 29, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 29, 05, 00, 10, 00, 11, 20, 11, 0a, 00, 10, 00, 11, 11, 00, 14, 00, 19, 0a, 00, 23, 00, 27, 0d, 00, 2a, 02, 06, 17, 02, 0c, 02, 06, 13, 03, 01, 00, 02] +Raw bytes (85): 0x[01, 01, 06, 01, 05, 05, 11, 05, 11, 09, 02, 0d, 17, 09, 02, 0b, 01, 17, 01, 04, 09, 28, 00, 02, 04, 08, 00, 29, 30, 05, 02, 01, 02, 00, 00, 08, 00, 09, 30, 0d, 09, 02, 00, 00, 00, 0d, 00, 29, 05, 00, 10, 00, 11, 20, 11, 0a, 00, 10, 00, 11, 11, 00, 14, 00, 19, 0a, 00, 23, 00, 27, 0d, 00, 2a, 02, 06, 17, 02, 0c, 02, 06, 13, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 6 @@ -178,7 +178,7 @@ Number of expressions: 6 - expression 4 operands: lhs = Counter(3), rhs = Expression(5, Add) - expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) Number of file 0 mappings: 11 -- Code(Counter(0)) at (prev + 24, 1) to (start + 4, 9) +- Code(Counter(0)) at (prev + 23, 1) to (start + 4, 9) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 4, 8) to (start + 0, 41) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9) true = c1 diff --git a/tests/coverage/mcdc/nested_if.coverage b/tests/coverage/mcdc/nested_if.coverage index 916bb94745d..37aa33d5c57 100644 --- a/tests/coverage/mcdc/nested_if.coverage +++ b/tests/coverage/mcdc/nested_if.coverage @@ -1,6 +1,5 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| |//@ min-llvm-version: 18 LL| |//@ ignore-llvm-version: 19 - 99 LL| |//@ compile-flags: -Zcoverage-options=mcdc LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/nested_if.rs b/tests/coverage/mcdc/nested_if.rs index f9ce7a0bc25..1443ccc23ab 100644 --- a/tests/coverage/mcdc/nested_if.rs +++ b/tests/coverage/mcdc/nested_if.rs @@ -1,6 +1,5 @@ #![feature(coverage_attribute)] //@ edition: 2021 -//@ min-llvm-version: 18 //@ ignore-llvm-version: 19 - 99 //@ compile-flags: -Zcoverage-options=mcdc //@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/non_control_flow.cov-map b/tests/coverage/mcdc/non_control_flow.cov-map index 0c6928b684d..f8576831e75 100644 --- a/tests/coverage/mcdc/non_control_flow.cov-map +++ b/tests/coverage/mcdc/non_control_flow.cov-map @@ -1,5 +1,5 @@ Function name: non_control_flow::assign_3 -Raw bytes (89): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 0a, 01, 17, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 22, 01, 00, 02, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 30, 1e, 11, 02, 03, 00, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 30, 09, 0d, 03, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Raw bytes (89): 0x[01, 01, 09, 05, 07, 0b, 11, 09, 0d, 01, 05, 01, 05, 22, 11, 01, 05, 22, 11, 01, 05, 0a, 01, 16, 01, 00, 28, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 22, 01, 00, 02, 00, 0d, 00, 0e, 22, 00, 12, 00, 13, 30, 1e, 11, 02, 03, 00, 00, 12, 00, 13, 1e, 00, 17, 00, 18, 30, 09, 0d, 03, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 9 @@ -13,7 +13,7 @@ Number of expressions: 9 - expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(4) - expression 8 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 10 -- Code(Counter(0)) at (prev + 23, 1) to (start + 0, 40) +- Code(Counter(0)) at (prev + 22, 1) to (start + 0, 40) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = (c1 + ((c2 + c3) + c4)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) @@ -35,7 +35,7 @@ Number of file 0 mappings: 10 = (c1 + ((c2 + c3) + c4)) Function name: non_control_flow::assign_3_bis -Raw bytes (85): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1c, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 1a, 01, 03, 02, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 03, 00, 02, 00, 12, 00, 13, 13, 00, 17, 00, 18, 30, 0d, 11, 02, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] +Raw bytes (85): 0x[01, 01, 07, 07, 11, 09, 0d, 01, 05, 05, 09, 16, 1a, 05, 09, 01, 05, 0a, 01, 1b, 01, 00, 2c, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 03, 00, 0d, 00, 18, 30, 05, 1a, 01, 03, 02, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 16, 03, 00, 02, 00, 12, 00, 13, 13, 00, 17, 00, 18, 30, 0d, 11, 02, 00, 00, 00, 17, 00, 18, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 7 @@ -47,7 +47,7 @@ Number of expressions: 7 - expression 5 operands: lhs = Counter(1), rhs = Counter(2) - expression 6 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 10 -- Code(Counter(0)) at (prev + 28, 1) to (start + 0, 44) +- Code(Counter(0)) at (prev + 27, 1) to (start + 0, 44) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c2 + c3) + c4) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) @@ -68,7 +68,7 @@ Number of file 0 mappings: 10 = ((c2 + c3) + c4) Function name: non_control_flow::assign_and -Raw bytes (64): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 08, 01, 0d, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (64): 0x[01, 01, 04, 07, 0e, 09, 0d, 01, 05, 01, 05, 08, 01, 0c, 01, 00, 21, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -77,7 +77,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(0), rhs = Counter(1) - expression 3 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 13, 1) to (start + 0, 33) +- Code(Counter(0)) at (prev + 12, 1) to (start + 0, 33) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c2 + c3) + (c0 - c1)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) @@ -93,7 +93,7 @@ Number of file 0 mappings: 8 = ((c2 + c3) + (c0 - c1)) Function name: non_control_flow::assign_or -Raw bytes (64): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 08, 01, 12, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 00, 02, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] +Raw bytes (64): 0x[01, 01, 04, 07, 0d, 05, 09, 01, 05, 01, 05, 08, 01, 11, 01, 00, 20, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 02, 00, 0d, 00, 13, 30, 05, 0e, 01, 00, 02, 00, 0d, 00, 0e, 0e, 00, 12, 00, 13, 30, 09, 0d, 02, 00, 00, 00, 12, 00, 13, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -102,7 +102,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Counter(0), rhs = Counter(1) - expression 3 operands: lhs = Counter(0), rhs = Counter(1) Number of file 0 mappings: 8 -- Code(Counter(0)) at (prev + 18, 1) to (start + 0, 32) +- Code(Counter(0)) at (prev + 17, 1) to (start + 0, 32) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = ((c1 + c2) + c3) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) @@ -119,15 +119,15 @@ Number of file 0 mappings: 8 = ((c1 + c2) + c3) Function name: non_control_flow::foo -Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 01, 02, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 25, 01, 02, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 38, 1) to (start + 2, 2) +- Code(Counter(0)) at (prev + 37, 1) to (start + 2, 2) Function name: non_control_flow::func_call -Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 2a, 01, 01, 0a, 28, 00, 02, 01, 09, 00, 0f, 30, 05, 02, 01, 02, 00, 00, 09, 00, 0a, 05, 00, 0e, 00, 0f, 30, 09, 0d, 02, 00, 00, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] +Raw bytes (52): 0x[01, 01, 03, 01, 05, 0b, 02, 09, 0d, 06, 01, 29, 01, 01, 0a, 28, 00, 02, 01, 09, 00, 0f, 30, 05, 02, 01, 02, 00, 00, 09, 00, 0a, 05, 00, 0e, 00, 0f, 30, 09, 0d, 02, 00, 00, 00, 0e, 00, 0f, 07, 01, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 3 @@ -135,7 +135,7 @@ Number of expressions: 3 - expression 1 operands: lhs = Expression(2, Add), rhs = Expression(0, Sub) - expression 2 operands: lhs = Counter(2), rhs = Counter(3) Number of file 0 mappings: 6 -- Code(Counter(0)) at (prev + 42, 1) to (start + 1, 10) +- Code(Counter(0)) at (prev + 41, 1) to (start + 1, 10) - MCDCDecision { bitmap_idx: 0, conditions_num: 2 } at (prev + 1, 9) to (start + 0, 15) - MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 2, false_next_id: 0 } at (prev + 0, 9) to (start + 0, 10) true = c1 @@ -148,7 +148,7 @@ Number of file 0 mappings: 6 = ((c2 + c3) + (c0 - c1)) Function name: non_control_flow::right_comb_tree -Raw bytes (139): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0e, 01, 21, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 05, 00, 0d, 00, 2a, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 30, 4a, 19, 02, 03, 00, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 30, 46, 15, 03, 04, 00, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 30, 42, 11, 04, 05, 00, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 30, 09, 0d, 05, 00, 00, 00, 24, 00, 27, 03, 01, 05, 01, 02] +Raw bytes (139): 0x[01, 01, 13, 07, 1a, 0b, 19, 0f, 15, 13, 11, 09, 0d, 01, 05, 01, 05, 05, 19, 05, 19, 4a, 15, 05, 19, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 46, 11, 4a, 15, 05, 19, 0e, 01, 20, 01, 00, 41, 03, 01, 09, 00, 0a, 01, 00, 0d, 00, 0e, 28, 00, 05, 00, 0d, 00, 2a, 30, 05, 1a, 01, 02, 00, 00, 0d, 00, 0e, 05, 00, 13, 00, 14, 30, 4a, 19, 02, 03, 00, 00, 13, 00, 14, 4a, 00, 19, 00, 1a, 30, 46, 15, 03, 04, 00, 00, 19, 00, 1a, 46, 00, 1f, 00, 20, 30, 42, 11, 04, 05, 00, 00, 1f, 00, 20, 42, 00, 24, 00, 27, 30, 09, 0d, 05, 00, 00, 00, 24, 00, 27, 03, 01, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 19 @@ -172,7 +172,7 @@ Number of expressions: 19 - expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5) - expression 18 operands: lhs = Counter(1), rhs = Counter(6) Number of file 0 mappings: 14 -- Code(Counter(0)) at (prev + 33, 1) to (start + 0, 65) +- Code(Counter(0)) at (prev + 32, 1) to (start + 0, 65) - Code(Expression(0, Add)) at (prev + 1, 9) to (start + 0, 10) = (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)) - Code(Counter(0)) at (prev + 0, 13) to (start + 0, 14) diff --git a/tests/coverage/mcdc/non_control_flow.coverage b/tests/coverage/mcdc/non_control_flow.coverage index c64f61a153c..74c19cf12df 100644 --- a/tests/coverage/mcdc/non_control_flow.coverage +++ b/tests/coverage/mcdc/non_control_flow.coverage @@ -1,6 +1,5 @@ LL| |#![feature(coverage_attribute)] LL| |//@ edition: 2021 - LL| |//@ min-llvm-version: 18 LL| |//@ ignore-llvm-version: 19 - 99 LL| |//@ compile-flags: -Zcoverage-options=mcdc LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/coverage/mcdc/non_control_flow.rs b/tests/coverage/mcdc/non_control_flow.rs index 633d381a1aa..e0145ed8268 100644 --- a/tests/coverage/mcdc/non_control_flow.rs +++ b/tests/coverage/mcdc/non_control_flow.rs @@ -1,6 +1,5 @@ #![feature(coverage_attribute)] //@ edition: 2021 -//@ min-llvm-version: 18 //@ ignore-llvm-version: 19 - 99 //@ compile-flags: -Zcoverage-options=mcdc //@ llvm-cov-flags: --show-branches=count --show-mcdc diff --git a/tests/crashes/120016.rs b/tests/crashes/120016.rs new file mode 100644 index 00000000000..09175689256 --- /dev/null +++ b/tests/crashes/120016.rs @@ -0,0 +1,19 @@ +//@ known-bug: #120016 +//@ compile-flags: -Zcrate-attr=feature(const_async_blocks) --edition=2021 + +#![feature(type_alias_impl_trait, const_async_blocks)] + +struct Bug { + V1: [(); { + type F = impl std::future::Future<Output = impl Sized>; + fn concrete_use() -> F { + //~^ ERROR to be a future that resolves to `u8`, but it resolves to `()` + async {} + } + let f: F = async { 1 }; + //~^ ERROR `async` blocks are not allowed in constants + 1 + }], +} + +fn main() {} diff --git a/tests/crashes/127804.rs b/tests/crashes/127804.rs new file mode 100644 index 00000000000..e583a7c1fc6 --- /dev/null +++ b/tests/crashes/127804.rs @@ -0,0 +1,12 @@ +//@ known-bug: #127804 + +struct Thing; + +pub trait Every { + type Assoc; +} +impl<T: ?Sized> Every for Thing { + type Assoc = T; +} + +fn foo(_: <Thing as Every>::Assoc) {} diff --git a/tests/crashes/128119.rs b/tests/crashes/128119.rs new file mode 100644 index 00000000000..7677b15a2f3 --- /dev/null +++ b/tests/crashes/128119.rs @@ -0,0 +1,15 @@ +//@ known-bug: #128119 + +trait Trait { + reuse to_reuse::foo { self } +} + +struct S; + +mod to_reuse { + pub fn foo(&self) -> u32 {} +} + +impl Trait S { + reuse to_reuse::foo { self } +} diff --git a/tests/crashes/128232.rs b/tests/crashes/128232.rs new file mode 100644 index 00000000000..67f61e1b240 --- /dev/null +++ b/tests/crashes/128232.rs @@ -0,0 +1,15 @@ +//@ known-bug: #128232 + +#![feature(generic_const_exprs, unsized_const_params)] + +fn function() {} + +struct Wrapper<const F: fn()>; + +impl Wrapper<{ bar() }> { + fn call() {} +} + +fn main() { + Wrapper::<function>::call; +} diff --git a/tests/crashes/130310.rs b/tests/crashes/130310.rs deleted file mode 100644 index d59dd39983c..00000000000 --- a/tests/crashes/130310.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@ known-bug: rust-lang/rust#130310 - -use std::marker::PhantomData; - -#[repr(C)] -struct A<T> { - a: *const A<A<T>>, - p: PhantomData<T>, -} - -extern "C" { - fn f(a: *const A<()>); -} - -fn main() {} diff --git a/tests/crashes/130411.rs b/tests/crashes/130411.rs new file mode 100644 index 00000000000..b733dcb30d5 --- /dev/null +++ b/tests/crashes/130411.rs @@ -0,0 +1,6 @@ +//@ known-bug: #130411 +trait Project { + const SELF: Self; +} + +fn take1(_: Project<SELF = {}>) {} diff --git a/tests/crashes/130413.rs b/tests/crashes/130413.rs new file mode 100644 index 00000000000..08435ac6450 --- /dev/null +++ b/tests/crashes/130413.rs @@ -0,0 +1,17 @@ +//@ known-bug: #130413 + +#![feature(transmutability)] +trait Aaa { + type Y; +} + +trait Bbb { + type B: std::mem::TransmuteFrom<()>; +} + +impl<T> Bbb for T +where + T: Aaa, +{ + type B = T::Y; +} diff --git a/tests/crashes/130425.rs b/tests/crashes/130425.rs new file mode 100644 index 00000000000..559b86f7bc2 --- /dev/null +++ b/tests/crashes/130425.rs @@ -0,0 +1,13 @@ +//@ known-bug: #130425 +//@ compile-flags: -Zmir-opt-level=5 -Zpolymorphize=on + +struct S<T>(T) +where + [T; ( + |_: u8| { + static FOO: Sync = AtomicUsize::new(0); + unsafe { &*(&FOO as *const _ as *const usize) } + }, + 1, + ) + .1]: Copy; diff --git a/tests/mir-opt/inline/cycle.main.Inline.panic-abort.diff b/tests/mir-opt/inline/cycle.main.Inline.panic-abort.diff index fd1f698c60d..6522ed6bffd 100644 --- a/tests/mir-opt/inline/cycle.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/cycle.main.Inline.panic-abort.diff @@ -4,35 +4,16 @@ fn main() -> () { let mut _0: (); let _1: (); -+ let mut _2: fn() {g}; -+ scope 1 (inlined f::<fn() {g}>) { -+ debug g => _2; -+ let mut _3: &fn() {g}; -+ let _4: (); -+ } bb0: { StorageLive(_1); -- _1 = f::<fn() {g}>(g) -> [return: bb1, unwind unreachable]; -+ StorageLive(_2); -+ _2 = g; -+ StorageLive(_4); -+ StorageLive(_3); -+ _3 = &_2; -+ _4 = <fn() {g} as Fn<()>>::call(move _3, const ()) -> [return: bb2, unwind unreachable]; + _1 = f::<fn() {g}>(g) -> [return: bb1, unwind unreachable]; } bb1: { -+ StorageDead(_4); -+ StorageDead(_2); StorageDead(_1); _0 = const (); return; -+ } -+ -+ bb2: { -+ StorageDead(_3); -+ drop(_2) -> [return: bb1, unwind unreachable]; } } diff --git a/tests/mir-opt/inline/cycle.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/cycle.main.Inline.panic-unwind.diff index e8299db47db..7a25830a4d0 100644 --- a/tests/mir-opt/inline/cycle.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/cycle.main.Inline.panic-unwind.diff @@ -4,43 +4,16 @@ fn main() -> () { let mut _0: (); let _1: (); -+ let mut _2: fn() {g}; -+ scope 1 (inlined f::<fn() {g}>) { -+ debug g => _2; -+ let mut _3: &fn() {g}; -+ let _4: (); -+ } bb0: { StorageLive(_1); -- _1 = f::<fn() {g}>(g) -> [return: bb1, unwind continue]; -+ StorageLive(_2); -+ _2 = g; -+ StorageLive(_4); -+ StorageLive(_3); -+ _3 = &_2; -+ _4 = <fn() {g} as Fn<()>>::call(move _3, const ()) -> [return: bb2, unwind: bb3]; + _1 = f::<fn() {g}>(g) -> [return: bb1, unwind continue]; } bb1: { -+ StorageDead(_4); -+ StorageDead(_2); StorageDead(_1); _0 = const (); return; -+ } -+ -+ bb2: { -+ StorageDead(_3); -+ drop(_2) -> [return: bb1, unwind continue]; -+ } -+ -+ bb3 (cleanup): { -+ drop(_2) -> [return: bb4, unwind terminate(cleanup)]; -+ } -+ -+ bb4 (cleanup): { -+ resume; } } diff --git a/tests/mir-opt/inline/cycle.rs b/tests/mir-opt/inline/cycle.rs index cb50638473f..383d0796a88 100644 --- a/tests/mir-opt/inline/cycle.rs +++ b/tests/mir-opt/inline/cycle.rs @@ -19,9 +19,5 @@ fn g() { // EMIT_MIR cycle.main.Inline.diff fn main() { - // CHECK-LABEL: fn main( - // CHECK-NOT: inlined - // CHECK: (inlined f::<fn() {g}>) - // CHECK-NOT: inlined f(g); } diff --git a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-abort.diff b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-abort.diff index 142b9c56598..d437dbf5763 100644 --- a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-abort.diff @@ -7,10 +7,6 @@ + scope 1 (inlined <C as Call>::call) { + scope 2 (inlined <B<A> as Call>::call) { + scope 3 (inlined <A as Call>::call) { -+ scope 4 (inlined <B<C> as Call>::call) { -+ scope 5 (inlined <C as Call>::call) { -+ } -+ } + } + } + } @@ -18,7 +14,7 @@ bb0: { StorageLive(_1); - _1 = <C as Call>::call() -> [return: bb1, unwind unreachable]; -+ _1 = <B<A> as Call>::call() -> [return: bb1, unwind unreachable]; ++ _1 = <B<C> as Call>::call() -> [return: bb1, unwind unreachable]; } bb1: { diff --git a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-unwind.diff b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-unwind.diff index 193ada05f02..8314526ee04 100644 --- a/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/inline_cycle_generic.main.Inline.panic-unwind.diff @@ -7,10 +7,6 @@ + scope 1 (inlined <C as Call>::call) { + scope 2 (inlined <B<A> as Call>::call) { + scope 3 (inlined <A as Call>::call) { -+ scope 4 (inlined <B<C> as Call>::call) { -+ scope 5 (inlined <C as Call>::call) { -+ } -+ } + } + } + } @@ -18,7 +14,7 @@ bb0: { StorageLive(_1); - _1 = <C as Call>::call() -> [return: bb1, unwind continue]; -+ _1 = <B<A> as Call>::call() -> [return: bb1, unwind continue]; ++ _1 = <B<C> as Call>::call() -> [return: bb1, unwind continue]; } bb1: { diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs index 42ed07d9daf..3b05fe2055c 100644 --- a/tests/run-make/compiler-builtins/rmake.rs +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -33,8 +33,8 @@ fn main() { let path = env_var("PATH"); let rustc = env_var("RUSTC"); - let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); - let mut cmd = cmd(bootstrap_cargo); + let cargo = env_var("CARGO"); + let mut cmd = cmd(cargo); cmd.args(&[ "build", "--manifest-path", diff --git a/tests/run-make/embed-source-dwarf/rmake.rs b/tests/run-make/embed-source-dwarf/rmake.rs index 06d550121b0..c7106967a85 100644 --- a/tests/run-make/embed-source-dwarf/rmake.rs +++ b/tests/run-make/embed-source-dwarf/rmake.rs @@ -1,11 +1,6 @@ //@ ignore-windows //@ ignore-apple -// LLVM 17's embed-source implementation requires that source code is attached -// for all files in the output DWARF debug info. This restriction was lifted in -// LLVM 18 (87e22bdd2bd6d77d782f9d64b3e3ae5bdcd5080d). -//@ min-llvm-version: 18 - // This test should be replaced with one in tests/debuginfo once we can easily // tell via GDB or LLDB if debuginfo contains source code. Cheap tricks in LLDB // like setting an invalid source map path don't appear to work, maybe this'll diff --git a/tests/run-make/invalid-symlink-search-path/rmake.rs b/tests/run-make/invalid-symlink-search-path/rmake.rs index ed2cd9c4bd2..7b7e7c79442 100644 --- a/tests/run-make/invalid-symlink-search-path/rmake.rs +++ b/tests/run-make/invalid-symlink-search-path/rmake.rs @@ -1,13 +1,14 @@ -// In this test, the symlink created is invalid (valid relative to the root, but not -// relatively to where it is located), and used to cause an internal -// compiler error (ICE) when passed as a library search path. This was fixed in #26044, -// and this test checks that the invalid symlink is instead simply ignored. +// In this test, the symlink created is invalid (valid relative to the root, but not relatively to +// where it is located), and used to cause an internal compiler error (ICE) when passed as a library +// search path. This was fixed in #26044, and this test checks that the invalid symlink is instead +// simply ignored. +// // See https://github.com/rust-lang/rust/issues/26006 //@ needs-symlink //Reason: symlink requires elevated permission in Windows -use run_make_support::{rfs, rustc}; +use run_make_support::{path, rfs, rustc}; fn main() { // We create two libs: `bar` which depends on `foo`. We need to compile `foo` first. @@ -20,9 +21,9 @@ fn main() { .metadata("foo") .output("out/foo/libfoo.rlib") .run(); - rfs::create_dir("out/bar"); - rfs::create_dir("out/bar/deps"); - rfs::create_symlink("out/foo/libfoo.rlib", "out/bar/deps/libfoo.rlib"); + rfs::create_dir_all("out/bar/deps"); + rfs::symlink_file(path("out/foo/libfoo.rlib"), path("out/bar/deps/libfoo.rlib")); + // Check that the invalid symlink does not cause an ICE rustc() .input("in/bar/lib.rs") diff --git a/tests/run-make/symlinked-extern/rmake.rs b/tests/run-make/symlinked-extern/rmake.rs index 7a4a8ce18cb..f35d5a13f5a 100644 --- a/tests/run-make/symlinked-extern/rmake.rs +++ b/tests/run-make/symlinked-extern/rmake.rs @@ -1,22 +1,22 @@ -// Crates that are resolved normally have their path canonicalized and all -// symlinks resolved. This did not happen for paths specified -// using the --extern option to rustc, which could lead to rustc thinking -// that it encountered two different versions of a crate, when it's -// actually the same version found through different paths. -// See https://github.com/rust-lang/rust/pull/16505 - -// This test checks that --extern and symlinks together -// can result in successful compilation. +// Crates that are resolved normally have their path canonicalized and all symlinks resolved. This +// did not happen for paths specified using the `--extern` option to rustc, which could lead to +// rustc thinking that it encountered two different versions of a crate, when it's actually the same +// version found through different paths. +// +// This test checks that `--extern` and symlinks together can result in successful compilation. +// +// See <https://github.com/rust-lang/rust/pull/16505>. //@ ignore-cross-compile //@ needs-symlink -use run_make_support::{cwd, rfs, rustc}; +use run_make_support::{cwd, path, rfs, rustc}; fn main() { rustc().input("foo.rs").run(); rfs::create_dir_all("other"); - rfs::create_symlink("libfoo.rlib", "other"); + rfs::symlink_file(path("libfoo.rlib"), path("other").join("libfoo.rlib")); + rustc().input("bar.rs").library_search_path(cwd()).run(); - rustc().input("baz.rs").extern_("foo", "other").library_search_path(cwd()).run(); + rustc().input("baz.rs").extern_("foo", "other/libfoo.rlib").library_search_path(cwd()).run(); } diff --git a/tests/run-make/symlinked-libraries/rmake.rs b/tests/run-make/symlinked-libraries/rmake.rs index e6449b61a1c..98f156fed8f 100644 --- a/tests/run-make/symlinked-libraries/rmake.rs +++ b/tests/run-make/symlinked-libraries/rmake.rs @@ -1,18 +1,15 @@ -// When a directory and a symlink simultaneously exist with the same name, -// setting that name as the library search path should not cause rustc -// to avoid looking in the symlink and cause an error. This test creates -// a directory and a symlink named "other", and places the library in the symlink. -// If it succeeds, the library was successfully found. -// See https://github.com/rust-lang/rust/issues/12459 +// Avoid erroring on symlinks pointing to the same file that are present in the library search path. +// +// See <https://github.com/rust-lang/rust/issues/12459>. //@ ignore-cross-compile //@ needs-symlink -use run_make_support::{dynamic_lib_name, rfs, rustc}; +use run_make_support::{cwd, dynamic_lib_name, path, rfs, rustc}; fn main() { rustc().input("foo.rs").arg("-Cprefer-dynamic").run(); rfs::create_dir_all("other"); - rfs::create_symlink(dynamic_lib_name("foo"), "other"); - rustc().input("bar.rs").library_search_path("other").run(); + rfs::symlink_file(dynamic_lib_name("foo"), path("other").join(dynamic_lib_name("foo"))); + rustc().input("bar.rs").library_search_path(cwd()).library_search_path("other").run(); } diff --git a/tests/run-make/symlinked-rlib/rmake.rs b/tests/run-make/symlinked-rlib/rmake.rs index 10ba6ba7cbb..fee432e419e 100644 --- a/tests/run-make/symlinked-rlib/rmake.rs +++ b/tests/run-make/symlinked-rlib/rmake.rs @@ -12,6 +12,6 @@ use run_make_support::{cwd, rfs, rustc}; fn main() { rustc().input("foo.rs").crate_type("rlib").output("foo.xxx").run(); - rfs::create_symlink("foo.xxx", "libfoo.rlib"); + rfs::symlink_file("foo.xxx", "libfoo.rlib"); rustc().input("bar.rs").library_search_path(cwd()).run(); } diff --git a/tests/run-make/thumb-none-cortex-m/rmake.rs b/tests/run-make/thumb-none-cortex-m/rmake.rs index 0ddb91d378f..9112646290f 100644 --- a/tests/run-make/thumb-none-cortex-m/rmake.rs +++ b/tests/run-make/thumb-none-cortex-m/rmake.rs @@ -36,10 +36,10 @@ fn main() { let path = env_var("PATH"); let rustc = env_var("RUSTC"); - let bootstrap_cargo = env_var("BOOTSTRAP_CARGO"); - // FIXME: extract bootstrap cargo invocations to a proper command + let cargo = env_var("CARGO"); + // FIXME: extract cargo invocations to a proper command // https://github.com/rust-lang/rust/issues/128734 - let mut cmd = cmd(bootstrap_cargo); + let mut cmd = cmd(cargo); cmd.args(&[ "build", "--manifest-path", diff --git a/tests/rustdoc-gui/docblock-code-block-line-number.goml b/tests/rustdoc-gui/docblock-code-block-line-number.goml index 03f8f80b10d..fed916ac246 100644 --- a/tests/rustdoc-gui/docblock-code-block-line-number.goml +++ b/tests/rustdoc-gui/docblock-code-block-line-number.goml @@ -39,7 +39,10 @@ define-function: ( { "color": |color|, "margin": "0px", - "padding": "14px 8px", + "padding-top": "14px", + "padding-bottom": "14px", + "padding-left": "8px", + "padding-right": "2px", "text-align": "right", // There should not be a radius on the right of the line numbers. "border-top-left-radius": "6px", @@ -141,3 +144,61 @@ assert-css: ( }, ALL, ) + +// Checking line numbers on scraped code examples. +go-to: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html" + +define-function: ( + "check-padding", + [path, padding_bottom], + block { + assert-css: (|path| + " .src-line-numbers", { + "padding-top": "0px", + "padding-bottom": "0px", + "padding-left": "0px", + "padding-right": "0px", + }) + assert-css: (|path| + " .src-line-numbers > pre", { + "padding-top": "14px", + "padding-bottom": |padding_bottom|, + "padding-left": "0px", + "padding-right": "0px", + }) + assert-css: (|path| + " .src-line-numbers > pre > span", { + "padding-top": "0px", + "padding-bottom": "0px", + "padding-left": "8px", + "padding-right": "8px", + }) + }, +) + +call-function: ("check-padding", { + "path": ".scraped-example .example-wrap", + "padding_bottom": "0px", +}) + +move-cursor-to: ".scraped-example .example-wrap .rust" +wait-for: ".scraped-example .example-wrap .button-holder .expand" +click: ".scraped-example .example-wrap .button-holder .expand" +wait-for: ".scraped-example.expanded" + +call-function: ("check-padding", { + "path": ".scraped-example.expanded .example-wrap", + "padding_bottom": "14px", +}) + +// Now checking the line numbers in the source code page. +click: ".src" +assert-css: (".src-line-numbers", { + "padding-top": "20px", + "padding-bottom": "20px", + "padding-left": "4px", + "padding-right": "0px", +}) +assert-css: (".src-line-numbers > a", { + "padding-top": "0px", + "padding-bottom": "0px", + "padding-left": "8px", + "padding-right": "8px", +}) diff --git a/tests/rustdoc-gui/scrape-examples-layout.goml b/tests/rustdoc-gui/scrape-examples-layout.goml index 6bea352bce4..be14e202b37 100644 --- a/tests/rustdoc-gui/scrape-examples-layout.goml +++ b/tests/rustdoc-gui/scrape-examples-layout.goml @@ -37,6 +37,7 @@ assert-property: ( // The "title" should be located at the right bottom corner of the code example. store-position: (".scraped-example .example-wrap", {"x": x, "y": y}) +assert-size: (".scraped-example .example-wrap", {"height": 130}) store-size: (".scraped-example .example-wrap", {"width": width, "height": height}) store-size: (".scraped-example .scraped-example-title", { "width": title_width, @@ -47,6 +48,13 @@ assert-position: (".scraped-example .scraped-example-title", { "y": |y| + |height| - |title_height| - 8, }) +store-size: (".more-scraped-examples .scraped-example .example-wrap", {"height": more_height}) +assert: |more_height| > |height| +assert-size: (".more-scraped-examples .scraped-example .example-wrap", { + "height": 250, + "width": |width|, +}) + // Check that the expand button works and also that line number aligns with code. move-cursor-to: ".scraped-example .rust" click: ".scraped-example .button-holder .expand" diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.md b/tests/rustdoc-ui/doctest/doctest-output-include-fail.md new file mode 100644 index 00000000000..a8e61238f31 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.md @@ -0,0 +1,7 @@ +With a code sample, that has an error: + +```rust +fn main() { + let x = 234 // no semicolon here! oh no! +} +``` diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs new file mode 100644 index 00000000000..4fc0674a0c9 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs @@ -0,0 +1,7 @@ +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout-test: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout-test: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ failure-status: 101 + +// https://github.com/rust-lang/rust/issues/130470 +#![doc = include_str!("doctest-output-include-fail.md")] diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout new file mode 100644 index 00000000000..22d15f8743c --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout @@ -0,0 +1,24 @@ + +running 1 test +test $DIR/doctest-output-include-fail.md - (line 3) ... FAILED + +failures: + +---- $DIR/doctest-output-include-fail.md - (line 3) stdout ---- +error: expected `;`, found `}` + --> $DIR/doctest-output-include-fail.md:5:16 + | +LL | let x = 234 // no semicolon here! oh no! + | ^ help: add `;` here +LL | } + | - unexpected token + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/doctest-output-include-fail.md - (line 3) + +test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/doctest-output.stdout b/tests/rustdoc-ui/doctest/doctest-output.stdout index 35b0e366fb5..c3b1570c43e 100644 --- a/tests/rustdoc-ui/doctest/doctest-output.stdout +++ b/tests/rustdoc-ui/doctest/doctest-output.stdout @@ -1,7 +1,7 @@ running 3 tests test $DIR/doctest-output.rs - (line 8) ... ok -test $DIR/doctest-output.rs - ExpandedStruct (line 24) ... ok +test $DIR/doctest-output.rs - ExpandedStruct (line 25) ... ok test $DIR/doctest-output.rs - foo::bar (line 18) ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/ui-fulldeps/fluent-messages/many-lines.ftl b/tests/ui-fulldeps/fluent-messages/many-lines.ftl new file mode 100644 index 00000000000..43660ebeacd --- /dev/null +++ b/tests/ui-fulldeps/fluent-messages/many-lines.ftl @@ -0,0 +1,11 @@ +no_crate_foo = foo + +# This file tests error reporting for +# fluent files with many lines. +# The error message should point to the correct line number +# and include no more context than necessary. + +no_crate_bar = + +no_crate_baz = + baz diff --git a/tests/ui-fulldeps/fluent-messages/test.rs b/tests/ui-fulldeps/fluent-messages/test.rs index 7bf1252ccf6..3361ebcef01 100644 --- a/tests/ui-fulldeps/fluent-messages/test.rs +++ b/tests/ui-fulldeps/fluent-messages/test.rs @@ -80,3 +80,8 @@ mod bad_escape { //~| ERROR invalid escape `\"` //~| ERROR invalid escape `\'` } + +mod many_lines { + rustc_fluent_macro::fluent_messages! { "./many-lines.ftl" } + //~^ ERROR could not parse Fluent resource +} diff --git a/tests/ui-fulldeps/fluent-messages/test.stderr b/tests/ui-fulldeps/fluent-messages/test.stderr index 09d4a384732..0b3bb14ce51 100644 --- a/tests/ui-fulldeps/fluent-messages/test.stderr +++ b/tests/ui-fulldeps/fluent-messages/test.stderr @@ -103,5 +103,20 @@ LL | rustc_fluent_macro::fluent_messages! { "./invalid-escape.ftl" } | = note: Fluent does not interpret these escape sequences (<https://projectfluent.org/fluent/guide/special.html>) -error: aborting due to 13 previous errors +error: could not parse Fluent resource + --> $DIR/test.rs:85:44 + | +LL | rustc_fluent_macro::fluent_messages! { "./many-lines.ftl" } + | ^^^^^^^^^^^^^^^^^^ + | + = help: see additional errors emitted + +error: expected a message field for "no_crate_bar" + --> ./many-lines.ftl:8:1 + | +8 | no_crate_bar = + | ^^^^^^^^^^^^^^ + | + +error: aborting due to 14 previous errors diff --git a/tests/ui-fulldeps/run-compiler-twice.rs b/tests/ui-fulldeps/run-compiler-twice.rs index 720fc42cc57..cce4eac0d7c 100644 --- a/tests/ui-fulldeps/run-compiler-twice.rs +++ b/tests/ui-fulldeps/run-compiler-twice.rs @@ -65,7 +65,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf, linker: Option<&Path output_dir: None, ice_file: None, file_loader: None, - locale_resources: &[], + locale_resources: Vec::new(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 9600c8dff67..b439a4bcf86 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -39,7 +39,6 @@ //@ revisions: loongarch64 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64] needs-llvm-components: loongarch -//@[loongarch64] min-llvm-version: 18 //FIXME: wasm is disabled due to <https://github.com/rust-lang/rust/issues/115666>. //FIXME @ revisions: wasm //FIXME @[wasm] compile-flags: --target wasm32-unknown-unknown diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index 70343b0599a..e6c26c5c4de 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -1,7 +1,6 @@ //@ revisions: aarch64 x32 x64 //@ run-pass //@[aarch64] only-aarch64 -//@[aarch64] min-llvm-version: 18 //@[x32] only-x86 //@[x64] only-x86_64 //@ ignore-sgx no processes diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index 22304257593..1c0e50250d7 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -1,7 +1,6 @@ //@ revisions: aarch64 x32 x64 //@ run-pass //@[aarch64] only-aarch64 -//@[aarch64] min-llvm-version: 18 //@[x32] only-x86 //@[x64] only-x86_64 //@ ignore-emscripten no processes diff --git a/tests/ui/abi/statics/static-mut-foreign.rs b/tests/ui/abi/statics/static-mut-foreign.rs index 167b7a4e36e..40cd57637e6 100644 --- a/tests/ui/abi/statics/static-mut-foreign.rs +++ b/tests/ui/abi/statics/static-mut-foreign.rs @@ -3,6 +3,9 @@ // statics cannot. This ensures that there's some form of error if this is // attempted. +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::ffi::c_int; #[link(name = "rust_test_helpers", kind = "static")] @@ -29,9 +32,7 @@ unsafe fn run() { rust_dbg_static_mut = -3; assert_eq!(rust_dbg_static_mut, -3); static_bound(&rust_dbg_static_mut); - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] static_bound_set(&mut rust_dbg_static_mut); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } pub fn main() { diff --git a/tests/ui/abi/statics/static-mut-foreign.stderr b/tests/ui/abi/statics/static-mut-foreign.stderr deleted file mode 100644 index 0c4bd5382a1..00000000000 --- a/tests/ui/abi/statics/static-mut-foreign.stderr +++ /dev/null @@ -1,31 +0,0 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/static-mut-foreign.rs:31:18 - | -LL | static_bound(&rust_dbg_static_mut); - | ^^^^^^^^^^^^^^^^^^^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | static_bound(addr_of!(rust_dbg_static_mut)); - | ~~~~~~~~~ + - -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/static-mut-foreign.rs:33:22 - | -LL | static_bound_set(&mut rust_dbg_static_mut); - | ^^^^^^^^^^^^^^^^^^^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | static_bound_set(addr_of_mut!(rust_dbg_static_mut)); - | ~~~~~~~~~~~~~ + - -warning: 2 warnings emitted - diff --git a/tests/ui/array-slice-vec/slice-panic-1.rs b/tests/ui/array-slice-vec/slice-panic-1.rs index 4436b633856..d4f584c1632 100644 --- a/tests/ui/array-slice-vec/slice-panic-1.rs +++ b/tests/ui/array-slice-vec/slice-panic-1.rs @@ -5,6 +5,8 @@ // Test that if a slicing expr[..] fails, the correct cleanups happen. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] use std::thread; diff --git a/tests/ui/array-slice-vec/slice-panic-2.rs b/tests/ui/array-slice-vec/slice-panic-2.rs index 4bd13988424..b3d1dc45573 100644 --- a/tests/ui/array-slice-vec/slice-panic-2.rs +++ b/tests/ui/array-slice-vec/slice-panic-2.rs @@ -5,6 +5,8 @@ // Test that if a slicing expr[..] fails, the correct cleanups happen. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] use std::thread; diff --git a/tests/ui/array-slice-vec/slice.rs b/tests/ui/array-slice-vec/slice.rs index 2adcd96f598..ad5db7a2102 100644 --- a/tests/ui/array-slice-vec/slice.rs +++ b/tests/ui/array-slice-vec/slice.rs @@ -3,6 +3,9 @@ // Test slicing sugar. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + extern crate core; use core::ops::{Index, IndexMut, Range, RangeTo, RangeFrom, RangeFull}; diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 4a50ec8d0d5..61e5078d6d9 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -1,9 +1,11 @@ error: unknown directive -.intel_syntax noprefix -^ -error: unknown directive -.intel_syntax noprefix -^ + | +note: instantiated into assembly here + --> <inline asm>:1:1 + | +LL | .intel_syntax noprefix + | ^ + error: unknown directive | note: instantiated into assembly here @@ -13,7 +15,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:35:15 + --> $DIR/inline-syntax.rs:29:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^ @@ -25,7 +27,7 @@ LL | .intel_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:39:15 + --> $DIR/inline-syntax.rs:32:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^ @@ -37,7 +39,7 @@ LL | .intel_syntax aaa noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:43:15 + --> $DIR/inline-syntax.rs:35:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^ @@ -49,7 +51,7 @@ LL | .att_syntax noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:47:15 + --> $DIR/inline-syntax.rs:38:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^ @@ -61,7 +63,7 @@ LL | .att_syntax bbb noprefix | ^ error: unknown directive - --> $DIR/inline-syntax.rs:51:15 + --> $DIR/inline-syntax.rs:41:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^ @@ -73,7 +75,7 @@ LL | .intel_syntax noprefix; nop | ^ error: unknown directive - --> $DIR/inline-syntax.rs:58:13 + --> $DIR/inline-syntax.rs:47:13 | LL | .intel_syntax noprefix | ^ @@ -84,5 +86,5 @@ note: instantiated into assembly here LL | .intel_syntax noprefix | ^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/asm/inline-syntax.arm_llvm_18.stderr b/tests/ui/asm/inline-syntax.arm_llvm_18.stderr deleted file mode 100644 index ada3f4891d3..00000000000 --- a/tests/ui/asm/inline-syntax.arm_llvm_18.stderr +++ /dev/null @@ -1,90 +0,0 @@ -error: unknown directive - | -note: instantiated into assembly here - --> <inline asm>:1:1 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - | -note: instantiated into assembly here - --> <inline asm>:1:1 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:35:15 - | -LL | asm!(".intel_syntax noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> <inline asm>:1:2 - | -LL | .intel_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:39:15 - | -LL | asm!(".intel_syntax aaa noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> <inline asm>:1:2 - | -LL | .intel_syntax aaa noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:43:15 - | -LL | asm!(".att_syntax noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> <inline asm>:1:2 - | -LL | .att_syntax noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:47:15 - | -LL | asm!(".att_syntax bbb noprefix", "nop"); - | ^ - | -note: instantiated into assembly here - --> <inline asm>:1:2 - | -LL | .att_syntax bbb noprefix - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:51:15 - | -LL | asm!(".intel_syntax noprefix; nop"); - | ^ - | -note: instantiated into assembly here - --> <inline asm>:1:2 - | -LL | .intel_syntax noprefix; nop - | ^ - -error: unknown directive - --> $DIR/inline-syntax.rs:58:13 - | -LL | .intel_syntax noprefix - | ^ - | -note: instantiated into assembly here - --> <inline asm>:2:13 - | -LL | .intel_syntax noprefix - | ^ - -error: aborting due to 8 previous errors - diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index 4a98d37aca0..b8486527e6f 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -1,16 +1,10 @@ -//@ revisions: x86_64 arm arm_llvm_18 +//@ revisions: x86_64 arm //@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu //@[x86_64] check-pass //@[x86_64] needs-llvm-components: x86 //@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf //@[arm] build-fail //@[arm] needs-llvm-components: arm -//@[arm] ignore-llvm-version: 18 - 99 -//Newer LLVM produces extra error notes. -//@[arm_llvm_18] compile-flags: --target armv7-unknown-linux-gnueabihf -//@[arm_llvm_18] build-fail -//@[arm_llvm_18] needs-llvm-components: arm -//@[arm_llvm_18] min-llvm-version: 18 //@ needs-asm-support #![feature(no_core, lang_items, rustc_attrs)] @@ -35,23 +29,18 @@ pub fn main() { asm!(".intel_syntax noprefix", "nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".intel_syntax aaa noprefix", "nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".att_syntax noprefix", "nop"); //[x86_64]~^ WARN avoid using `.att_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".att_syntax bbb noprefix", "nop"); //[x86_64]~^ WARN avoid using `.att_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!(".intel_syntax noprefix; nop"); //[x86_64]~^ WARN avoid using `.intel_syntax` //[arm]~^^ ERROR unknown directive - //[arm_llvm_18]~^^^ ERROR unknown directive asm!( r" @@ -60,7 +49,6 @@ pub fn main() { ); //[x86_64]~^^^ WARN avoid using `.intel_syntax` //[arm]~^^^^ ERROR unknown directive - //[arm_llvm_18]~^^^^^ ERROR unknown directive } } diff --git a/tests/ui/asm/inline-syntax.x86_64.stderr b/tests/ui/asm/inline-syntax.x86_64.stderr index 66dc37f3089..59c95194322 100644 --- a/tests/ui/asm/inline-syntax.x86_64.stderr +++ b/tests/ui/asm/inline-syntax.x86_64.stderr @@ -1,5 +1,5 @@ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:67:14 + --> $DIR/inline-syntax.rs:55:14 | LL | global_asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -7,37 +7,37 @@ LL | global_asm!(".intel_syntax noprefix", "nop"); = note: `#[warn(bad_asm_style)]` on by default warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:35:15 + --> $DIR/inline-syntax.rs:29:15 | LL | asm!(".intel_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:39:15 + --> $DIR/inline-syntax.rs:32:15 | LL | asm!(".intel_syntax aaa noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:43:15 + --> $DIR/inline-syntax.rs:35:15 | LL | asm!(".att_syntax noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - --> $DIR/inline-syntax.rs:47:15 + --> $DIR/inline-syntax.rs:38:15 | LL | asm!(".att_syntax bbb noprefix", "nop"); | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:51:15 + --> $DIR/inline-syntax.rs:41:15 | LL | asm!(".intel_syntax noprefix; nop"); | ^^^^^^^^^^^^^^^^^^^^^^ warning: avoid using `.intel_syntax`, Intel syntax is the default - --> $DIR/inline-syntax.rs:58:13 + --> $DIR/inline-syntax.rs:47:13 | LL | .intel_syntax noprefix | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs index e2fc2961a44..a33d4928f01 100644 --- a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.rs @@ -7,4 +7,5 @@ impl Fail<i32> { fn main() { Fail::<()>::C + //~^ ERROR no associated item named `C` found for struct `Fail<()>` in the current scope } diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr index f0a6ccf243a..c5816393107 100644 --- a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg.stderr @@ -7,6 +7,19 @@ LL | struct Fail<T>; = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -error: aborting due to 1 previous error +error[E0599]: no associated item named `C` found for struct `Fail<()>` in the current scope + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg.rs:9:17 + | +LL | struct Fail<T>; + | -------------- associated item `C` not found for this struct +... +LL | Fail::<()>::C + | ^ associated item not found in `Fail<()>` + | + = note: the associated item was found for + - `Fail<i32>` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0392`. +Some errors have detailed explanations: E0392, E0599. +For more information about an error, try `rustc --explain E0392`. diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs index cb53d902ba1..d891b7bd62b 100644 --- a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.rs @@ -14,4 +14,5 @@ impl Fail<i32, i32> { fn main() { Fail::<i32, u32>::C //~^ ERROR: type mismatch + //~| ERROR no associated item named `C` found for struct `Fail<i32, u32>` in the current scope } diff --git a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr index 0d63b7f5b29..18d458aea80 100644 --- a/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr +++ b/tests/ui/associated-consts/wrong-projection-self-ty-invalid-bivariant-arg2.stderr @@ -1,3 +1,15 @@ +error[E0599]: no associated item named `C` found for struct `Fail<i32, u32>` in the current scope + --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:23 + | +LL | struct Fail<T: Proj<Assoc = U>, U>(T); + | ---------------------------------- associated item `C` not found for this struct +... +LL | Fail::<i32, u32>::C + | ^ associated item not found in `Fail<i32, u32>` + | + = note: the associated item was found for + - `Fail<i32, i32>` + error[E0271]: type mismatch resolving `<i32 as Proj>::Assoc == u32` --> $DIR/wrong-projection-self-ty-invalid-bivariant-arg2.rs:15:5 | @@ -15,6 +27,7 @@ note: required by a bound in `Fail` LL | struct Fail<T: Proj<Assoc = U>, U>(T); | ^^^^^^^^^ required by this bound in `Fail` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +Some errors have detailed explanations: E0271, E0599. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/async-await/async-closures/foreign.rs b/tests/ui/async-await/async-closures/foreign.rs index 50bef9cf11d..ab6fe06a3f4 100644 --- a/tests/ui/async-await/async-closures/foreign.rs +++ b/tests/ui/async-await/async-closures/foreign.rs @@ -12,8 +12,13 @@ extern crate foreign; struct NoCopy; +async fn call_once(f: impl async FnOnce()) { + f().await; +} + fn main() { block_on::block_on(async { foreign::closure()().await; + call_once(foreign::closure()).await; }); } diff --git a/tests/ui/async-await/async-closures/inline-body.rs b/tests/ui/async-await/async-closures/inline-body.rs new file mode 100644 index 00000000000..a842d98d1de --- /dev/null +++ b/tests/ui/async-await/async-closures/inline-body.rs @@ -0,0 +1,34 @@ +//@ edition: 2021 +//@ compile-flags: -Zinline-mir +//@ build-pass + +// Ensure that we don't hit a Steal ICE because we forgot to ensure +// `mir_inliner_callees` for the synthetic by-move coroutine body since +// its def-id wasn't previously being considered. + +#![feature(async_closure, noop_waker)] + +use std::future::Future; +use std::pin::pin; +use std::task::*; + +pub fn block_on<T>(fut: impl Future<Output = T>) -> T { + let mut fut = pin!(fut); + let ctx = &mut Context::from_waker(Waker::noop()); + + loop { + match fut.as_mut().poll(ctx) { + Poll::Pending => {} + Poll::Ready(t) => break t, + } + } +} + +async fn call_once<T>(f: impl async FnOnce() -> T) -> T { + f().await +} + +fn main() { + let c = async || {}; + block_on(call_once(c)); +} diff --git a/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs b/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs index 51e85931dbf..547c7414526 100644 --- a/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs +++ b/tests/ui/async-await/issues/issue-67611-static-mut-refs.rs @@ -2,6 +2,8 @@ //@ edition:2018 #![feature(if_let_guard)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] static mut A: [i32; 5] = [1, 2, 3, 4, 5]; diff --git a/tests/ui/async-await/pin-reborrow-arg.rs b/tests/ui/async-await/pin-reborrow-arg.rs new file mode 100644 index 00000000000..2008bd1f52d --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-arg.rs @@ -0,0 +1,36 @@ +//@ check-pass + +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn baz(self: Pin<&mut Self>) { + } +} + +fn foo(_: Pin<&mut Foo>) { +} + +fn foo_const(_: Pin<&Foo>) { +} + +fn bar(x: Pin<&mut Foo>) { + foo(x); + foo(x); // for this to work we need to automatically reborrow, + // as if the user had written `foo(x.as_mut())`. + + Foo::baz(x); + Foo::baz(x); + + foo_const(x); // make sure we can reborrow &mut as &. + + let x: Pin<&Foo> = Pin::new(&Foo); + + foo_const(x); // make sure reborrowing from & to & works. +} + +fn main() {} diff --git a/tests/ui/async-await/pin-reborrow-const-as-mut.rs b/tests/ui/async-await/pin-reborrow-const-as-mut.rs new file mode 100644 index 00000000000..27c70a7b4df --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-const-as-mut.rs @@ -0,0 +1,18 @@ +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// make sure we can't accidentally reborrow Pin<&T> as Pin<&mut T> + +use std::pin::Pin; + +struct Foo; + +fn foo(_: Pin<&mut Foo>) { +} + +fn bar(x: Pin<&Foo>) { + foo(x); //~ ERROR mismatched types + //| ERROR types differ in mutability +} + +fn main() {} diff --git a/tests/ui/async-await/pin-reborrow-const-as-mut.stderr b/tests/ui/async-await/pin-reborrow-const-as-mut.stderr new file mode 100644 index 00000000000..2c2d9ec2717 --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-const-as-mut.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/pin-reborrow-const-as-mut.rs:14:9 + | +LL | foo(x); + | --- ^ types differ in mutability + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin<&mut Foo>` + found struct `Pin<&Foo>` +note: function defined here + --> $DIR/pin-reborrow-const-as-mut.rs:10:4 + | +LL | fn foo(_: Pin<&mut Foo>) { + | ^^^ ---------------- + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/async-await/pin-reborrow-once.rs b/tests/ui/async-await/pin-reborrow-once.rs new file mode 100644 index 00000000000..241efadef7d --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-once.rs @@ -0,0 +1,13 @@ +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +// Make sure with pin reborrowing that we can only get one mutable reborrow of a pinned reference. + +use std::pin::{pin, Pin}; + +fn twice(_: Pin<&mut i32>, _: Pin<&mut i32>) {} + +fn main() { + let x = pin!(42); + twice(x, x); //~ ERROR cannot borrow +} diff --git a/tests/ui/async-await/pin-reborrow-once.stderr b/tests/ui/async-await/pin-reborrow-once.stderr new file mode 100644 index 00000000000..b8fde8ffee8 --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-once.stderr @@ -0,0 +1,12 @@ +error[E0499]: cannot borrow `*x.__pointer` as mutable more than once at a time + --> $DIR/pin-reborrow-once.rs:12:14 + | +LL | twice(x, x); + | ----- - ^ second mutable borrow occurs here + | | | + | | first mutable borrow occurs here + | first borrow later used by call + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. diff --git a/tests/ui/async-await/pin-reborrow-self.rs b/tests/ui/async-await/pin-reborrow-self.rs new file mode 100644 index 00000000000..b60b6982bb8 --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-self.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ignore-test + +// Currently ignored due to self reborrowing not being implemented for Pin + +#![feature(pin_ergonomics)] +#![allow(incomplete_features)] + +use std::pin::Pin; + +struct Foo; + +impl Foo { + fn foo(self: Pin<&mut Self>) { + } +} + +fn bar(x: Pin<&mut Foo>) { + x.foo(); + x.foo(); // for this to work we need to automatically reborrow, + // as if the user had written `x.as_mut().foo()`. +} + +fn main() {} diff --git a/tests/ui/async-await/pin-reborrow-shorter.rs b/tests/ui/async-await/pin-reborrow-shorter.rs new file mode 100644 index 00000000000..06c266e0035 --- /dev/null +++ b/tests/ui/async-await/pin-reborrow-shorter.rs @@ -0,0 +1,14 @@ +//@check-pass + +#![feature(pin_ergonomics)] +#![allow(dead_code, incomplete_features)] + +use std::pin::Pin; + +fn shorter<'b, T: 'b>(_: Pin<&'b mut T>) {} + +fn test<'a: 'b, 'b, T: 'a>(x: Pin<&'a mut T>) { + shorter::<'b>(x); +} + +fn main() {} diff --git a/tests/ui/binding/order-drop-with-match.rs b/tests/ui/binding/order-drop-with-match.rs index c12c5e4c627..cd0ff73cf12 100644 --- a/tests/ui/binding/order-drop-with-match.rs +++ b/tests/ui/binding/order-drop-with-match.rs @@ -5,6 +5,8 @@ // in ORDER matching up to when it ran. // Correct order is: matched, inner, outer +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] static mut ORDER: [usize; 3] = [0, 0, 0]; static mut INDEX: usize = 0; diff --git a/tests/ui/borrowck/borrowck-access-permissions.rs b/tests/ui/borrowck/borrowck-access-permissions.rs index be11286a523..0e9e2bc1354 100644 --- a/tests/ui/borrowck/borrowck-access-permissions.rs +++ b/tests/ui/borrowck/borrowck-access-permissions.rs @@ -16,7 +16,6 @@ fn main() { let _y1 = &mut static_x; //~ ERROR [E0596] unsafe { let _y2 = &mut static_x_mut; - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } } diff --git a/tests/ui/borrowck/borrowck-access-permissions.stderr b/tests/ui/borrowck/borrowck-access-permissions.stderr index 36e259fa6e8..ade10dbbfbd 100644 --- a/tests/ui/borrowck/borrowck-access-permissions.stderr +++ b/tests/ui/borrowck/borrowck-access-permissions.stderr @@ -1,18 +1,3 @@ -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/borrowck-access-permissions.rs:18:23 - | -LL | let _y2 = &mut static_x_mut; - | ^^^^^^^^^^^^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | let _y2 = addr_of_mut!(static_x_mut); - | ~~~~~~~~~~~~~ + - error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable --> $DIR/borrowck-access-permissions.rs:10:19 | @@ -31,7 +16,7 @@ LL | let _y1 = &mut static_x; | ^^^^^^^^^^^^^ cannot borrow as mutable error[E0596]: cannot borrow `*box_x` as mutable, as `box_x` is not declared as mutable - --> $DIR/borrowck-access-permissions.rs:28:19 + --> $DIR/borrowck-access-permissions.rs:27:19 | LL | let _y1 = &mut *box_x; | ^^^^^^^^^^^ cannot borrow as mutable @@ -42,7 +27,7 @@ LL | let mut box_x = Box::new(1); | +++ error[E0596]: cannot borrow `*ref_x` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-access-permissions.rs:37:19 + --> $DIR/borrowck-access-permissions.rs:36:19 | LL | let _y1 = &mut *ref_x; | ^^^^^^^^^^^ `ref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable @@ -53,7 +38,7 @@ LL | let ref_x = &mut x; | +++ error[E0596]: cannot borrow `*ptr_x` as mutable, as it is behind a `*const` pointer - --> $DIR/borrowck-access-permissions.rs:47:23 + --> $DIR/borrowck-access-permissions.rs:46:23 | LL | let _y1 = &mut *ptr_x; | ^^^^^^^^^^^ `ptr_x` is a `*const` pointer, so the data it refers to cannot be borrowed as mutable @@ -64,7 +49,7 @@ LL | let ptr_x: *const _ = &mut x; | +++ error[E0596]: cannot borrow `*foo_ref.f` as mutable, as it is behind a `&` reference - --> $DIR/borrowck-access-permissions.rs:60:18 + --> $DIR/borrowck-access-permissions.rs:59:18 | LL | let _y = &mut *foo_ref.f; | ^^^^^^^^^^^^^^^ `foo_ref` is a `&` reference, so the data it refers to cannot be borrowed as mutable @@ -74,6 +59,6 @@ help: consider changing this to be a mutable reference LL | let foo_ref = &mut foo; | +++ -error: aborting due to 6 previous errors; 1 warning emitted +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index a727c9414c5..e4dfa6d7fba 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -4,14 +4,13 @@ warning: creating a mutable reference to mutable static is discouraged LL | let sfoo: *mut Foo = &mut SFOO; | ^^^^^^^^^ mutable reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer +help: use `&raw mut` instead to create a raw pointer | -LL | let sfoo: *mut Foo = addr_of_mut!(SFOO); - | ~~~~~~~~~~~~~ + +LL | let sfoo: *mut Foo = &raw mut SFOO; + | ~~~~~~~~ warning: 1 warning emitted diff --git a/tests/ui/borrowck/issue-20801.rs b/tests/ui/borrowck/issue-20801.rs index 7e3d3703dc7..c3f136f2876 100644 --- a/tests/ui/borrowck/issue-20801.rs +++ b/tests/ui/borrowck/issue-20801.rs @@ -12,7 +12,6 @@ fn imm_ref() -> &'static T { fn mut_ref() -> &'static mut T { unsafe { &mut GLOBAL_MUT_T } - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } fn mut_ptr() -> *mut T { diff --git a/tests/ui/borrowck/issue-20801.stderr b/tests/ui/borrowck/issue-20801.stderr index 769b34831c1..5fda92634d8 100644 --- a/tests/ui/borrowck/issue-20801.stderr +++ b/tests/ui/borrowck/issue-20801.stderr @@ -1,20 +1,5 @@ -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/issue-20801.rs:14:14 - | -LL | unsafe { &mut GLOBAL_MUT_T } - | ^^^^^^^^^^^^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | unsafe { addr_of_mut!(GLOBAL_MUT_T) } - | ~~~~~~~~~~~~~ + - error[E0507]: cannot move out of a mutable reference - --> $DIR/issue-20801.rs:27:22 + --> $DIR/issue-20801.rs:26:22 | LL | let a = unsafe { *mut_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -34,7 +19,7 @@ LL + let a = unsafe { mut_ref() }; | error[E0507]: cannot move out of a shared reference - --> $DIR/issue-20801.rs:30:22 + --> $DIR/issue-20801.rs:29:22 | LL | let b = unsafe { *imm_ref() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -54,7 +39,7 @@ LL + let b = unsafe { imm_ref() }; | error[E0507]: cannot move out of a raw pointer - --> $DIR/issue-20801.rs:33:22 + --> $DIR/issue-20801.rs:32:22 | LL | let c = unsafe { *mut_ptr() }; | ^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -69,7 +54,7 @@ LL | let c = unsafe { *mut_ptr() }; | ---------- you could clone this value error[E0507]: cannot move out of a raw pointer - --> $DIR/issue-20801.rs:36:22 + --> $DIR/issue-20801.rs:35:22 | LL | let d = unsafe { *const_ptr() }; | ^^^^^^^^^^^^ move occurs because value has type `T`, which does not implement the `Copy` trait @@ -83,6 +68,6 @@ LL | struct T(u8); LL | let d = unsafe { *const_ptr() }; | ------------ you could clone this value -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs index c3909d05963..e22bf42e021 100644 --- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs +++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.rs @@ -10,7 +10,6 @@ mod borrowck_closures_unique { //~^ ERROR is not declared as mutable unsafe { c1(&mut Y); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } } } @@ -25,7 +24,6 @@ mod borrowck_closures_unique_grandparent { }; unsafe { c1(&mut Z); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } } } @@ -62,7 +60,6 @@ fn main() { static mut X: isize = 2; unsafe { borrowck_closures_unique::e(&mut X); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] } mutability_errors::capture_assign_whole((1000,)); diff --git a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr index 72fd0d8ce16..6fc8d3a6c35 100644 --- a/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr +++ b/tests/ui/borrowck/issue-55492-borrowck-migrate-scans-parents.stderr @@ -1,46 +1,3 @@ -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:12:16 - | -LL | c1(&mut Y); - | ^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | c1(addr_of_mut!(Y)); - | ~~~~~~~~~~~~~ + - -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:27:16 - | -LL | c1(&mut Z); - | ^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | c1(addr_of_mut!(Z)); - | ~~~~~~~~~~~~~ + - -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:64:37 - | -LL | borrowck_closures_unique::e(&mut X); - | ^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | borrowck_closures_unique::e(addr_of_mut!(X)); - | ~~~~~~~~~~~~~ + - error[E0594]: cannot assign to `x`, as it is not declared as mutable --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:9:46 | @@ -53,7 +10,7 @@ LL | pub fn e(mut x: &'static mut isize) { | +++ error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:22:50 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:21:50 | LL | let mut c2 = |y: &'static mut isize| x = y; | ^^^^^ cannot assign @@ -64,7 +21,7 @@ LL | pub fn ee(mut x: &'static mut isize) { | +++ error[E0594]: cannot assign to `x`, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:37:13 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:35:13 | LL | x = (1,); | ^^^^^^^^ cannot assign @@ -75,7 +32,7 @@ LL | pub fn capture_assign_whole(mut x: (i32,)) { | +++ error[E0594]: cannot assign to `x.0`, as `x` is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:43:13 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:41:13 | LL | x.0 = 1; | ^^^^^^^ cannot assign @@ -86,7 +43,7 @@ LL | pub fn capture_assign_part(mut x: (i32,)) { | +++ error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:49:13 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:47:13 | LL | &mut x; | ^^^^^^ cannot borrow as mutable @@ -97,7 +54,7 @@ LL | pub fn capture_reborrow_whole(mut x: (i32,)) { | +++ error[E0596]: cannot borrow `x.0` as mutable, as `x` is not declared as mutable - --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:55:13 + --> $DIR/issue-55492-borrowck-migrate-scans-parents.rs:53:13 | LL | &mut x.0; | ^^^^^^^^ cannot borrow as mutable @@ -107,7 +64,7 @@ help: consider changing this to be mutable LL | pub fn capture_reborrow_part(mut x: (i32,)) { | +++ -error: aborting due to 6 previous errors; 3 warnings emitted +error: aborting due to 6 previous errors Some errors have detailed explanations: E0594, E0596. For more information about an error, try `rustc --explain E0594`. diff --git a/tests/ui/cfg/cfg-false-feature.rs b/tests/ui/cfg/cfg-false-feature.rs index 6645f667d7e..716b18492c7 100644 --- a/tests/ui/cfg/cfg-false-feature.rs +++ b/tests/ui/cfg/cfg-false-feature.rs @@ -5,7 +5,7 @@ #![feature(decl_macro)] #![cfg(FALSE)] -#![feature(box_syntax)] +#![feature(box_patterns)] macro mac() {} // OK diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr new file mode 100644 index 00000000000..ab7e77686ee --- /dev/null +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -0,0 +1,40 @@ +warning: unexpected `cfg` condition name: `tru` + --> $DIR/raw-keywords.rs:14:7 + | +LL | #[cfg(tru)] + | ^^^ help: there is a config with a similar name: `r#true` + | + = help: to expect this configuration use `--check-cfg=cfg(tru)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `r#false` + --> $DIR/raw-keywords.rs:19:7 + | +LL | #[cfg(r#false)] + | ^^^^^^^ + | + = help: expected names are: `async`, `clippy`, `debug_assertions`, `doc`, `doctest`, `edition2015`, `edition2021`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `r#true`, `ub_checks`, `unix`, and `windows` + = help: to expect this configuration use `--check-cfg=cfg(r#false)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `await` + --> $DIR/raw-keywords.rs:27:29 + | +LL | #[cfg_attr(edition2015, cfg(await))] + | ^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(await)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `raw` + --> $DIR/raw-keywords.rs:33:7 + | +LL | #[cfg(r#raw)] + | ^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(raw)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: 4 warnings emitted + diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr new file mode 100644 index 00000000000..1ae1cad4e6b --- /dev/null +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -0,0 +1,40 @@ +warning: unexpected `cfg` condition name: `tru` + --> $DIR/raw-keywords.rs:14:7 + | +LL | #[cfg(tru)] + | ^^^ help: there is a config with a similar name: `r#true` + | + = help: to expect this configuration use `--check-cfg=cfg(tru)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: unexpected `cfg` condition name: `r#false` + --> $DIR/raw-keywords.rs:19:7 + | +LL | #[cfg(r#false)] + | ^^^^^^^ + | + = help: expected names are: `r#async`, `clippy`, `debug_assertions`, `doc`, `doctest`, `edition2015`, `edition2021`, `fmt_debug`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `r#true`, `ub_checks`, `unix`, and `windows` + = help: to expect this configuration use `--check-cfg=cfg(r#false)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `r#await` + --> $DIR/raw-keywords.rs:28:29 + | +LL | #[cfg_attr(edition2021, cfg(r#await))] + | ^^^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(r#await)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: unexpected `cfg` condition name: `raw` + --> $DIR/raw-keywords.rs:33:7 + | +LL | #[cfg(r#raw)] + | ^^^^^ + | + = help: to expect this configuration use `--check-cfg=cfg(raw)` + = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration + +warning: 4 warnings emitted + diff --git a/tests/ui/check-cfg/raw-keywords.rs b/tests/ui/check-cfg/raw-keywords.rs new file mode 100644 index 00000000000..5de13240d7e --- /dev/null +++ b/tests/ui/check-cfg/raw-keywords.rs @@ -0,0 +1,40 @@ +// This test check that using raw keywords works with --cfg and --check-cfg +// and that the diagnostics suggestions are coherent +// +//@ check-pass +//@ no-auto-check-cfg +//@ compile-flags: --cfg=true --cfg=async --check-cfg=cfg(r#true,r#async,edition2015,edition2021) +// +//@ revisions: edition2015 edition2021 +//@ [edition2021] compile-flags: --edition 2021 + +#[cfg(r#true)] +fn foo() {} + +#[cfg(tru)] +//~^ WARNING unexpected `cfg` condition name: `tru` +//~^^ SUGGESTION r#true +fn foo() {} + +#[cfg(r#false)] +//~^ WARNING unexpected `cfg` condition name: `r#false` +fn foo() {} + +#[cfg_attr(edition2015, cfg(async))] +#[cfg_attr(edition2021, cfg(r#async))] +fn bar() {} + +#[cfg_attr(edition2015, cfg(await))] +#[cfg_attr(edition2021, cfg(r#await))] +//[edition2015]~^^ WARNING unexpected `cfg` condition name: `await` +//[edition2021]~^^ WARNING unexpected `cfg` condition name: `r#await` +fn zoo() {} + +#[cfg(r#raw)] +//~^ WARNING unexpected `cfg` condition name: `raw` +fn foo() {} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr b/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr deleted file mode 100644 index ef5633f7134..00000000000 --- a/tests/ui/coercion/coerce-issue-49593-box-never.fallback.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0277]: the trait bound `(): std::error::Error` is not satisfied - --> $DIR/coerce-issue-49593-box-never.rs:18:5 - | -LL | Box::<_ /* ! */>::new(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()` - | - = note: required for the cast from `Box<()>` to `Box<(dyn std::error::Error + 'static)>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/coercion/coerce-issue-49593-box-never.rs b/tests/ui/coercion/coerce-issue-49593-box-never.rs index 53071be47dc..e5569592045 100644 --- a/tests/ui/coercion/coerce-issue-49593-box-never.rs +++ b/tests/ui/coercion/coerce-issue-49593-box-never.rs @@ -1,5 +1,5 @@ //@ revisions: nofallback fallback -//@check-fail +//@[fallback] check-pass #![feature(never_type)] #![cfg_attr(fallback, feature(never_type_fallback))] @@ -13,10 +13,10 @@ fn raw_ptr_box<T>(t: T) -> *mut T { } fn foo(x: !) -> Box<dyn Error> { - // Subtyping during method resolution will generate new inference vars and - // subtype them. Thus fallback will not fall back to `!`, but `()` instead. + // Method resolution will generate new inference vars and relate them. + // Thus fallback will not fall back to `!`, but `()` instead. Box::<_ /* ! */>::new(x) - //~^ ERROR trait bound `(): std::error::Error` is not satisfied + //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied } fn foo_raw_ptr(x: !) -> *mut dyn Error { diff --git a/tests/ui/compiletest-self-test/compile-flags-last.stderr b/tests/ui/compiletest-self-test/compile-flags-last.stderr index d8d40a7d9f1..72d92206e2b 100644 --- a/tests/ui/compiletest-self-test/compile-flags-last.stderr +++ b/tests/ui/compiletest-self-test/compile-flags-last.stderr @@ -1,2 +1,5 @@ error: Argument to option 'cap-lints' missing + Usage: + --cap-lints LEVEL Set the most restrictive lint level. More restrictive + lints are capped at this level diff --git a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr index 217f609459e..b88fe966b77 100644 --- a/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr +++ b/tests/ui/const-generics/generic_arg_infer/issue-91614.stderr @@ -8,7 +8,7 @@ note: required by a const generic parameter in `Mask::<T, N>::splat` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | -LL | let y: Mask<T, N> = Mask::<_, _>::splat(false); +LL | let y: Mask<_, N> = Mask::<_, _>::splat(false); | ++++++++++++ error[E0284]: type annotations needed for `Mask<_, _>` @@ -21,7 +21,7 @@ note: required by a const generic parameter in `Mask` --> $SRC_DIR/core/src/../../portable-simd/crates/core_simd/src/masks.rs:LL:COL help: consider giving `y` an explicit type, where the value of const parameter `N` is specified | -LL | let y: Mask<T, N> = Mask::<_, _>::splat(false); +LL | let y: Mask<_, N> = Mask::<_, _>::splat(false); | ++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs index c6dd981cced..5303b247173 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.rs @@ -3,8 +3,7 @@ impl EntriesBuffer { fn a(&self) -> impl Iterator { - self.0.iter_mut() //~ ERROR: cannot borrow `*self.0` as mutable, as it is behind a `&` reference - //~| ERROR captures lifetime that does not appear in bounds + self.0.iter_mut() } } diff --git a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr index 24f3ed7cdf1..fcbd6904599 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr +++ b/tests/ui/const-generics/generic_const_exprs/issue-109141.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `HashesEntryLEN` in this scope - --> $DIR/issue-109141.rs:11:32 + --> $DIR/issue-109141.rs:10:32 | LL | struct EntriesBuffer(Box<[[u8; HashesEntryLEN]; 5]>); | ^^^^^^^^^^^^^^ not found in this scope @@ -9,33 +9,6 @@ help: you might be missing a const parameter LL | struct EntriesBuffer<const HashesEntryLEN: /* Type */>(Box<[[u8; HashesEntryLEN]; 5]>); | ++++++++++++++++++++++++++++++++++ -error[E0596]: cannot borrow `*self.0` as mutable, as it is behind a `&` reference - --> $DIR/issue-109141.rs:6:9 - | -LL | self.0.iter_mut() - | ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable - | -help: consider changing this to be a mutable reference - | -LL | fn a(&mut self) -> impl Iterator { - | ~~~~~~~~~ - -error[E0700]: hidden type for `impl Iterator` captures lifetime that does not appear in bounds - --> $DIR/issue-109141.rs:6:9 - | -LL | fn a(&self) -> impl Iterator { - | ----- ------------- opaque type defined here - | | - | hidden type `std::slice::IterMut<'_, [u8; {const error}]>` captures the anonymous lifetime defined here -LL | self.0.iter_mut() - | ^^^^^^^^^^^^^^^^^ - | -help: add a `use<...>` bound to explicitly capture `'_` - | -LL | fn a(&self) -> impl Iterator + use<'_> { - | +++++++++ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0425, E0596, E0700. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs index 7209290a36e..56b8acbf88c 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.rs +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.rs @@ -2,7 +2,6 @@ #![allow(incomplete_features)] type Foo = impl Sized; -//~^ ERROR: unconstrained opaque type fn with_bound<const N: usize>() -> Foo where diff --git a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr index c7a266205b4..e9fb8c0f403 100644 --- a/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr +++ b/tests/ui/const-generics/generic_const_exprs/opaque_type.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/opaque_type.rs:11:17 + --> $DIR/opaque_type.rs:10:17 | LL | type Foo = impl Sized; | ---------- the found opaque type @@ -11,20 +11,12 @@ LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; found opaque type `Foo` error[E0605]: non-primitive cast: `usize` as `Foo` - --> $DIR/opaque_type.rs:11:17 + --> $DIR/opaque_type.rs:10:17 | LL | let _: [u8; (N / 2) as Foo] = [0; (N / 2) as usize]; | ^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object -error: unconstrained opaque type - --> $DIR/opaque_type.rs:4:12 - | -LL | type Foo = impl Sized; - | ^^^^^^^^^^ - | - = note: `Foo` must be used in combination with a concrete type within the same module - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors Some errors have detailed explanations: E0308, E0605. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.rs b/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.rs deleted file mode 100644 index 81be8d5c7d7..00000000000 --- a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.rs +++ /dev/null @@ -1,54 +0,0 @@ -//@ known-bug: #110395 -//@ known-bug: #97156 - -#![feature(const_type_id, const_trait_impl, generic_const_exprs)] -#![allow(incomplete_features)] - -use std::any::TypeId; -// `One` and `Two` are currently considered equal types, as both -// `One <: Two` and `One :> Two` holds. -type One = for<'a> fn(&'a (), &'a ()); -type Two = for<'a, 'b> fn(&'a (), &'b ()); -trait AssocCt { - const ASSOC: usize; -} -const fn to_usize<T: 'static>() -> usize { - const WHAT_A_TYPE: TypeId = TypeId::of::<One>(); - match TypeId::of::<T>() { - WHAT_A_TYPE => 0, - _ => 1000, - } -} -impl<T: 'static> AssocCt for T { - const ASSOC: usize = to_usize::<T>(); -} - -trait WithAssoc<U> { - type Assoc; -} -impl<T: 'static> WithAssoc<()> for T -where - [(); <T as AssocCt>::ASSOC]:, -{ - type Assoc = [u8; <T as AssocCt>::ASSOC]; -} - -fn generic<T: 'static, U>(x: <T as WithAssoc<U>>::Assoc) -> <T as WithAssoc<U>>::Assoc -where - [(); <T as AssocCt>::ASSOC]:, - T: WithAssoc<U>, -{ - x -} - -fn unsound<T>(x: <One as WithAssoc<T>>::Assoc) -> <Two as WithAssoc<T>>::Assoc -where - One: WithAssoc<T>, -{ - let x: <Two as WithAssoc<T>>::Assoc = generic::<One, T>(x); - x -} - -fn main() { - println!("{:?}", unsound::<()>([])); -} diff --git a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr b/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr deleted file mode 100644 index 26e724c9061..00000000000 --- a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: to use a constant of type `TypeId` in a pattern, `TypeId` must be annotated with `#[derive(PartialEq)]` - --> $DIR/typeid-equality-by-subtyping.rs:18:9 - | -LL | WHAT_A_TYPE => 0, - | ^^^^^^^^^^^ - | - = note: the traits must be derived, manual `impl`s are not sufficient - = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details - -error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc<T>` is not satisfied - --> $DIR/typeid-equality-by-subtyping.rs:44:51 - | -LL | fn unsound<T>(x: <One as WithAssoc<T>>::Assoc) -> <Two as WithAssoc<T>>::Assoc - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WithAssoc<T>` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())` - -error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc<T>` is not satisfied - --> $DIR/typeid-equality-by-subtyping.rs:47:1 - | -LL | / { -LL | | let x: <Two as WithAssoc<T>>::Assoc = generic::<One, T>(x); -LL | | x -LL | | } - | |_^ the trait `WithAssoc<T>` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index 87b94a7be67..9a1b84dbf09 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -4,14 +4,13 @@ warning: creating a mutable reference to mutable static is discouraged LL | let ptr = unsafe { &mut BB }; | ^^^^^^^ mutable reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer +help: use `&raw mut` instead to create a raw pointer | -LL | let ptr = unsafe { addr_of_mut!(BB) }; - | ~~~~~~~~~~~~~ + +LL | let ptr = unsafe { &raw mut BB }; + | ~~~~~~~~ warning: 1 warning emitted diff --git a/tests/ui/consts/issue-116186.rs b/tests/ui/consts/issue-116186.rs index a77c38c64dc..8bfb47629e7 100644 --- a/tests/ui/consts/issue-116186.rs +++ b/tests/ui/consts/issue-116186.rs @@ -4,7 +4,7 @@ fn something(path: [usize; N]) -> impl Clone { //~^ ERROR cannot find value `N` in this scope match path { - [] => 0, //~ ERROR cannot pattern-match on an array without a fixed length + [] => 0, _ => 1, }; } diff --git a/tests/ui/consts/issue-116186.stderr b/tests/ui/consts/issue-116186.stderr index e6eae2d9f55..46931f79dd0 100644 --- a/tests/ui/consts/issue-116186.stderr +++ b/tests/ui/consts/issue-116186.stderr @@ -9,13 +9,6 @@ help: you might be missing a const parameter LL | fn something<const N: /* Type */>(path: [usize; N]) -> impl Clone { | +++++++++++++++++++++ -error[E0730]: cannot pattern-match on an array without a fixed length - --> $DIR/issue-116186.rs:7:9 - | -LL | [] => 0, - | ^^ - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -Some errors have detailed explanations: E0425, E0730. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/consts/static-mut-refs.rs b/tests/ui/consts/static-mut-refs.rs index d4ebfbbf17a..c0d0bb61332 100644 --- a/tests/ui/consts/static-mut-refs.rs +++ b/tests/ui/consts/static-mut-refs.rs @@ -1,6 +1,9 @@ //@ run-pass #![allow(dead_code)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + // Checks that mutable static items can have mutable slices and other references diff --git a/tests/ui/coroutine/static-mut-reference-across-yield.rs b/tests/ui/coroutine/static-mut-reference-across-yield.rs index 40d5fdf2d57..d45d6e6428b 100644 --- a/tests/ui/coroutine/static-mut-reference-across-yield.rs +++ b/tests/ui/coroutine/static-mut-reference-across-yield.rs @@ -1,6 +1,8 @@ //@ build-pass #![feature(coroutines, stmt_expr_attributes)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] static mut A: [i32; 5] = [1, 2, 3, 4, 5]; diff --git a/tests/ui/drop/drop-struct-as-object.rs b/tests/ui/drop/drop-struct-as-object.rs index 07c8950f1b2..46633bd7075 100644 --- a/tests/ui/drop/drop-struct-as-object.rs +++ b/tests/ui/drop/drop-struct-as-object.rs @@ -5,6 +5,9 @@ // Test that destructor on a struct runs successfully after the struct // is boxed and converted to an object. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + static mut value: usize = 0; struct Cat { diff --git a/tests/ui/drop/drop-struct-as-object.stderr b/tests/ui/drop/drop-struct-as-object.stderr index 10527c968ed..16f6d1110ba 100644 --- a/tests/ui/drop/drop-struct-as-object.stderr +++ b/tests/ui/drop/drop-struct-as-object.stderr @@ -1,5 +1,5 @@ warning: method `get` is never used - --> $DIR/drop-struct-as-object.rs:15:8 + --> $DIR/drop-struct-as-object.rs:18:8 | LL | trait Dummy { | ----- method in this trait diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs index 1fa68a2e738..6fee4520cbc 100644 --- a/tests/ui/drop/issue-23338-ensure-param-drop-order.rs +++ b/tests/ui/drop/issue-23338-ensure-param-drop-order.rs @@ -1,5 +1,7 @@ //@ run-pass #![allow(non_upper_case_globals)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] // This test is ensuring that parameters are indeed dropped after // temporaries in a fn body. @@ -91,7 +93,6 @@ pub mod d { pub fn max_width() -> u32 { unsafe { (mem::size_of_val(&trails) * 8) as u32 - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] } } diff --git a/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr b/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr deleted file mode 100644 index 9126e602391..00000000000 --- a/tests/ui/drop/issue-23338-ensure-param-drop-order.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/issue-23338-ensure-param-drop-order.rs:93:31 - | -LL | (mem::size_of_val(&trails) * 8) as u32 - | ^^^^^^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | (mem::size_of_val(addr_of!(trails)) * 8) as u32 - | ~~~~~~~~~ + - -warning: 1 warning emitted - diff --git a/tests/ui/drop/issue-23611-enum-swap-in-drop.rs b/tests/ui/drop/issue-23611-enum-swap-in-drop.rs index 1afaff0f735..410b07b16fc 100644 --- a/tests/ui/drop/issue-23611-enum-swap-in-drop.rs +++ b/tests/ui/drop/issue-23611-enum-swap-in-drop.rs @@ -1,5 +1,7 @@ //@ run-pass #![allow(non_upper_case_globals)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] // Issue 23611: this test is ensuring that, for an instance `X` of the // enum `E`, if you swap in a different variant during the execution @@ -187,7 +189,6 @@ pub mod d { pub fn max_width() -> u32 { unsafe { (mem::size_of_val(&trails) * 8) as u32 - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] } } diff --git a/tests/ui/drop/issue-23611-enum-swap-in-drop.stderr b/tests/ui/drop/issue-23611-enum-swap-in-drop.stderr deleted file mode 100644 index 6da87416802..00000000000 --- a/tests/ui/drop/issue-23611-enum-swap-in-drop.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/issue-23611-enum-swap-in-drop.rs:189:31 - | -LL | (mem::size_of_val(&trails) * 8) as u32 - | ^^^^^^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | (mem::size_of_val(addr_of!(trails)) * 8) as u32 - | ~~~~~~~~~ + - -warning: 1 warning emitted - diff --git a/tests/ui/drop/issue-48962.rs b/tests/ui/drop/issue-48962.rs index 428a6ca6cd2..98d3af49ac1 100644 --- a/tests/ui/drop/issue-48962.rs +++ b/tests/ui/drop/issue-48962.rs @@ -1,6 +1,9 @@ //@ run-pass #![allow(unused_must_use)] // Test that we are able to reinitialize box with moved referent +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + static mut ORDER: [usize; 3] = [0, 0, 0]; static mut INDEX: usize = 0; diff --git a/tests/ui/drop/repeat-drop.rs b/tests/ui/drop/repeat-drop.rs index b83bee8c1bf..7488d9113b0 100644 --- a/tests/ui/drop/repeat-drop.rs +++ b/tests/ui/drop/repeat-drop.rs @@ -3,6 +3,9 @@ #![allow(dropping_references, dropping_copy_types)] +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + static mut CHECK: usize = 0; struct DropChecker(usize); diff --git a/tests/ui/issues/issue-17302.rs b/tests/ui/drop/static-issue-17302.rs index c499cc5281f..060f5564efd 100644 --- a/tests/ui/issues/issue-17302.rs +++ b/tests/ui/drop/static-issue-17302.rs @@ -1,5 +1,8 @@ //@ run-pass +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + static mut DROPPED: [bool; 2] = [false, false]; struct A(usize); diff --git a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs new file mode 100644 index 00000000000..8d36981b41b --- /dev/null +++ b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.rs @@ -0,0 +1,19 @@ +// Here, there are two types with the same name. One of these has a `derive` annotation, but in the +// expansion these `impl`s are associated to the the *other* type. There is a suggestion to remove +// unneded type parameters, but because we're now point at a type with no type parameters, the +// suggestion would suggest removing code from an empty span, which would ICE in nightly. +// +// issue: rust-lang/rust#108748 + +struct NotSM; + +#[derive(PartialEq, Eq)] +//~^ ERROR struct takes 0 generic arguments +//~| ERROR struct takes 0 generic arguments +//~| ERROR struct takes 0 generic arguments +//~| ERROR struct takes 0 generic arguments +struct NotSM<T>(T); +//~^ ERROR the name `NotSM` is defined multiple times +//~| ERROR no field `0` + +fn main() {} diff --git a/tests/ui/duplicate/multiple-types-with-same-name-and-derive.stderr b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.stderr new file mode 100644 index 00000000000..32c3cf44031 --- /dev/null +++ b/tests/ui/duplicate/multiple-types-with-same-name-and-derive.stderr @@ -0,0 +1,71 @@ +error[E0428]: the name `NotSM` is defined multiple times + --> $DIR/multiple-types-with-same-name-and-derive.rs:15:1 + | +LL | struct NotSM; + | ------------- previous definition of the type `NotSM` here +... +LL | struct NotSM<T>(T); + | ^^^^^^^^^^^^^^^^^^^ `NotSM` redefined here + | + = note: `NotSM` must be defined only once in the type namespace of this module + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/multiple-types-with-same-name-and-derive.rs:10:10 + | +LL | #[derive(PartialEq, Eq)] + | ^^^^^^^^^ expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/multiple-types-with-same-name-and-derive.rs:8:8 + | +LL | struct NotSM; + | ^^^^^ + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/multiple-types-with-same-name-and-derive.rs:10:10 + | +LL | #[derive(PartialEq, Eq)] + | ^^^^^^^^^ expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/multiple-types-with-same-name-and-derive.rs:8:8 + | +LL | struct NotSM; + | ^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/multiple-types-with-same-name-and-derive.rs:10:21 + | +LL | #[derive(PartialEq, Eq)] + | ^^ expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/multiple-types-with-same-name-and-derive.rs:8:8 + | +LL | struct NotSM; + | ^^^^^ + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/multiple-types-with-same-name-and-derive.rs:10:10 + | +LL | #[derive(PartialEq, Eq)] + | ^^^^^^^^^ expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/multiple-types-with-same-name-and-derive.rs:8:8 + | +LL | struct NotSM; + | ^^^^^ + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0609]: no field `0` on type `&NotSM` + --> $DIR/multiple-types-with-same-name-and-derive.rs:15:17 + | +LL | struct NotSM<T>(T); + | ^ unknown field + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0107, E0428, E0609. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs new file mode 100644 index 00000000000..d694531d53a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.rs @@ -0,0 +1,15 @@ +#![allow(dead_code, incomplete_features)] + +use std::pin::Pin; + +struct Foo; + +fn foo(_: Pin<&mut Foo>) { +} + +fn bar(mut x: Pin<&mut Foo>) { + foo(x); + foo(x); //~ ERROR use of moved value: `x` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr new file mode 100644 index 00000000000..6c9029d8176 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-pin_ergonomics.stderr @@ -0,0 +1,21 @@ +error[E0382]: use of moved value: `x` + --> $DIR/feature-gate-pin_ergonomics.rs:12:9 + | +LL | fn bar(mut x: Pin<&mut Foo>) { + | ----- move occurs because `x` has type `Pin<&mut Foo>`, which does not implement the `Copy` trait +LL | foo(x); + | - value moved here +LL | foo(x); + | ^ value used here after move + | +note: consider changing this parameter type in function `foo` to borrow instead if owning the value isn't necessary + --> $DIR/feature-gate-pin_ergonomics.rs:7:11 + | +LL | fn foo(_: Pin<&mut Foo>) { + | --- ^^^^^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index ca60bc62b51..afffb3b1443 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -164,4 +164,28 @@ mod repr { //~| NOTE not a struct, enum, or union } + +#[repr(Rust)] +//~^ ERROR: attribute should be applied to a struct, enum, or union +mod repr_rust { +//~^ NOTE not a struct, enum, or union + mod inner { #![repr(Rust)] } + //~^ ERROR: attribute should be applied to a struct, enum, or union + //~| NOTE not a struct, enum, or union + + #[repr(Rust)] fn f() { } + //~^ ERROR: attribute should be applied to a struct, enum, or union + //~| NOTE not a struct, enum, or union + + struct S; + + #[repr(Rust)] type T = S; + //~^ ERROR: attribute should be applied to a struct, enum, or union + //~| NOTE not a struct, enum, or union + + #[repr(Rust)] impl S { } + //~^ ERROR: attribute should be applied to a struct, enum, or union + //~| NOTE not a struct, enum, or union +} + fn main() {} diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index ac2bf78157d..fe764ff4925 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -107,6 +107,21 @@ LL | | LL | | } | |_- not a struct, enum, or union +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:168:8 + | +LL | #[repr(Rust)] + | ^^^^ +LL | +LL | / mod repr_rust { +LL | | +LL | | mod inner { #![repr(Rust)] } +LL | | +... | +LL | | +LL | | } + | |_- not a struct, enum, or union + error: attribute should be applied to an `extern crate` item --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:26:1 | @@ -329,7 +344,31 @@ error[E0517]: attribute should be applied to a struct, enum, or union LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union -error: aborting due to 39 previous errors +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:172:25 + | +LL | mod inner { #![repr(Rust)] } + | --------------------^^^^---- not a struct, enum, or union + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:176:12 + | +LL | #[repr(Rust)] fn f() { } + | ^^^^ ---------- not a struct, enum, or union + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:182:12 + | +LL | #[repr(Rust)] type T = S; + | ^^^^ ----------- not a struct, enum, or union + +error[E0517]: attribute should be applied to a struct, enum, or union + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:186:12 + | +LL | #[repr(Rust)] impl S { } + | ^^^^ ---------- not a struct, enum, or union + +error: aborting due to 44 previous errors Some errors have detailed explanations: E0517, E0518, E0658. For more information about an error, try `rustc --explain E0517`. diff --git a/tests/ui/for-loop-while/cleanup-rvalue-during-if-and-while.rs b/tests/ui/for-loop-while/cleanup-rvalue-during-if-and-while.rs index fef9f24d462..57e8b228649 100644 --- a/tests/ui/for-loop-while/cleanup-rvalue-during-if-and-while.rs +++ b/tests/ui/for-loop-while/cleanup-rvalue-during-if-and-while.rs @@ -2,6 +2,9 @@ // This test verifies that temporaries created for `while`'s and `if` // conditions are dropped after the condition is evaluated. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + struct Temporary; static mut DROPPED: isize = 0; diff --git a/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.rs b/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.rs index 500633edf12..a05d20ffbd2 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.rs +++ b/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.rs @@ -2,6 +2,10 @@ // The `foo` module attempts to maintains an invariant that each `S` // has a unique `u64` id. + +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use self::foo::S; mod foo { use std::cell::{UnsafeCell}; diff --git a/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr b/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr index 692d98bc53c..8924800ef27 100644 --- a/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr +++ b/tests/ui/functional-struct-update/functional-struct-update-respects-privacy.stderr @@ -1,5 +1,5 @@ error[E0451]: field `secret_uid` of struct `S` is private - --> $DIR/functional-struct-update-respects-privacy.rs:28:49 + --> $DIR/functional-struct-update-respects-privacy.rs:32:49 | LL | let s_2 = foo::S { b: format!("ess two"), ..s_1 }; // FRU ... | ^^^ field `secret_uid` is private diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr index c14021e009b..9831348de75 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions1.stderr @@ -3,6 +3,17 @@ error: expected a pattern range bound, found an expression | LL | 0..5+1 => errors_only.push(x), | ^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 5+1; +LL ~ match x as i32 { +LL ~ 0..VAL => errors_only.push(x), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | 0..const { 5+1 } => errors_only.push(x), + | +++++++ + error[E0408]: variable `n` is not bound in all patterns --> $DIR/range_pat_interactions1.rs:10:25 diff --git a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr index 136296fa5b0..1b5e875cccb 100644 --- a/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr +++ b/tests/ui/half-open-range-patterns/range_pat_interactions2.stderr @@ -1,9 +1,3 @@ -error: expected a pattern range bound, found an expression - --> $DIR/range_pat_interactions2.rs:10:18 - | -LL | 0..=(5+1) => errors_only.push(x), - | ^^^ arbitrary expressions are not allowed in patterns - error: range pattern bounds cannot have parentheses --> $DIR/range_pat_interactions2.rs:10:17 | @@ -16,6 +10,23 @@ LL - 0..=(5+1) => errors_only.push(x), LL + 0..=5+1 => errors_only.push(x), | +error: expected a pattern range bound, found an expression + --> $DIR/range_pat_interactions2.rs:10:18 + | +LL | 0..=(5+1) => errors_only.push(x), + | ^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 5+1; +LL ~ match x as i32 { +LL ~ 0..=(VAL) => errors_only.push(x), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | 0..=(const { 5+1 }) => errors_only.push(x), + | +++++++ + + error[E0658]: inline-const in pattern position is experimental --> $DIR/range_pat_interactions2.rs:15:20 | diff --git a/tests/ui/impl-trait/issues/issue-62742.rs b/tests/ui/impl-trait/issues/issue-62742.rs index 56c63a1daa8..c64278c2c5b 100644 --- a/tests/ui/impl-trait/issues/issue-62742.rs +++ b/tests/ui/impl-trait/issues/issue-62742.rs @@ -2,7 +2,8 @@ use std::marker::PhantomData; fn a() { WrongImpl::foo(0i32); - //~^ ERROR overflow assigning `_` to `[_]` + //~^ ERROR the function or associated item `foo` exists for struct `SafeImpl<_, RawImpl<_>>`, but its trait bounds were not satisfied + //~| ERROR the trait bound `RawImpl<_>: Raw<_>` is not satisfied } fn b() { diff --git a/tests/ui/impl-trait/issues/issue-62742.stderr b/tests/ui/impl-trait/issues/issue-62742.stderr index 7a1bcfc70d5..94822e41ccd 100644 --- a/tests/ui/impl-trait/issues/issue-62742.stderr +++ b/tests/ui/impl-trait/issues/issue-62742.stderr @@ -1,11 +1,43 @@ -error[E0275]: overflow assigning `_` to `[_]` +error[E0599]: the function or associated item `foo` exists for struct `SafeImpl<_, RawImpl<_>>`, but its trait bounds were not satisfied --> $DIR/issue-62742.rs:4:16 | LL | WrongImpl::foo(0i32); - | ^^^ + | ^^^ function or associated item cannot be called on `SafeImpl<_, RawImpl<_>>` due to unsatisfied trait bounds +... +LL | pub struct RawImpl<T>(PhantomData<T>); + | --------------------- doesn't satisfy `RawImpl<_>: Raw<_>` +... +LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>); + | ----------------------------------------- function or associated item `foo` not found for this struct + | +note: trait bound `RawImpl<_>: Raw<_>` was not satisfied + --> $DIR/issue-62742.rs:35:20 + | +LL | impl<T: ?Sized, A: Raw<T>> SafeImpl<T, A> { + | ^^^^^^ -------------- + | | + | unsatisfied trait bound introduced here +note: the trait `Raw` must be implemented + --> $DIR/issue-62742.rs:19:1 + | +LL | pub trait Raw<T: ?Sized> { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `RawImpl<_>: Raw<_>` is not satisfied + --> $DIR/issue-62742.rs:4:5 + | +LL | WrongImpl::foo(0i32); + | ^^^^^^^^^ the trait `Raw<_>` is not implemented for `RawImpl<_>` + | + = help: the trait `Raw<[_]>` is implemented for `RawImpl<_>` +note: required by a bound in `SafeImpl` + --> $DIR/issue-62742.rs:33:35 + | +LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>); + | ^^^^^^ required by this bound in `SafeImpl` error[E0599]: the function or associated item `foo` exists for struct `SafeImpl<(), RawImpl<()>>`, but its trait bounds were not satisfied - --> $DIR/issue-62742.rs:9:22 + --> $DIR/issue-62742.rs:10:22 | LL | WrongImpl::<()>::foo(0i32); | ^^^ function or associated item cannot be called on `SafeImpl<(), RawImpl<()>>` due to unsatisfied trait bounds @@ -17,20 +49,20 @@ LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>); | ----------------------------------------- function or associated item `foo` not found for this struct | note: trait bound `RawImpl<()>: Raw<()>` was not satisfied - --> $DIR/issue-62742.rs:34:20 + --> $DIR/issue-62742.rs:35:20 | LL | impl<T: ?Sized, A: Raw<T>> SafeImpl<T, A> { | ^^^^^^ -------------- | | | unsatisfied trait bound introduced here note: the trait `Raw` must be implemented - --> $DIR/issue-62742.rs:18:1 + --> $DIR/issue-62742.rs:19:1 | LL | pub trait Raw<T: ?Sized> { | ^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `RawImpl<()>: Raw<()>` is not satisfied - --> $DIR/issue-62742.rs:9:5 + --> $DIR/issue-62742.rs:10:5 | LL | WrongImpl::<()>::foo(0i32); | ^^^^^^^^^^^^^^^ the trait `Raw<()>` is not implemented for `RawImpl<()>` @@ -38,12 +70,12 @@ LL | WrongImpl::<()>::foo(0i32); = help: the trait `Raw<[()]>` is implemented for `RawImpl<()>` = help: for that trait implementation, expected `[()]`, found `()` note: required by a bound in `SafeImpl` - --> $DIR/issue-62742.rs:32:35 + --> $DIR/issue-62742.rs:33:35 | LL | pub struct SafeImpl<T: ?Sized, A: Raw<T>>(PhantomData<(A, T)>); | ^^^^^^ required by this bound in `SafeImpl` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0275, E0277, E0599. -For more information about an error, try `rustc --explain E0275`. +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/opaque-hidden-inferred-rpitit.rs b/tests/ui/impl-trait/opaque-hidden-inferred-rpitit.rs new file mode 100644 index 00000000000..1582cca5cd2 --- /dev/null +++ b/tests/ui/impl-trait/opaque-hidden-inferred-rpitit.rs @@ -0,0 +1,16 @@ +//@ check-pass + +// Make sure that the `opaque_hidden_inferred_bound` lint doesn't fire on +// RPITITs with no hidden type. + +trait T0 {} + +trait T1 { + type A: Send; +} + +trait T2 { + fn foo() -> impl T1<A = ((), impl T0)>; +} + +fn main() {} diff --git a/tests/ui/inference/need_type_info/type-alias-indirect.stderr b/tests/ui/inference/need_type_info/type-alias-indirect.stderr index 5c5b4c149c1..535c0044aec 100644 --- a/tests/ui/inference/need_type_info/type-alias-indirect.stderr +++ b/tests/ui/inference/need_type_info/type-alias-indirect.stderr @@ -2,7 +2,7 @@ error[E0282]: type annotations needed --> $DIR/type-alias-indirect.rs:14:5 | LL | IndirectAlias::new(); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` + | ^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` error: aborting due to 1 previous error diff --git a/tests/ui/inference/need_type_info/type-alias.stderr b/tests/ui/inference/need_type_info/type-alias.stderr index fd9bcf2fe3f..2c39a3f5646 100644 --- a/tests/ui/inference/need_type_info/type-alias.stderr +++ b/tests/ui/inference/need_type_info/type-alias.stderr @@ -2,19 +2,19 @@ error[E0282]: type annotations needed --> $DIR/type-alias.rs:12:5 | LL | DirectAlias::new() - | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectAlias` + | ^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` error[E0282]: type annotations needed --> $DIR/type-alias.rs:18:5 | LL | IndirectAlias::new(); - | ^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` + | ^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `IndirectAlias` error[E0282]: type annotations needed --> $DIR/type-alias.rs:32:5 | LL | DirectButWithDefaultAlias::new(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the type alias `DirectButWithDefaultAlias` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` error: aborting due to 3 previous errors diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr index 15fa3f6ee11..b8b7d6e1a5e 100644 --- a/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr +++ b/tests/ui/instrument-coverage/mcdc-condition-limit.bad.stderr @@ -1,5 +1,5 @@ warning: number of conditions in decision (7) exceeds limit (6), so MC/DC analysis will not count this expression - --> $DIR/mcdc-condition-limit.rs:29:8 + --> $DIR/mcdc-condition-limit.rs:28:8 | LL | if a && b && c && d && e && f && g { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/instrument-coverage/mcdc-condition-limit.rs b/tests/ui/instrument-coverage/mcdc-condition-limit.rs index eb19ddec78f..91ff6381df7 100644 --- a/tests/ui/instrument-coverage/mcdc-condition-limit.rs +++ b/tests/ui/instrument-coverage/mcdc-condition-limit.rs @@ -1,5 +1,4 @@ //@ edition: 2021 -//@ min-llvm-version: 18 //@ revisions: good bad //@ check-pass //@ compile-flags: -Cinstrument-coverage -Zcoverage-options=mcdc -Zno-profiler-runtime diff --git a/tests/ui/invalid-compile-flags/print-without-arg.rs b/tests/ui/invalid-compile-flags/print-without-arg.rs new file mode 100644 index 00000000000..a762cb22275 --- /dev/null +++ b/tests/ui/invalid-compile-flags/print-without-arg.rs @@ -0,0 +1 @@ +//@ compile-flags: --print diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr new file mode 100644 index 00000000000..a18d2779cad --- /dev/null +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -0,0 +1,5 @@ +error: Argument to option 'print' missing + Usage: + --print [crate-name|file-names|sysroot|target-libdir|cfg|check-cfg|calling-conventions|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|all-target-specs-json|native-static-libs|stack-protector-strategies|link-args|deployment-target] + Compiler information to print on stdout + diff --git a/tests/ui/issues/issue-10734.rs b/tests/ui/issues/issue-10734.rs index 8daa401748c..6d815aeca07 100644 --- a/tests/ui/issues/issue-10734.rs +++ b/tests/ui/issues/issue-10734.rs @@ -1,6 +1,9 @@ //@ run-pass #![allow(non_upper_case_globals)] +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + static mut drop_count: usize = 0; struct Foo { diff --git a/tests/ui/issues/issue-15858.rs b/tests/ui/issues/issue-15858.rs index 2d4aac01fbe..27887d49e3e 100644 --- a/tests/ui/issues/issue-15858.rs +++ b/tests/ui/issues/issue-15858.rs @@ -1,4 +1,6 @@ //@ run-pass +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] static mut DROP_RAN: bool = false; trait Bar { diff --git a/tests/ui/issues/issue-15858.stderr b/tests/ui/issues/issue-15858.stderr index f36bcc21bd7..0c082cc0862 100644 --- a/tests/ui/issues/issue-15858.stderr +++ b/tests/ui/issues/issue-15858.stderr @@ -1,5 +1,5 @@ warning: method `do_something` is never used - --> $DIR/issue-15858.rs:5:8 + --> $DIR/issue-15858.rs:7:8 | LL | trait Bar { | --- method in this trait diff --git a/tests/ui/issues/issue-16151.rs b/tests/ui/issues/issue-16151.rs index 20a3b5a04da..b18108e0a8a 100644 --- a/tests/ui/issues/issue-16151.rs +++ b/tests/ui/issues/issue-16151.rs @@ -1,5 +1,8 @@ //@ run-pass +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + use std::mem; static mut DROP_COUNT: usize = 0; diff --git a/tests/ui/issues/issue-39367.rs b/tests/ui/issues/issue-39367.rs index dd16d4da1bd..937d6c4b9e0 100644 --- a/tests/ui/issues/issue-39367.rs +++ b/tests/ui/issues/issue-39367.rs @@ -20,6 +20,7 @@ fn arena() -> &'static ArenaSet<Vec<u8>> { static mut ONCE: Once = Once::new(); ONCE.call_once(|| { + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] DATA = transmute ::<Box<ArenaSet<Vec<u8>>>, *const ArenaSet<Vec<u8>>> (Box::new(__static_ref_initialize())); diff --git a/tests/ui/issues/issue-39367.stderr b/tests/ui/issues/issue-39367.stderr new file mode 100644 index 00000000000..333c9f9634a --- /dev/null +++ b/tests/ui/issues/issue-39367.stderr @@ -0,0 +1,17 @@ +warning: creating a shared reference to mutable static is discouraged + --> $DIR/issue-39367.rs:22:13 + | +LL | / ONCE.call_once(|| { +LL | | +LL | | DATA = transmute +LL | | ::<Box<ArenaSet<Vec<u8>>>, *const ArenaSet<Vec<u8>>> +LL | | (Box::new(__static_ref_initialize())); +LL | | }); + | |______________^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = note: `#[warn(static_mut_refs)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/issues/issue-4734.rs b/tests/ui/issues/issue-4734.rs index 938d7064789..58aa0179693 100644 --- a/tests/ui/issues/issue-4734.rs +++ b/tests/ui/issues/issue-4734.rs @@ -3,7 +3,8 @@ // Ensures that destructors are run for expressions of the form "e;" where // `e` is a type which requires a destructor. - +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] #![allow(path_statements)] struct A { n: isize } diff --git a/tests/ui/issues/issue-54410.rs b/tests/ui/issues/issue-54410.rs index 208be6f221c..e3e8ca985b9 100644 --- a/tests/ui/issues/issue-54410.rs +++ b/tests/ui/issues/issue-54410.rs @@ -5,5 +5,4 @@ extern "C" { fn main() { println!("{:p}", unsafe { &symbol }); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] } diff --git a/tests/ui/issues/issue-54410.stderr b/tests/ui/issues/issue-54410.stderr index 6cc5cd95e2f..97e5990750e 100644 --- a/tests/ui/issues/issue-54410.stderr +++ b/tests/ui/issues/issue-54410.stderr @@ -6,21 +6,6 @@ LL | pub static mut symbol: [i8]; | = help: the trait `Sized` is not implemented for `[i8]` -warning: creating a shared reference to mutable static is discouraged - --> $DIR/issue-54410.rs:7:31 - | -LL | println!("{:p}", unsafe { &symbol }); - | ^^^^^^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | println!("{:p}", unsafe { addr_of!(symbol) }); - | ~~~~~~~~~ + - -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-6892.rs b/tests/ui/issues/issue-6892.rs index aff00cf60ba..7d99aef4ac5 100644 --- a/tests/ui/issues/issue-6892.rs +++ b/tests/ui/issues/issue-6892.rs @@ -3,6 +3,8 @@ // Ensures that destructors are run for expressions of the form "let _ = e;" // where `e` is a type which requires a destructor. +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] struct Foo; struct Bar { x: isize } diff --git a/tests/ui/issues/issue-8860.rs b/tests/ui/issues/issue-8860.rs index 67e9a276ae4..3af61576fe1 100644 --- a/tests/ui/issues/issue-8860.rs +++ b/tests/ui/issues/issue-8860.rs @@ -1,4 +1,6 @@ //@ run-pass +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] #![allow(dead_code)] static mut DROP: isize = 0; diff --git a/tests/ui/link-section.rs b/tests/ui/link-section.rs index 1a791b88ef9..a8de8c2e1e7 100644 --- a/tests/ui/link-section.rs +++ b/tests/ui/link-section.rs @@ -1,5 +1,8 @@ //@ run-pass +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + #![allow(non_upper_case_globals)] #[cfg(not(target_vendor = "apple"))] #[link_section = ".moretext"] diff --git a/tests/ui/linkage-attr/linkage-attr-mutable-static.rs b/tests/ui/linkage-attr/linkage-attr-mutable-static.rs index ed11947f59e..cc93f61e28f 100644 --- a/tests/ui/linkage-attr/linkage-attr-mutable-static.rs +++ b/tests/ui/linkage-attr/linkage-attr-mutable-static.rs @@ -2,6 +2,8 @@ //! them at runtime, so deny mutable statics with #[linkage]. #![feature(linkage)] +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] fn main() { #[rustfmt::skip] diff --git a/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr b/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr index ad999769047..50d5d812196 100644 --- a/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr +++ b/tests/ui/linkage-attr/linkage-attr-mutable-static.stderr @@ -1,5 +1,5 @@ error: extern mutable statics are not allowed with `#[linkage]` - --> $DIR/linkage-attr-mutable-static.rs:9:9 + --> $DIR/linkage-attr-mutable-static.rs:11:9 | LL | #[linkage = "extern_weak"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.rs b/tests/ui/lint/improper-types-stack-overflow-130310.rs new file mode 100644 index 00000000000..60eb8739817 --- /dev/null +++ b/tests/ui/lint/improper-types-stack-overflow-130310.rs @@ -0,0 +1,20 @@ +// Regression test for #130310 +// Tests that we do not fall into infinite +// recursion while checking FFI safety of +// recursive types like `A<T>` below + +//@ build-pass +use std::marker::PhantomData; + +#[repr(C)] +struct A<T> { + a: *const A<A<T>>, // Recursive because of this field + p: PhantomData<T>, +} + +extern "C" { + fn f(a: *const A<()>); + //~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe +} + +fn main() {} diff --git a/tests/ui/lint/improper-types-stack-overflow-130310.stderr b/tests/ui/lint/improper-types-stack-overflow-130310.stderr new file mode 100644 index 00000000000..6981bb25755 --- /dev/null +++ b/tests/ui/lint/improper-types-stack-overflow-130310.stderr @@ -0,0 +1,11 @@ +warning: `extern` block uses type `*const A<()>`, which is not FFI-safe + --> $DIR/improper-types-stack-overflow-130310.rs:16:13 + | +LL | fn f(a: *const A<()>); + | ^^^^^^^^^^^^ not FFI-safe + | + = note: type is infinitely recursive + = note: `#[warn(improper_ctypes)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr new file mode 100644 index 00000000000..09f560652e7 --- /dev/null +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -0,0 +1,143 @@ +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:39:18 + | +LL | let _y = &X; + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = note: `#[warn(static_mut_refs)]` on by default +help: use `&raw const` instead to create a raw pointer + | +LL | let _y = &raw const X; + | ~~~~~~~~~~ + +warning: creating a mutable reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:43:18 + | +LL | let _y = &mut X; + | ^^^^^^ mutable reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives +help: use `&raw mut` instead to create a raw pointer + | +LL | let _y = &raw mut X; + | ~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:51:22 + | +LL | let ref _a = X; + | ^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:55:25 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let (_b, _c) = (&raw const X, &Y); + | ~~~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:55:29 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let (_b, _c) = (&X, &raw const Y); + | ~~~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:61:13 + | +LL | foo(&X); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | foo(&raw const X); + | ~~~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:67:17 + | +LL | let _ = Z.len(); + | ^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:73:33 + | +LL | let _ = format!("{:?}", Z); + | ^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:77:18 + | +LL | let _v = &A.value; + | ^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let _v = &raw const A.value; + | ~~~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:81:18 + | +LL | let _s = &A.s.value; + | ^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let _s = &raw const A.s.value; + | ~~~~~~~~~~ + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:85:22 + | +LL | let ref _v = A.value; + | ^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a mutable reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:15:14 + | +LL | &mut ($x.0) + | ^^^^^^ mutable reference to mutable static +... +LL | let _x = bar!(FOO); + | --------- in this macro invocation + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = note: this warning originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 12 warnings emitted + diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr new file mode 100644 index 00000000000..2d2a4f7afe0 --- /dev/null +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -0,0 +1,143 @@ +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:39:18 + | +LL | let _y = &X; + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = note: `#[deny(static_mut_refs)]` on by default +help: use `&raw const` instead to create a raw pointer + | +LL | let _y = &raw const X; + | ~~~~~~~~~~ + +error: creating a mutable reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:43:18 + | +LL | let _y = &mut X; + | ^^^^^^ mutable reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives +help: use `&raw mut` instead to create a raw pointer + | +LL | let _y = &raw mut X; + | ~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:51:22 + | +LL | let ref _a = X; + | ^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:55:25 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let (_b, _c) = (&raw const X, &Y); + | ~~~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:55:29 + | +LL | let (_b, _c) = (&X, &Y); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let (_b, _c) = (&X, &raw const Y); + | ~~~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:61:13 + | +LL | foo(&X); + | ^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | foo(&raw const X); + | ~~~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:67:17 + | +LL | let _ = Z.len(); + | ^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:73:33 + | +LL | let _ = format!("{:?}", Z); + | ^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:77:18 + | +LL | let _v = &A.value; + | ^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let _v = &raw const A.value; + | ~~~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:81:18 + | +LL | let _s = &A.s.value; + | ^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer + | +LL | let _s = &raw const A.s.value; + | ~~~~~~~~~~ + +error: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:85:22 + | +LL | let ref _v = A.value; + | ^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +error: creating a mutable reference to mutable static is discouraged + --> $DIR/static-mut-refs.rs:15:14 + | +LL | &mut ($x.0) + | ^^^^^^ mutable reference to mutable static +... +LL | let _x = bar!(FOO); + | --------- in this macro invocation + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 12 previous errors + diff --git a/tests/ui/lint/static-mut-refs.rs b/tests/ui/lint/static-mut-refs.rs new file mode 100644 index 00000000000..3d84d7dbf40 --- /dev/null +++ b/tests/ui/lint/static-mut-refs.rs @@ -0,0 +1,95 @@ +// Test `static_mut_refs` lint. + +//@ revisions: e2021 e2024 + +//@ [e2021] edition:2021 +//@ [e2021] run-pass + +//@ [e2024] edition:2024 +//@ [e2024] compile-flags: -Zunstable-options + +static mut FOO: (u32, u32) = (1, 2); + +macro_rules! bar { + ($x:expr) => { + &mut ($x.0) + //[e2021]~^ WARN creating a mutable reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a mutable reference to mutable static is discouraged [static_mut_refs] + }; +} + +static mut STATIC: i64 = 1; + +fn main() { + static mut X: i32 = 1; + + static mut Y: i32 = 1; + + struct TheStruct { + pub value: i32, + } + struct MyStruct { + pub value: i32, + pub s: TheStruct, + } + + static mut A: MyStruct = MyStruct { value: 1, s: TheStruct { value: 2 } }; + + unsafe { + let _y = &X; + //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + + let _y = &mut X; + //[e2021]~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR mutable reference to mutable static is discouraged [static_mut_refs] + + let _z = &raw mut X; + + let _p = &raw const X; + + let ref _a = X; + //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + + let (_b, _c) = (&X, &Y); + //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^^^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + + foo(&X); + //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + + static mut Z: &[i32; 3] = &[0, 1, 2]; + + let _ = Z.len(); + //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + + let _ = Z[0]; + + let _ = format!("{:?}", Z); + //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + + let _v = &A.value; + //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + + let _s = &A.s.value; + //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + + let ref _v = A.value; + //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + + let _x = bar!(FOO); + + STATIC += 1; + } +} + +fn foo<'a>(_x: &'a i32) {} diff --git a/tests/ui/lto/lto-still-runs-thread-dtors.rs b/tests/ui/lto/lto-still-runs-thread-dtors.rs index 504923a93c2..900368496eb 100644 --- a/tests/ui/lto/lto-still-runs-thread-dtors.rs +++ b/tests/ui/lto/lto-still-runs-thread-dtors.rs @@ -3,6 +3,9 @@ //@ no-prefer-dynamic //@ needs-threads +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + use std::thread; static mut HIT: usize = 0; diff --git a/tests/ui/macros/auxiliary/metavar_2018.rs b/tests/ui/macros/auxiliary/metavar_2018.rs new file mode 100644 index 00000000000..7e8523a9edf --- /dev/null +++ b/tests/ui/macros/auxiliary/metavar_2018.rs @@ -0,0 +1,14 @@ +//@ edition: 2018 +#[macro_export] +macro_rules! make_matcher { + ($name:ident, $fragment_type:ident, $d:tt) => { + #[macro_export] + macro_rules! $name { + ($d _:$fragment_type) => { true }; + (const { 0 }) => { false }; + (A | B) => { false }; + } + }; +} +make_matcher!(is_expr_from_2018, expr, $); +make_matcher!(is_pat_from_2018, pat, $); diff --git a/tests/ui/macros/metavar_cross_edition_recursive_macros.rs b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs new file mode 100644 index 00000000000..3eec1208b89 --- /dev/null +++ b/tests/ui/macros/metavar_cross_edition_recursive_macros.rs @@ -0,0 +1,38 @@ +//@ compile-flags: --edition=2024 -Z unstable-options +//@ aux-build: metavar_2018.rs +//@ known-bug: #130484 +//@ run-pass + +// This test captures the behavior of macro-generating-macros with fragment +// specifiers across edition boundaries. + +#![feature(expr_fragment_specifier_2024)] +#![feature(macro_metavar_expr)] +#![allow(incomplete_features)] + +extern crate metavar_2018; + +use metavar_2018::{is_expr_from_2018, is_pat_from_2018, make_matcher}; + +make_matcher!(is_expr_from_2024, expr, $); +make_matcher!(is_pat_from_2024, pat, $); + +fn main() { + // Check expr + let from_2018 = is_expr_from_2018!(const { 0 }); + dbg!(from_2018); + let from_2024 = is_expr_from_2024!(const { 0 }); + dbg!(from_2024); + + assert!(!from_2018); + assert!(!from_2024); // from_2024 will be true once #130484 is fixed + + // Check pat + let from_2018 = is_pat_from_2018!(A | B); + dbg!(from_2018); + let from_2024 = is_pat_from_2024!(A | B); + dbg!(from_2024); + + assert!(!from_2018); + assert!(!from_2024); // from_2024 will be true once #130484 is fixed +} diff --git a/tests/ui/methods/method-self-arg-trait.rs b/tests/ui/methods/method-self-arg-trait.rs index 63594380753..ccffe02328b 100644 --- a/tests/ui/methods/method-self-arg-trait.rs +++ b/tests/ui/methods/method-self-arg-trait.rs @@ -1,6 +1,9 @@ //@ run-pass // Test method calls with self as an argument +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + static mut COUNT: u64 = 1; #[derive(Copy, Clone)] diff --git a/tests/ui/methods/method-self-arg.rs b/tests/ui/methods/method-self-arg.rs index d26b9663fd0..2e058ee1077 100644 --- a/tests/ui/methods/method-self-arg.rs +++ b/tests/ui/methods/method-self-arg.rs @@ -1,6 +1,9 @@ //@ run-pass // Test method calls with self as an argument +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + static mut COUNT: usize = 1; #[derive(Copy, Clone)] diff --git a/tests/ui/methods/receiver-equality.rs b/tests/ui/methods/receiver-equality.rs new file mode 100644 index 00000000000..891435a425e --- /dev/null +++ b/tests/ui/methods/receiver-equality.rs @@ -0,0 +1,16 @@ +// Tests that we probe receivers invariantly when using path-based method lookup. + +struct B<T>(T); + +impl B<fn(&'static ())> { + fn method(self) { + println!("hey"); + } +} + +fn foo(y: B<fn(&'static ())>) { + B::<for<'a> fn(&'a ())>::method(y); + //~^ ERROR no function or associated item named `method` found +} + +fn main() {} diff --git a/tests/ui/methods/receiver-equality.stderr b/tests/ui/methods/receiver-equality.stderr new file mode 100644 index 00000000000..cea3340e386 --- /dev/null +++ b/tests/ui/methods/receiver-equality.stderr @@ -0,0 +1,12 @@ +error[E0599]: no function or associated item named `method` found for struct `B<for<'a> fn(&'a ())>` in the current scope + --> $DIR/receiver-equality.rs:12:30 + | +LL | struct B<T>(T); + | ----------- function or associated item `method` not found for this struct +... +LL | B::<for<'a> fn(&'a ())>::method(y); + | ^^^^^^ function or associated item not found in `B<fn(&())>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/mir/mir_early_return_scope.rs b/tests/ui/mir/mir_early_return_scope.rs index 6dc3f8bc39b..42f1cc50d15 100644 --- a/tests/ui/mir/mir_early_return_scope.rs +++ b/tests/ui/mir/mir_early_return_scope.rs @@ -1,5 +1,7 @@ //@ run-pass #![allow(unused_variables)] +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] static mut DROP: bool = false; struct ConnWrap(Conn); diff --git a/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.rs b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.rs new file mode 100644 index 00000000000..16c4e639c29 --- /dev/null +++ b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.rs @@ -0,0 +1,8 @@ +trait Bar { + fn foo(&mut self) -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + Self::foo() //~ ERROR this function takes 1 argument but 0 arguments were supplied + } +} + +fn main() {} diff --git a/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr new file mode 100644 index 00000000000..0e4b94b98e2 --- /dev/null +++ b/tests/ui/mismatched_types/mismatch-args-crash-issue-130400.stderr @@ -0,0 +1,26 @@ +error[E0061]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/mismatch-args-crash-issue-130400.rs:4:9 + | +LL | Self::foo() + | ^^^^^^^^^-- argument #1 is missing + | +note: method defined here + --> $DIR/mismatch-args-crash-issue-130400.rs:2:8 + | +LL | fn foo(&mut self) -> _ { + | ^^^ --------- +help: provide the argument + | +LL | Self::foo(/* value */) + | ~~~~~~~~~~~~~ + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/mismatch-args-crash-issue-130400.rs:2:26 + | +LL | fn foo(&mut self) -> _ { + | ^ not allowed in type signatures + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0061, E0121. +For more information about an error, try `rustc --explain E0061`. diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index a6d4f9a2a5c..1f01d3e8260 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -4,14 +4,13 @@ warning: creating a mutable reference to mutable static is discouraged LL | S1 { a: unsafe { &mut X1 } } | ^^^^^^^ mutable reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of_mut!` instead to create a raw pointer +help: use `&raw mut` instead to create a raw pointer | -LL | S1 { a: unsafe { addr_of_mut!(X1) } } - | ~~~~~~~~~~~~~ + +LL | S1 { a: unsafe { &raw mut X1 } } + | ~~~~~~~~ warning: 1 warning emitted diff --git a/tests/ui/nll/issue-69114-static-mut-ty.rs b/tests/ui/nll/issue-69114-static-mut-ty.rs index ce37da053e3..95c787488c0 100644 --- a/tests/ui/nll/issue-69114-static-mut-ty.rs +++ b/tests/ui/nll/issue-69114-static-mut-ty.rs @@ -1,5 +1,8 @@ // Check that borrowck ensures that `static mut` items have the expected type. +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + static FOO: u8 = 42; static mut BAR: &'static u8 = &FOO; static mut BAR_ELIDED: &u8 = &FOO; diff --git a/tests/ui/nll/issue-69114-static-mut-ty.stderr b/tests/ui/nll/issue-69114-static-mut-ty.stderr index 1b41230d7ba..eebfdee2896 100644 --- a/tests/ui/nll/issue-69114-static-mut-ty.stderr +++ b/tests/ui/nll/issue-69114-static-mut-ty.stderr @@ -1,5 +1,5 @@ error[E0597]: `n` does not live long enough - --> $DIR/issue-69114-static-mut-ty.rs:19:15 + --> $DIR/issue-69114-static-mut-ty.rs:22:15 | LL | let n = 42; | - binding `n` declared here @@ -14,7 +14,7 @@ LL | } | - `n` dropped here while still borrowed error[E0597]: `n` does not live long enough - --> $DIR/issue-69114-static-mut-ty.rs:27:22 + --> $DIR/issue-69114-static-mut-ty.rs:30:22 | LL | let n = 42; | - binding `n` declared here diff --git a/tests/ui/numbers-arithmetic/shift-near-oflo.rs b/tests/ui/numbers-arithmetic/shift-near-oflo.rs index 5cca31af0e3..97227bd843f 100644 --- a/tests/ui/numbers-arithmetic/shift-near-oflo.rs +++ b/tests/ui/numbers-arithmetic/shift-near-oflo.rs @@ -1,6 +1,9 @@ //@ run-pass //@ compile-flags: -C debug-assertions +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + // Check that we do *not* overflow on a number of edge cases. // (compare with test/run-fail/overflowing-{lsh,rsh}*.rs) diff --git a/tests/ui/parser/bad-name.rs b/tests/ui/parser/bad-name.rs index 59432a1d9a5..fefe9122a08 100644 --- a/tests/ui/parser/bad-name.rs +++ b/tests/ui/parser/bad-name.rs @@ -1,5 +1,6 @@ -//@ error-pattern: expected - fn main() { let x.y::<isize>.z foo; + //~^ error: field expressions cannot have generic arguments + //~| error: expected a pattern, found an expression + //~| error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo` } diff --git a/tests/ui/parser/bad-name.stderr b/tests/ui/parser/bad-name.stderr index e133d4e4839..3fc416dd531 100644 --- a/tests/ui/parser/bad-name.stderr +++ b/tests/ui/parser/bad-name.stderr @@ -1,8 +1,20 @@ -error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` - --> $DIR/bad-name.rs:4:8 +error: field expressions cannot have generic arguments + --> $DIR/bad-name.rs:2:12 | LL | let x.y::<isize>.z foo; - | ^ expected one of `:`, `;`, `=`, `@`, or `|` + | ^^^^^^^ -error: aborting due to 1 previous error +error: expected a pattern, found an expression + --> $DIR/bad-name.rs:2:7 + | +LL | let x.y::<isize>.z foo; + | ^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: expected one of `(`, `.`, `::`, `:`, `;`, `=`, `?`, `|`, or an operator, found `foo` + --> $DIR/bad-name.rs:2:22 + | +LL | let x.y::<isize>.z foo; + | ^^^ expected one of 9 possible tokens + +error: aborting due to 3 previous errors diff --git a/tests/ui/parser/issues/issue-24375.stderr b/tests/ui/parser/issues/issue-24375.stderr index e6ef07d13fd..a25c277d78a 100644 --- a/tests/ui/parser/issues/issue-24375.stderr +++ b/tests/ui/parser/issues/issue-24375.stderr @@ -3,6 +3,21 @@ error: expected a pattern, found an expression | LL | tmp[0] => {} | ^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == tmp[0] => {} + | ~~~ ++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = tmp[0]; +LL ~ match z { +LL ~ VAL => {} + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { tmp[0] } => {} + | +++++++ + error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr b/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr index 00f372bc008..c2c0faa21d1 100644 --- a/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr +++ b/tests/ui/parser/issues/issue-70549-resolve-after-recovered-self-ctor.stderr @@ -14,10 +14,6 @@ help: there is a keyword `mut` with a similar name | LL | fn foo(&mut Self) {} | ~~~ -help: declare the type after the parameter binding - | -LL | fn foo(<identifier>: <type>) {} - | ~~~~~~~~~~~~~~~~~~~~ error: unexpected lifetime `'static` in pattern --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:8:13 @@ -47,10 +43,6 @@ help: there is a keyword `mut` with a similar name | LL | fn bar(&'static mut Self) {} | ~~~ -help: declare the type after the parameter binding - | -LL | fn bar(<identifier>: <type>) {} - | ~~~~~~~~~~~~~~~~~~~~ error: expected one of `:`, `@`, or `|`, found keyword `Self` --> $DIR/issue-70549-resolve-after-recovered-self-ctor.rs:14:17 diff --git a/tests/ui/parser/misspelled-keywords/impl-trait.stderr b/tests/ui/parser/misspelled-keywords/impl-trait.stderr index 15a8f99b8b1..02a0c808311 100644 --- a/tests/ui/parser/misspelled-keywords/impl-trait.stderr +++ b/tests/ui/parser/misspelled-keywords/impl-trait.stderr @@ -8,10 +8,6 @@ help: there is a keyword `impl` with a similar name | LL | fn code<T: impl Debug>() -> u8 {} | ~~~~ -help: you might have meant to end the type parameters here - | -LL | fn code<T: impll> Debug>() -> u8 {} - | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr index 3a79b7bdb00..b8b52702314 100644 --- a/tests/ui/parser/misspelled-keywords/ref.stderr +++ b/tests/ui/parser/misspelled-keywords/ref.stderr @@ -8,10 +8,6 @@ help: there is a keyword `ref` with a similar name | LL | Some(ref list) => println!("{list:?}"), | ~~~ -help: missing `,` - | -LL | Some(refe, list) => println!("{list:?}"), - | + error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 1 field --> $DIR/ref.rs:4:14 diff --git a/tests/ui/parser/pat-lt-bracket-6.stderr b/tests/ui/parser/pat-lt-bracket-6.stderr index 10c638a63e4..892883c4aed 100644 --- a/tests/ui/parser/pat-lt-bracket-6.stderr +++ b/tests/ui/parser/pat-lt-bracket-6.stderr @@ -1,8 +1,8 @@ error: expected a pattern, found an expression - --> $DIR/pat-lt-bracket-6.rs:5:15 + --> $DIR/pat-lt-bracket-6.rs:5:14 | LL | let Test(&desc[..]) = x; - | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | ^^^^^^^^^ arbitrary expressions are not allowed in patterns error[E0308]: mismatched types --> $DIR/pat-lt-bracket-6.rs:10:30 diff --git a/tests/ui/parser/pat-recover-exprs.rs b/tests/ui/parser/pat-recover-exprs.rs deleted file mode 100644 index ecd471467e3..00000000000 --- a/tests/ui/parser/pat-recover-exprs.rs +++ /dev/null @@ -1,28 +0,0 @@ -fn main() { - match u8::MAX { - u8::MAX.abs() => (), - //~^ error: expected a pattern, found a method call - x.sqrt() @ .. => (), - //~^ error: expected a pattern, found a method call - //~| error: left-hand side of `@` must be a binding - z @ w @ v.u() => (), - //~^ error: expected a pattern, found a method call - y.ilog(3) => (), - //~^ error: expected a pattern, found a method call - n + 1 => (), - //~^ error: expected a pattern, found an expression - ("".f() + 14 * 8) => (), - //~^ error: expected a pattern, found an expression - 0 | ((1) | 2) | 3 => (), - f?() => (), - //~^ error: expected a pattern, found an expression - (_ + 1) => (), - //~^ error: expected one of `)`, `,`, or `|`, found `+` - } - - let 1 + 1 = 2; - //~^ error: expected a pattern, found an expression - - let b = matches!(x, (x * x | x.f()) | x[0]); - //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` -} diff --git a/tests/ui/parser/pat-recover-exprs.stderr b/tests/ui/parser/pat-recover-exprs.stderr deleted file mode 100644 index 787fd03b0c3..00000000000 --- a/tests/ui/parser/pat-recover-exprs.stderr +++ /dev/null @@ -1,76 +0,0 @@ -error: expected a pattern, found a method call - --> $DIR/pat-recover-exprs.rs:3:9 - | -LL | u8::MAX.abs() => (), - | ^^^^^^^^^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found a method call - --> $DIR/pat-recover-exprs.rs:5:9 - | -LL | x.sqrt() @ .. => (), - | ^^^^^^^^ method calls are not allowed in patterns - -error: left-hand side of `@` must be a binding - --> $DIR/pat-recover-exprs.rs:5:9 - | -LL | x.sqrt() @ .. => (), - | --------^^^-- - | | | - | | also a pattern - | interpreted as a pattern, not a binding - | - = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` - -error: expected a pattern, found a method call - --> $DIR/pat-recover-exprs.rs:8:17 - | -LL | z @ w @ v.u() => (), - | ^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found a method call - --> $DIR/pat-recover-exprs.rs:10:9 - | -LL | y.ilog(3) => (), - | ^^^^^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found an expression - --> $DIR/pat-recover-exprs.rs:12:9 - | -LL | n + 1 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns - -error: expected a pattern, found an expression - --> $DIR/pat-recover-exprs.rs:14:10 - | -LL | ("".f() + 14 * 8) => (), - | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns - -error: expected a pattern, found an expression - --> $DIR/pat-recover-exprs.rs:17:9 - | -LL | f?() => (), - | ^^^^ arbitrary expressions are not allowed in patterns - -error: expected one of `)`, `,`, or `|`, found `+` - --> $DIR/pat-recover-exprs.rs:19:12 - | -LL | (_ + 1) => (), - | ^ expected one of `)`, `,`, or `|` - -error: expected a pattern, found an expression - --> $DIR/pat-recover-exprs.rs:23:9 - | -LL | let 1 + 1 = 2; - | ^^^^^ arbitrary expressions are not allowed in patterns - -error: expected one of `)`, `,`, `@`, or `|`, found `*` - --> $DIR/pat-recover-exprs.rs:26:28 - | -LL | let b = matches!(x, (x * x | x.f()) | x[0]); - | ^ expected one of `)`, `,`, `@`, or `|` - --> $SRC_DIR/core/src/macros/mod.rs:LL:COL - | - = note: while parsing argument for this `pat` macro fragment - -error: aborting due to 11 previous errors - diff --git a/tests/ui/parser/pat-recover-methodcalls.rs b/tests/ui/parser/pat-recover-methodcalls.rs deleted file mode 100644 index 54104e9a535..00000000000 --- a/tests/ui/parser/pat-recover-methodcalls.rs +++ /dev/null @@ -1,37 +0,0 @@ -struct Foo(String); -struct Bar { baz: String } - -fn foo(foo: Foo) -> bool { - match foo { - Foo("hi".to_owned()) => true, - //~^ error: expected a pattern, found a method call - _ => false - } -} - -fn bar(bar: Bar) -> bool { - match bar { - Bar { baz: "hi".to_owned() } => true, - //~^ error: expected a pattern, found a method call - _ => false - } -} - -fn baz() { // issue #90121 - let foo = vec!["foo".to_string()]; - - match foo.as_slice() { - &["foo".to_string()] => {} - //~^ error: expected a pattern, found a method call - _ => {} - }; -} - -fn main() { - if let (-1.some(4)) = (0, Some(4)) {} - //~^ error: expected a pattern, found a method call - - if let (-1.Some(4)) = (0, Some(4)) {} - //~^ error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.` - //~| help: missing `,` -} diff --git a/tests/ui/parser/pat-recover-methodcalls.stderr b/tests/ui/parser/pat-recover-methodcalls.stderr deleted file mode 100644 index 1f9ae81dc0c..00000000000 --- a/tests/ui/parser/pat-recover-methodcalls.stderr +++ /dev/null @@ -1,35 +0,0 @@ -error: expected a pattern, found a method call - --> $DIR/pat-recover-methodcalls.rs:6:13 - | -LL | Foo("hi".to_owned()) => true, - | ^^^^^^^^^^^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found a method call - --> $DIR/pat-recover-methodcalls.rs:14:20 - | -LL | Bar { baz: "hi".to_owned() } => true, - | ^^^^^^^^^^^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found a method call - --> $DIR/pat-recover-methodcalls.rs:24:11 - | -LL | &["foo".to_string()] => {} - | ^^^^^^^^^^^^^^^^^ method calls are not allowed in patterns - -error: expected a pattern, found a method call - --> $DIR/pat-recover-methodcalls.rs:31:13 - | -LL | if let (-1.some(4)) = (0, Some(4)) {} - | ^^^^^^^^^^ method calls are not allowed in patterns - -error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.` - --> $DIR/pat-recover-methodcalls.rs:34:15 - | -LL | if let (-1.Some(4)) = (0, Some(4)) {} - | ^ - | | - | expected one of `)`, `,`, `...`, `..=`, `..`, or `|` - | help: missing `,` - -error: aborting due to 5 previous errors - diff --git a/tests/ui/parser/pat-recover-ranges.stderr b/tests/ui/parser/pat-recover-ranges.stderr deleted file mode 100644 index a7d62bd7f8a..00000000000 --- a/tests/ui/parser/pat-recover-ranges.stderr +++ /dev/null @@ -1,132 +0,0 @@ -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:4:13 - | -LL | 0..=(1) => (), - | ^ ^ - | -help: remove these parentheses - | -LL - 0..=(1) => (), -LL + 0..=1 => (), - | - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:6:9 - | -LL | (-12)..=4 => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (-12)..=4 => (), -LL + -12..=4 => (), - | - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:8:9 - | -LL | (0)..=(-4) => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (0)..=(-4) => (), -LL + 0..=(-4) => (), - | - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:8:15 - | -LL | (0)..=(-4) => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (0)..=(-4) => (), -LL + (0)..=-4 => (), - | - -error: expected a pattern range bound, found an expression - --> $DIR/pat-recover-ranges.rs:11:12 - | -LL | ..=1 + 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:13:9 - | -LL | (4).. => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (4).. => (), -LL + 4.. => (), - | - -error: expected a pattern range bound, found an expression - --> $DIR/pat-recover-ranges.rs:15:10 - | -LL | (-4 + 0).. => (), - | ^^^^^^ arbitrary expressions are not allowed in patterns - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:15:9 - | -LL | (-4 + 0).. => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (-4 + 0).. => (), -LL + -4 + 0.. => (), - | - -error: expected a pattern range bound, found an expression - --> $DIR/pat-recover-ranges.rs:18:10 - | -LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns - -error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-ranges.rs:18:9 - | -LL | (1 + 4)...1 * 2 => (), - | ^ ^ - | -help: remove these parentheses - | -LL - (1 + 4)...1 * 2 => (), -LL + 1 + 4...1 * 2 => (), - | - -error: expected a pattern range bound, found an expression - --> $DIR/pat-recover-ranges.rs:18:19 - | -LL | (1 + 4)...1 * 2 => (), - | ^^^^^ arbitrary expressions are not allowed in patterns - -error: expected a pattern range bound, found a method call - --> $DIR/pat-recover-ranges.rs:24:9 - | -LL | 0.x()..="y".z() => (), - | ^^^^^ method calls are not allowed in patterns - -error: expected a pattern range bound, found a method call - --> $DIR/pat-recover-ranges.rs:24:17 - | -LL | 0.x()..="y".z() => (), - | ^^^^^^^ method calls are not allowed in patterns - -warning: `...` range patterns are deprecated - --> $DIR/pat-recover-ranges.rs:18:16 - | -LL | (1 + 4)...1 * 2 => (), - | ^^^ help: use `..=` for an inclusive range - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> - = note: `#[warn(ellipsis_inclusive_range_patterns)]` on by default - -error: aborting due to 13 previous errors; 1 warning emitted - diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs new file mode 100644 index 00000000000..e5e25df0c01 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-exprs.rs @@ -0,0 +1,106 @@ +// FieldExpression, TupleIndexingExpression +fn field_access() { + match 0 { + x => (), + x.y => (), //~ error: expected a pattern, found an expression + x.0 => (), //~ error: expected a pattern, found an expression + x._0 => (), //~ error: expected a pattern, found an expression + x.0.1 => (), //~ error: expected a pattern, found an expression + x.4.y.17.__z => (), //~ error: expected a pattern, found an expression + } + + { let x.0e0; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + { let x.-0.0; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + { let x.-0; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + + { let x.0u32; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + { let x.0.0_f64; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` +} + +// IndexExpression, ArrayExpression +fn array_indexing() { + match 0 { + x[0] => (), //~ error: expected a pattern, found an expression + x[..] => (), //~ error: expected a pattern, found an expression + } + + { let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + { let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^ missing `,` +} + +// MethodCallExpression, CallExpression, ErrorPropagationExpression +fn method_call() { + match 0 { + x.f() => (), //~ error: expected a pattern, found an expression + x._f() => (), //~ error: expected a pattern, found an expression + x? => (), //~ error: expected a pattern, found an expression + ().f() => (), //~ error: expected a pattern, found an expression + (0, x)?.f() => (), //~ error: expected a pattern, found an expression + x.f().g() => (), //~ error: expected a pattern, found an expression + 0.f()?.g()?? => (), //~ error: expected a pattern, found an expression + } +} + +// TypeCastExpression +fn type_cast() { + match 0 { + x as usize => (), //~ error: expected a pattern, found an expression + 0 as usize => (), //~ error: expected a pattern, found an expression + x.f().0.4 as f32 => (), //~ error: expected a pattern, found an expression + } +} + +// ArithmeticOrLogicalExpression, also check if parentheses are added as needed +fn operator() { + match 0 { + 1 + 1 => (), //~ error: expected a pattern, found an expression + (1 + 2) * 3 => (), + //~^ error: expected a pattern, found an expression + //~| error: expected a pattern, found an expression + x.0 > 2 => (), //~ error: expected a pattern, found an expression + x.0 == 2 => (), //~ error: expected a pattern, found an expression + } + + // preexisting match arm guard + match (0, 0) { + (x, y.0 > 2) if x != 0 => (), //~ error: expected a pattern, found an expression + (x, y.0 > 2) if x != 0 || x != 1 => (), //~ error: expected a pattern, found an expression + } +} + +const _: u32 = match 12 { + 1 + 2 * PI.cos() => 2, //~ error: expected a pattern, found an expression + _ => 0, +}; + +fn main() { + match u8::MAX { + u8::MAX.abs() => (), + //~^ error: expected a pattern, found an expression + x.sqrt() @ .. => (), + //~^ error: expected a pattern, found an expression + //~| error: left-hand side of `@` must be a binding + z @ w @ v.u() => (), + //~^ error: expected a pattern, found an expression + y.ilog(3) => (), + //~^ error: expected a pattern, found an expression + n + 1 => (), + //~^ error: expected a pattern, found an expression + ("".f() + 14 * 8) => (), + //~^ error: expected a pattern, found an expression + 0 | ((1) | 2) | 3 => (), + f?() => (), + //~^ error: expected a pattern, found an expression + (_ + 1) => (), + //~^ error: expected one of `)`, `,`, or `|`, found `+` + } + + let 1 + 1 = 2; + //~^ error: expected a pattern, found an expression + + let b = matches!(x, (x * x | x.f()) | x[0]); + //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` +} diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr new file mode 100644 index 00000000000..63956f35c07 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -0,0 +1,772 @@ +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:5:9 + | +LL | x.y => (), + | ^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.y => (), + | ~~~ +++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.y; +LL ~ match 0 { +LL | x => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.y } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:6:9 + | +LL | x.0 => (), + | ^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.0 => (), + | ~~~ +++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.0; +LL ~ match 0 { +LL | x => (), +LL | x.y => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.0 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:7:9 + | +LL | x._0 => (), + | ^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x._0 => (), + | ~~~ ++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x._0; +LL ~ match 0 { +LL | x => (), +LL | x.y => (), +LL | x.0 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x._0 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:8:9 + | +LL | x.0.1 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.0.1 => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.0.1; +LL ~ match 0 { +LL | x => (), +... +LL | x._0 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.0.1 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:9:9 + | +LL | x.4.y.17.__z => (), + | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.4.y.17.__z => (), + | ~~~ ++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.4.y.17.__z; +LL ~ match 0 { +LL | x => (), +... +LL | x.0.1 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.4.y.17.__z } => (), + | +++++++ + + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + --> $DIR/recover-pat-exprs.rs:12:12 + | +LL | { let x.0e0; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + --> $DIR/recover-pat-exprs.rs:13:12 + | +LL | { let x.-0.0; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + --> $DIR/recover-pat-exprs.rs:14:12 + | +LL | { let x.-0; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + --> $DIR/recover-pat-exprs.rs:16:12 + | +LL | { let x.0u32; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `.` + --> $DIR/recover-pat-exprs.rs:17:12 + | +LL | { let x.0.0_f64; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:23:9 + | +LL | x[0] => (), + | ^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x[0] => (), + | ~~~ ++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x[0]; +LL ~ match 0 { +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x[0] } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:24:9 + | +LL | x[..] => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x[..] => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x[..]; +LL ~ match 0 { +LL | x[0] => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x[..] } => (), + | +++++++ + + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + --> $DIR/recover-pat-exprs.rs:27:12 + | +LL | { let x[0, 1, 2]; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + --> $DIR/recover-pat-exprs.rs:28:12 + | +LL | { let x[0; 20]; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` + --> $DIR/recover-pat-exprs.rs:29:12 + | +LL | { let x[]; } + | ^ expected one of `:`, `;`, `=`, `@`, or `|` + +error: expected one of `)`, `,`, `@`, or `|`, found `[` + --> $DIR/recover-pat-exprs.rs:30:13 + | +LL | { let (x[]); } + | ^ + | | + | expected one of `)`, `,`, `@`, or `|` + | help: missing `,` + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:37:9 + | +LL | x.f() => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.f() => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.f(); +LL ~ match 0 { +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.f() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:38:9 + | +LL | x._f() => (), + | ^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x._f() => (), + | ~~~ ++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x._f(); +LL ~ match 0 { +LL | x.f() => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x._f() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:39:9 + | +LL | x? => (), + | ^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x? => (), + | ~~~ ++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x?; +LL ~ match 0 { +LL | x.f() => (), +LL | x._f() => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x? } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:40:9 + | +LL | ().f() => (), + | ^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == ().f() => (), + | ~~~ ++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = ().f(); +LL ~ match 0 { +LL | x.f() => (), +LL | x._f() => (), +LL | x? => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { ().f() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:41:9 + | +LL | (0, x)?.f() => (), + | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == (0, x)?.f() => (), + | ~~~ +++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = (0, x)?.f(); +LL ~ match 0 { +LL | x.f() => (), +... +LL | ().f() => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { (0, x)?.f() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:42:9 + | +LL | x.f().g() => (), + | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.f().g() => (), + | ~~~ +++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.f().g(); +LL ~ match 0 { +LL | x.f() => (), +... +LL | (0, x)?.f() => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.f().g() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:43:9 + | +LL | 0.f()?.g()?? => (), + | ^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == 0.f()?.g()?? => (), + | ~~~ ++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 0.f()?.g()??; +LL ~ match 0 { +LL | x.f() => (), +... +LL | x.f().g() => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { 0.f()?.g()?? } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:50:9 + | +LL | x as usize => (), + | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x as usize => (), + | ~~~ ++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x as usize; +LL ~ match 0 { +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x as usize } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:51:9 + | +LL | 0 as usize => (), + | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == 0 as usize => (), + | ~~~ ++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 0 as usize; +LL ~ match 0 { +LL | x as usize => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { 0 as usize } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:52:9 + | +LL | x.f().0.4 as f32 => (), + | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == x.f().0.4 as f32 => (), + | ~~~ ++++++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.f().0.4 as f32; +LL ~ match 0 { +LL | x as usize => (), +LL | 0 as usize => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.f().0.4 as f32 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:59:9 + | +LL | 1 + 1 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == 1 + 1 => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 + 1; +LL ~ match 0 { +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { 1 + 1 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:60:9 + | +LL | (1 + 2) * 3 => (), + | ^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == (1 + 2) * 3 => (), + | ~~~ +++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = (1 + 2) * 3; +LL ~ match 0 { +LL | 1 + 1 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { (1 + 2) * 3 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:63:9 + | +LL | x.0 > 2 => (), + | ^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == (x.0 > 2) => (), + | ~~~ +++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.0 > 2; +LL ~ match 0 { +LL | 1 + 1 => (), +... +LL | +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.0 > 2 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:64:9 + | +LL | x.0 == 2 => (), + | ^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == (x.0 == 2) => (), + | ~~~ ++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = x.0 == 2; +LL ~ match 0 { +LL | 1 + 1 => (), +... +LL | x.0 > 2 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { x.0 == 2 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:69:13 + | +LL | (x, y.0 > 2) if x != 0 => (), + | ^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to the match arm guard + | +LL | (x, val) if x != 0 && val == (y.0 > 2) => (), + | ~~~ +++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = y.0 > 2; +LL ~ match (0, 0) { +LL ~ (x, VAL) if x != 0 => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (x, const { y.0 > 2 }) if x != 0 => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:70:13 + | +LL | (x, y.0 > 2) if x != 0 || x != 1 => (), + | ^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to the match arm guard + | +LL | (x, val) if (x != 0 || x != 1) && val == (y.0 > 2) => (), + | ~~~ + +++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = y.0 > 2; +LL ~ match (0, 0) { +LL | (x, y.0 > 2) if x != 0 => (), +LL ~ (x, VAL) if x != 0 || x != 1 => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (x, const { y.0 > 2 }) if x != 0 || x != 1 => (), + | +++++++ + + +error: left-hand side of `@` must be a binding + --> $DIR/recover-pat-exprs.rs:83:9 + | +LL | x.sqrt() @ .. => (), + | --------^^^-- + | | | + | | also a pattern + | interpreted as a pattern, not a binding + | + = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` + +error: expected one of `)`, `,`, or `|`, found `+` + --> $DIR/recover-pat-exprs.rs:97:12 + | +LL | (_ + 1) => (), + | ^ expected one of `)`, `,`, or `|` + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:81:9 + | +LL | u8::MAX.abs() => (), + | ^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == u8::MAX.abs() => (), + | ~~~ +++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = u8::MAX.abs(); +LL ~ match u8::MAX { +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { u8::MAX.abs() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:86:17 + | +LL | z @ w @ v.u() => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | z @ w @ val if val == v.u() => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = v.u(); +LL ~ match u8::MAX { +LL | u8::MAX.abs() => (), +... +LL | +LL ~ z @ w @ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | z @ w @ const { v.u() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:88:9 + | +LL | y.ilog(3) => (), + | ^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == y.ilog(3) => (), + | ~~~ +++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = y.ilog(3); +LL ~ match u8::MAX { +LL | u8::MAX.abs() => (), +... +LL | +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { y.ilog(3) } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:90:9 + | +LL | n + 1 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == n + 1 => (), + | ~~~ +++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = n + 1; +LL ~ match u8::MAX { +LL | u8::MAX.abs() => (), +... +LL | +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { n + 1 } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:92:10 + | +LL | ("".f() + 14 * 8) => (), + | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | (val) if val == "".f() + 14 * 8 => (), + | ~~~ +++++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = "".f() + 14 * 8; +LL ~ match u8::MAX { +LL | u8::MAX.abs() => (), +... +LL | +LL ~ (VAL) => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (const { "".f() + 14 * 8 }) => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:95:9 + | +LL | f?() => (), + | ^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | val if val == f?() => (), + | ~~~ ++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = f?(); +LL ~ match u8::MAX { +LL | u8::MAX.abs() => (), +... +LL | 0 | ((1) | 2) | 3 => (), +LL ~ VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { f?() } => (), + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:101:9 + | +LL | let 1 + 1 = 2; + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: expected one of `)`, `,`, `@`, or `|`, found `*` + --> $DIR/recover-pat-exprs.rs:104:28 + | +LL | let b = matches!(x, (x * x | x.f()) | x[0]); + | ^ expected one of `)`, `,`, `@`, or `|` + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL + | + = note: while parsing argument for this `pat` macro fragment + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:60:10 + | +LL | (1 + 2) * 3 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:75:5 + | +LL | 1 + 2 * PI.cos() => 2, + | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/recover-pat-exprs.rs:83:9 + | +LL | x.sqrt() @ .. => (), + | ^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: aborting due to 45 previous errors + diff --git a/tests/ui/parser/recover/recover-pat-issues.rs b/tests/ui/parser/recover/recover-pat-issues.rs new file mode 100644 index 00000000000..5b900fe80e5 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-issues.rs @@ -0,0 +1,46 @@ +struct Foo(String); +struct Bar { baz: String } + +fn foo(foo: Foo) -> bool { + match foo { + Foo("hi".to_owned()) => true, + //~^ error: expected a pattern, found an expression + _ => false + } +} + +fn bar(bar: Bar) -> bool { + match bar { + Bar { baz: "hi".to_owned() } => true, + //~^ error: expected a pattern, found an expression + _ => false + } +} + +/// Issue #90121 +fn baz() { + let foo = vec!["foo".to_string()]; + + match foo.as_slice() { + &["foo".to_string()] => {} + //~^ error: expected a pattern, found an expression + _ => {} + }; +} + +/// Issue #104996 +fn qux() { + struct Magic(pub u16); + const MAGIC: Magic = Magic(42); + + if let Some(MAGIC.0 as usize) = None::<usize> {} + //~^ error: expected a pattern, found an expression +} + +fn main() { + if let (-1.some(4)) = (0, Some(4)) {} + //~^ error: expected a pattern, found an expression + + if let (-1.Some(4)) = (0, Some(4)) {} + //~^ error: expected a pattern, found an expression +} diff --git a/tests/ui/parser/recover/recover-pat-issues.stderr b/tests/ui/parser/recover/recover-pat-issues.stderr new file mode 100644 index 00000000000..596bff21395 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-issues.stderr @@ -0,0 +1,113 @@ +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:6:13 + | +LL | Foo("hi".to_owned()) => true, + | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | Foo(val) if val == "hi".to_owned() => true, + | ~~~ +++++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = "hi".to_owned(); +LL ~ match foo { +LL ~ Foo(VAL) => true, + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | Foo(const { "hi".to_owned() }) => true, + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:14:20 + | +LL | Bar { baz: "hi".to_owned() } => true, + | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | Bar { baz } if baz == "hi".to_owned() => true, + | ~~~ +++++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const BAZ: /* Type */ = "hi".to_owned(); +LL ~ match bar { +LL ~ Bar { baz: BAZ } => true, + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | Bar { baz: const { "hi".to_owned() } } => true, + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:25:11 + | +LL | &["foo".to_string()] => {} + | ^^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider moving the expression to a match arm guard + | +LL | &[val] if val == "foo".to_string() => {} + | ~~~ +++++++++++++++++++++++++++ +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = "foo".to_string(); +LL ~ match foo.as_slice() { +LL ~ &[VAL] => {} + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | &[const { "foo".to_string() }] => {} + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:36:17 + | +LL | if let Some(MAGIC.0 as usize) = None::<usize> {} + | ^^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = MAGIC.0 as usize; +LL ~ if let Some(VAL) = None::<usize> {} + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | if let Some(const { MAGIC.0 as usize }) = None::<usize> {} + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:41:13 + | +LL | if let (-1.some(4)) = (0, Some(4)) {} + | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = -1.some(4); +LL ~ if let (VAL) = (0, Some(4)) {} + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | if let (const { -1.some(4) }) = (0, Some(4)) {} + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-issues.rs:44:13 + | +LL | if let (-1.Some(4)) = (0, Some(4)) {} + | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = -1.Some(4); +LL ~ if let (VAL) = (0, Some(4)) {} + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | if let (const { -1.Some(4) }) = (0, Some(4)) {} + | +++++++ + + +error: aborting due to 6 previous errors + diff --git a/tests/ui/parser/recover/recover-pat-lets.rs b/tests/ui/parser/recover/recover-pat-lets.rs new file mode 100644 index 00000000000..6681cc25db3 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-lets.rs @@ -0,0 +1,20 @@ +fn main() { + let x = Some(2); + + let x.expect("foo"); + //~^ error: expected a pattern, found an expression + + let x.unwrap(): u32; + //~^ error: expected a pattern, found an expression + + let x[0] = 1; + //~^ error: expected a pattern, found an expression + + let Some(1 + 1) = x else { //~ error: expected a pattern, found an expression + return; + }; + + if let Some(1 + 1) = x { //~ error: expected a pattern, found an expression + return; + } +} diff --git a/tests/ui/parser/recover/recover-pat-lets.stderr b/tests/ui/parser/recover/recover-pat-lets.stderr new file mode 100644 index 00000000000..e54586b0924 --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-lets.stderr @@ -0,0 +1,52 @@ +error: expected a pattern, found an expression + --> $DIR/recover-pat-lets.rs:4:9 + | +LL | let x.expect("foo"); + | ^^^^^^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/recover-pat-lets.rs:7:9 + | +LL | let x.unwrap(): u32; + | ^^^^^^^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/recover-pat-lets.rs:10:9 + | +LL | let x[0] = 1; + | ^^^^ arbitrary expressions are not allowed in patterns + +error: expected a pattern, found an expression + --> $DIR/recover-pat-lets.rs:13:14 + | +LL | let Some(1 + 1) = x else { + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 + 1; +LL ~ let Some(VAL) = x else { + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | let Some(const { 1 + 1 }) = x else { + | +++++++ + + +error: expected a pattern, found an expression + --> $DIR/recover-pat-lets.rs:17:17 + | +LL | if let Some(1 + 1) = x { + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 + 1; +LL ~ if let Some(VAL) = x { + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | if let Some(const { 1 + 1 }) = x { + | +++++++ + + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/pat-recover-ranges.rs b/tests/ui/parser/recover/recover-pat-ranges.rs index 7d77e950d90..e3f061c625d 100644 --- a/tests/ui/parser/pat-recover-ranges.rs +++ b/tests/ui/parser/recover/recover-pat-ranges.rs @@ -22,8 +22,8 @@ fn main() { //~| warning: `...` range patterns are deprecated //~| warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! 0.x()..="y".z() => (), - //~^ error: expected a pattern range bound, found a method call - //~| error: expected a pattern range bound, found a method call + //~^ error: expected a pattern range bound, found an expression + //~| error: expected a pattern range bound, found an expression }; } diff --git a/tests/ui/parser/recover/recover-pat-ranges.stderr b/tests/ui/parser/recover/recover-pat-ranges.stderr new file mode 100644 index 00000000000..088f83b0ccb --- /dev/null +++ b/tests/ui/parser/recover/recover-pat-ranges.stderr @@ -0,0 +1,216 @@ +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:4:13 + | +LL | 0..=(1) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - 0..=(1) => (), +LL + 0..=1 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:6:9 + | +LL | (-12)..=4 => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (-12)..=4 => (), +LL + -12..=4 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:8:9 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + 0..=(-4) => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:8:15 + | +LL | (0)..=(-4) => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (0)..=(-4) => (), +LL + (0)..=-4 => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:13:9 + | +LL | (4).. => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (4).. => (), +LL + 4.. => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:15:9 + | +LL | (-4 + 0).. => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (-4 + 0).. => (), +LL + -4 + 0.. => (), + | + +error: range pattern bounds cannot have parentheses + --> $DIR/recover-pat-ranges.rs:18:9 + | +LL | (1 + 4)...1 * 2 => (), + | ^ ^ + | +help: remove these parentheses + | +LL - (1 + 4)...1 * 2 => (), +LL + 1 + 4...1 * 2 => (), + | + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:11:12 + | +LL | ..=1 + 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 + 2; +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ ..=VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | ..=const { 1 + 2 } => (), + | +++++++ + + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:15:10 + | +LL | (-4 + 0).. => (), + | ^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = -4 + 0; +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ (VAL).. => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (const { -4 + 0 }).. => (), + | +++++++ + + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:18:10 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 + 4; +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ (VAL)...1 * 2 => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (const { 1 + 4 })...1 * 2 => (), + | +++++++ + + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:18:19 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 1 * 2; +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ (1 + 4)...VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | (1 + 4)...const { 1 * 2 } => (), + | +++++++ + + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:24:9 + | +LL | 0.x()..="y".z() => (), + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 0.x(); +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ VAL..="y".z() => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | const { 0.x() }..="y".z() => (), + | +++++++ + + +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-ranges.rs:24:17 + | +LL | 0.x()..="y".z() => (), + | ^^^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = "y".z(); +LL ~ match -1 { +LL | 0..=1 => (), +... +LL | +LL ~ 0.x()..=VAL => (), + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | 0.x()..=const { "y".z() } => (), + | +++++++ + + +warning: `...` range patterns are deprecated + --> $DIR/recover-pat-ranges.rs:18:16 + | +LL | (1 + 4)...1 * 2 => (), + | ^^^ help: use `..=` for an inclusive range + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: `#[warn(ellipsis_inclusive_range_patterns)]` on by default + +error: aborting due to 13 previous errors; 1 warning emitted + diff --git a/tests/ui/parser/pat-recover-wildcards.rs b/tests/ui/parser/recover/recover-pat-wildcards.rs index f506e2223d6..f506e2223d6 100644 --- a/tests/ui/parser/pat-recover-wildcards.rs +++ b/tests/ui/parser/recover/recover-pat-wildcards.rs diff --git a/tests/ui/parser/pat-recover-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index e36ff237bb0..30307726a97 100644 --- a/tests/ui/parser/pat-recover-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -1,35 +1,35 @@ error: expected one of `=>`, `if`, or `|`, found `+` - --> $DIR/pat-recover-wildcards.rs:5:11 + --> $DIR/recover-pat-wildcards.rs:5:11 | LL | _ + 1 => () | ^ expected one of `=>`, `if`, or `|` error: expected one of `)`, `,`, or `|`, found `%` - --> $DIR/pat-recover-wildcards.rs:11:12 + --> $DIR/recover-pat-wildcards.rs:11:12 | LL | (_ % 4) => () | ^ expected one of `)`, `,`, or `|` error: expected one of `=>`, `if`, or `|`, found `.` - --> $DIR/pat-recover-wildcards.rs:17:10 + --> $DIR/recover-pat-wildcards.rs:17:10 | LL | _.x() => () | ^ expected one of `=>`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `..=` - --> $DIR/pat-recover-wildcards.rs:23:10 + --> $DIR/recover-pat-wildcards.rs:23:10 | LL | _..=4 => () | ^^^ expected one of `=>`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` - --> $DIR/pat-recover-wildcards.rs:29:11 + --> $DIR/recover-pat-wildcards.rs:29:11 | LL | .._ => () | ^ expected one of `=>`, `if`, or `|` error[E0586]: inclusive range with no end - --> $DIR/pat-recover-wildcards.rs:35:10 + --> $DIR/recover-pat-wildcards.rs:35:10 | LL | 0..._ => () | ^^^ @@ -42,31 +42,25 @@ LL + 0.._ => () | error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` - --> $DIR/pat-recover-wildcards.rs:35:13 + --> $DIR/recover-pat-wildcards.rs:35:13 | LL | 0..._ => () | ^ expected one of `=>`, `if`, or `|` error: expected one of `)`, `,`, or `|`, found `*` - --> $DIR/pat-recover-wildcards.rs:43:12 + --> $DIR/recover-pat-wildcards.rs:43:12 | LL | (_ * 0)..5 => () | ^ expected one of `)`, `,`, or `|` error: expected one of `=>`, `if`, or `|`, found `(` - --> $DIR/pat-recover-wildcards.rs:49:11 + --> $DIR/recover-pat-wildcards.rs:49:11 | LL | ..(_) => () | ^ expected one of `=>`, `if`, or `|` -error: expected a pattern range bound, found an expression - --> $DIR/pat-recover-wildcards.rs:55:14 - | -LL | 4..=(2 + _) => () - | ^^^^^ arbitrary expressions are not allowed in patterns - error: range pattern bounds cannot have parentheses - --> $DIR/pat-recover-wildcards.rs:55:13 + --> $DIR/recover-pat-wildcards.rs:55:13 | LL | 4..=(2 + _) => () | ^ ^ @@ -77,6 +71,23 @@ LL - 4..=(2 + _) => () LL + 4..=2 + _ => () | +error: expected a pattern range bound, found an expression + --> $DIR/recover-pat-wildcards.rs:55:14 + | +LL | 4..=(2 + _) => () + | ^^^^^ arbitrary expressions are not allowed in patterns + | +help: consider extracting the expression into a `const` + | +LL + const VAL: /* Type */ = 2 + _; +LL ~ match 9 { +LL ~ 4..=(VAL) => () + | +help: consider wrapping the expression in an inline `const` (requires `#![feature(inline_const_pat)]`) + | +LL | 4..=(const { 2 + _ }) => () + | +++++++ + + error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0586`. diff --git a/tests/ui/parser/triple-colon-delegation.fixed b/tests/ui/parser/triple-colon-delegation.fixed new file mode 100644 index 00000000000..fbb614b57da --- /dev/null +++ b/tests/ui/parser/triple-colon-delegation.fixed @@ -0,0 +1,44 @@ +//@ run-rustfix + +#![feature(fn_delegation)] +#![allow(incomplete_features, unused)] + +trait Trait { + fn foo(&self) {} +} + +struct F; +impl Trait for F {} + +pub mod to_reuse { + pub fn bar() {} +} + +mod fn_to_other { + use super::*; + + reuse Trait::foo; //~ ERROR path separator must be a double colon + reuse to_reuse::bar; //~ ERROR path separator must be a double colon +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait::* { //~ ERROR path separator must be a double colon + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); +} diff --git a/tests/ui/parser/triple-colon-delegation.rs b/tests/ui/parser/triple-colon-delegation.rs new file mode 100644 index 00000000000..9fbaa4477ae --- /dev/null +++ b/tests/ui/parser/triple-colon-delegation.rs @@ -0,0 +1,44 @@ +//@ run-rustfix + +#![feature(fn_delegation)] +#![allow(incomplete_features, unused)] + +trait Trait { + fn foo(&self) {} +} + +struct F; +impl Trait for F {} + +pub mod to_reuse { + pub fn bar() {} +} + +mod fn_to_other { + use super::*; + + reuse Trait:::foo; //~ ERROR path separator must be a double colon + reuse to_reuse:::bar; //~ ERROR path separator must be a double colon +} + +impl Trait for u8 {} + +struct S(u8); + +mod to_import { + pub fn check(arg: &u8) -> &u8 { arg } +} + +impl Trait for S { + reuse Trait:::* { //~ ERROR path separator must be a double colon + use to_import::check; + + let _arr = Some(self.0).map(|x| [x * 2; 3]); + check(&self.0) + } +} + +fn main() { + let s = S(0); + s.foo(); +} diff --git a/tests/ui/parser/triple-colon-delegation.stderr b/tests/ui/parser/triple-colon-delegation.stderr new file mode 100644 index 00000000000..d748c7d92b5 --- /dev/null +++ b/tests/ui/parser/triple-colon-delegation.stderr @@ -0,0 +1,38 @@ +error: path separator must be a double colon + --> $DIR/triple-colon-delegation.rs:20:18 + | +LL | reuse Trait:::foo; + | ^ + | +help: use a double colon instead + | +LL - reuse Trait:::foo; +LL + reuse Trait::foo; + | + +error: path separator must be a double colon + --> $DIR/triple-colon-delegation.rs:21:21 + | +LL | reuse to_reuse:::bar; + | ^ + | +help: use a double colon instead + | +LL - reuse to_reuse:::bar; +LL + reuse to_reuse::bar; + | + +error: path separator must be a double colon + --> $DIR/triple-colon-delegation.rs:33:18 + | +LL | reuse Trait:::* { + | ^ + | +help: use a double colon instead + | +LL - reuse Trait:::* { +LL + reuse Trait::* { + | + +error: aborting due to 3 previous errors + diff --git a/tests/ui/parser/triple-colon.fixed b/tests/ui/parser/triple-colon.fixed new file mode 100644 index 00000000000..168e4c1f618 --- /dev/null +++ b/tests/ui/parser/triple-colon.fixed @@ -0,0 +1,23 @@ +//@ run-rustfix + +#![allow(unused)] + +use ::std::{cell as _}; //~ ERROR path separator must be a double colon +use std::cell::*; //~ ERROR path separator must be a double colon +use std::cell::Cell; //~ ERROR path separator must be a double colon +use std::{cell as _}; //~ ERROR path separator must be a double colon + +mod foo{ + use ::{}; //~ ERROR path separator must be a double colon + use ::*; //~ ERROR path separator must be a double colon +} + +fn main() { + let c: ::std::cell::Cell::<u8> = Cell::<u8>::new(0); + //~^ ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon +} diff --git a/tests/ui/parser/triple-colon.rs b/tests/ui/parser/triple-colon.rs new file mode 100644 index 00000000000..1a70012685f --- /dev/null +++ b/tests/ui/parser/triple-colon.rs @@ -0,0 +1,23 @@ +//@ run-rustfix + +#![allow(unused)] + +use :::std::{cell as _}; //~ ERROR path separator must be a double colon +use std::cell:::*; //~ ERROR path separator must be a double colon +use std::cell:::Cell; //~ ERROR path separator must be a double colon +use std:::{cell as _}; //~ ERROR path separator must be a double colon + +mod foo{ + use :::{}; //~ ERROR path separator must be a double colon + use :::*; //~ ERROR path separator must be a double colon +} + +fn main() { + let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + //~^ ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon + //~| ERROR path separator must be a double colon +} diff --git a/tests/ui/parser/triple-colon.stderr b/tests/ui/parser/triple-colon.stderr new file mode 100644 index 00000000000..8d57fd7ebc9 --- /dev/null +++ b/tests/ui/parser/triple-colon.stderr @@ -0,0 +1,146 @@ +error: path separator must be a double colon + --> $DIR/triple-colon.rs:5:7 + | +LL | use :::std::{cell as _}; + | ^ + | +help: use a double colon instead + | +LL - use :::std::{cell as _}; +LL + use ::std::{cell as _}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:6:16 + | +LL | use std::cell:::*; + | ^ + | +help: use a double colon instead + | +LL - use std::cell:::*; +LL + use std::cell::*; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:7:16 + | +LL | use std::cell:::Cell; + | ^ + | +help: use a double colon instead + | +LL - use std::cell:::Cell; +LL + use std::cell::Cell; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:8:10 + | +LL | use std:::{cell as _}; + | ^ + | +help: use a double colon instead + | +LL - use std:::{cell as _}; +LL + use std::{cell as _}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:11:11 + | +LL | use :::{}; + | ^ + | +help: use a double colon instead + | +LL - use :::{}; +LL + use ::{}; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:12:11 + | +LL | use :::*; + | ^ + | +help: use a double colon instead + | +LL - use :::*; +LL + use ::*; + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:14 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: ::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:20 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: :::std::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:27 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: :::std:::cell::Cell:::<u8> = Cell:::<u8>:::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:34 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: :::std:::cell:::Cell::<u8> = Cell:::<u8>:::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:48 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: :::std:::cell:::Cell:::<u8> = Cell::<u8>:::new(0); + | + +error: path separator must be a double colon + --> $DIR/triple-colon.rs:16:55 + | +LL | let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); + | ^ + | +help: use a double colon instead + | +LL - let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>:::new(0); +LL + let c: :::std:::cell:::Cell:::<u8> = Cell:::<u8>::new(0); + | + +error: aborting due to 12 previous errors + diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.rs b/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.rs new file mode 100644 index 00000000000..a66c4464417 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.rs @@ -0,0 +1,16 @@ +// Make sure we consider `!` to be a union read. + +#![feature(never_type, never_patterns)] +//~^ WARN the feature `never_patterns` is incomplete + +union U { + a: !, + b: usize, +} + +fn foo<T>(u: U) -> ! { + let U { a: ! } = u; + //~^ ERROR access to union field is unsafe +} + +fn main() {} diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.stderr new file mode 100644 index 00000000000..d7dc7a47e72 --- /dev/null +++ b/tests/ui/rfcs/rfc-0000-never_patterns/never-pattern-is-a-read.stderr @@ -0,0 +1,20 @@ +warning: the feature `never_patterns` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/never-pattern-is-a-read.rs:3:24 + | +LL | #![feature(never_type, never_patterns)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #118155 <https://github.com/rust-lang/rust/issues/118155> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/never-pattern-is-a-read.rs:12:16 + | +LL | let U { a: ! } = u; + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/rust-2024/raw-gen-lt.rs b/tests/ui/rust-2024/raw-gen-lt.rs new file mode 100644 index 00000000000..b2df217d1a1 --- /dev/null +++ b/tests/ui/rust-2024/raw-gen-lt.rs @@ -0,0 +1,8 @@ +//@ edition: 2021 +//@ check-pass + +#![deny(keyword_idents_2024)] + +fn foo<'r#gen>() {} + +fn main() {} diff --git a/tests/ui/self/where-for-self.rs b/tests/ui/self/where-for-self.rs index 9b4e8325664..d31bb1f22c4 100644 --- a/tests/ui/self/where-for-self.rs +++ b/tests/ui/self/where-for-self.rs @@ -2,6 +2,8 @@ // Test that we can quantify lifetimes outside a constraint (i.e., including // the self type) in a where clause. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] static mut COUNT: u32 = 1; diff --git a/tests/ui/simd/simd-bitmask-notpow2.rs b/tests/ui/simd/simd-bitmask-notpow2.rs index ff43206a3fd..3499bf33ed5 100644 --- a/tests/ui/simd/simd-bitmask-notpow2.rs +++ b/tests/ui/simd/simd-bitmask-notpow2.rs @@ -1,7 +1,6 @@ //@run-pass -// SEGFAULTS on LLVM 17. This should be merged into `simd-bitmask` once we require LLVM 18. -//@ min-llvm-version: 18 // FIXME: broken codegen on big-endian (https://github.com/rust-lang/rust/issues/127205) +// This should be merged into `simd-bitmask` once that's fixed. //@ ignore-endian-big #![feature(repr_simd, intrinsics)] diff --git a/tests/ui/static/reference-to-mut-static-safe.e2021.stderr b/tests/ui/static/reference-to-mut-static-safe.e2021.stderr deleted file mode 100644 index 9fdfc00dfcd..00000000000 --- a/tests/ui/static/reference-to-mut-static-safe.e2021.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static-safe.rs:9:14 - | -LL | let _x = &X; - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | let _x = addr_of!(X); - | ~~~~~~~~~ + - -error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/reference-to-mut-static-safe.rs:9:15 - | -LL | let _x = &X; - | ^ use of mutable static - | - = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior - -error: aborting due to 1 previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/static/reference-to-mut-static-safe.e2024.stderr b/tests/ui/static/reference-to-mut-static-safe.e2024.stderr deleted file mode 100644 index b3e0c84d1d8..00000000000 --- a/tests/ui/static/reference-to-mut-static-safe.e2024.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static-safe.rs:9:14 - | -LL | let _x = &X; - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let _x = addr_of!(X); - | ~~~~~~~~~ + - -error[E0133]: use of mutable static is unsafe and requires unsafe block - --> $DIR/reference-to-mut-static-safe.rs:9:15 - | -LL | let _x = &X; - | ^ use of mutable static - | - = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0133, E0796. -For more information about an error, try `rustc --explain E0133`. diff --git a/tests/ui/static/reference-to-mut-static-safe.rs b/tests/ui/static/reference-to-mut-static-safe.rs deleted file mode 100644 index 98afdadf4d2..00000000000 --- a/tests/ui/static/reference-to-mut-static-safe.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ revisions: e2021 e2024 - -//@ [e2021] edition:2021 -//@ [e2024] compile-flags: --edition 2024 -Z unstable-options - -fn main() { - static mut X: i32 = 1; - - let _x = &X; - //[e2024]~^ creating a shared reference to a mutable static [E0796] - //~^^ use of mutable static is unsafe and requires unsafe - //[e2021]~^^^ shared reference to mutable static is discouraged [static_mut_refs] -} diff --git a/tests/ui/static/reference-to-mut-static-unsafe-fn.rs b/tests/ui/static/reference-to-mut-static-unsafe-fn.rs deleted file mode 100644 index d63fd5460d8..00000000000 --- a/tests/ui/static/reference-to-mut-static-unsafe-fn.rs +++ /dev/null @@ -1,28 +0,0 @@ -//@ compile-flags: --edition 2024 -Z unstable-options - -fn main() {} - -unsafe fn _foo() { - unsafe { - static mut X: i32 = 1; - static mut Y: i32 = 1; - - let _y = &X; - //~^ ERROR creating a shared reference to a mutable static [E0796] - - let ref _a = X; - //~^ ERROR creating a shared reference to a mutable static [E0796] - - let ref mut _a = X; - //~^ ERROR creating a mutable reference to a mutable static [E0796] - - let (_b, _c) = (&X, &mut Y); - //~^ ERROR creating a shared reference to a mutable static [E0796] - //~^^ ERROR creating a mutable reference to a mutable static [E0796] - - foo(&X); - //~^ ERROR creating a shared reference to a mutable static [E0796] - } -} - -fn foo<'a>(_x: &'a i32) {} diff --git a/tests/ui/static/reference-to-mut-static-unsafe-fn.stderr b/tests/ui/static/reference-to-mut-static-unsafe-fn.stderr deleted file mode 100644 index ca9cfbf7ac7..00000000000 --- a/tests/ui/static/reference-to-mut-static-unsafe-fn.stderr +++ /dev/null @@ -1,75 +0,0 @@ -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:10:18 - | -LL | let _y = &X; - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let _y = addr_of!(X); - | ~~~~~~~~~ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:13:22 - | -LL | let ref _a = X; - | ^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let ref _a = addr_of!(X); - | +++++++++ + - -error[E0796]: creating a mutable reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:16:26 - | -LL | let ref mut _a = X; - | ^ mutable reference to mutable static - | - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | let ref mut _a = addr_of_mut!(X); - | +++++++++++++ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:19:25 - | -LL | let (_b, _c) = (&X, &mut Y); - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let (_b, _c) = (addr_of!(X), &mut Y); - | ~~~~~~~~~ + - -error[E0796]: creating a mutable reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:19:29 - | -LL | let (_b, _c) = (&X, &mut Y); - | ^^^^^^ mutable reference to mutable static - | - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | let (_b, _c) = (&X, addr_of_mut!(Y)); - | ~~~~~~~~~~~~~ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static-unsafe-fn.rs:23:13 - | -LL | foo(&X); - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | foo(addr_of!(X)); - | ~~~~~~~~~ + - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0796`. diff --git a/tests/ui/static/reference-to-mut-static.e2021.stderr b/tests/ui/static/reference-to-mut-static.e2021.stderr deleted file mode 100644 index 667d7602f34..00000000000 --- a/tests/ui/static/reference-to-mut-static.e2021.stderr +++ /dev/null @@ -1,91 +0,0 @@ -error: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:16:18 - | -LL | let _y = &X; - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -note: the lint level is defined here - --> $DIR/reference-to-mut-static.rs:6:9 - | -LL | #![deny(static_mut_refs)] - | ^^^^^^^^^^^^^^^ -help: use `addr_of!` instead to create a raw pointer - | -LL | let _y = addr_of!(X); - | ~~~~~~~~~ + - -error: creating a mutable reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:20:18 - | -LL | let _y = &mut X; - | ^^^^^^ mutable reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | let _y = addr_of_mut!(X); - | ~~~~~~~~~~~~~ + - -error: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:28:22 - | -LL | let ref _a = X; - | ^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let ref _a = addr_of!(X); - | +++++++++ + - -error: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:32:25 - | -LL | let (_b, _c) = (&X, &Y); - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let (_b, _c) = (addr_of!(X), &Y); - | ~~~~~~~~~ + - -error: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:32:29 - | -LL | let (_b, _c) = (&X, &Y); - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let (_b, _c) = (&X, addr_of!(Y)); - | ~~~~~~~~~ + - -error: creating a shared reference to mutable static is discouraged - --> $DIR/reference-to-mut-static.rs:38:13 - | -LL | foo(&X); - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | foo(addr_of!(X)); - | ~~~~~~~~~ + - -error: aborting due to 6 previous errors - diff --git a/tests/ui/static/reference-to-mut-static.e2024.stderr b/tests/ui/static/reference-to-mut-static.e2024.stderr deleted file mode 100644 index e77f4355466..00000000000 --- a/tests/ui/static/reference-to-mut-static.e2024.stderr +++ /dev/null @@ -1,75 +0,0 @@ -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static.rs:16:18 - | -LL | let _y = &X; - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let _y = addr_of!(X); - | ~~~~~~~~~ + - -error[E0796]: creating a mutable reference to a mutable static - --> $DIR/reference-to-mut-static.rs:20:18 - | -LL | let _y = &mut X; - | ^^^^^^ mutable reference to mutable static - | - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer - | -LL | let _y = addr_of_mut!(X); - | ~~~~~~~~~~~~~ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static.rs:28:22 - | -LL | let ref _a = X; - | ^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let ref _a = addr_of!(X); - | +++++++++ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static.rs:32:25 - | -LL | let (_b, _c) = (&X, &Y); - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let (_b, _c) = (addr_of!(X), &Y); - | ~~~~~~~~~ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static.rs:32:29 - | -LL | let (_b, _c) = (&X, &Y); - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let (_b, _c) = (&X, addr_of!(Y)); - | ~~~~~~~~~ + - -error[E0796]: creating a shared reference to a mutable static - --> $DIR/reference-to-mut-static.rs:38:13 - | -LL | foo(&X); - | ^^ shared reference to mutable static - | - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | foo(addr_of!(X)); - | ~~~~~~~~~ + - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0796`. diff --git a/tests/ui/static/reference-to-mut-static.rs b/tests/ui/static/reference-to-mut-static.rs deleted file mode 100644 index af2cab7dd87..00000000000 --- a/tests/ui/static/reference-to-mut-static.rs +++ /dev/null @@ -1,50 +0,0 @@ -//@ revisions: e2021 e2024 - -//@ [e2021] edition:2021 -//@ [e2024] compile-flags: --edition 2024 -Z unstable-options - -#![deny(static_mut_refs)] - -use std::ptr::{addr_of, addr_of_mut}; - -fn main() { - static mut X: i32 = 1; - - static mut Y: i32 = 1; - - unsafe { - let _y = &X; - //[e2024]~^ ERROR creating a shared reference to a mutable static [E0796] - //[e2021]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - - let _y = &mut X; - //[e2024]~^ ERROR creating a mutable reference to a mutable static [E0796] - //[e2021]~^^ ERROR mutable reference to mutable static is discouraged [static_mut_refs] - - let _z = addr_of_mut!(X); - - let _p = addr_of!(X); - - let ref _a = X; - //[e2024]~^ ERROR creating a shared reference to a mutable static [E0796] - //[e2021]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - - let (_b, _c) = (&X, &Y); - //[e2024]~^ ERROR creating a shared reference to a mutable static [E0796] - //[e2021]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^^ ERROR creating a shared reference to a mutable static [E0796] - //[e2021]~^^^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - - foo(&X); - //[e2024]~^ ERROR creating a shared reference to a mutable static [E0796] - //[e2021]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - - static mut Z: &[i32; 3] = &[0, 1, 2]; - - let _ = Z.len(); - let _ = Z[0]; - let _ = format!("{:?}", Z); - } -} - -fn foo<'a>(_x: &'a i32) {} diff --git a/tests/ui/static/safe-extern-statics-mut.rs b/tests/ui/static/safe-extern-statics-mut.rs index 05a1bee8891..4dfcd41b73c 100644 --- a/tests/ui/static/safe-extern-statics-mut.rs +++ b/tests/ui/static/safe-extern-statics-mut.rs @@ -10,8 +10,6 @@ extern "C" { fn main() { let b = B; //~ ERROR use of mutable static is unsafe let rb = &B; //~ ERROR use of mutable static is unsafe - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] let xb = XB; //~ ERROR use of mutable static is unsafe let xrb = &XB; //~ ERROR use of mutable static is unsafe - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] } diff --git a/tests/ui/static/safe-extern-statics-mut.stderr b/tests/ui/static/safe-extern-statics-mut.stderr index 7705a897e27..e390625f20a 100644 --- a/tests/ui/static/safe-extern-statics-mut.stderr +++ b/tests/ui/static/safe-extern-statics-mut.stderr @@ -1,32 +1,3 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/safe-extern-statics-mut.rs:12:14 - | -LL | let rb = &B; - | ^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer - | -LL | let rb = addr_of!(B); - | ~~~~~~~~~ + - -warning: creating a shared reference to mutable static is discouraged - --> $DIR/safe-extern-statics-mut.rs:15:15 - | -LL | let xrb = &XB; - | ^^^ shared reference to mutable static - | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior -help: use `addr_of!` instead to create a raw pointer - | -LL | let xrb = addr_of!(XB); - | ~~~~~~~~~ + - error[E0133]: use of mutable static is unsafe and requires unsafe function or block --> $DIR/safe-extern-statics-mut.rs:11:13 | @@ -44,7 +15,7 @@ LL | let rb = &B; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:14:14 + --> $DIR/safe-extern-statics-mut.rs:13:14 | LL | let xb = XB; | ^^ use of mutable static @@ -52,13 +23,13 @@ LL | let xb = XB; = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior error[E0133]: use of mutable static is unsafe and requires unsafe function or block - --> $DIR/safe-extern-statics-mut.rs:15:16 + --> $DIR/safe-extern-statics-mut.rs:14:16 | LL | let xrb = &XB; | ^^ use of mutable static | = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior -error: aborting due to 4 previous errors; 2 warnings emitted +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index 6035eef5b71..417dbae9db1 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -4,14 +4,13 @@ warning: creating a shared reference to mutable static is discouraged LL | static n: &'static usize = unsafe { &n_mut }; | ^^^^^^ shared reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer +help: use `&raw const` instead to create a raw pointer | -LL | static n: &'static usize = unsafe { addr_of!(n_mut) }; - | ~~~~~~~~~ + +LL | static n: &'static usize = unsafe { &raw const n_mut }; + | ~~~~~~~~~~ warning: 1 warning emitted diff --git a/tests/ui/statics/static-mut-xc.rs b/tests/ui/statics/static-mut-xc.rs index a772d4151f7..c23cc822ce7 100644 --- a/tests/ui/statics/static-mut-xc.rs +++ b/tests/ui/statics/static-mut-xc.rs @@ -17,14 +17,19 @@ fn static_bound_set(a: &'static mut isize) { unsafe fn run() { assert_eq!(static_mut_xc::a, 3); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] static_mut_xc::a = 4; assert_eq!(static_mut_xc::a, 4); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] static_mut_xc::a += 1; assert_eq!(static_mut_xc::a, 5); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] static_mut_xc::a *= 3; assert_eq!(static_mut_xc::a, 15); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] static_mut_xc::a = -3; assert_eq!(static_mut_xc::a, -3); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] static_bound(&static_mut_xc::a); //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] static_bound_set(&mut static_mut_xc::a); diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index 9751f754332..176deb518fc 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -1,31 +1,74 @@ warning: creating a shared reference to mutable static is discouraged - --> $DIR/static-mut-xc.rs:28:18 + --> $DIR/static-mut-xc.rs:19:16 + | +LL | assert_eq!(static_mut_xc::a, 3); + | ^^^^^^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + = note: `#[warn(static_mut_refs)]` on by default + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-xc.rs:22:16 + | +LL | assert_eq!(static_mut_xc::a, 4); + | ^^^^^^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-xc.rs:25:16 + | +LL | assert_eq!(static_mut_xc::a, 5); + | ^^^^^^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-xc.rs:28:16 + | +LL | assert_eq!(static_mut_xc::a, 15); + | ^^^^^^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-xc.rs:31:16 + | +LL | assert_eq!(static_mut_xc::a, -3); + | ^^^^^^^^^^^^^^^^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-mut-xc.rs:33:18 | LL | static_bound(&static_mut_xc::a); | ^^^^^^^^^^^^^^^^^ shared reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior - = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives +help: use `&raw const` instead to create a raw pointer | -LL | static_bound(addr_of!(static_mut_xc::a)); - | ~~~~~~~~~ + +LL | static_bound(&raw const static_mut_xc::a); + | ~~~~~~~~~~ warning: creating a mutable reference to mutable static is discouraged - --> $DIR/static-mut-xc.rs:30:22 + --> $DIR/static-mut-xc.rs:35:22 | LL | static_bound_set(&mut static_mut_xc::a); | ^^^^^^^^^^^^^^^^^^^^^ mutable reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior -help: use `addr_of_mut!` instead to create a raw pointer + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives +help: use `&raw mut` instead to create a raw pointer | -LL | static_bound_set(addr_of_mut!(static_mut_xc::a)); - | ~~~~~~~~~~~~~ + +LL | static_bound_set(&raw mut static_mut_xc::a); + | ~~~~~~~~ -warning: 2 warnings emitted +warning: 7 warnings emitted diff --git a/tests/ui/statics/static-recursive.rs b/tests/ui/statics/static-recursive.rs index 29b80818b7d..da23b54d1fc 100644 --- a/tests/ui/statics/static-recursive.rs +++ b/tests/ui/statics/static-recursive.rs @@ -17,6 +17,7 @@ static L3: StaticDoubleLinked = StaticDoubleLinked { prev: &L2, next: &L1, data: pub fn main() { unsafe { assert_eq!(S, *(S as *const *const u8)); + //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] } let mut test_vec = Vec::new(); diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index a7a1a1610af..f2dd5b8a6cf 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -4,14 +4,22 @@ warning: creating a shared reference to mutable static is discouraged LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; | ^^ shared reference to mutable static | - = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447> - = note: this will be a hard error in the 2024 edition - = note: this shared reference has lifetime `'static`, but if the static ever gets mutated, or a mutable reference is created, then any further use of this shared reference is Undefined Behavior + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[warn(static_mut_refs)]` on by default -help: use `addr_of!` instead to create a raw pointer +help: use `&raw const` instead to create a raw pointer | -LL | static mut S: *const u8 = unsafe { addr_of!(S) as *const *const u8 as *const u8 }; - | ~~~~~~~~~ + +LL | static mut S: *const u8 = unsafe { &raw const S as *const *const u8 as *const u8 }; + | ~~~~~~~~~~ -warning: 1 warning emitted +warning: creating a shared reference to mutable static is discouraged + --> $DIR/static-recursive.rs:19:20 + | +LL | assert_eq!(S, *(S as *const *const u8)); + | ^ shared reference to mutable static + | + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> + = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives + +warning: 2 warnings emitted diff --git a/tests/ui/structs-enums/type-sizes.rs b/tests/ui/structs-enums/type-sizes.rs index 5ca9c8678b7..f49ce33841a 100644 --- a/tests/ui/structs-enums/type-sizes.rs +++ b/tests/ui/structs-enums/type-sizes.rs @@ -209,6 +209,23 @@ struct ReorderEndNiche { b: MiddleNiche4, } +// We want that the niche selection doesn't depend on order of the fields. See issue #125630. +pub enum NicheFieldOrder1 { + A { + x: NonZero<u64>, + y: [NonZero<u64>; 2], + }, + B([u64; 2]), +} + +pub enum NicheFieldOrder2 { + A { + y: [NonZero<u64>; 2], + x: NonZero<u64>, + }, + B([u64; 2]), +} + // standins for std types which we want to be laid out in a reasonable way struct RawVecDummy { @@ -260,6 +277,9 @@ pub fn main() { size_of::<EnumWithMaybeUninhabitedVariant<()>>()); assert_eq!(size_of::<NicheFilledEnumWithAbsentVariant>(), size_of::<&'static ()>()); + assert_eq!(size_of::<NicheFieldOrder1>(), 24); + assert_eq!(size_of::<NicheFieldOrder2>(), 24); + assert_eq!(size_of::<Option<Option<(bool, &())>>>(), size_of::<(bool, &())>()); assert_eq!(size_of::<Option<Option<(&(), bool)>>>(), size_of::<(bool, &())>()); assert_eq!(size_of::<Option<Option2<bool, &()>>>(), size_of::<(bool, &())>()); diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.rs b/tests/ui/suggestions/mut-borrow-needed-by-trait.rs index 924bfd82eb8..66e1e77c905 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.rs +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.rs @@ -17,7 +17,6 @@ fn main() { let fp = BufWriter::new(fp); //~^ ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - //~| ERROR the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied writeln!(fp, "hello world").unwrap(); //~ ERROR the method } diff --git a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr index b2f9150142f..09a9b1d3b34 100644 --- a/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/tests/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -14,16 +14,6 @@ error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satis --> $DIR/mut-borrow-needed-by-trait.rs:17:14 | LL | let fp = BufWriter::new(fp); - | ^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` - | - = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` -note: required by a bound in `BufWriter` - --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL - -error[E0277]: the trait bound `&dyn std::io::Write: std::io::Write` is not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:17:14 - | -LL | let fp = BufWriter::new(fp); | ^^^^^^^^^^^^^^^^^^ the trait `std::io::Write` is not implemented for `&dyn std::io::Write` | = note: `std::io::Write` is implemented for `&mut dyn std::io::Write`, but not for `&dyn std::io::Write` @@ -31,13 +21,13 @@ note: required by a bound in `BufWriter` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn Write>`, but its trait bounds were not satisfied - --> $DIR/mut-borrow-needed-by-trait.rs:22:14 + --> $DIR/mut-borrow-needed-by-trait.rs:21:14 | LL | writeln!(fp, "hello world").unwrap(); | ---------^^---------------- method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds | note: must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method - --> $DIR/mut-borrow-needed-by-trait.rs:22:14 + --> $DIR/mut-borrow-needed-by-trait.rs:21:14 | LL | writeln!(fp, "hello world").unwrap(); | ^^ @@ -45,7 +35,7 @@ LL | writeln!(fp, "hello world").unwrap(); `&dyn std::io::Write: std::io::Write` which is required by `BufWriter<&dyn std::io::Write>: std::io::Write` -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0599. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/thread-local/thread-local-issue-37508.rs b/tests/ui/thread-local/thread-local-issue-37508.rs index db430a3229c..34c39453451 100644 --- a/tests/ui/thread-local/thread-local-issue-37508.rs +++ b/tests/ui/thread-local/thread-local-issue-37508.rs @@ -4,6 +4,9 @@ // // Regression test for issue #37508 +// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint +#![allow(static_mut_refs)] + #![no_main] #![no_std] #![feature(thread_local, lang_items)] diff --git a/tests/ui/traits/coherence-alias-hang.rs b/tests/ui/traits/coherence-alias-hang.rs index 37b80739589..c2b4d2e42d2 100644 --- a/tests/ui/traits/coherence-alias-hang.rs +++ b/tests/ui/traits/coherence-alias-hang.rs @@ -1,4 +1,6 @@ //@ check-pass +//@ revisions: current next +//[next]@ compile-flags: -Znext-solver // Regression test for nalgebra hang <https://github.com/rust-lang/rust/issues/130056>. diff --git a/tests/ui/traits/impl.rs b/tests/ui/traits/impl.rs index 348096f37f7..b45935fd86a 100644 --- a/tests/ui/traits/impl.rs +++ b/tests/ui/traits/impl.rs @@ -3,6 +3,9 @@ //@ aux-build:traitimpl.rs +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + extern crate traitimpl; use traitimpl::Bar; diff --git a/tests/ui/traits/impl.stderr b/tests/ui/traits/impl.stderr index 98b6fb03d83..9216a33c1d0 100644 --- a/tests/ui/traits/impl.stderr +++ b/tests/ui/traits/impl.stderr @@ -1,5 +1,5 @@ warning: method `t` is never used - --> $DIR/impl.rs:12:8 + --> $DIR/impl.rs:15:8 | LL | trait T { | - method in this trait diff --git a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr index 8d7d8cee08a..150100f2c53 100644 --- a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr +++ b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr @@ -4,6 +4,7 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait` LL | impls::<W<_>>(); | ^^^^ | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`) note: required by a bound in `impls` --> $DIR/fixpoint-exponential-growth.rs:30:13 | diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs index 1b80287d9da..86d428cc0f0 100644 --- a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs +++ b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.rs @@ -1,6 +1,7 @@ +//~ ERROR overflow evaluating the requirement `Self: Trait` +//~^ ERROR overflow evaluating the requirement `Self well-formed` // This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE. //@ compile-flags: -Znext-solver --crate-type=lib -//@ check-pass #![recursion_limit = "0"] trait Trait {} diff --git a/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr new file mode 100644 index 00000000000..2eed7e8f723 --- /dev/null +++ b/tests/ui/traits/next-solver/overflow/recursion-limit-zero-issue-115351.stderr @@ -0,0 +1,11 @@ +error[E0275]: overflow evaluating the requirement `Self: Trait` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error[E0275]: overflow evaluating the requirement `Self well-formed` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/sized-coniductive.rs b/tests/ui/traits/sized-coniductive.rs new file mode 100644 index 00000000000..5f63b166f98 --- /dev/null +++ b/tests/ui/traits/sized-coniductive.rs @@ -0,0 +1,14 @@ +//@ check-pass +// https://github.com/rust-lang/rust/issues/129541 + +#[derive(Clone)] +struct Test { + field: std::borrow::Cow<'static, [Self]>, +} + +#[derive(Clone)] +struct Hello { + a: <[Hello] as std::borrow::ToOwned>::Owned, +} + +fn main(){} diff --git a/tests/ui/typeck/auxiliary/foreign_struct_trait_unimplemented.rs b/tests/ui/typeck/auxiliary/foreign_struct_trait_unimplemented.rs new file mode 100644 index 00000000000..097a269e8ee --- /dev/null +++ b/tests/ui/typeck/auxiliary/foreign_struct_trait_unimplemented.rs @@ -0,0 +1 @@ +pub struct B; diff --git a/tests/ui/typeck/foreign_struct_trait_unimplemented.rs b/tests/ui/typeck/foreign_struct_trait_unimplemented.rs new file mode 100644 index 00000000000..8ac56bac9b0 --- /dev/null +++ b/tests/ui/typeck/foreign_struct_trait_unimplemented.rs @@ -0,0 +1,15 @@ +//@ aux-build:foreign_struct_trait_unimplemented.rs + +extern crate foreign_struct_trait_unimplemented; + +pub trait Test {} + +struct A; +impl Test for A {} + +fn needs_test(_: impl Test) {} + +fn main() { + needs_test(foreign_struct_trait_unimplemented::B); + //~^ ERROR the trait bound `B: Test` is not satisfied +} diff --git a/tests/ui/typeck/foreign_struct_trait_unimplemented.stderr b/tests/ui/typeck/foreign_struct_trait_unimplemented.stderr new file mode 100644 index 00000000000..b9bb97548f6 --- /dev/null +++ b/tests/ui/typeck/foreign_struct_trait_unimplemented.stderr @@ -0,0 +1,23 @@ +error[E0277]: the trait bound `B: Test` is not satisfied + --> $DIR/foreign_struct_trait_unimplemented.rs:13:16 + | +LL | needs_test(foreign_struct_trait_unimplemented::B); + | ---------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Test` is not implemented for `B` + | | + | required by a bound introduced by this call + | +help: there are multiple different versions of crate `foreign_struct_trait_unimplemented` in the dependency graph + --> $DIR/foreign_struct_trait_unimplemented.rs:3:1 + | +LL | extern crate foreign_struct_trait_unimplemented; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one version of crate `foreign_struct_trait_unimplemented` is used here, as a direct dependency of the current crate + = help: you can use `cargo tree` to explore your dependency tree +note: required by a bound in `needs_test` + --> $DIR/foreign_struct_trait_unimplemented.rs:10:23 + | +LL | fn needs_test(_: impl Test) {} + | ^^^^ required by this bound in `needs_test` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/union/union-drop-assign.rs b/tests/ui/union/union-drop-assign.rs index e9165fddc51..451fb14eddf 100644 --- a/tests/ui/union/union-drop-assign.rs +++ b/tests/ui/union/union-drop-assign.rs @@ -3,6 +3,9 @@ // Drop works for union itself. +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] + use std::mem::ManuallyDrop; struct S; diff --git a/tests/ui/union/union-drop.rs b/tests/ui/union/union-drop.rs index e467eda2935..6f6d5c14165 100644 --- a/tests/ui/union/union-drop.rs +++ b/tests/ui/union/union-drop.rs @@ -2,6 +2,8 @@ #![allow(dead_code)] #![allow(unused_variables)] +// FIXME(static_mut_refs): this could use an atomic +#![allow(static_mut_refs)] // Drop works for union itself. diff --git a/tests/ui/unsafe/union-pat-in-param.rs b/tests/ui/unsafe/union-pat-in-param.rs new file mode 100644 index 00000000000..8454bfb20dc --- /dev/null +++ b/tests/ui/unsafe/union-pat-in-param.rs @@ -0,0 +1,19 @@ +union U { + a: &'static i32, + b: usize, +} + +fn fun(U { a }: U) { + //~^ ERROR access to union field is unsafe + dbg!(*a); +} + +fn main() { + fun(U { b: 0 }); + + let closure = |U { a }| { + //~^ ERROR access to union field is unsafe + dbg!(*a); + }; + closure(U { b: 0 }); +} diff --git a/tests/ui/unsafe/union-pat-in-param.stderr b/tests/ui/unsafe/union-pat-in-param.stderr new file mode 100644 index 00000000000..b9709b7cc9b --- /dev/null +++ b/tests/ui/unsafe/union-pat-in-param.stderr @@ -0,0 +1,19 @@ +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-pat-in-param.rs:6:12 + | +LL | fn fun(U { a }: U) { + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error[E0133]: access to union field is unsafe and requires unsafe function or block + --> $DIR/union-pat-in-param.rs:14:24 + | +LL | let closure = |U { a }| { + | ^ access to union field + | + = note: the field may not be properly initialized: using uninitialized data will cause undefined behavior + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/triagebot.toml b/triagebot.toml index 467e64bb8b0..a6a0b02d7b7 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -966,6 +966,7 @@ libs = [ "@jhpratt", "@tgross35", "@thomcc", + "@ibraheemdev", ] bootstrap = [ "@Mark-Simulacrum", |
