diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index b4848a8cf099..d38cc0e8a17f 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -6,7 +6,7 @@ name: Post merge analysis on: push: branches: - - master + - main jobs: analysis: diff --git a/.gitignore b/.gitignore index 0030f22363c2..91a2647ca98f 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,7 @@ no_llvm_build /llvm/ /mingw-build/ /build -/build-rust-analyzer/ +/build-rust-analyzer /dist/ /unicode-downloads /target diff --git a/Cargo.lock b/Cargo.lock index d2b96e6d1027..9350112939bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "annotate-snippets" -version = "0.12.8" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025c7edcdffa4ccc5c0905f472a0ae3759378cfbef88ef518a3575e19ae3aebd" +checksum = "a44baf24dd94e781f74dfe67ffee75a09a57971ddf0f615a178b4f6d404b48ff" dependencies = [ "anstyle", "unicode-width 0.2.2", @@ -1288,7 +1288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -2154,7 +2154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.3", ] [[package]] @@ -3766,7 +3766,7 @@ dependencies = [ name = "rustc_errors" version = "0.0.0" dependencies = [ - "annotate-snippets 0.12.8", + "annotate-snippets 0.12.9", "anstream", "anstyle", "derive_setters", @@ -4905,7 +4905,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5273,9 +5273,9 @@ dependencies = [ [[package]] name = "stringdex" -version = "0.0.2" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18b3bd4f10d15ef859c40291769f0d85209de6b0f1c30713ff9cdf45ac43ea36" +checksum = "556a6126952cb2f5150057c98a77cc6c771027dea2825bf7fa03d3d638b0a4f8" dependencies = [ "stacker", ] @@ -6070,9 +6070,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "37.0.2" +version = "38.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d0fcd636ad2b29a7c0490799a23ad61d1c8dedfafdb970447fddd0549502b60" +checksum = "7ec3ef3783e18f2457796ed91b1e6c2adc46f2905f740d1527ab3053fe8e5682" [[package]] name = "wasm-bindgen" @@ -6134,9 +6134,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.18" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f565dfcfd9aabb10d865b608a92ce1f93051aeb56f4c89550ed9cd97d8ce0e" +checksum = "4bfc50dd0b883d841bc1dba5ff7020ca52fa7b2c3bb1266d8bf6a09dd032e115" dependencies = [ "anyhow", "clap", @@ -6144,7 +6144,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.240.0", + "wasmparser 0.241.2", "wat", "windows-sys 0.61.2", "winsplit", @@ -6171,24 +6171,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.240.0" +version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06d642d8c5ecc083aafe9ceb32809276a304547a3a6eeecceb5d8152598bc71f" +checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8" dependencies = [ "leb128fmt", - "wasmparser 0.240.0", + "wasmparser 0.241.2", ] [[package]] name = "wasm-metadata" -version = "0.240.0" +version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee093e1e1ccffa005b9b778f7a10ccfd58e25a20eccad294a1a93168d076befb" +checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.240.0", - "wasmparser 0.240.0", + "wasm-encoder 0.241.2", + "wasmparser 0.241.2", ] [[package]] @@ -6213,9 +6213,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.240.0" +version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b722dcf61e0ea47440b53ff83ccb5df8efec57a69d150e4f24882e4eba7e24a4" +checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8" dependencies = [ "bitflags", "hashbrown", @@ -6226,22 +6226,22 @@ dependencies = [ [[package]] name = "wast" -version = "240.0.0" +version = "241.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0efe1c93db4ac562b9733e3dca19ed7fc878dba29aef22245acf84f13da4a19" +checksum = "63f66e07e2ddf531fef6344dbf94d112df7c2f23ed6ffb10962e711500b8d816" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.240.0", + "wasm-encoder 0.241.2", ] [[package]] name = "wat" -version = "1.240.0" +version = "1.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec9b6eab7ecd4d639d78515e9ea491c9bacf494aa5eda10823bd35992cf8c1e" +checksum = "45f923705c40830af909c5dec2352ec2821202e4a66008194585e1917458a26d" dependencies = [ "wast", ] @@ -6679,9 +6679,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.240.0" +version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dc5474b078addc5fe8a72736de8da3acfb3ff324c2491133f8b59594afa1a20" +checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241" dependencies = [ "anyhow", "bitflags", @@ -6690,17 +6690,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.240.0", + "wasm-encoder 0.241.2", "wasm-metadata", - "wasmparser 0.240.0", + "wasmparser 0.241.2", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.240.0" +version = "0.241.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9875ea3fa272f57cc1fc50f225a7b94021a7878c484b33792bccad0d93223439" +checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91" dependencies = [ "anyhow", "id-arena", @@ -6711,7 +6711,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.240.0", + "wasmparser 0.241.2", ] [[package]] diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 6f37e51a47de..5318f9a507a5 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -59,7 +59,10 @@ # toolchain or changing LLVM locally, you probably want to leave this enabled. # # Set this to `true` to download if CI llvm available otherwise it builds -# from `src/llvm-project`. +# from `src/llvm-project`. If you set it to `true`, it's safe and time-saving to run +# `git submodule deinit src/llvm-project` to avoid git updating the llvm-project submodule +# when building compiler locally. +# # # Set this to `"if-unchanged"` to download only if the llvm-project has not # been modified. You can also use this if you are unsure whether you're on a @@ -320,7 +323,7 @@ #build.npm = "npm" # Python interpreter to use for various tasks throughout the build, notably -# rustdoc tests, the lldb python interpreter, and some dist bits and pieces. +# rustdoc tests, and some dist bits and pieces. # # Defaults to the Python interpreter used to execute x.py. #build.python = "python" diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index e3b2b1eff72d..6a5ea36f2a42 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -269,13 +269,56 @@ pub fn supports_c_variadic(self) -> CVariadicStatus { | Self::Aapcs { .. } | Self::Win64 { .. } | Self::SysV64 { .. } - | Self::EfiApi => CVariadicStatus::Stable, - Self::System { .. } => { - CVariadicStatus::Unstable { feature: rustc_span::sym::extern_system_varargs } - } + | Self::EfiApi + | Self::System { .. } => CVariadicStatus::Stable, _ => CVariadicStatus::NotSupported, } } + + /// Returns whether the ABI supports guaranteed tail calls. + #[cfg(feature = "nightly")] + pub fn supports_guaranteed_tail_call(self) -> bool { + match self { + Self::CmseNonSecureCall | Self::CmseNonSecureEntry => { + // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers + // before returning, and hence cannot guarantee a tail call. + false + } + Self::AvrInterrupt + | Self::AvrNonBlockingInterrupt + | Self::Msp430Interrupt + | Self::RiscvInterruptM + | Self::RiscvInterruptS + | Self::X86Interrupt => { + // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly. + false + } + Self::GpuKernel | Self::PtxKernel => { + // See https://godbolt.org/z/jq5TE5jK1. + false + } + Self::Custom => { + // This ABI does not support calls at all (except via assembly). + false + } + Self::C { .. } + | Self::System { .. } + | Self::Rust + | Self::RustCall + | Self::RustCold + | Self::RustInvalid + | Self::Unadjusted + | Self::EfiApi + | Self::Aapcs { .. } + | Self::Cdecl { .. } + | Self::Stdcall { .. } + | Self::Fastcall { .. } + | Self::Thiscall { .. } + | Self::Vectorcall { .. } + | Self::SysV64 { .. } + | Self::Win64 { .. } => true, + } + } } pub fn all_names() -> Vec<&'static str> { diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index e65688f3532e..4de9d61e7f75 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -648,9 +648,10 @@ pub fn to_ty(&self) -> Option> { PatKind::Path(qself, path) => TyKind::Path(qself.clone(), path.clone()), PatKind::MacCall(mac) => TyKind::MacCall(mac.clone()), // `&mut? P` can be reinterpreted as `&mut? T` where `T` is `P` reparsed as a type. - PatKind::Ref(pat, mutbl) => { - pat.to_ty().map(|ty| TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }))? - } + PatKind::Ref(pat, pinned, mutbl) => pat.to_ty().map(|ty| match pinned { + Pinnedness::Not => TyKind::Ref(None, MutTy { ty, mutbl: *mutbl }), + Pinnedness::Pinned => TyKind::PinnedRef(None, MutTy { ty, mutbl: *mutbl }), + })?, // A slice/array pattern `[P]` can be reparsed as `[T]`, an unsized array, // when `P` can be reparsed as a type `T`. PatKind::Slice(pats) if let [pat] = pats.as_slice() => { @@ -696,7 +697,7 @@ pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { // Trivial wrappers over inner patterns. PatKind::Box(s) | PatKind::Deref(s) - | PatKind::Ref(s, _) + | PatKind::Ref(s, _, _) | PatKind::Paren(s) | PatKind::Guard(s, _) => s.walk(it), @@ -717,7 +718,7 @@ pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) { /// Strip off all reference patterns (`&`, `&mut`) and return the inner pattern. pub fn peel_refs(&self) -> &Pat { let mut current = self; - while let PatKind::Ref(inner, _) = ¤t.kind { + while let PatKind::Ref(inner, _, _) = ¤t.kind { current = inner; } current @@ -765,7 +766,9 @@ pub fn descr(&self) -> Option { PatKind::Missing => unreachable!(), PatKind::Wild => Some("_".to_string()), PatKind::Ident(BindingMode::NONE, ident, None) => Some(format!("{ident}")), - PatKind::Ref(pat, mutbl) => pat.descr().map(|d| format!("&{}{d}", mutbl.prefix_str())), + PatKind::Ref(pat, pinned, mutbl) => { + pat.descr().map(|d| format!("&{}{d}", pinned.prefix_str(*mutbl))) + } _ => None, } } @@ -913,7 +916,7 @@ pub enum PatKind { Deref(Box), /// A reference pattern (e.g., `&mut (a, b)`). - Ref(Box, Mutability), + Ref(Box, Pinnedness, Mutability), /// A literal, const block or path. Expr(Box), diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 4ae193a42ff2..094875f38d74 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -645,8 +645,9 @@ fn visit_assoc_item(&mut self, item: &$($lt)? $($mut)? AssocItem, ctxt: AssocCtx fn visit_fn( &mut self, fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>, + _: &AttrVec, _: Span, - _: NodeId + _: NodeId, ) -> Self::Result { walk_fn(self, fk) } @@ -740,6 +741,7 @@ pub trait WalkItemKind { type Ctxt; fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -773,7 +775,7 @@ fn walk_item_inner<$($lt,)? K: WalkItemKind, V: $Visitor$(<$lt>)?>( ) -> V::Result { let Item { attrs, id, kind, vis, span, tokens: _ } = item; visit_visitable!($($mut)? visitor, id, attrs, vis); - try_visit!(kind.walk(*span, *id, vis, ctxt, visitor)); + try_visit!(kind.walk(attrs, *span, *id, vis, ctxt, visitor)); visit_visitable!($($mut)? visitor, span); V::Result::output() } @@ -799,6 +801,7 @@ impl WalkItemKind for ItemKind { type Ctxt = (); fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -808,7 +811,7 @@ fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( match self { ItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func); - try_visit!(vis.visit_fn(kind, span, id)); + try_visit!(vis.visit_fn(kind, attrs, span, id)); } ItemKind::ExternCrate(orig_name, ident) => visit_visitable!($($mut)? vis, orig_name, ident), @@ -856,6 +859,7 @@ impl WalkItemKind for AssocItemKind { type Ctxt = AssocCtxt; fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -867,7 +871,7 @@ fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( visit_visitable!($($mut)? vis, item), AssocItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func); - try_visit!(vis.visit_fn(kind, span, id)) + try_visit!(vis.visit_fn(kind, attrs, span, id)) } AssocItemKind::Type(alias) => visit_visitable!($($mut)? vis, alias), @@ -886,6 +890,7 @@ impl WalkItemKind for ForeignItemKind { type Ctxt = (); fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -897,7 +902,7 @@ fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( visit_visitable!($($mut)? vis, item), ForeignItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func); - try_visit!(vis.visit_fn(kind, span, id)) + try_visit!(vis.visit_fn(kind, attrs, span, id)) } ForeignItemKind::TyAlias(alias) => visit_visitable!($($mut)? vis, alias), @@ -999,7 +1004,7 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)? }) => { visit_visitable!($($mut)? vis, constness, movability, capture_clause); let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body); - try_visit!(vis.visit_fn(kind, *span, *id)); + try_visit!(vis.visit_fn(kind, attrs, *span, *id)); visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span); } ExprKind::Block(block, opt_label) => diff --git a/compiler/rustc_ast_ir/src/lib.rs b/compiler/rustc_ast_ir/src/lib.rs index 0f63fad15743..fb00f5d9b623 100644 --- a/compiler/rustc_ast_ir/src/lib.rs +++ b/compiler/rustc_ast_ir/src/lib.rs @@ -317,4 +317,15 @@ impl Pinnedness { pub fn is_pinned(self) -> bool { matches!(self, Self::Pinned) } + + /// Returns `""` (empty string), "mut", `"pin mut "` or `"pin const "` depending + /// on the pinnedness and mutability. + pub fn prefix_str(self, mutbl: Mutability) -> &'static str { + match (self, mutbl) { + (Pinnedness::Pinned, Mutability::Mut) => "pin mut ", + (Pinnedness::Pinned, Mutability::Not) => "pin const ", + (Pinnedness::Not, Mutability::Mut) => "mut ", + (Pinnedness::Not, Mutability::Not) => "", + } + } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 2be2fca87c3c..524f8b054cb4 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::errors::report_lit_error; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; +use rustc_span::{ByteSymbol, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use visit::{Visitor, walk_expr}; @@ -924,7 +924,7 @@ fn make_lowered_await( arena_vec![self; new_unchecked, get_context], ), }; - self.arena.alloc(self.expr_unsafe(call)) + self.arena.alloc(self.expr_unsafe(span, call)) }; // `::std::task::Poll::Ready(result) => break result` @@ -1771,8 +1771,14 @@ fn lower_expr_for( let pat = self.lower_pat(pat); let for_span = self.mark_span_with_reason(DesugaringKind::ForLoop, self.lower_span(e.span), None); - let head_span = self.mark_span_with_reason(DesugaringKind::ForLoop, head.span, None); - let pat_span = self.mark_span_with_reason(DesugaringKind::ForLoop, pat.span, None); + let for_ctxt = for_span.ctxt(); + + // Try to point both the head and pat spans to their position in the for loop + // rather than inside a macro. + let head_span = + head.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(head.span).with_ctxt(for_ctxt); + let pat_span = + pat.span.find_ancestor_in_same_ctxt(e.span).unwrap_or(pat.span).with_ctxt(for_ctxt); let loop_hir_id = self.lower_node_id(e.id); let label = self.lower_label(opt_label, e.id, loop_hir_id); @@ -1826,7 +1832,7 @@ fn lower_expr_for( arena_vec![self; iter], )); // `unsafe { ... }` - let iter = self.arena.alloc(self.expr_unsafe(iter)); + let iter = self.arena.alloc(self.expr_unsafe(head_span, iter)); let kind = self.make_lowered_await(head_span, iter, FutureKind::AsyncIterator); self.arena.alloc(hir::Expr { hir_id: self.next_id(), kind, span: head_span }) } @@ -1881,7 +1887,7 @@ fn lower_expr_for( arena_vec![self; iter], )); // `unsafe { ... }` - let iter = self.arena.alloc(self.expr_unsafe(iter)); + let iter = self.arena.alloc(self.expr_unsafe(head_span, iter)); let inner_match_expr = self.arena.alloc(self.expr_match( for_span, iter, @@ -1923,7 +1929,7 @@ fn lower_expr_for( /// ControlFlow::Break(residual) => /// #[allow(unreachable_code)] /// // If there is an enclosing `try {...}`: - /// break 'catch_target Try::from_residual(residual), + /// break 'catch_target Residual::into_try_type(residual), /// // Otherwise: /// return Try::from_residual(residual), /// } @@ -1973,7 +1979,11 @@ fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir> let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident); let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid); let from_residual_expr = self.wrap_in_try_constructor( - hir::LangItem::TryTraitFromResidual, + if self.catch_scope.is_some() { + hir::LangItem::ResidualIntoTryType + } else { + hir::LangItem::TryTraitFromResidual + }, try_span, self.arena.alloc(residual_expr), unstable_span, @@ -2097,26 +2107,6 @@ pub(super) fn expr_unit(&mut self, sp: Span) -> &'hir hir::Expr<'hir> { self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[]))) } - fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> { - let lit = hir::Lit { - span: self.lower_span(sp), - node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)), - }; - self.expr(sp, hir::ExprKind::Lit(lit)) - } - - pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> { - self.expr_uint(sp, ast::UintTy::Usize, value as u128) - } - - pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> { - self.expr_uint(sp, ast::UintTy::U32, value as u128) - } - - pub(super) fn expr_u16(&mut self, sp: Span, value: u16) -> hir::Expr<'hir> { - self.expr_uint(sp, ast::UintTy::U16, value as u128) - } - pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { let lit = hir::Lit { span: self.lower_span(sp), @@ -2125,6 +2115,14 @@ pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { self.expr(sp, hir::ExprKind::Lit(lit)) } + pub(super) fn expr_byte_str(&mut self, sp: Span, value: ByteSymbol) -> hir::Expr<'hir> { + let lit = hir::Lit { + span: self.lower_span(sp), + node: ast::LitKind::ByteStr(value, ast::StrStyle::Cooked), + }; + self.expr(sp, hir::ExprKind::Lit(lit)) + } + pub(super) fn expr_call_mut( &mut self, span: Span, @@ -2256,9 +2254,12 @@ pub(super) fn expr_ident_mut( self.expr(span, expr_path) } - fn expr_unsafe(&mut self, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> { + pub(super) fn expr_unsafe( + &mut self, + span: Span, + expr: &'hir hir::Expr<'hir>, + ) -> hir::Expr<'hir> { let hir_id = self.next_id(); - let span = expr.span; self.expr( span, hir::ExprKind::Block( @@ -2296,15 +2297,6 @@ pub(super) fn block_expr_block( self.arena.alloc(self.expr_block(b)) } - pub(super) fn expr_array_ref( - &mut self, - span: Span, - elements: &'hir [hir::Expr<'hir>], - ) -> hir::Expr<'hir> { - let array = self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))); - self.expr_ref(span, array) - } - pub(super) fn expr_ref(&mut self, span: Span, expr: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> { self.expr(span, hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr)) } diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index c5bbc2b81188..602635af1324 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir as hir; use rustc_session::config::FmtDebug; -use rustc_span::{DesugaringKind, Ident, Span, Symbol, sym}; +use rustc_span::{ByteSymbol, DesugaringKind, Ident, Span, Symbol, sym}; use super::LoweringContext; @@ -90,20 +90,14 @@ fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, For let mut inlined_anything = false; for i in 0..fmt.template.len() { - let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue }; - let Ok(arg_index) = placeholder.argument.index else { continue }; - - let mut literal = None; - - if let FormatTrait::Display = placeholder.format_trait + if let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] + && let Ok(arg_index) = placeholder.argument.index + && let FormatTrait::Display = placeholder.format_trait && placeholder.format_options == Default::default() && let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs() && let ExprKind::Lit(lit) = arg.kind + && let Some(literal) = self.try_inline_lit(lit) { - literal = self.try_inline_lit(lit); - } - - if let Some(literal) = literal { // Now we need to mutate the outer FormatArgs. // If this is the first time, this clones the outer FormatArgs. let fmt = fmt.to_mut(); @@ -265,136 +259,21 @@ fn make_argument<'hir>( ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg)) } -/// Generate a hir expression for a format_args Count. +/// Get the value for a `width` or `precision` field. /// -/// Generates: -/// -/// ```text -/// ::Is(…) -/// ``` -/// -/// or -/// -/// ```text -/// ::Param(…) -/// ``` -/// -/// or -/// -/// ```text -/// ::Implied -/// ``` -fn make_count<'hir>( - ctx: &mut LoweringContext<'_, 'hir>, - sp: Span, - count: &Option, +/// Returns the value and whether it is indirect (an indexed argument) or not. +fn make_count( + count: &FormatCount, argmap: &mut FxIndexMap<(usize, ArgumentType), Option>, -) -> hir::Expr<'hir> { +) -> (bool, u16) { match count { - Some(FormatCount::Literal(n)) => { - let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative( - sp, - hir::LangItem::FormatCount, - sym::Is, - )); - let value = ctx.arena.alloc_from_iter([ctx.expr_u16(sp, *n)]); - ctx.expr_call_mut(sp, count_is, value) - } - Some(FormatCount::Argument(arg)) => { - if let Ok(arg_index) = arg.index { - let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize), arg.span); - let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative( - sp, - hir::LangItem::FormatCount, - sym::Param, - )); - let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]); - ctx.expr_call_mut(sp, count_param, value) - } else { - ctx.expr( - sp, - hir::ExprKind::Err( - ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count"), - ), - ) - } - } - None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied), - } -} - -/// Generate a hir expression for a format_args placeholder specification. -/// -/// Generates -/// -/// ```text -/// , -/// width: , -/// } -/// ``` -fn make_format_spec<'hir>( - ctx: &mut LoweringContext<'_, 'hir>, - sp: Span, - placeholder: &FormatPlaceholder, - argmap: &mut FxIndexMap<(usize, ArgumentType), Option>, -) -> hir::Expr<'hir> { - let position = match placeholder.argument.index { - Ok(arg_index) => { - let (i, _) = argmap.insert_full( - (arg_index, ArgumentType::Format(placeholder.format_trait)), - placeholder.span, - ); - ctx.expr_usize(sp, i) - } - Err(_) => ctx.expr( - sp, - hir::ExprKind::Err(ctx.dcx().span_delayed_bug(sp, "lowered bad format_args count")), + FormatCount::Literal(n) => (false, *n), + FormatCount::Argument(arg) => ( + true, + argmap.insert_full((arg.index.unwrap_or(usize::MAX), ArgumentType::Usize), arg.span).0 + as u16, ), - }; - let &FormatOptions { - ref width, - ref precision, - alignment, - fill, - sign, - alternate, - zero_pad, - debug_hex, - } = &placeholder.format_options; - let fill = fill.unwrap_or(' '); - // These need to match the constants in library/core/src/fmt/rt.rs. - let align = match alignment { - Some(FormatAlignment::Left) => 0, - Some(FormatAlignment::Right) => 1, - Some(FormatAlignment::Center) => 2, - None => 3, - }; - // This needs to match the constants in library/core/src/fmt/rt.rs. - let flags: u32 = fill as u32 - | ((sign == Some(FormatSign::Plus)) as u32) << 21 - | ((sign == Some(FormatSign::Minus)) as u32) << 22 - | (alternate as u32) << 23 - | (zero_pad as u32) << 24 - | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 - | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 - | (width.is_some() as u32) << 27 - | (precision.is_some() as u32) << 28 - | align << 29 - | 1 << 31; // Highest bit always set. - let flags = ctx.expr_u32(sp, flags); - let precision = make_count(ctx, sp, precision, argmap); - let width = make_count(ctx, sp, width, argmap); - let position = ctx.expr_field(Ident::new(sym::position, sp), ctx.arena.alloc(position), sp); - let flags = ctx.expr_field(Ident::new(sym::flags, sp), ctx.arena.alloc(flags), sp); - let precision = ctx.expr_field(Ident::new(sym::precision, sp), ctx.arena.alloc(precision), sp); - let width = ctx.expr_field(Ident::new(sym::width, sp), ctx.arena.alloc(width), sp); - let placeholder = - ctx.arena.alloc(ctx.make_lang_item_qpath(hir::LangItem::FormatPlaceholder, sp, None)); - let fields = ctx.arena.alloc_from_iter([position, flags, precision, width]); - ctx.expr(sp, hir::ExprKind::Struct(placeholder, fields, hir::StructTailExpr::None)) + } } fn expand_format_args<'hir>( @@ -405,85 +284,152 @@ fn expand_format_args<'hir>( ) -> hir::ExprKind<'hir> { let macsp = ctx.lower_span(macsp); - let mut incomplete_lit = String::new(); - let lit_pieces = - ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| { - match piece { - &FormatArgsPiece::Literal(s) => { - // Coalesce adjacent literal pieces. - if let Some(FormatArgsPiece::Literal(_)) = fmt.template.get(i + 1) { - incomplete_lit.push_str(s.as_str()); - None - } else if !incomplete_lit.is_empty() { - incomplete_lit.push_str(s.as_str()); - let s = Symbol::intern(&incomplete_lit); - incomplete_lit.clear(); - Some(ctx.expr_str(fmt.span, s)) - } else { - Some(ctx.expr_str(fmt.span, s)) - } - } - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { - Some(ctx.expr_str(fmt.span, sym::empty)) - } else { - None - } - } - } - })); - let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces); - - // Whether we'll use the `Arguments::new_v1_formatted` form (true), - // or the `Arguments::new_v1` form (false). - let mut use_format_options = false; - // Create a list of all _unique_ (argument, format trait) combinations. // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] + // + // We use usize::MAX for arguments that don't exist, because that can never be a valid index + // into the arguments array. let mut argmap = FxIndexMap::default(); - for piece in &fmt.template { - let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; - if placeholder.format_options != Default::default() { - // Can't use basic form if there's any formatting options. - use_format_options = true; - } - if let Ok(index) = placeholder.argument.index { - if argmap - .insert((index, ArgumentType::Format(placeholder.format_trait)), placeholder.span) - .is_some() - { - // Duplicate (argument, format trait) combination, - // which we'll only put once in the args array. - use_format_options = true; + + let mut incomplete_lit = String::new(); + + let mut implicit_arg_index = 0; + + let mut bytecode = Vec::new(); + + let template = if fmt.template.is_empty() { + // Treat empty templates as a single literal piece (with an empty string), + // so we produce `from_str("")` for those. + &[FormatArgsPiece::Literal(sym::empty)][..] + } else { + &fmt.template[..] + }; + + // See library/core/src/fmt/mod.rs for the format string encoding format. + + for (i, piece) in template.iter().enumerate() { + match piece { + &FormatArgsPiece::Literal(sym) => { + // Coalesce adjacent literal pieces. + if let Some(FormatArgsPiece::Literal(_)) = template.get(i + 1) { + incomplete_lit.push_str(sym.as_str()); + continue; + } + let mut s = if incomplete_lit.is_empty() { + sym.as_str() + } else { + incomplete_lit.push_str(sym.as_str()); + &incomplete_lit + }; + + // If this is the last piece and was the only piece, that means + // there are no placeholders and the entire format string is just a literal. + // + // In that case, we can just use `from_str`. + if i + 1 == template.len() && bytecode.is_empty() { + // Generate: + // ::from_str("meow") + let from_str = ctx.arena.alloc(ctx.expr_lang_item_type_relative( + macsp, + hir::LangItem::FormatArguments, + if allow_const { sym::from_str } else { sym::from_str_nonconst }, + )); + let sym = if incomplete_lit.is_empty() { sym } else { Symbol::intern(s) }; + let s = ctx.expr_str(fmt.span, sym); + let args = ctx.arena.alloc_from_iter([s]); + return hir::ExprKind::Call(from_str, args); + } + + // Encode the literal in chunks of up to u16::MAX bytes, split at utf-8 boundaries. + while !s.is_empty() { + let len = s.floor_char_boundary(usize::from(u16::MAX)); + if len < 0x80 { + bytecode.push(len as u8); + } else { + bytecode.push(0x80); + bytecode.extend_from_slice(&(len as u16).to_le_bytes()); + } + bytecode.extend(&s.as_bytes()[..len]); + s = &s[len..]; + } + + incomplete_lit.clear(); + } + FormatArgsPiece::Placeholder(p) => { + // Push the start byte and remember its index so we can set the option bits later. + let i = bytecode.len(); + bytecode.push(0xC0); + + let position = argmap + .insert_full( + ( + p.argument.index.unwrap_or(usize::MAX), + ArgumentType::Format(p.format_trait), + ), + p.span, + ) + .0 as u64; + + // This needs to match the constants in library/core/src/fmt/mod.rs. + let o = &p.format_options; + let align = match o.alignment { + Some(FormatAlignment::Left) => 0, + Some(FormatAlignment::Right) => 1, + Some(FormatAlignment::Center) => 2, + None => 3, + }; + let default_flags = 0x6000_0020; + let flags: u32 = o.fill.unwrap_or(' ') as u32 + | ((o.sign == Some(FormatSign::Plus)) as u32) << 21 + | ((o.sign == Some(FormatSign::Minus)) as u32) << 22 + | (o.alternate as u32) << 23 + | (o.zero_pad as u32) << 24 + | ((o.debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25 + | ((o.debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26 + | (o.width.is_some() as u32) << 27 + | (o.precision.is_some() as u32) << 28 + | align << 29; + if flags != default_flags { + bytecode[i] |= 1; + bytecode.extend_from_slice(&flags.to_le_bytes()); + if let Some(val) = &o.width { + let (indirect, val) = make_count(val, &mut argmap); + // Only encode if nonzero; zero is the default. + if indirect || val != 0 { + bytecode[i] |= 1 << 1 | (indirect as u8) << 4; + bytecode.extend_from_slice(&val.to_le_bytes()); + } + } + if let Some(val) = &o.precision { + let (indirect, val) = make_count(val, &mut argmap); + // Only encode if nonzero; zero is the default. + if indirect || val != 0 { + bytecode[i] |= 1 << 2 | (indirect as u8) << 5; + bytecode.extend_from_slice(&val.to_le_bytes()); + } + } + } + if implicit_arg_index != position { + bytecode[i] |= 1 << 3; + bytecode.extend_from_slice(&(position as u16).to_le_bytes()); + } + implicit_arg_index = position + 1; } } } - let format_options = use_format_options.then(|| { - // Generate: - // &[format_spec_0, format_spec_1, format_spec_2] - let elements = ctx.arena.alloc_from_iter(fmt.template.iter().filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(make_format_spec(ctx, macsp, placeholder, &mut argmap)) - })); - ctx.expr_array_ref(macsp, elements) - }); + assert!(incomplete_lit.is_empty()); + + // Zero terminator. + bytecode.push(0); + + // Ensure all argument indexes actually fit in 16 bits, as we truncated them to 16 bits before. + if argmap.len() > u16::MAX as usize { + ctx.dcx().span_err(macsp, "too many format arguments"); + } let arguments = fmt.arguments.all_args(); - if allow_const && arguments.is_empty() && argmap.is_empty() { - // Generate: - // ::new_const(lit_pieces) - let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative( - macsp, - hir::LangItem::FormatArguments, - sym::new_const, - )); - let new_args = ctx.arena.alloc_from_iter([lit_pieces]); - return hir::ExprKind::Call(new, new_args); - } - let (let_statements, args) = if arguments.is_empty() { // Generate: // [] @@ -512,22 +458,30 @@ fn expand_format_args<'hir>( // ]; let args = ctx.arena.alloc_from_iter(argmap.iter().map( |(&(arg_index, ty), &placeholder_span)| { - let arg = &arguments[arg_index]; - let placeholder_span = - placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt()); - let arg_span = match arg.kind { - FormatArgumentKind::Captured(_) => placeholder_span, - _ => arg.expr.span.with_ctxt(macsp.ctxt()), - }; - let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id); - let arg = ctx.arena.alloc(ctx.expr( - arg_span, - hir::ExprKind::Field( - args_ident_expr, - Ident::new(sym::integer(arg_index), macsp), - ), - )); - make_argument(ctx, placeholder_span, arg, ty) + if let Some(arg) = arguments.get(arg_index) { + let placeholder_span = + placeholder_span.unwrap_or(arg.expr.span).with_ctxt(macsp.ctxt()); + let arg_span = match arg.kind { + FormatArgumentKind::Captured(_) => placeholder_span, + _ => arg.expr.span.with_ctxt(macsp.ctxt()), + }; + let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id); + let arg = ctx.arena.alloc(ctx.expr( + arg_span, + hir::ExprKind::Field( + args_ident_expr, + Ident::new(sym::integer(arg_index), macsp), + ), + )); + make_argument(ctx, placeholder_span, arg, ty) + } else { + ctx.expr( + macsp, + hir::ExprKind::Err( + ctx.dcx().span_delayed_bug(macsp, "missing format_args argument"), + ), + ) + } }, )); let args = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args))); @@ -540,58 +494,38 @@ fn expand_format_args<'hir>( }; // Generate: - // &args - let args = ctx.expr_ref(macsp, args); - - let call = if let Some(format_options) = format_options { - // Generate: - // unsafe { - // ::new_v1_formatted( - // lit_pieces, - // args, - // format_options, - // ) - // } - let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative( + // unsafe { + // ::new(b"…", &args) + // } + let template = ctx.expr_byte_str(macsp, ByteSymbol::intern(&bytecode)); + let call = { + let new = ctx.arena.alloc(ctx.expr_lang_item_type_relative( macsp, hir::LangItem::FormatArguments, - sym::new_v1_formatted, + sym::new, )); - let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options]); - let call = ctx.expr_call(macsp, new_v1_formatted, args); - let hir_id = ctx.next_id(); - hir::ExprKind::Block( - ctx.arena.alloc(hir::Block { - stmts: &[], - expr: Some(call), - hir_id, - rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated), - span: macsp, - targeted_by_break: false, - }), - None, - ) - } else { - // Generate: - // ::new_v1( - // lit_pieces, - // args, - // ) - let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative( - macsp, - hir::LangItem::FormatArguments, - sym::new_v1, - )); - let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]); - hir::ExprKind::Call(new_v1, new_args) + let args = ctx.expr_ref(macsp, args); + let new_args = ctx.arena.alloc_from_iter([template, args]); + ctx.expr_call(macsp, new, new_args) }; + let call = hir::ExprKind::Block( + ctx.arena.alloc(hir::Block { + stmts: &[], + expr: Some(call), + hir_id: ctx.next_id(), + rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated), + span: macsp, + targeted_by_break: false, + }), + None, + ); if !let_statements.is_empty() { // Generate: // { // super let … // super let … - // ::new_…(…) + // ::new(…) // } let call = ctx.arena.alloc(ctx.expr(macsp, call)); let block = ctx.block_all(macsp, ctx.arena.alloc_from_iter(let_statements), Some(call)); diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 5f8933aa2beb..8d7351d3a510 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -125,9 +125,9 @@ fn insert_nested(&mut self, item: LocalDefId) { } impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { - /// Because we want to track parent items and so forth, enable - /// deep walking so that we walk nested items in the context of - /// their outer items. + // Because we want to track parent items and so forth, enable + // deep walking so that we walk nested items in the context of + // their outer items. fn visit_nested_item(&mut self, item: ItemId) { debug!("visit_nested_item: {:?}", item); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9cb17ea67a37..1f36454ec861 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -183,7 +183,12 @@ fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_contracts: [sym::contracts_internals].into(), - allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(), + allow_try_trait: [ + sym::try_trait_v2, + sym::try_trait_v2_residual, + sym::yeet_desugar_details, + ] + .into(), allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(), allow_gen_future: if tcx.features().async_fn_track_caller() { [sym::gen_future, sym::closure_track_caller].into() diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 815338c84fa6..3571fd652397 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -124,8 +124,8 @@ fn lower_pat_mut(&mut self, mut pattern: &Pat) -> hir::Pat<'hir> { PatKind::Deref(inner) => { break hir::PatKind::Deref(self.lower_pat(inner)); } - PatKind::Ref(inner, mutbl) => { - break hir::PatKind::Ref(self.lower_pat(inner), *mutbl); + PatKind::Ref(inner, pinned, mutbl) => { + break hir::PatKind::Ref(self.lower_pat(inner), *pinned, *mutbl); } PatKind::Range(e1, e2, Spanned { node: end, .. }) => { break hir::PatKind::Range( diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 7d618f16b26e..1bfe4b07551f 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,10 @@ ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` .label = `extern "{$abi}"` because of this .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list +ast_passes_c_variadic_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions + .label = `extern "{$abi}"` because of this + .help = C-variadic function must have a compatible calling convention + ast_passes_c_variadic_must_be_unsafe = functions with a C variable argument list must be unsafe .suggestion = add the `unsafe` keyword to this definition diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c203e5426b1e..37e40bd6e94d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -21,7 +21,7 @@ use std::str::FromStr; use itertools::{Either, Itertools}; -use rustc_abi::{CanonAbi, ExternAbi, InterruptKind}; +use rustc_abi::{CVariadicStatus, CanonAbi, ExternAbi, InterruptKind}; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; @@ -35,6 +35,7 @@ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, }; +use rustc_session::parse::feature_err; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; @@ -661,7 +662,7 @@ fn check_foreign_item_ascii_only(&self, ident: Ident) { /// C-variadics must be: /// - Non-const /// - Either foreign, or free and `unsafe extern "C"` semantically - fn check_c_variadic_type(&self, fk: FnKind<'a>) { + fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) { // `...` is already rejected when it is not the final parameter. let variadic_param = match fk.decl().inputs.last() { Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param, @@ -693,36 +694,92 @@ fn check_c_variadic_type(&self, fk: FnKind<'a>) { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + FnCtxt::Free | FnCtxt::Assoc(_) => { + match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + // Just bail if the ABI is not even recognized. + let Ok(abi) = ExternAbi::from_str(symbol_unescaped.as_str()) else { + return; + }; + + self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig); + + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); } } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); + } + } + } + + fn check_c_variadic_abi( + &self, + abi: ExternAbi, + attrs: &'a AttrVec, + dotdotdot_span: Span, + sig: &FnSig, + ) { + // For naked functions we accept any ABI that is accepted on c-variadic + // foreign functions, if the c_variadic_naked_functions feature is enabled. + if attr::contains_name(attrs, sym::naked) { + match abi.supports_c_variadic() { + CVariadicStatus::Stable if let ExternAbi::C { .. } = abi => { + // With `c_variadic` naked c-variadic `extern "C"` functions are allowed. + } + CVariadicStatus::Stable => { + // For e.g. aapcs or sysv64 `c_variadic_naked_functions` must also be enabled. + if !self.features.enabled(sym::c_variadic_naked_functions) { + let msg = format!("Naked c-variadic `extern {abi}` functions are unstable"); + feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg) + .emit(); + } + } + CVariadicStatus::Unstable { feature } => { + // Some ABIs need additional features. + if !self.features.enabled(sym::c_variadic_naked_functions) { + let msg = format!("Naked c-variadic `extern {abi}` functions are unstable"); + feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg) + .emit(); } - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + if !self.features.enabled(feature) { + let msg = format!( + "C-variadic functions with the {abi} calling convention are unstable" + ); + feature_err(&self.sess, feature, sig.span, msg).emit(); } } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); + CVariadicStatus::NotSupported => { + // Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions. + self.dcx().emit_err(errors::CVariadicBadNakedExtern { + span: dotdotdot_span, + abi: abi.as_str(), + extern_span: sig.extern_span(), + }); } - }, + } + } else if !matches!(abi, ExternAbi::C { .. }) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: dotdotdot_span, + abi: abi.as_str(), + extern_span: sig.extern_span(), + }); } } @@ -1106,7 +1163,7 @@ fn visit_item(&mut self, item: &'a Item) { } let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func); - self.visit_fn(kind, item.span, item.id); + self.visit_fn(kind, &item.attrs, item.span, item.id); } ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => { let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span)); @@ -1473,7 +1530,7 @@ fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) { visit::walk_param_bound(self, bound) } - fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) { + fn visit_fn(&mut self, fk: FnKind<'a>, attrs: &AttrVec, span: Span, id: NodeId) { // Only associated `fn`s can have `self` parameters. let self_semantic = match fk.ctxt() { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, @@ -1492,7 +1549,7 @@ fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) { self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig); } - self.check_c_variadic_type(fk); + self.check_c_variadic_type(fk, attrs); // Functions cannot both be `const async` or `const gen` if let Some(&FnHeader { @@ -1643,7 +1700,7 @@ fn visit_assoc_item(&mut self, item: &'a AssocItem, ctxt: AssocCtxt) { { self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident); let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func); - self.visit_fn(kind, item.span, item.id); + self.visit_fn(kind, &item.attrs, item.span, item.id); } AssocItemKind::Type(_) => { let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 7c63b8e35999..20813d936c00 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -347,7 +347,18 @@ pub(crate) struct CVariadicMustBeUnsafe { pub(crate) struct CVariadicBadExtern { #[primary_span] pub span: Span, - pub abi: Symbol, + pub abi: &'static str, + #[label] + pub extern_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_bad_naked_extern)] +#[help] +pub(crate) struct CVariadicBadNakedExtern { + #[primary_span] + pub span: Span, + pub abi: &'static str, #[label] pub extern_span: Span, } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b8a29a9a08f4..2d87d8c84d7c 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,5 @@ -use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; -use rustc_ast::{NodeId, PatKind, attr, token}; +use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; @@ -392,7 +391,7 @@ fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { visit::walk_poly_trait_ref(self, t); } - fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) { if let Some(_header) = fn_kind.header() { // Stability of const fn methods are covered in `visit_assoc_item` below. } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 93f4e47342f1..21b14f161077 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1807,8 +1807,14 @@ fn print_pat(&mut self, pat: &ast::Pat) { self.print_pat(inner); self.pclose(); } - PatKind::Ref(inner, mutbl) => { + PatKind::Ref(inner, pinned, mutbl) => { self.word("&"); + if pinned.is_pinned() { + self.word("pin "); + if mutbl.is_not() { + self.word("const "); + } + } if mutbl.is_mut() { self.word("mut "); } diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs new file mode 100644 index 000000000000..8006fb963b19 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -0,0 +1,89 @@ +use rustc_ast::token::Token; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{AttrStyle, NodeId, token}; +use rustc_feature::{AttributeTemplate, Features}; +use rustc_hir::AttrPath; +use rustc_hir::attrs::CfgEntry; +use rustc_parse::exp; +use rustc_parse::parser::Parser; +use rustc_session::Session; +use rustc_span::{ErrorGuaranteed, Ident, Span}; + +use crate::parser::MetaItemOrLitParser; +use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry}; + +pub enum CfgSelectPredicate { + Cfg(CfgEntry), + Wildcard(Token), +} + +#[derive(Default)] +pub struct CfgSelectBranches { + /// All the conditional branches. + pub reachable: Vec<(CfgEntry, TokenStream, Span)>, + /// The first wildcard `_ => { ... }` branch. + pub wildcard: Option<(Token, TokenStream, Span)>, + /// All branches after the first wildcard, including further wildcards. + /// These branches are kept for formatting. + pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, +} + +pub fn parse_cfg_select( + p: &mut Parser<'_>, + sess: &Session, + features: Option<&Features>, + lint_node_id: NodeId, +) -> Result { + let mut branches = CfgSelectBranches::default(); + + while p.token != token::Eof { + if p.eat_keyword(exp!(Underscore)) { + let underscore = p.prev_token; + p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; + + let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?; + let span = underscore.span.to(p.token.span); + + match branches.wildcard { + None => branches.wildcard = Some((underscore, tts, span)), + Some(_) => { + branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span)) + } + } + } else { + let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints) + .map_err(|diag| diag.emit())?; + let cfg_span = meta.span(); + let cfg = AttributeParser::parse_single_args( + sess, + cfg_span, + cfg_span, + AttrStyle::Inner, + AttrPath { + segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(), + span: cfg_span, + }, + ParsedDescription::Macro, + cfg_span, + lint_node_id, + features, + ShouldEmit::ErrorsAndLints, + &meta, + parse_cfg_entry, + &AttributeTemplate::default(), + )?; + + p.expect(exp!(FatArrow)).map_err(|e| e.emit())?; + + let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?; + let span = cfg_span.to(p.token.span); + + match branches.wildcard { + None => branches.reachable.push((cfg, tts, span)), + Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)), + } + } + } + + Ok(branches) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 093969586596..65e0957ca900 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -33,6 +33,7 @@ pub(crate) mod body; pub(crate) mod cfg; pub(crate) mod cfg_old; +pub(crate) mod cfg_select; pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod crate_level; diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 473935abdcd5..046cca4c742b 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -106,6 +106,7 @@ CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg, parse_cfg_attr, parse_cfg_entry, }; pub use attributes::cfg_old::*; +pub use attributes::cfg_select::*; pub use attributes::util::{is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 927417f89f8c..4065fe7ce173 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -209,7 +209,7 @@ pub fn check_attribute_safety( // - Normal builtin attribute // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes - (Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { + (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => { psess.dcx().emit_err(errors::InvalidAttrUnsafe { span: unsafe_span, name: attr_item.path.clone(), @@ -218,15 +218,10 @@ pub fn check_attribute_safety( // - Normal builtin attribute // - No explicit `#[unsafe(..)]` written. - (Some(AttributeSafety::Normal), Safety::Default) => { + (None | Some(AttributeSafety::Normal), Safety::Default) => { // OK } - // - Non-builtin attribute - (None, Safety::Unsafe(_) | Safety::Default) => { - // OK (not checked here) - } - ( Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None, Safety::Safe(..), diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f84ed64e036e..9a8927c10297 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -3087,6 +3087,39 @@ fn report_local_value_does_not_live_long_enough( }); explanation.add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None); + + // Detect buffer reuse pattern + if let BorrowExplanation::UsedLater(_dropped_local, _, _, _) = explanation { + // Check all locals at the borrow location to find Vec<&T> types + for (local, local_decl) in self.body.local_decls.iter_enumerated() { + if let ty::Adt(adt_def, args) = local_decl.ty.kind() + && self.infcx.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) + && args.len() > 0 + { + let vec_inner_ty = args.type_at(0); + // Check if Vec contains references + if vec_inner_ty.is_ref() { + let local_place = local.into(); + if let Some(local_name) = self.describe_place(local_place) { + err.span_label( + local_decl.source_info.span, + format!("variable `{local_name}` declared here"), + ); + err.note( + format!( + "`{local_name}` is a collection that stores borrowed references, \ + but {name} does not live long enough to be stored in it" + ) + ); + err.help( + "buffer reuse with borrowed references requires unsafe code or restructuring" + ); + break; + } + } + } + } + } } err diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index be2a5b1ebe22..3322c590a6ce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -867,11 +867,12 @@ fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { for (binding_span, opt_ref_pat) in finder.ref_pat_for_binding { if let Some(ref_pat) = opt_ref_pat && !finder.cannot_remove.contains(&ref_pat.hir_id) - && let hir::PatKind::Ref(subpat, mutbl) = ref_pat.kind + && let hir::PatKind::Ref(subpat, pinned, mutbl) = ref_pat.kind && let Some(ref_span) = ref_pat.span.trim_end(subpat.span) { + let pinned_str = if pinned.is_pinned() { "pinned " } else { "" }; let mutable_str = if mutbl.is_mut() { "mutable " } else { "" }; - let msg = format!("consider removing the {mutable_str}borrow"); + let msg = format!("consider removing the {pinned_str}{mutable_str}borrow"); suggestions.push((ref_span, msg, "".to_string())); } else { let msg = "consider borrowing the pattern binding".to_string(); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 2f011dba60b1..c06e3c8b3c17 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -213,7 +213,7 @@ pub(crate) fn report_mutability_error( AccessKind::Mutate => { err = self.cannot_assign(span, &(item_msg + &reason)); act = "assign"; - acted_on = "written"; + acted_on = "written to"; span } AccessKind::MutableBorrow => { @@ -518,8 +518,8 @@ pub(crate) fn report_mutability_error( err.span_label( span, format!( - "`{name}` is a `{pointer_sigil}` {pointer_desc}, \ - so the data it refers to cannot be {acted_on}", + "`{name}` is a `{pointer_sigil}` {pointer_desc}, so it cannot be \ + {acted_on}", ), ); @@ -542,7 +542,7 @@ pub(crate) fn report_mutability_error( self.expected_fn_found_fn_mut_call(&mut err, span, act); } - PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => { + PlaceRef { local, projection: [.., ProjectionElem::Deref] } => { err.span_label(span, format!("cannot {act}")); match opt_source { @@ -559,11 +559,36 @@ pub(crate) fn report_mutability_error( )); self.suggest_map_index_mut_alternatives(ty, &mut err, span); } - _ => (), + _ => { + let local = &self.body.local_decls[local]; + match local.local_info() { + LocalInfo::StaticRef { def_id, .. } => { + let span = self.infcx.tcx.def_span(def_id); + err.span_label(span, format!("this `static` cannot be {acted_on}")); + } + LocalInfo::ConstRef { def_id } => { + let span = self.infcx.tcx.def_span(def_id); + err.span_label(span, format!("this `const` cannot be {acted_on}")); + } + LocalInfo::BlockTailTemp(_) | LocalInfo::Boring + if !local.source_info.span.overlaps(span) => + { + err.span_label( + local.source_info.span, + format!("this cannot be {acted_on}"), + ); + } + _ => {} + } + } } } - _ => { + PlaceRef { local, .. } => { + let local = &self.body.local_decls[local]; + if !local.source_info.span.overlaps(span) { + err.span_label(local.source_info.span, format!("this cannot be {acted_on}")); + } err.span_label(span, format!("cannot {act}")); } } @@ -772,11 +797,11 @@ fn construct_mut_suggestion_for_local_binding_patterns( && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(&body).break_value() && let node = self.infcx.tcx.hir_node(hir_id) && let hir::Node::LetStmt(hir::LetStmt { - pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, + pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. }, .. }) | hir::Node::Param(Param { - pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, + pat: hir::Pat { kind: hir::PatKind::Ref(_, _, _), .. }, .. }) = node { @@ -1494,7 +1519,7 @@ fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) -> Self::Result { } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) -> Self::Result { - if let hir::Pat { kind: hir::PatKind::Ref(_, _), span, .. } = param.pat + if let hir::Pat { kind: hir::PatKind::Ref(_, _, _), span, .. } = param.pat && *span == self.span { ControlFlow::Break(param.hir_id) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index bad5f03e4127..05826bea66bf 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -690,6 +690,17 @@ fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag< ); diag.span_label(*span, format!("`{fr_name}` escapes the {escapes_from} body here")); + } else { + diag.span_label( + *span, + format!("a temporary borrow escapes the {escapes_from} body here"), + ); + if let Some((Some(outlived_name), _)) = outlived_fr_name_and_span { + diag.help(format!( + "`{outlived_name}` is declared outside the {escapes_from}, \ + so any data borrowed inside the {escapes_from} cannot be stored into it" + )); + } } // Only show an extra note if we can find an 'error region' for both of the region diff --git a/compiler/rustc_builtin_macros/src/cfg_select.rs b/compiler/rustc_builtin_macros/src/cfg_select.rs index f22d5f255c29..f2e454c3d437 100644 --- a/compiler/rustc_builtin_macros/src/cfg_select.rs +++ b/compiler/rustc_builtin_macros/src/cfg_select.rs @@ -1,7 +1,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_attr_parsing as attr; +use rustc_attr_parsing::{ + CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, ShouldEmit, parse_cfg_select, +}; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; -use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select}; use rustc_span::{Ident, Span, sym}; use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; @@ -9,11 +11,11 @@ /// Selects the first arm whose predicate evaluates to true. fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> { for (cfg, tt, arm_span) in branches.reachable { - if attr::cfg_matches( - &cfg, + if let EvalConfigResult::True = attr::eval_config_entry( &ecx.sess, + &cfg, ecx.current_expansion.lint_node_id, - Some(ecx.ecfg.features), + ShouldEmit::ErrorsAndLints, ) { return Some((tt, arm_span)); } @@ -27,37 +29,41 @@ pub(super) fn expand_cfg_select<'cx>( sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { - ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) { - Ok(branches) => { - if let Some((underscore, _, _)) = branches.wildcard { - // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. - for (predicate, _, _) in &branches.unreachable { - let span = match predicate { - CfgSelectPredicate::Wildcard(underscore) => underscore.span, - CfgSelectPredicate::Cfg(cfg) => cfg.span(), - }; - let err = CfgSelectUnreachable { span, wildcard_span: underscore.span }; - ecx.dcx().emit_warn(err); + ExpandResult::Ready( + match parse_cfg_select( + &mut ecx.new_parser_from_tts(tts), + ecx.sess, + Some(ecx.ecfg.features), + ecx.current_expansion.lint_node_id, + ) { + Ok(branches) => { + if let Some((underscore, _, _)) = branches.wildcard { + // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. + for (predicate, _, _) in &branches.unreachable { + let span = match predicate { + CfgSelectPredicate::Wildcard(underscore) => underscore.span, + CfgSelectPredicate::Cfg(cfg) => cfg.span(), + }; + let err = CfgSelectUnreachable { span, wildcard_span: underscore.span }; + ecx.dcx().emit_warn(err); + } + } + + if let Some((tts, arm_span)) = select_arm(ecx, branches) { + return ExpandResult::from_tts( + ecx, + tts, + sp, + arm_span, + Ident::with_dummy_span(sym::cfg_select), + ); + } else { + // Emit a compiler error when none of the predicates matched. + let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp }); + DummyResult::any(sp, guar) } } - - if let Some((tts, arm_span)) = select_arm(ecx, branches) { - return ExpandResult::from_tts( - ecx, - tts, - sp, - arm_span, - Ident::with_dummy_span(sym::cfg_select), - ); - } else { - // Emit a compiler error when none of the predicates matched. - let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp }); - DummyResult::any(sp, guar) - } - } - Err(err) => { - let guar = err.emit(); - DummyResult::any(sp, guar) - } - }) + Err(guar) => DummyResult::any(sp, guar), + }, + ) } diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 63342880b094..06b0d0512e70 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -1,4 +1,4 @@ -use rustc_ast::MetaItem; +use rustc_ast::{MetaItem, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::Span; @@ -24,6 +24,8 @@ pub(crate) fn expand_deriving_copy( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push); @@ -48,6 +50,8 @@ pub(crate) fn expand_deriving_const_param_ty( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 3c78f53c5cb0..af3db65bd0a6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -1,7 +1,7 @@ -use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; +use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, Safety, VariantData}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Span, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; @@ -68,6 +68,29 @@ pub(crate) fn expand_deriving_clone( _ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), } + // If the clone method is just copying the value, also mark the type as + // `TrivialClone` to allow some library optimizations. + if is_simple { + let trivial_def = TraitDef { + span, + path: path_std!(clone::TrivialClone), + skip_path_as_bound: false, + needs_copy_as_bound_if_packed: true, + additional_bounds: bounds.clone(), + supports_unions: true, + methods: Vec::new(), + associated_types: Vec::new(), + is_const, + is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Unsafe(DUMMY_SP), + // `TrivialClone` is not part of an API guarantee, so it shouldn't + // appear in rustdoc output. + document: false, + }; + + trivial_def.expand_ext(cx, mitem, item, push, true); + } + let trait_def = TraitDef { span, path: path_std!(clone::Clone), @@ -88,6 +111,8 @@ pub(crate) fn expand_deriving_clone( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand_ext(cx, mitem, item, push, is_simple) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 29d531219a69..d9b82e97cb46 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -1,4 +1,4 @@ -use rustc_ast::{self as ast, MetaItem}; +use rustc_ast::{self as ast, MetaItem, Safety}; use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Span, sym}; @@ -44,6 +44,8 @@ pub(crate) fn expand_deriving_eq( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand_ext(cx, mitem, item, push, true) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index 0e1ecf3118ac..4a8e4e07942b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -1,4 +1,4 @@ -use rustc_ast::MetaItem; +use rustc_ast::{MetaItem, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; @@ -35,6 +35,8 @@ pub(crate) fn expand_deriving_ord( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index a0f71a1868b5..03ee270d66ba 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -1,4 +1,4 @@ -use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability}; +use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Span, sym}; use thin_vec::thin_vec; @@ -30,6 +30,8 @@ pub(crate) fn expand_deriving_partial_eq( associated_types: Vec::new(), is_const: false, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; structural_trait_def.expand(cx, mitem, item, push); @@ -59,6 +61,8 @@ pub(crate) fn expand_deriving_partial_eq( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index f5d262ece36e..d9d7dd9c3620 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,4 +1,4 @@ -use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind}; +use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; @@ -65,6 +65,8 @@ pub(crate) fn expand_deriving_partial_ord( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 597af0e09c06..48a2f19261f6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -1,4 +1,4 @@ -use rustc_ast::{self as ast, EnumDef, MetaItem}; +use rustc_ast::{self as ast, EnumDef, MetaItem, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_session::config::FmtDebug; use rustc_span::{Ident, Span, Symbol, sym}; @@ -42,6 +42,8 @@ pub(crate) fn expand_deriving_debug( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 2462114ec24a..86d53b23e1f1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -1,8 +1,7 @@ use core::ops::ControlFlow; -use rustc_ast as ast; use rustc_ast::visit::visit_opt; -use rustc_ast::{EnumDef, VariantData, attr}; +use rustc_ast::{self as ast, EnumDef, Safety, VariantData, attr}; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym}; use smallvec::SmallVec; @@ -52,6 +51,8 @@ pub(crate) fn expand_deriving_default( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; trait_def.expand(cx, mitem, item, push) } diff --git a/compiler/rustc_builtin_macros/src/deriving/from.rs b/compiler/rustc_builtin_macros/src/deriving/from.rs index ab25de7c9175..2e4369f3bb1c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/from.rs +++ b/compiler/rustc_builtin_macros/src/deriving/from.rs @@ -1,5 +1,5 @@ use rustc_ast as ast; -use rustc_ast::{ItemKind, VariantData}; +use rustc_ast::{ItemKind, Safety, VariantData}; use rustc_errors::MultiSpan; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_span::{Ident, Span, kw, sym}; @@ -127,6 +127,8 @@ pub(crate) fn expand_deriving_from( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; from_trait_def.expand(cx, mitem, annotatable, push); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 24a71ae94389..2b1e10120a0d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -225,6 +225,12 @@ pub(crate) struct TraitDef<'a> { pub is_const: bool, pub is_staged_api_crate: bool, + + /// The safety of the `impl`. + pub safety: Safety, + + /// Whether the added `impl` should appear in rustdoc output. + pub document: bool, } pub(crate) struct MethodDef<'a> { @@ -826,13 +832,17 @@ fn create_derived_impl( ) } + if !self.document { + attrs.push(cx.attr_nested_word(sym::doc, sym::hidden, self.span)); + } + cx.item( self.span, attrs, ast::ItemKind::Impl(ast::Impl { generics: trait_generics, of_trait: Some(Box::new(ast::TraitImplHeader { - safety: ast::Safety::Default, + safety: self.safety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, constness: if self.is_const { diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 78534449895c..e18c8e1750d8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -1,4 +1,4 @@ -use rustc_ast::{MetaItem, Mutability}; +use rustc_ast::{MetaItem, Mutability, Safety}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Span, sym}; use thin_vec::thin_vec; @@ -42,6 +42,8 @@ pub(crate) fn expand_deriving_hash( associated_types: Vec::new(), is_const, is_staged_api_crate: cx.ecfg.features.staged_api(), + safety: Safety::Default, + document: true, }; hash_trait_def.expand(cx, mitem, item, push); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 9ac282df5b5e..09d71f5dd557 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -916,8 +916,8 @@ pub(crate) fn codegen_call_with_unwind_action( pub(crate) fn lib_call_arg_param(tcx: TyCtxt<'_>, ty: Type, is_signed: bool) -> AbiParam { let param = AbiParam::new(ty); if ty.is_int() && u64::from(ty.bits()) < tcx.data_layout.pointer_size().bits() { - match (&tcx.sess.target.arch, tcx.sess.target.vendor.as_ref()) { - (Arch::X86_64, _) | (Arch::AArch64, "apple") => match (ty, is_signed) { + match (&tcx.sess.target.arch, tcx.sess.target.is_like_darwin) { + (Arch::X86_64, _) | (Arch::AArch64, true) => match (ty, is_signed) { (types::I8 | types::I16, true) => param.sext(), (types::I8 | types::I16, false) => param.uext(), _ => param, diff --git a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs index 91f7220667ff..86bff32dc623 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_f16_f128.rs @@ -5,7 +5,7 @@ pub(crate) fn f16_to_f32(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { let (value, arg_ty) = - if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64 { + if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 { ( fx.bcx.ins().bitcast(types::I16, MemFlags::new(), value), lib_call_arg_param(fx.tcx, types::I16, false), @@ -22,8 +22,7 @@ fn f16_to_f64(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { } pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { - let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64 - { + let ret_ty = if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 { types::I16 } else { types::F16 @@ -38,8 +37,7 @@ pub(crate) fn f32_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value } fn f64_to_f16(fx: &mut FunctionCx<'_, '_, '_>, value: Value) -> Value { - let ret_ty = if fx.tcx.sess.target.vendor == "apple" && fx.tcx.sess.target.arch == Arch::X86_64 - { + let ret_ty = if fx.tcx.sess.target.is_like_darwin && fx.tcx.sess.target.arch == Arch::X86_64 { types::I16 } else { types::F16 diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 4745243a6eab..5fdecd014ac0 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -49,7 +49,7 @@ use rustc_session::Session; use rustc_session::config::OutputFilenames; use rustc_span::{Symbol, sym}; -use rustc_target::spec::Arch; +use rustc_target::spec::{Abi, Arch, Env, Os}; pub use crate::config::*; use crate::prelude::*; @@ -163,15 +163,15 @@ fn init(&self, sess: &Session) { fn target_config(&self, sess: &Session) -> TargetConfig { // FIXME return the actually used target features. this is necessary for #[cfg(target_feature)] let target_features = match sess.target.arch { - Arch::X86_64 if sess.target.os != "none" => { + Arch::X86_64 if sess.target.os != Os::None => { // x86_64 mandates SSE2 support and rustc requires the x87 feature to be enabled vec![sym::fxsr, sym::sse, sym::sse2, Symbol::intern("x87")] } - Arch::AArch64 => match &*sess.target.os { - "none" => vec![], + Arch::AArch64 => match &sess.target.os { + Os::None => vec![], // On macOS the aes, sha2 and sha3 features are enabled by default and ring // fails to compile on macOS when they are not present. - "macos" => vec![sym::neon, sym::aes, sym::sha2, sym::sha3], + Os::MacOs => vec![sym::neon, sym::aes, sym::sha2, sym::sha3], // AArch64 mandates Neon support _ => vec![sym::neon], }, @@ -184,9 +184,9 @@ fn target_config(&self, sess: &Session) -> TargetConfig { // targets due to GCC using a different ABI than LLVM. Therefore `f16` and `f128` // won't be available when using a LLVM-built sysroot. let has_reliable_f16_f128 = !(sess.target.arch == Arch::X86_64 - && sess.target.os == "windows" - && sess.target.env == "gnu" - && sess.target.abi != "llvm"); + && sess.target.os == Os::Windows + && sess.target.env == Env::Gnu + && sess.target.abi != Abi::Llvm); TargetConfig { target_features, diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index b7e2583aad39..52f94dc2970a 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -62,7 +62,7 @@ jobs: - name: Build run: | ./y.sh prepare --only-libcore - EMBED_LTO_BITCODE=1 ./y.sh build --sysroot --release --release-sysroot + ./y.sh build --sysroot --release --release-sysroot ./y.sh test --cargo-tests ./y.sh clean all @@ -72,19 +72,14 @@ jobs: git config --global user.name "User" ./y.sh prepare - - name: Add more failing tests because of undefined symbol errors (FIXME) - run: cat tests/failing-lto-tests.txt >> tests/failing-ui-tests.txt - - name: Run tests run: | # FIXME(antoyo): we cannot enable LTO for stdarch tests currently because of some failing LTO tests using proc-macros. - # FIXME(antoyo): this should probably not be needed since we embed the LTO bitcode. - printf '[profile.release]\nlto = "fat"\n' >> build/build_sysroot/sysroot_src/library/Cargo.toml - EMBED_LTO_BITCODE=1 ./y.sh test --release --clean --release-sysroot --build-sysroot --keep-lto-tests ${{ matrix.commands }} + CG_RUSTFLAGS="-Cembed-bitcode=yes" ./y.sh test --release --clean --release-sysroot --build-sysroot --keep-lto-tests ${{ matrix.commands }} - - name: Run y.sh cargo build + - name: LTO test run: | - EMBED_LTO_BITCODE=1 CHANNEL="release" ./y.sh cargo build --release --manifest-path tests/hello-world/Cargo.toml + CHANNEL="release" ./y.sh cargo build --release --manifest-path tests/hello-world/Cargo.toml call_found=$(objdump -dj .text tests/hello-world/target/release/hello_world | grep -c "call .*mylib.*my_func" ) ||: if [ $call_found -gt 0 ]; then echo "ERROR: call my_func found in asm" @@ -92,6 +87,21 @@ jobs: exit 1 fi + - name: Cross-language LTO test + run: | + pushd tests/cross_lang_lto + gcc -c -flto add.c -masm=intel -fPIC -O3 + ar rcs libadd.a add.o + popd + + CHANNEL="release" CG_RUSTFLAGS="-L native=. -Clinker-plugin-lto -Clinker=gcc" ./y.sh cargo build --release --manifest-path tests/cross_lang_lto/Cargo.toml + call_found=$(objdump -dj .text tests/cross_lang_lto/target/release/cross_lang_lto | grep -c "call .*my_add" ) ||: + if [ $call_found -gt 0 ]; then + echo "ERROR: call my_add found in asm" + echo "Test is done with cross-language LTO enabled, hence inlining should occur across object files" + exit 1 + fi + # Summary job for the merge queue. # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! success_release: diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index 20d009f08a79..184f122cc1c1 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -41,7 +41,7 @@ jobs: # TODO: remove when we have binutils version 2.43 in the repo. - name: Install more recent binutils run: | - echo "deb http://archive.ubuntu.com/ubuntu oracular main universe" | sudo tee /etc/apt/sources.list.d/oracular-copies.list + echo "deb http://archive.ubuntu.com/ubuntu plucky main universe" | sudo tee /etc/apt/sources.list.d/plucky-copies.list sudo apt-get update sudo apt-get install binutils diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index a5b972baf98e..181d3aa89bc8 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0e310ef75f396cd11b2443b353d55376656ca92c13cba36f92b7aff346ac1a" +checksum = "60362e038e71e4bdc1a5b23fb45e1aba587b5947fe0db58f4871d95608f89eca" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.8.2" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ed7572b30cd32430294dde6fb70822d58e67c6846a548647e8739776a0125b" +checksum = "ddd542c8414e122217551c6af6b7d33acf51a227aee85276f218c087525e01bb" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 6031933bd2d2..d3ff2757857b 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -24,7 +24,7 @@ default = ["master"] [dependencies] object = { version = "0.37.0", default-features = false, features = ["std", "read"] } tempfile = "3.20" -gccjit = "2.8" +gccjit = "2.10" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index 859bb1568f4e..cd6aeae4b42e 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -70,11 +70,17 @@ $ ../gcc/configure \ $ make -j4 # You can replace `4` with another number depending on how many cores you have. ``` -If you want to run libgccjit tests, you will need to also enable the C++ language in the `configure`: +If you want to run libgccjit tests, you will need to +* Enable the C++ language in the `configure` step: ```bash --enable-languages=jit,c++ ``` +* Install [dejagnu](https://www.gnu.org/software/dejagnu/#downloading) to run the tests: + +```bash +$ sudo apt install dejagnu +``` Then to run libgccjit tests: @@ -135,16 +141,6 @@ $ CHANNEL="release" $CG_GCCJIT_DIR/y.sh cargo run If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./y.sh test`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely. -### LTO - -To use LTO, you need to set the variable `EMBED_LTO_BITCODE=1` in addition to setting `lto = "fat"` in the `Cargo.toml`. - -Failing to set `EMBED_LTO_BITCODE` will give you the following error: - -``` -error: failed to copy bitcode to object file: No such file or directory (os error 2) -``` - ### Rustc If you want to run `rustc` directly, you can do so with: diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index 94b40319f4a7..6aa5faec4c81 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -149,6 +149,9 @@ pub fn build_sysroot(env: &HashMap, config: &ConfigInfo) -> Resu // Copy files to sysroot let sysroot_path = start_dir.join(format!("sysroot/lib/rustlib/{}/lib/", config.target_triple)); + // To avoid errors like "multiple candidates for `rmeta` dependency `core` found", we clean the + // sysroot directory before copying the sysroot build artifacts. + let _ = fs::remove_dir_all(&sysroot_path); create_dir(&sysroot_path)?; let mut copier = |dir_to_copy: &Path| { // FIXME: should not use shell command! diff --git a/compiler/rustc_codegen_gcc/build_system/src/clean.rs b/compiler/rustc_codegen_gcc/build_system/src/clean.rs index a441ed613f94..43f01fdf35ec 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/clean.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/clean.rs @@ -69,8 +69,13 @@ fn clean_all() -> Result<(), String> { } fn clean_ui_tests() -> Result<(), String> { - let path = Path::new(crate::BUILD_DIR).join("rust/build/x86_64-unknown-linux-gnu/test/ui/"); - run_command(&[&"find", &path, &"-name", &"stamp", &"-delete"], None)?; + let directories = ["run-make", "run-make-cargo", "ui"]; + for directory in directories { + let path = Path::new(crate::BUILD_DIR) + .join("rust/build/x86_64-unknown-linux-gnu/test/") + .join(directory); + run_command(&[&"find", &path, &"-name", &"stamp", &"-delete"], None)?; + } Ok(()) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/prepare.rs b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs index 35a6e20fb86b..1e97e8d93274 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/prepare.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/prepare.rs @@ -1,5 +1,7 @@ +use std::ffi::OsStr; use std::fs; use std::path::{Path, PathBuf}; +use std::process::Output; use crate::rustc_info::get_rustc_path; use crate::utils::{ @@ -7,6 +9,41 @@ run_command_with_output, walk_dir, }; +// This is needed on systems where nothing is configured. +// git really needs something here, or it will fail. +// Even using --author is not enough. +const GIT_CMD: [&dyn AsRef; 9] = [ + &"git", + &"-c", + &"user.name=None", + &"-c", + &"user.email=none@example.com", + &"-c", + &"core.autocrlf=false", + &"-c", + &"commit.gpgSign=false", +]; + +fn run_git_command( + command: &dyn AsRef, + input: &[&dyn AsRef], + cwd: Option<&Path>, +) -> Result { + let git_cmd = + &GIT_CMD.into_iter().chain([command]).chain(input.iter().cloned()).collect::>()[..]; + run_command(git_cmd, cwd) +} + +fn run_git_command_with_output( + command: &dyn AsRef, + input: &[&dyn AsRef], + cwd: Option<&Path>, +) -> Result<(), String> { + let git_cmd = + &GIT_CMD.into_iter().chain([command]).chain(input.iter().cloned()).collect::>()[..]; + run_command_with_output(git_cmd, cwd) +} + fn prepare_libcore( sysroot_path: &Path, libgccjit12_patches: bool, @@ -55,19 +92,12 @@ fn prepare_libcore( run_command(&[&"cp", &"-r", &rustlib_dir.join("library"), &sysroot_dir], None)?; println!("[GIT] init (cwd): `{}`", sysroot_dir.display()); - run_command(&[&"git", &"init"], Some(&sysroot_dir))?; + run_git_command(&"init", &[], Some(&sysroot_dir))?; println!("[GIT] add (cwd): `{}`", sysroot_dir.display()); - run_command(&[&"git", &"add", &"."], Some(&sysroot_dir))?; + run_git_command(&"add", &[&"."], Some(&sysroot_dir))?; println!("[GIT] commit (cwd): `{}`", sysroot_dir.display()); - // This is needed on systems where nothing is configured. - // git really needs something here, or it will fail. - // Even using --author is not enough. - run_command(&[&"git", &"config", &"user.email", &"none@example.com"], Some(&sysroot_dir))?; - run_command(&[&"git", &"config", &"user.name", &"None"], Some(&sysroot_dir))?; - run_command(&[&"git", &"config", &"core.autocrlf", &"false"], Some(&sysroot_dir))?; - run_command(&[&"git", &"config", &"commit.gpgSign", &"false"], Some(&sysroot_dir))?; - run_command(&[&"git", &"commit", &"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?; + run_git_command(&"commit", &[&"-m", &"Initial commit", &"-q"], Some(&sysroot_dir))?; let mut patches = Vec::new(); walk_dir( @@ -105,10 +135,11 @@ fn prepare_libcore( for file_path in patches { println!("[GIT] apply `{}`", file_path.display()); let path = Path::new("../../..").join(file_path); - run_command_with_output(&[&"git", &"apply", &path], Some(&sysroot_dir))?; - run_command_with_output(&[&"git", &"add", &"-A"], Some(&sysroot_dir))?; - run_command_with_output( - &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())], + run_git_command_with_output(&"apply", &[&path], Some(&sysroot_dir))?; + run_git_command_with_output(&"add", &[&"-A"], Some(&sysroot_dir))?; + run_git_command_with_output( + &"commit", + &[&"-m", &format!("Patch {}", path.display())], Some(&sysroot_dir), )?; } @@ -124,10 +155,11 @@ fn prepare_rand() -> Result<(), String> { let rand_dir = Path::new("build/rand"); println!("[GIT] apply `{file_path}`"); let path = Path::new("../..").join(file_path); - run_command_with_output(&[&"git", &"apply", &path], Some(rand_dir))?; - run_command_with_output(&[&"git", &"add", &"-A"], Some(rand_dir))?; - run_command_with_output( - &[&"git", &"commit", &"--no-gpg-sign", &"-m", &format!("Patch {}", path.display())], + run_git_command_with_output(&"apply", &[&path], Some(rand_dir))?; + run_git_command_with_output(&"add", &[&"-A"], Some(rand_dir))?; + run_git_command_with_output( + &"commit", + &[&"-m", &format!("Patch {}", path.display())], Some(rand_dir), )?; @@ -154,8 +186,8 @@ fn clone_and_setup(repo_url: &str, checkout_commit: &str, extra: Option) - println!("`{}` has already been cloned", clone_result.repo_name); } let repo_path = Path::new(crate::BUILD_DIR).join(&clone_result.repo_name); - run_command(&[&"git", &"checkout", &"--", &"."], Some(&repo_path))?; - run_command(&[&"git", &"checkout", &checkout_commit], Some(&repo_path))?; + run_git_command(&"checkout", &[&"--", &"."], Some(&repo_path))?; + run_git_command(&"checkout", &[&checkout_commit], Some(&repo_path))?; if let Some(extra) = extra { extra(&repo_path)?; } diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 1823aa71f408..dbdaf2a63ef2 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -910,6 +910,7 @@ fn test_rustc_inner( prepare_files_callback: F, run_error_pattern_test: bool, test_type: &str, + run_ignored_tests: bool, ) -> Result<(), String> where F: Fn(&Path) -> Result, @@ -944,17 +945,7 @@ fn test_rustc_inner( rust_path.join("tests/ui"), &mut |dir| { let dir_name = dir.file_name().and_then(|name| name.to_str()).unwrap_or(""); - if [ - "abi", - "extern", - "unsized-locals", - "proc-macro", - "threads-sendsync", - "borrowck", - "test-attrs", - ] - .contains(&dir_name) - { + if ["abi", "extern", "proc-macro", "threads-sendsync"].contains(&dir_name) { remove_dir_all(dir).map_err(|error| { format!("Failed to remove folder `{}`: {:?}", dir.display(), error) })?; @@ -1061,30 +1052,34 @@ fn file_handling(keep_lto_tests: bool) -> impl Fn(&Path) -> Result<(), String> { env.get_mut("RUSTFLAGS").unwrap().clear(); - run_command_with_output_and_env( - &[ - &"./x.py", - &"test", - &"--run", - &"always", - &"--stage", - &"0", - &"--set", - &"build.compiletest-allow-stage0=true", - &format!("tests/{test_type}"), - &"--compiletest-rustc-args", - &rustc_args, - ], - Some(&rust_path), - Some(&env), - )?; + let test_dir = format!("tests/{test_type}"); + let mut command: Vec<&dyn AsRef> = vec![ + &"./x.py", + &"test", + &"--run", + &"always", + &"--stage", + &"0", + &"--set", + &"build.compiletest-allow-stage0=true", + &test_dir, + &"--compiletest-rustc-args", + &rustc_args, + ]; + + if run_ignored_tests { + command.push(&"--"); + command.push(&"--ignored"); + } + + run_command_with_output_and_env(&command, Some(&rust_path), Some(&env))?; Ok(()) } fn test_rustc(env: &Env, args: &TestArg) -> Result<(), String> { - test_rustc_inner(env, args, |_| Ok(false), false, "run-make")?; - test_rustc_inner(env, args, |_| Ok(false), false, "run-make-cargo")?; - test_rustc_inner(env, args, |_| Ok(false), false, "ui") + test_rustc_inner(env, args, |_| Ok(false), false, "run-make", false)?; + test_rustc_inner(env, args, |_| Ok(false), false, "run-make-cargo", false)?; + test_rustc_inner(env, args, |_| Ok(false), false, "ui", false) } fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { @@ -1094,6 +1089,7 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { retain_files_callback("tests/failing-run-make-tests.txt", "run-make"), false, "run-make", + true, ); let run_make_cargo_result = test_rustc_inner( @@ -1102,6 +1098,7 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { retain_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), false, "run-make", + true, ); let ui_result = test_rustc_inner( @@ -1110,6 +1107,7 @@ fn test_failing_rustc(env: &Env, args: &TestArg) -> Result<(), String> { retain_files_callback("tests/failing-ui-tests.txt", "ui"), false, "ui", + true, ); run_make_result.and(run_make_cargo_result).and(ui_result) @@ -1122,6 +1120,7 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { remove_files_callback("tests/failing-ui-tests.txt", "ui"), false, "ui", + false, )?; test_rustc_inner( env, @@ -1129,6 +1128,7 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { remove_files_callback("tests/failing-run-make-tests.txt", "run-make"), false, "run-make", + false, )?; test_rustc_inner( env, @@ -1136,6 +1136,7 @@ fn test_successful_rustc(env: &Env, args: &TestArg) -> Result<(), String> { remove_files_callback("tests/failing-run-make-tests.txt", "run-make-cargo"), false, "run-make-cargo", + false, ) } @@ -1146,6 +1147,7 @@ fn test_failing_ui_pattern_tests(env: &Env, args: &TestArg) -> Result<(), String remove_files_callback("tests/failing-ice-tests.txt", "ui"), true, "ui", + false, ) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/utils.rs b/compiler/rustc_codegen_gcc/build_system/src/utils.rs index fc948c54b24a..112322f8688c 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/utils.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/utils.rs @@ -112,8 +112,7 @@ pub fn run_command_with_output( cwd: Option<&Path>, ) -> Result<(), String> { let exit_status = exec_command(input, cwd, None)?; - check_exit_status(input, cwd, exit_status, None, true)?; - Ok(()) + check_exit_status(input, cwd, exit_status, None, true) } pub fn run_command_with_output_and_env( @@ -122,8 +121,7 @@ pub fn run_command_with_output_and_env( env: Option<&HashMap>, ) -> Result<(), String> { let exit_status = exec_command(input, cwd, env)?; - check_exit_status(input, cwd, exit_status, None, true)?; - Ok(()) + check_exit_status(input, cwd, exit_status, None, true) } #[cfg(not(unix))] @@ -133,8 +131,7 @@ pub fn run_command_with_output_and_env_no_err( env: Option<&HashMap>, ) -> Result<(), String> { let exit_status = exec_command(input, cwd, env)?; - check_exit_status(input, cwd, exit_status, None, false)?; - Ok(()) + check_exit_status(input, cwd, exit_status, None, false) } pub fn cargo_install(to_install: &str) -> Result<(), String> { diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 9dfb12be2436..64a5b431bd07 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -603,7 +603,7 @@ impl, U: ?Sized, A: Allocator> CoerceUnsized> fo impl Box { pub fn new(val: T) -> Box { unsafe { - let size = intrinsics::size_of::(); + let size = size_of::(); let ptr = libc::malloc(size); intrinsics::copy(&val as *const T as *const u8, ptr, size); Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global) @@ -657,11 +657,11 @@ pub mod intrinsics { #[rustc_intrinsic] pub fn abort() -> !; #[rustc_intrinsic] - pub fn size_of() -> usize; + pub const fn size_of() -> usize; #[rustc_intrinsic] pub unsafe fn size_of_val(val: *const T) -> usize; #[rustc_intrinsic] - pub fn align_of() -> usize; + pub const fn align_of() -> usize; #[rustc_intrinsic] pub unsafe fn align_of_val(val: *const T) -> usize; #[rustc_intrinsic] @@ -671,7 +671,7 @@ pub mod intrinsics { #[rustc_intrinsic] pub unsafe fn ctlz_nonzero(x: T) -> u32; #[rustc_intrinsic] - pub fn needs_drop() -> bool; + pub const fn needs_drop() -> bool; #[rustc_intrinsic] pub fn bitreverse(x: T) -> T; #[rustc_intrinsic] @@ -699,6 +699,24 @@ pub mod libc { } } +pub const fn size_of() -> usize { + ::SIZE +} + +pub const fn align_of() -> usize { + ::ALIGN +} + +trait SizedTypeProperties: Sized { + #[lang = "mem_size_const"] + const SIZE: usize = intrinsics::size_of::(); + + #[lang = "mem_align_const"] + const ALIGN: usize = intrinsics::align_of::(); +} + +impl SizedTypeProperties for T {} + #[lang = "index"] pub trait Index { type Output: ?Sized; diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 85489f850e24..d5c386ffb3dd 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -90,8 +90,8 @@ fn start( ) -> isize { if argc == 3 { unsafe { puts(*argv); } - unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const u8)); } - unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const u8)); } + unsafe { puts(*((argv as usize + size_of::<*const u8>()) as *const *const u8)); } + unsafe { puts(*((argv as usize + 2 * size_of::<*const u8>()) as *const *const u8)); } } main().report(); @@ -154,7 +154,7 @@ fn main() { let slice = &[0, 1] as &[i32]; let slice_ptr = slice as *const [i32] as *const i32; - let align = intrinsics::align_of::<*const i32>(); + let align = align_of::<*const i32>(); assert_eq!(slice_ptr as usize % align, 0); //return; @@ -195,11 +195,9 @@ fn main() { assert_eq!(intrinsics::size_of_val(a) as u8, 8); assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4); - assert_eq!(intrinsics::align_of::() as u8, 2); - assert_eq!(intrinsics::align_of_val(&a) as u8, intrinsics::align_of::<&str>() as u8); + assert_eq!(align_of::() as u8, 2); + assert_eq!(intrinsics::align_of_val(&a) as u8, align_of::<&str>() as u8); - /* - * TODO: re-enable in the next sync. let u8_needs_drop = const { intrinsics::needs_drop::() }; assert!(!u8_needs_drop); let slice_needs_drop = const { intrinsics::needs_drop::<[u8]>() }; @@ -208,7 +206,6 @@ fn main() { assert!(noisy_drop); let noisy_unsized_drop = const { intrinsics::needs_drop::() }; assert!(noisy_unsized_drop); - */ Unique { pointer: 0 as *const &str, diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index dc9a00128646..b8d4166542bc 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -4e995bd73c4490edfe5080ec6014d63aa9abed5f +28b84db392ac0a572f1a2a2a1317aa5f2bc742cb diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 04d33dfb116c..9813bbea00c4 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-08-25" +channel = "nightly-2025-11-04" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index bcc28e476dba..cc2c9fca94df 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -90,7 +90,7 @@ fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> { 64 => cx.type_f64(), _ => bug!("unsupported float: {:?}", self), }, - RegKind::Vector => unimplemented!(), //cx.type_vector(cx.type_i8(), self.size.bytes()), + RegKind::Vector => cx.type_vector(cx.type_i8(), self.size.bytes()), } } } diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 598bbe74007d..404064fb7a06 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -21,6 +21,7 @@ use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::sync::Arc; +use std::sync::atomic::Ordering; use gccjit::{Context, OutputKind}; use object::read::archive::ArchiveFile; @@ -39,7 +40,7 @@ use crate::back::write::save_temp_bitcode; use crate::errors::LtoBitcodeFromRlib; -use crate::{GccCodegenBackend, GccContext, SyncContext, to_gcc_opt_level}; +use crate::{GccCodegenBackend, GccContext, LTO_SUPPORTED, LtoMode, SyncContext, to_gcc_opt_level}; struct LtoData { // TODO(antoyo): use symbols_below_threshold. @@ -229,7 +230,7 @@ fn fat_lto( info!("linking {:?}", name); match bc_decoded { SerializedModule::Local(ref module_buffer) => { - module.module_llvm.should_combine_object_files = true; + module.module_llvm.lto_mode = LtoMode::Fat; module .module_llvm .context @@ -534,7 +535,7 @@ pub fn optimize_thin_module( // that LLVM Context and Module. //let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); //let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &dcx)? as *const _; - let mut should_combine_object_files = false; + let mut lto_mode = LtoMode::None; let context = match thin_module.shared.thin_buffers.get(thin_module.idx) { Some(thin_buffer) => Arc::clone(&thin_buffer.context), None => { @@ -545,7 +546,7 @@ pub fn optimize_thin_module( SerializedModule::Local(ref module_buffer) => { let path = module_buffer.0.to_str().expect("path"); context.add_driver_option(path); - should_combine_object_files = true; + lto_mode = LtoMode::Thin; /*module.module_llvm.should_combine_object_files = true; module .module_llvm @@ -560,11 +561,13 @@ pub fn optimize_thin_module( Arc::new(SyncContext::new(context)) } }; + let lto_supported = LTO_SUPPORTED.load(Ordering::SeqCst); let module = ModuleCodegen::new_regular( thin_module.name().to_string(), GccContext { context, - should_combine_object_files, + lto_mode, + lto_supported, // TODO(antoyo): use the correct relocation model here. relocation_model: RelocModel::Pic, temp_dir: None, diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 8ba730e32502..eae0f2aa00f6 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -11,7 +11,7 @@ use crate::base::add_pic_option; use crate::errors::CopyBitcode; -use crate::{GccCodegenBackend, GccContext}; +use crate::{GccCodegenBackend, GccContext, LtoMode}; pub(crate) fn codegen( cgcx: &CodegenContext, @@ -25,12 +25,8 @@ pub(crate) fn codegen( { let context = &module.module_llvm.context; - let should_combine_object_files = module.module_llvm.should_combine_object_files; - - // NOTE: Only generate object files with GIMPLE when this environment variable is set for - // now because this requires a particular setup (same gcc/lto1/lto-wrapper commit as libgccjit). - // TODO(antoyo): remove this environment variable. - let fat_lto = env::var("EMBED_LTO_BITCODE").as_deref() == Ok("1"); + let lto_mode = module.module_llvm.lto_mode; + let lto_supported = module.module_llvm.lto_supported; let bc_out = cgcx.output_filenames.temp_path_for_cgu( OutputType::Bitcode, @@ -44,80 +40,46 @@ pub(crate) fn codegen( ); if config.bitcode_needed() { - if fat_lto { + let _timer = cgcx + .prof + .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); + + // TODO(antoyo) + /*if let Some(bitcode_filename) = bc_out.file_name() { + cgcx.prof.artifact_size( + "llvm_bitcode", + bitcode_filename.to_string_lossy(), + data.len() as u64, + ); + }*/ + + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { let _timer = cgcx .prof - .generic_activity_with_arg("GCC_module_codegen_make_bitcode", &*module.name); - - // TODO(antoyo) - /*if let Some(bitcode_filename) = bc_out.file_name() { - cgcx.prof.artifact_size( - "llvm_bitcode", - bitcode_filename.to_string_lossy(), - data.len() as u64, - ); - }*/ - - if config.emit_bc || config.emit_obj == EmitObj::Bitcode { - let _timer = cgcx.prof.generic_activity_with_arg( - "GCC_module_codegen_emit_bitcode", - &*module.name, - ); + .generic_activity_with_arg("GCC_module_codegen_emit_bitcode", &*module.name); + if lto_supported { context.add_command_line_option("-flto=auto"); context.add_command_line_option("-flto-partition=one"); - // TODO(antoyo): remove since we don't want fat objects when it is for Bitcode only. - context.add_command_line_option("-ffat-lto-objects"); - context.compile_to_file( - OutputKind::ObjectFile, - bc_out.to_str().expect("path to str"), - ); } + context + .compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str")); + } - if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { - let _timer = cgcx.prof.generic_activity_with_arg( - "GCC_module_codegen_embed_bitcode", - &*module.name, - ); + if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { + let _timer = cgcx + .prof + .generic_activity_with_arg("GCC_module_codegen_embed_bitcode", &*module.name); + if lto_supported { // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); context.add_command_line_option("-flto=auto"); context.add_command_line_option("-flto-partition=one"); context.add_command_line_option("-ffat-lto-objects"); - // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). - context.compile_to_file( - OutputKind::ObjectFile, - bc_out.to_str().expect("path to str"), - ); - } - } else { - if config.emit_bc || config.emit_obj == EmitObj::Bitcode { - let _timer = cgcx.prof.generic_activity_with_arg( - "GCC_module_codegen_emit_bitcode", - &*module.name, - ); - context.compile_to_file( - OutputKind::ObjectFile, - bc_out.to_str().expect("path to str"), - ); - } - - if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { - // TODO(antoyo): we might want to emit to emit an error here, saying to set the - // environment variable EMBED_LTO_BITCODE. - let _timer = cgcx.prof.generic_activity_with_arg( - "GCC_module_codegen_embed_bitcode", - &*module.name, - ); - // TODO(antoyo): maybe we should call embed_bitcode to have the proper iOS fixes? - //embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data); - - // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). - context.compile_to_file( - OutputKind::ObjectFile, - bc_out.to_str().expect("path to str"), - ); } + // TODO(antoyo): Send -plugin/usr/lib/gcc/x86_64-pc-linux-gnu/11.1.0/liblto_plugin.so to linker (this should be done when specifying the appropriate rustc cli argument). + context + .compile_to_file(OutputKind::ObjectFile, bc_out.to_str().expect("path to str")); } } @@ -166,7 +128,10 @@ pub(crate) fn codegen( context.set_debug_info(true); context.dump_to_file(path, true); } - if should_combine_object_files { + if lto_mode != LtoMode::None { + let fat_lto = lto_mode == LtoMode::Fat; + // We need to check if we're doing LTO since this code is also used for the + // dummy ThinLTO implementation to combine the object files. if fat_lto { context.add_command_line_option("-flto=auto"); context.add_command_line_option("-flto-partition=one"); diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 0a0f0ed37f0b..18058d9491ad 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -21,7 +21,7 @@ use crate::builder::Builder; use crate::context::CodegenCx; -use crate::{GccContext, LockedTargetInfo, SyncContext, gcc_util, new_context}; +use crate::{GccContext, LockedTargetInfo, LtoMode, SyncContext, gcc_util, new_context}; #[cfg(feature = "master")] pub fn visibility_to_gcc(visibility: Visibility) -> gccjit::Visibility { @@ -74,6 +74,7 @@ pub fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, target_info: LockedTargetInfo, + lto_supported: bool, ) -> (ModuleCodegen, u64) { let prof_timer = tcx.prof.generic_activity("codegen_module"); let start_time = Instant::now(); @@ -82,7 +83,7 @@ pub fn compile_codegen_unit( let (module, _) = tcx.dep_graph.with_task( dep_node, tcx, - (cgu_name, target_info), + (cgu_name, target_info, lto_supported), module_codegen, Some(dep_graph::hash_result), ); @@ -95,7 +96,7 @@ pub fn compile_codegen_unit( fn module_codegen( tcx: TyCtxt<'_>, - (cgu_name, target_info): (Symbol, LockedTargetInfo), + (cgu_name, target_info, lto_supported): (Symbol, LockedTargetInfo, bool), ) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); // Instantiate monomorphizations without filling out definitions yet... @@ -247,7 +248,8 @@ fn module_codegen( GccContext { context: Arc::new(SyncContext::new(context)), relocation_model: tcx.sess.relocation_model(), - should_combine_object_files: false, + lto_supported, + lto_mode: LtoMode::None, temp_dir: None, }, ) diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 86031a8a4634..132c43ef3cda 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -668,32 +668,38 @@ fn unreachable(&mut self) { } fn add(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_add(a, b) + self.assign_to_var(self.gcc_add(a, b)) } fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - a + b + self.assign_to_var(a + b) } // TODO(antoyo): should we also override the `unchecked_` versions? fn sub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_sub(a, b) + self.assign_to_var(self.gcc_sub(a, b)) } fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - a - b + self.assign_to_var(a - b) } fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_mul(a, b) + self.assign_to_var(self.gcc_mul(a, b)) } fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.cx.context.new_binary_op(self.location, BinaryOp::Mult, a.get_type(), a, b) + self.assign_to_var(self.cx.context.new_binary_op( + self.location, + BinaryOp::Mult, + a.get_type(), + a, + b, + )) } fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_udiv(a, b) + self.assign_to_var(self.gcc_udiv(a, b)) } fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { @@ -702,11 +708,11 @@ fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { let a = self.gcc_int_cast(a, a_type); let b_type = b.get_type().to_unsigned(self); let b = self.gcc_int_cast(b, b_type); - self.gcc_udiv(a, b) + self.assign_to_var(self.gcc_udiv(a, b)) } fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_sdiv(a, b) + self.assign_to_var(self.gcc_sdiv(a, b)) } fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { @@ -715,19 +721,19 @@ fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { // should be the same. let typ = a.get_type().to_signed(self); let b = self.gcc_int_cast(b, typ); - self.gcc_sdiv(a, b) + self.assign_to_var(self.gcc_sdiv(a, b)) } fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - a / b + self.assign_to_var(a / b) } fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_urem(a, b) + self.assign_to_var(self.gcc_urem(a, b)) } fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - self.gcc_srem(a, b) + self.assign_to_var(self.gcc_srem(a, b)) } fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { @@ -865,22 +871,26 @@ fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> { fn fadd_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - set_rvalue_location(self, lhs + rhs) + let result = set_rvalue_location(self, lhs + rhs); + self.assign_to_var(result) } fn fsub_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - set_rvalue_location(self, lhs - rhs) + let result = set_rvalue_location(self, lhs - rhs); + self.assign_to_var(result) } fn fmul_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - set_rvalue_location(self, lhs * rhs) + let result = set_rvalue_location(self, lhs * rhs); + self.assign_to_var(result) } fn fdiv_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - set_rvalue_location(self, lhs / rhs) + let result = set_rvalue_location(self, lhs / rhs); + self.assign_to_var(result) } fn frem_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { @@ -892,22 +902,22 @@ fn frem_fast(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { fn fadd_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - lhs + rhs + self.assign_to_var(lhs + rhs) } fn fsub_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - lhs - rhs + self.assign_to_var(lhs - rhs) } fn fmul_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - lhs * rhs + self.assign_to_var(lhs * rhs) } fn fdiv_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC. - lhs / rhs + self.assign_to_var(lhs / rhs) } fn frem_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { @@ -2409,6 +2419,15 @@ pub fn vector_select( let res = then_vals | else_vals; self.bitcast_if_needed(res, result_type) } + + // GCC doesn't like deeply nested expressions. + // By assigning intermediate expressions to a variable, this allow us to avoid deeply nested + // expressions and GCC will use much less RAM. + fn assign_to_var(&self, value: RValue<'gcc>) -> RValue<'gcc> { + let var = self.current_func().new_local(self.location, value.get_type(), "opResult"); + self.llbb().add_assignment(self.location, var, value); + var.to_rvalue() + } } fn difference_or_zero<'gcc>( diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 9fb7f6bad684..aa1d3b6b091c 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -83,12 +83,11 @@ pub fn gcc_lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { let a_size = a_type.get_size(); let b_size = b_type.get_size(); match a_size.cmp(&b_size) { - std::cmp::Ordering::Less => { - let a = self.context.new_cast(self.location, a, b_type); - a >> b - } std::cmp::Ordering::Equal => a >> b, - std::cmp::Ordering::Greater => { + _ => { + // NOTE: it is OK to cast even if b has a type bigger than a because b has + // been masked by codegen_ssa before calling Builder::lshr or + // Builder::ashr. let b = self.context.new_cast(self.location, b, a_type); a >> b } @@ -692,12 +691,10 @@ pub fn gcc_shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { let a_size = a_type.get_size(); let b_size = b_type.get_size(); match a_size.cmp(&b_size) { - std::cmp::Ordering::Less => { - let a = self.context.new_cast(self.location, a, b_type); - a << b - } std::cmp::Ordering::Equal => a << b, - std::cmp::Ordering::Greater => { + _ => { + // NOTE: it is OK to cast even if b has a type bigger than a because b has + // been masked by codegen_ssa before calling Builder::shl. let b = self.context.new_cast(self.location, b, a_type); a << b } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs index d1b2a93243d2..c51bcbcedd67 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs @@ -85,12 +85,41 @@ fn aarch64(name: &str, full_name: &str) -> &'static str { fn amdgcn(name: &str, full_name: &str) -> &'static str { match name { // amdgcn + "add.max.i32" => "__builtin_amdgcn_add_max_i32", + "add.max.u32" => "__builtin_amdgcn_add_max_u32", + "add.min.i32" => "__builtin_amdgcn_add_min_i32", + "add.min.u32" => "__builtin_amdgcn_add_min_u32", "alignbyte" => "__builtin_amdgcn_alignbyte", "ashr.pk.i8.i32" => "__builtin_amdgcn_ashr_pk_i8_i32", "ashr.pk.u8.i32" => "__builtin_amdgcn_ashr_pk_u8_i32", "buffer.wbinvl1" => "__builtin_amdgcn_buffer_wbinvl1", "buffer.wbinvl1.sc" => "__builtin_amdgcn_buffer_wbinvl1_sc", "buffer.wbinvl1.vol" => "__builtin_amdgcn_buffer_wbinvl1_vol", + "cluster.id.x" => "__builtin_amdgcn_cluster_id_x", + "cluster.id.y" => "__builtin_amdgcn_cluster_id_y", + "cluster.id.z" => "__builtin_amdgcn_cluster_id_z", + "cluster.load.async.to.lds.b128" => { + "__builtin_amdgcn_cluster_load_async_to_lds_b128" + } + "cluster.load.async.to.lds.b32" => { + "__builtin_amdgcn_cluster_load_async_to_lds_b32" + } + "cluster.load.async.to.lds.b64" => { + "__builtin_amdgcn_cluster_load_async_to_lds_b64" + } + "cluster.load.async.to.lds.b8" => { + "__builtin_amdgcn_cluster_load_async_to_lds_b8" + } + "cluster.workgroup.flat.id" => "__builtin_amdgcn_cluster_workgroup_flat_id", + "cluster.workgroup.id.x" => "__builtin_amdgcn_cluster_workgroup_id_x", + "cluster.workgroup.id.y" => "__builtin_amdgcn_cluster_workgroup_id_y", + "cluster.workgroup.id.z" => "__builtin_amdgcn_cluster_workgroup_id_z", + "cluster.workgroup.max.flat.id" => { + "__builtin_amdgcn_cluster_workgroup_max_flat_id" + } + "cluster.workgroup.max.id.x" => "__builtin_amdgcn_cluster_workgroup_max_id_x", + "cluster.workgroup.max.id.y" => "__builtin_amdgcn_cluster_workgroup_max_id_y", + "cluster.workgroup.max.id.z" => "__builtin_amdgcn_cluster_workgroup_max_id_z", "cubeid" => "__builtin_amdgcn_cubeid", "cubema" => "__builtin_amdgcn_cubema", "cubesc" => "__builtin_amdgcn_cubesc", @@ -101,18 +130,36 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "cvt.f32.fp8" => "__builtin_amdgcn_cvt_f32_fp8", "cvt.f32.fp8.e5m3" => "__builtin_amdgcn_cvt_f32_fp8_e5m3", "cvt.off.f32.i4" => "__builtin_amdgcn_cvt_off_f32_i4", + "cvt.pk.bf8.f16" => "__builtin_amdgcn_cvt_pk_bf8_f16", "cvt.pk.bf8.f32" => "__builtin_amdgcn_cvt_pk_bf8_f32", "cvt.pk.f16.bf8" => "__builtin_amdgcn_cvt_pk_f16_bf8", "cvt.pk.f16.fp8" => "__builtin_amdgcn_cvt_pk_f16_fp8", "cvt.pk.f32.bf8" => "__builtin_amdgcn_cvt_pk_f32_bf8", "cvt.pk.f32.fp8" => "__builtin_amdgcn_cvt_pk_f32_fp8", + "cvt.pk.fp8.f16" => "__builtin_amdgcn_cvt_pk_fp8_f16", "cvt.pk.fp8.f32" => "__builtin_amdgcn_cvt_pk_fp8_f32", + "cvt.pk.fp8.f32.e5m3" => "__builtin_amdgcn_cvt_pk_fp8_f32_e5m3", "cvt.pk.i16" => "__builtin_amdgcn_cvt_pk_i16", "cvt.pk.u16" => "__builtin_amdgcn_cvt_pk_u16", "cvt.pk.u8.f32" => "__builtin_amdgcn_cvt_pk_u8_f32", "cvt.pknorm.i16" => "__builtin_amdgcn_cvt_pknorm_i16", "cvt.pknorm.u16" => "__builtin_amdgcn_cvt_pknorm_u16", "cvt.pkrtz" => "__builtin_amdgcn_cvt_pkrtz", + "cvt.scale.pk16.bf16.bf6" => "__builtin_amdgcn_cvt_scale_pk16_bf16_bf6", + "cvt.scale.pk16.bf16.fp6" => "__builtin_amdgcn_cvt_scale_pk16_bf16_fp6", + "cvt.scale.pk16.f16.bf6" => "__builtin_amdgcn_cvt_scale_pk16_f16_bf6", + "cvt.scale.pk16.f16.fp6" => "__builtin_amdgcn_cvt_scale_pk16_f16_fp6", + "cvt.scale.pk16.f32.bf6" => "__builtin_amdgcn_cvt_scale_pk16_f32_bf6", + "cvt.scale.pk16.f32.fp6" => "__builtin_amdgcn_cvt_scale_pk16_f32_fp6", + "cvt.scale.pk8.bf16.bf8" => "__builtin_amdgcn_cvt_scale_pk8_bf16_bf8", + "cvt.scale.pk8.bf16.fp4" => "__builtin_amdgcn_cvt_scale_pk8_bf16_fp4", + "cvt.scale.pk8.bf16.fp8" => "__builtin_amdgcn_cvt_scale_pk8_bf16_fp8", + "cvt.scale.pk8.f16.bf8" => "__builtin_amdgcn_cvt_scale_pk8_f16_bf8", + "cvt.scale.pk8.f16.fp4" => "__builtin_amdgcn_cvt_scale_pk8_f16_fp4", + "cvt.scale.pk8.f16.fp8" => "__builtin_amdgcn_cvt_scale_pk8_f16_fp8", + "cvt.scale.pk8.f32.bf8" => "__builtin_amdgcn_cvt_scale_pk8_f32_bf8", + "cvt.scale.pk8.f32.fp4" => "__builtin_amdgcn_cvt_scale_pk8_f32_fp4", + "cvt.scale.pk8.f32.fp8" => "__builtin_amdgcn_cvt_scale_pk8_f32_fp8", "cvt.scalef32.2xpk16.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_bf6_f32", "cvt.scalef32.2xpk16.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_2xpk16_fp6_f32", "cvt.scalef32.f16.bf8" => "__builtin_amdgcn_cvt_scalef32_f16_bf8", @@ -137,6 +184,12 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "cvt.scalef32.pk.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_bf16", "cvt.scalef32.pk.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f16", "cvt.scalef32.pk.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_pk_fp8_f32", + "cvt.scalef32.pk16.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk16_bf6_bf16", + "cvt.scalef32.pk16.bf6.f16" => "__builtin_amdgcn_cvt_scalef32_pk16_bf6_f16", + "cvt.scalef32.pk16.bf6.f32" => "__builtin_amdgcn_cvt_scalef32_pk16_bf6_f32", + "cvt.scalef32.pk16.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk16_fp6_bf16", + "cvt.scalef32.pk16.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_pk16_fp6_f16", + "cvt.scalef32.pk16.fp6.f32" => "__builtin_amdgcn_cvt_scalef32_pk16_fp6_f32", "cvt.scalef32.pk32.bf16.bf6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_bf6", "cvt.scalef32.pk32.bf16.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_bf16_fp6", "cvt.scalef32.pk32.bf6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_bf6_bf16", @@ -147,6 +200,15 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "cvt.scalef32.pk32.f32.fp6" => "__builtin_amdgcn_cvt_scalef32_pk32_f32_fp6", "cvt.scalef32.pk32.fp6.bf16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_bf16", "cvt.scalef32.pk32.fp6.f16" => "__builtin_amdgcn_cvt_scalef32_pk32_fp6_f16", + "cvt.scalef32.pk8.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk8_bf8_bf16", + "cvt.scalef32.pk8.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_pk8_bf8_f16", + "cvt.scalef32.pk8.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_pk8_bf8_f32", + "cvt.scalef32.pk8.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_pk8_fp4_bf16", + "cvt.scalef32.pk8.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_pk8_fp4_f16", + "cvt.scalef32.pk8.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_pk8_fp4_f32", + "cvt.scalef32.pk8.fp8.bf16" => "__builtin_amdgcn_cvt_scalef32_pk8_fp8_bf16", + "cvt.scalef32.pk8.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_pk8_fp8_f16", + "cvt.scalef32.pk8.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_pk8_fp8_f32", "cvt.scalef32.sr.bf8.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_bf16", "cvt.scalef32.sr.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f16", "cvt.scalef32.sr.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_bf8_f32", @@ -156,6 +218,24 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "cvt.scalef32.sr.pk.fp4.bf16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_bf16", "cvt.scalef32.sr.pk.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f16", "cvt.scalef32.sr.pk.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk_fp4_f32", + "cvt.scalef32.sr.pk16.bf6.bf16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_bf6_bf16" + } + "cvt.scalef32.sr.pk16.bf6.f16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_bf6_f16" + } + "cvt.scalef32.sr.pk16.bf6.f32" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_bf6_f32" + } + "cvt.scalef32.sr.pk16.fp6.bf16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_fp6_bf16" + } + "cvt.scalef32.sr.pk16.fp6.f16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_fp6_f16" + } + "cvt.scalef32.sr.pk16.fp6.f32" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk16_fp6_f32" + } "cvt.scalef32.sr.pk32.bf6.bf16" => { "__builtin_amdgcn_cvt_scalef32_sr_pk32_bf6_bf16" } @@ -174,10 +254,30 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "cvt.scalef32.sr.pk32.fp6.f32" => { "__builtin_amdgcn_cvt_scalef32_sr_pk32_fp6_f32" } + "cvt.scalef32.sr.pk8.bf8.bf16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk8_bf8_bf16" + } + "cvt.scalef32.sr.pk8.bf8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_bf8_f16", + "cvt.scalef32.sr.pk8.bf8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_bf8_f32", + "cvt.scalef32.sr.pk8.fp4.bf16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp4_bf16" + } + "cvt.scalef32.sr.pk8.fp4.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp4_f16", + "cvt.scalef32.sr.pk8.fp4.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp4_f32", + "cvt.scalef32.sr.pk8.fp8.bf16" => { + "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp8_bf16" + } + "cvt.scalef32.sr.pk8.fp8.f16" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp8_f16", + "cvt.scalef32.sr.pk8.fp8.f32" => "__builtin_amdgcn_cvt_scalef32_sr_pk8_fp8_f32", "cvt.sr.bf16.f32" => "__builtin_amdgcn_cvt_sr_bf16_f32", + "cvt.sr.bf8.f16" => "__builtin_amdgcn_cvt_sr_bf8_f16", "cvt.sr.bf8.f32" => "__builtin_amdgcn_cvt_sr_bf8_f32", "cvt.sr.f16.f32" => "__builtin_amdgcn_cvt_sr_f16_f32", + "cvt.sr.fp8.f16" => "__builtin_amdgcn_cvt_sr_fp8_f16", "cvt.sr.fp8.f32" => "__builtin_amdgcn_cvt_sr_fp8_f32", + "cvt.sr.fp8.f32.e5m3" => "__builtin_amdgcn_cvt_sr_fp8_f32_e5m3", + "cvt.sr.pk.bf16.f32" => "__builtin_amdgcn_cvt_sr_pk_bf16_f32", + "cvt.sr.pk.f16.f32" => "__builtin_amdgcn_cvt_sr_pk_f16_f32", "dispatch.id" => "__builtin_amdgcn_dispatch_id", "dot4.f32.bf8.bf8" => "__builtin_amdgcn_dot4_f32_bf8_bf8", "dot4.f32.bf8.fp8" => "__builtin_amdgcn_dot4_f32_bf8_fp8", @@ -297,8 +397,20 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "mqsad.u32.u8" => "__builtin_amdgcn_mqsad_u32_u8", "msad.u8" => "__builtin_amdgcn_msad_u8", "perm" => "__builtin_amdgcn_perm", + "perm.pk16.b4.u4" => "__builtin_amdgcn_perm_pk16_b4_u4", + "perm.pk16.b6.u4" => "__builtin_amdgcn_perm_pk16_b6_u4", + "perm.pk16.b8.u4" => "__builtin_amdgcn_perm_pk16_b8_u4", + "permlane.bcast" => "__builtin_amdgcn_permlane_bcast", + "permlane.down" => "__builtin_amdgcn_permlane_down", + "permlane.idx.gen" => "__builtin_amdgcn_permlane_idx_gen", + "permlane.up" => "__builtin_amdgcn_permlane_up", + "permlane.xor" => "__builtin_amdgcn_permlane_xor", "permlane16.var" => "__builtin_amdgcn_permlane16_var", "permlanex16.var" => "__builtin_amdgcn_permlanex16_var", + "pk.add.max.i16" => "__builtin_amdgcn_pk_add_max_i16", + "pk.add.max.u16" => "__builtin_amdgcn_pk_add_max_u16", + "pk.add.min.i16" => "__builtin_amdgcn_pk_add_min_i16", + "pk.add.min.u16" => "__builtin_amdgcn_pk_add_min_u16", "prng.b32" => "__builtin_amdgcn_prng_b32", "qsad.pk.u16.u8" => "__builtin_amdgcn_qsad_pk_u16_u8", "queue.ptr" => "__builtin_amdgcn_queue_ptr", @@ -306,11 +418,15 @@ fn amdgcn(name: &str, full_name: &str) -> &'static str { "rcp.legacy" => "__builtin_amdgcn_rcp_legacy", "rsq.legacy" => "__builtin_amdgcn_rsq_legacy", "s.barrier" => "__builtin_amdgcn_s_barrier", + "s.barrier.init" => "__builtin_amdgcn_s_barrier_init", + "s.barrier.join" => "__builtin_amdgcn_s_barrier_join", + "s.barrier.leave" => "__builtin_amdgcn_s_barrier_leave", "s.barrier.signal" => "__builtin_amdgcn_s_barrier_signal", "s.barrier.signal.isfirst" => "__builtin_amdgcn_s_barrier_signal_isfirst", "s.barrier.signal.var" => "__builtin_amdgcn_s_barrier_signal_var", "s.barrier.wait" => "__builtin_amdgcn_s_barrier_wait", "s.buffer.prefetch.data" => "__builtin_amdgcn_s_buffer_prefetch_data", + "s.cluster.barrier" => "__builtin_amdgcn_s_cluster_barrier", "s.dcache.inv" => "__builtin_amdgcn_s_dcache_inv", "s.dcache.inv.vol" => "__builtin_amdgcn_s_dcache_inv_vol", "s.dcache.wb" => "__builtin_amdgcn_s_dcache_wb", @@ -1900,6 +2016,8 @@ fn hexagon(name: &str, full_name: &str) -> &'static str { "V6.vfneg.hf.128B" => "__builtin_HEXAGON_V6_vfneg_hf_128B", "V6.vfneg.sf" => "__builtin_HEXAGON_V6_vfneg_sf", "V6.vfneg.sf.128B" => "__builtin_HEXAGON_V6_vfneg_sf_128B", + "V6.vgather.vscattermh" => "__builtin_HEXAGON_V6_vgather_vscattermh", + "V6.vgather.vscattermh.128B" => "__builtin_HEXAGON_V6_vgather_vscattermh_128B", "V6.vgathermh" => "__builtin_HEXAGON_V6_vgathermh", "V6.vgathermh.128B" => "__builtin_HEXAGON_V6_vgathermh_128B", "V6.vgathermhq" => "__builtin_HEXAGON_V6_vgathermhq", @@ -2382,6 +2500,8 @@ fn hexagon(name: &str, full_name: &str) -> &'static str { "V6.vsub.hf.f8.128B" => "__builtin_HEXAGON_V6_vsub_hf_f8_128B", "V6.vsub.hf.hf" => "__builtin_HEXAGON_V6_vsub_hf_hf", "V6.vsub.hf.hf.128B" => "__builtin_HEXAGON_V6_vsub_hf_hf_128B", + "V6.vsub.hf.mix" => "__builtin_HEXAGON_V6_vsub_hf_mix", + "V6.vsub.hf.mix.128B" => "__builtin_HEXAGON_V6_vsub_hf_mix_128B", "V6.vsub.qf16" => "__builtin_HEXAGON_V6_vsub_qf16", "V6.vsub.qf16.128B" => "__builtin_HEXAGON_V6_vsub_qf16_128B", "V6.vsub.qf16.mix" => "__builtin_HEXAGON_V6_vsub_qf16_mix", @@ -2396,6 +2516,8 @@ fn hexagon(name: &str, full_name: &str) -> &'static str { "V6.vsub.sf.bf.128B" => "__builtin_HEXAGON_V6_vsub_sf_bf_128B", "V6.vsub.sf.hf" => "__builtin_HEXAGON_V6_vsub_sf_hf", "V6.vsub.sf.hf.128B" => "__builtin_HEXAGON_V6_vsub_sf_hf_128B", + "V6.vsub.sf.mix" => "__builtin_HEXAGON_V6_vsub_sf_mix", + "V6.vsub.sf.mix.128B" => "__builtin_HEXAGON_V6_vsub_sf_mix_128B", "V6.vsub.sf.sf" => "__builtin_HEXAGON_V6_vsub_sf_sf", "V6.vsub.sf.sf.128B" => "__builtin_HEXAGON_V6_vsub_sf_sf_128B", "V6.vsubb" => "__builtin_HEXAGON_V6_vsubb", @@ -4883,6 +5005,26 @@ fn nvvm(name: &str, full_name: &str) -> &'static str { "f2ull.rp.ftz" => "__nvvm_f2ull_rp_ftz", "f2ull.rz" => "__nvvm_f2ull_rz", "f2ull.rz.ftz" => "__nvvm_f2ull_rz_ftz", + "f32x4.to.e2m1x4.rs.relu.satfinite" => { + "__nvvm_f32x4_to_e2m1x4_rs_relu_satfinite" + } + "f32x4.to.e2m1x4.rs.satfinite" => "__nvvm_f32x4_to_e2m1x4_rs_satfinite", + "f32x4.to.e2m3x4.rs.relu.satfinite" => { + "__nvvm_f32x4_to_e2m3x4_rs_relu_satfinite" + } + "f32x4.to.e2m3x4.rs.satfinite" => "__nvvm_f32x4_to_e2m3x4_rs_satfinite", + "f32x4.to.e3m2x4.rs.relu.satfinite" => { + "__nvvm_f32x4_to_e3m2x4_rs_relu_satfinite" + } + "f32x4.to.e3m2x4.rs.satfinite" => "__nvvm_f32x4_to_e3m2x4_rs_satfinite", + "f32x4.to.e4m3x4.rs.relu.satfinite" => { + "__nvvm_f32x4_to_e4m3x4_rs_relu_satfinite" + } + "f32x4.to.e4m3x4.rs.satfinite" => "__nvvm_f32x4_to_e4m3x4_rs_satfinite", + "f32x4.to.e5m2x4.rs.relu.satfinite" => { + "__nvvm_f32x4_to_e5m2x4_rs_relu_satfinite" + } + "f32x4.to.e5m2x4.rs.satfinite" => "__nvvm_f32x4_to_e5m2x4_rs_satfinite", "fabs.d" => "__nvvm_fabs_d", "fabs.f" => "__nvvm_fabs_f", "fabs.ftz.f" => "__nvvm_fabs_ftz_f", @@ -4902,10 +5044,18 @@ fn nvvm(name: &str, full_name: &str) -> &'static str { "ff.to.ue8m0x2.rz.satfinite" => "__nvvm_ff_to_ue8m0x2_rz_satfinite", "ff2bf16x2.rn" => "__nvvm_ff2bf16x2_rn", "ff2bf16x2.rn.relu" => "__nvvm_ff2bf16x2_rn_relu", + "ff2bf16x2.rs" => "__nvvm_ff2bf16x2_rs", + "ff2bf16x2.rs.relu" => "__nvvm_ff2bf16x2_rs_relu", + "ff2bf16x2.rs.relu.satfinite" => "__nvvm_ff2bf16x2_rs_relu_satfinite", + "ff2bf16x2.rs.satfinite" => "__nvvm_ff2bf16x2_rs_satfinite", "ff2bf16x2.rz" => "__nvvm_ff2bf16x2_rz", "ff2bf16x2.rz.relu" => "__nvvm_ff2bf16x2_rz_relu", "ff2f16x2.rn" => "__nvvm_ff2f16x2_rn", "ff2f16x2.rn.relu" => "__nvvm_ff2f16x2_rn_relu", + "ff2f16x2.rs" => "__nvvm_ff2f16x2_rs", + "ff2f16x2.rs.relu" => "__nvvm_ff2f16x2_rs_relu", + "ff2f16x2.rs.relu.satfinite" => "__nvvm_ff2f16x2_rs_relu_satfinite", + "ff2f16x2.rs.satfinite" => "__nvvm_ff2f16x2_rs_satfinite", "ff2f16x2.rz" => "__nvvm_ff2f16x2_rz", "ff2f16x2.rz.relu" => "__nvvm_ff2f16x2_rz_relu", "floor.d" => "__nvvm_floor_d", @@ -5129,6 +5279,7 @@ fn nvvm(name: &str, full_name: &str) -> &'static str { "read.ptx.sreg.envreg8" => "__nvvm_read_ptx_sreg_envreg8", "read.ptx.sreg.envreg9" => "__nvvm_read_ptx_sreg_envreg9", "read.ptx.sreg.globaltimer" => "__nvvm_read_ptx_sreg_globaltimer", + "read.ptx.sreg.globaltimer.lo" => "__nvvm_read_ptx_sreg_globaltimer_lo", "read.ptx.sreg.gridid" => "__nvvm_read_ptx_sreg_gridid", // [DUPLICATE]: "read.ptx.sreg.gridid" => "__nvvm_read_ptx_sreg_", "read.ptx.sreg.laneid" => "__nvvm_read_ptx_sreg_laneid", @@ -5803,6 +5954,8 @@ fn ppc(name: &str, full_name: &str) -> &'static str { "altivec.vupklsw" => "__builtin_altivec_vupklsw", "bcdadd" => "__builtin_ppc_bcdadd", "bcdadd.p" => "__builtin_ppc_bcdadd_p", + "bcdcopysign" => "__builtin_ppc_bcdcopysign", + "bcdsetsign" => "__builtin_ppc_bcdsetsign", "bcdsub" => "__builtin_ppc_bcdsub", "bcdsub.p" => "__builtin_ppc_bcdsub_p", "bpermd" => "__builtin_bpermd", @@ -6160,6 +6313,9 @@ fn riscv(name: &str, full_name: &str) -> &'static str { "aes64im" => "__builtin_riscv_aes64im", "aes64ks1i" => "__builtin_riscv_aes64ks1i", "aes64ks2" => "__builtin_riscv_aes64ks2", + "mips.ehb" => "__builtin_riscv_mips_ehb", + "mips.ihb" => "__builtin_riscv_mips_ihb", + "mips.pause" => "__builtin_riscv_mips_pause", "sha512sig0" => "__builtin_riscv_sha512sig0", "sha512sig0h" => "__builtin_riscv_sha512sig0h", "sha512sig0l" => "__builtin_riscv_sha512sig0l", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 99a4f9b9f7e7..8225df568641 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -308,10 +308,6 @@ fn codegen_intrinsic_call( .or_else(|| get_simple_function_f128(self, name)) .or_else(|| get_simple_function_f128_2args(self, name)); - // FIXME(tempdragon): Re-enable `clippy::suspicious_else_formatting` if the following issue is solved: - // https://github.com/rust-lang/rust-clippy/issues/12497 - // and leave `else if use_integer_compare` to be placed "as is". - #[allow(clippy::suspicious_else_formatting)] let value = match name { _ if simple.is_some() => { let func = simple.expect("simple intrinsic function"); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index c8bb57c1b472..409b7886740a 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -72,10 +72,7 @@ use std::fmt::Debug; use std::ops::Deref; use std::path::PathBuf; -#[cfg(not(feature = "master"))] -use std::sync::atomic::AtomicBool; -#[cfg(not(feature = "master"))] -use std::sync::atomic::Ordering; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use back::lto::{ThinBuffer, ThinData}; @@ -167,8 +164,11 @@ fn supports_target_dependent_type(&self, typ: CType) -> bool { #[derive(Clone)] pub struct GccCodegenBackend { target_info: LockedTargetInfo, + lto_supported: Arc, } +static LTO_SUPPORTED: AtomicBool = AtomicBool::new(false); + impl CodegenBackend for GccCodegenBackend { fn locale_resource(&self) -> &'static str { crate::DEFAULT_LOCALE_RESOURCE @@ -193,7 +193,13 @@ fn init(&self, _sess: &Session) { } #[cfg(feature = "master")] - gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); + { + let lto_supported = gccjit::is_lto_supported(); + LTO_SUPPORTED.store(lto_supported, Ordering::SeqCst); + self.lto_supported.store(lto_supported, Ordering::SeqCst); + + gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); + } #[cfg(not(feature = "master"))] { @@ -276,10 +282,12 @@ fn codegen_allocator( module_name: &str, methods: &[AllocatorMethod], ) -> Self::Module { + let lto_supported = self.lto_supported.load(Ordering::SeqCst); let mut mods = GccContext { context: Arc::new(SyncContext::new(new_context(tcx))), relocation_model: tcx.sess.relocation_model(), - should_combine_object_files: false, + lto_mode: LtoMode::None, + lto_supported, temp_dir: None, }; @@ -294,7 +302,12 @@ fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, ) -> (ModuleCodegen, u64) { - base::compile_codegen_unit(tcx, cgu_name, self.target_info.clone()) + base::compile_codegen_unit( + tcx, + cgu_name, + self.target_info.clone(), + self.lto_supported.load(Ordering::SeqCst), + ) } fn target_machine_factory( @@ -308,12 +321,20 @@ fn target_machine_factory( } } +#[derive(Clone, Copy, PartialEq)] +pub enum LtoMode { + None, + Thin, + Fat, +} + pub struct GccContext { context: Arc, /// This field is needed in order to be able to set the flag -fPIC when necessary when doing /// LTO. relocation_model: RelocModel, - should_combine_object_files: bool, + lto_mode: LtoMode, + lto_supported: bool, // Temporary directory used by LTO. We keep it here so that it's not removed before linking. temp_dir: Option, } @@ -425,7 +446,10 @@ pub fn __rustc_codegen_backend() -> Box { supports_128bit_integers: AtomicBool::new(false), }))); - Box::new(GccCodegenBackend { target_info: LockedTargetInfo { info } }) + Box::new(GccCodegenBackend { + lto_supported: Arc::new(AtomicBool::new(false)), + target_info: LockedTargetInfo { info }, + }) } fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { diff --git a/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.lock b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.lock new file mode 100644 index 000000000000..a0c9d2df383d --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cross_lang_lto" +version = "0.1.0" diff --git a/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.toml b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.toml new file mode 100644 index 000000000000..777e6548df2b --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "cross_lang_lto" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/compiler/rustc_codegen_gcc/tests/cross_lang_lto/add.c b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/add.c new file mode 100644 index 000000000000..cc59f113bc19 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/add.c @@ -0,0 +1,5 @@ +#include + +uint32_t my_add(uint32_t a, uint32_t b) { + return a + b; +} diff --git a/compiler/rustc_codegen_gcc/tests/cross_lang_lto/src/main.rs b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/src/main.rs new file mode 100644 index 000000000000..3c64248ba2a0 --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/cross_lang_lto/src/main.rs @@ -0,0 +1,18 @@ +/* + * Compile the C code with: + * gcc -c -flto add.c -ffat-lto-objects + * ar rcs libadd.a add.o + * + * Compile the Rust code with: + * CG_RUSTFLAGS="-L native=. -Clinker-plugin-lto -Clinker=gcc" y cargo run --release + */ + +#[link(name="add")] +unsafe extern "C" { + fn my_add(a: u32, b: u32) -> u32; +} + +fn main() { + let res = unsafe { my_add(30, 12) }; + println!("{}", res); +} diff --git a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt deleted file mode 100644 index bf0633f73200..000000000000 --- a/compiler/rustc_codegen_gcc/tests/failing-lto-tests.txt +++ /dev/null @@ -1,33 +0,0 @@ -tests/ui/lint/unsafe_code/forge_unsafe_block.rs -tests/ui/lint/unused-qualification-in-derive-expansion.rs -tests/ui/macros/macro-quote-test.rs -tests/ui/macros/proc_macro.rs -tests/ui/panic-runtime/lto-unwind.rs -tests/ui/resolve/derive-macro-1.rs -tests/ui/resolve/derive-macro-2.rs -tests/ui/rfcs/rfc-2565-param-attrs/param-attrs-pretty.rs -tests/ui/rfcs/rfc-2565-param-attrs/issue-64682-dropping-first-attrs-in-impl-fns.rs -tests/ui/rfcs/rfc-3348-c-string-literals/edition-spans.rs -tests/ui/rust-2018/suggestions-not-always-applicable.rs -tests/ui/rust-2021/reserved-prefixes-via-macro.rs -tests/ui/underscore-imports/duplicate.rs -tests/ui/async-await/issues/issue-60674.rs -tests/ui/attributes/main-removed-2/main.rs -tests/ui/cfg/assume-incomplete-release/assume-incomplete.rs -tests/ui/crate-loading/cross-compiled-proc-macro.rs -tests/ui/derives/derive-marker-tricky.rs -tests/ui/diagnostic_namespace/existing_proc_macros.rs -tests/ui/fmt/format-args-capture-issue-106408.rs -tests/ui/fmt/indoc-issue-106408.rs -tests/ui/hygiene/issue-77523-def-site-async-await.rs -tests/ui/inherent-impls-overlap-check/no-overlap.rs -tests/ui/enum-discriminant/issue-46519.rs -tests/ui/issues/issue-45731.rs -tests/ui/lint/test-allow-dead-extern-static-no-warning.rs -tests/ui/macros/macro-comma-behavior-rpass.rs -tests/ui/macros/rfc-2011-nicer-assert-messages/assert-with-custom-errors-does-not-create-unnecessary-code.rs -tests/ui/macros/rfc-2011-nicer-assert-messages/feature-gate-generic_assert.rs -tests/ui/macros/stringify.rs -tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-in-test.rs -tests/ui/binding/fn-arg-incomplete-pattern-drop-order.rs -tests/ui/lto/debuginfo-lto-alloc.rs diff --git a/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt index c5e22970c660..822aaec0edeb 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-run-make-tests.txt @@ -1,35 +1,11 @@ -tests/run-make/a-b-a-linker-guard/ -tests/run-make/CURRENT_RUSTC_VERSION/ tests/run-make/cross-lang-lto/ tests/run-make/cross-lang-lto-upstream-rlibs/ -tests/run-make/doctests-keep-binaries/ -tests/run-make/doctests-runtool/ -tests/run-make/emit-shared-files/ -tests/run-make/exit-code/ tests/run-make/llvm-ident/ tests/run-make/native-link-modifier-bundle/ tests/run-make/remap-path-prefix-dwarf/ -tests/run-make/repr128-dwarf/ tests/run-make/rlib-format-packed-bundled-libs/ tests/run-make/rlib-format-packed-bundled-libs-2/ -tests/run-make/rustdoc-determinism/ -tests/run-make/rustdoc-error-lines/ -tests/run-make/rustdoc-map-file/ -tests/run-make/rustdoc-output-path/ -tests/run-make/rustdoc-scrape-examples-invalid-expr/ -tests/run-make/rustdoc-scrape-examples-multiple/ -tests/run-make/rustdoc-scrape-examples-ordering/ -tests/run-make/rustdoc-scrape-examples-remap/ -tests/run-make/rustdoc-scrape-examples-test/ -tests/run-make/rustdoc-scrape-examples-whitespace/ -tests/run-make/rustdoc-scrape-examples-macros/ -tests/run-make/rustdoc-with-out-dir-option/ -tests/run-make/rustdoc-verify-output-files/ -tests/run-make/rustdoc-themes/ -tests/run-make/rustdoc-with-short-out-dir-option/ -tests/run-make/rustdoc-with-output-option/ tests/run-make/arguments-non-c-like-enum/ -tests/run-make/c-link-to-rust-staticlib/ tests/run-make/foreign-double-unwind/ tests/run-make/foreign-exceptions/ tests/run-make/glibc-staticlib-args/ diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index e2615bce190e..cc00432ceb54 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -1,5 +1,3 @@ -tests/ui/allocator/no_std-alloc-error-handler-custom.rs -tests/ui/allocator/no_std-alloc-error-handler-default.rs tests/ui/asm/may_unwind.rs tests/ui/asm/x86_64/may_unwind.rs tests/ui/drop/dynamic-drop-async.rs @@ -17,7 +15,6 @@ tests/ui/panic-runtime/link-to-abort.rs tests/ui/parser/unclosed-delimiter-in-dep.rs tests/ui/consts/missing_span_in_backtrace.rs tests/ui/drop/dynamic-drop.rs -tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs tests/ui/simd/issue-17170.rs tests/ui/simd/issue-39720.rs tests/ui/drop/panic-during-drop-14875.rs @@ -31,11 +28,9 @@ tests/ui/coroutine/resume-after-return.rs tests/ui/simd/masked-load-store.rs tests/ui/simd/repr_packed.rs tests/ui/async-await/in-trait/dont-project-to-specializable-projection.rs -tests/ui/consts/try-operator.rs tests/ui/coroutine/unwind-abort-mix.rs tests/ui/consts/issue-miri-1910.rs tests/ui/consts/const_cmp_type_id.rs -tests/ui/consts/issue-73976-monomorphic.rs tests/ui/consts/issue-94675.rs tests/ui/traits/const-traits/const-drop-fail.rs tests/ui/runtime/on-broken-pipe/child-processes.rs @@ -53,7 +48,6 @@ tests/ui/sanitizer/cfi/virtual-auto.rs tests/ui/sanitizer/cfi/sized-associated-ty.rs tests/ui/sanitizer/cfi/can-reveal-opaques.rs tests/ui/sanitizer/kcfi-mangling.rs -tests/ui/statics/const_generics.rs tests/ui/backtrace/dylib-dep.rs tests/ui/delegation/fn-header.rs tests/ui/consts/const-eval/parse_ints.rs @@ -74,13 +68,7 @@ tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs tests/ui/simd/simd-bitmask-notpow2.rs tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs tests/ui/numbers-arithmetic/u128-as-f32.rs -tests/ui/lto/all-crates.rs -tests/ui/uninhabited/uninhabited-transparent-return-abi.rs -tests/ui/coroutine/panic-drops-resume.rs -tests/ui/coroutine/panic-drops.rs -tests/ui/coroutine/panic-safe.rs tests/ui/process/nofile-limit.rs -tests/ui/simd/intrinsic/generic-arithmetic-pass.rs tests/ui/linking/no-gc-encapsulation-symbols.rs tests/ui/panics/unwind-force-no-unwind-tables.rs tests/ui/attributes/fn-align-dyn.rs @@ -88,3 +76,4 @@ tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs tests/ui/explicit-tail-calls/recursion-etc.rs tests/ui/explicit-tail-calls/indexer.rs tests/ui/explicit-tail-calls/drop-order.rs +tests/ui/c-variadic/valid.rs diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c55fe3fad008..fde7dd6ef7a8 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -683,7 +683,7 @@ fn handle_offload<'ll>(cx: &'ll SimpleCx<'_>, old_fn: &llvm::Value) { // Here we map the old arguments to the new arguments, with an offset of 1 to make sure // that we don't use the newly added `%dyn_ptr`. unsafe { - llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn); + llvm::LLVMRustOffloadMapper(old_fn, new_fn); } llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index f0c1dfc53aca..b10a1282f4dd 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -760,30 +760,18 @@ fn write_operand_repeatedly( count: u64, dest: PlaceRef<'tcx, &'ll Value>, ) { - let zero = self.const_usize(0); - let count = self.const_usize(count); - - let header_bb = self.append_sibling_block("repeat_loop_header"); - let body_bb = self.append_sibling_block("repeat_loop_body"); - let next_bb = self.append_sibling_block("repeat_loop_next"); - - self.br(header_bb); - - let mut header_bx = Self::build(self.cx, header_bb); - let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]); - - let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count); - header_bx.cond_br(keep_going, body_bb, next_bb); - - let mut body_bx = Self::build(self.cx, body_bb); - let dest_elem = dest.project_index(&mut body_bx, i); - cg_elem.val.store(&mut body_bx, dest_elem); - - let next = body_bx.unchecked_uadd(i, self.const_usize(1)); - body_bx.br(header_bb); - header_bx.add_incoming_to_phi(i, next, body_bb); - - *self = Self::build(self.cx, next_bb); + if self.cx.sess().opts.optimize == OptLevel::No { + // To let debuggers single-step over lines like + // + // let foo = ["bar"; 42]; + // + // we need the debugger-friendly LLVM IR that `_unoptimized()` + // provides. The `_optimized()` version generates trickier LLVM IR. + // See PR #148058 for a failed attempt at handling that. + self.write_operand_repeatedly_unoptimized(cg_elem, count, dest); + } else { + self.write_operand_repeatedly_optimized(cg_elem, count, dest); + } } fn range_metadata(&mut self, load: &'ll Value, range: WrappingRange) { @@ -1514,6 +1502,78 @@ pub(crate) fn set_unpredictable(&mut self, inst: &'ll Value) { self.set_metadata_node(inst, llvm::MD_unpredictable, &[]); } + fn write_operand_repeatedly_optimized( + &mut self, + cg_elem: OperandRef<'tcx, &'ll Value>, + count: u64, + dest: PlaceRef<'tcx, &'ll Value>, + ) { + let zero = self.const_usize(0); + let count = self.const_usize(count); + + let header_bb = self.append_sibling_block("repeat_loop_header"); + let body_bb = self.append_sibling_block("repeat_loop_body"); + let next_bb = self.append_sibling_block("repeat_loop_next"); + + self.br(header_bb); + + let mut header_bx = Self::build(self.cx, header_bb); + let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]); + + let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count); + header_bx.cond_br(keep_going, body_bb, next_bb); + + let mut body_bx = Self::build(self.cx, body_bb); + let dest_elem = dest.project_index(&mut body_bx, i); + cg_elem.val.store(&mut body_bx, dest_elem); + + let next = body_bx.unchecked_uadd(i, self.const_usize(1)); + body_bx.br(header_bb); + header_bx.add_incoming_to_phi(i, next, body_bb); + + *self = Self::build(self.cx, next_bb); + } + + fn write_operand_repeatedly_unoptimized( + &mut self, + cg_elem: OperandRef<'tcx, &'ll Value>, + count: u64, + dest: PlaceRef<'tcx, &'ll Value>, + ) { + let zero = self.const_usize(0); + let count = self.const_usize(count); + let start = dest.project_index(self, zero).val.llval; + let end = dest.project_index(self, count).val.llval; + + let header_bb = self.append_sibling_block("repeat_loop_header"); + let body_bb = self.append_sibling_block("repeat_loop_body"); + let next_bb = self.append_sibling_block("repeat_loop_next"); + + self.br(header_bb); + + let mut header_bx = Self::build(self.cx, header_bb); + let current = header_bx.phi(self.val_ty(start), &[start], &[self.llbb()]); + + let keep_going = header_bx.icmp(IntPredicate::IntNE, current, end); + header_bx.cond_br(keep_going, body_bb, next_bb); + + let mut body_bx = Self::build(self.cx, body_bb); + let align = dest.val.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); + cg_elem + .val + .store(&mut body_bx, PlaceRef::new_sized_aligned(current, cg_elem.layout, align)); + + let next = body_bx.inbounds_gep( + self.backend_type(cg_elem.layout), + current, + &[self.const_usize(1)], + ); + body_bx.br(header_bb); + header_bx.add_incoming_to_phi(current, next, body_bb); + + *self = Self::build(self.cx, next_bb); + } + pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { self.call_intrinsic("llvm.minnum", &[self.val_ty(lhs)], &[lhs, rhs]) } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index f0d4c546e986..9215273eed17 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -7,7 +7,7 @@ use rustc_codegen_ssa::common; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, Env}; use tracing::debug; use crate::context::CodegenCx; @@ -145,7 +145,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t if cx.use_dll_storage_attrs && let Some(library) = tcx.native_library(instance_def_id) && library.kind.is_dllimport() - && !matches!(tcx.sess.target.env.as_ref(), "gnu" | "uclibc") + && !matches!(tcx.sess.target.env, Env::Gnu | Env::Uclibc) { llvm::set_dllimport_storage_class(llfn); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index c01f163f2ee1..b60c8a7d3719 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -29,7 +29,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{ - Arch, HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel, + Abi, Arch, Env, HasTargetSpec, Os, RelocModel, SmallDataThresholdSupport, Target, TlsModel, }; use smallvec::SmallVec; @@ -335,9 +335,9 @@ pub(crate) unsafe fn create_module<'ll>( // Control Flow Guard is currently only supported by MSVC and LLVM on Windows. if sess.target.is_like_msvc - || (sess.target.options.os == "windows" - && sess.target.options.env == "gnu" - && sess.target.options.abi == "llvm") + || (sess.target.options.os == Os::Windows + && sess.target.options.env == Env::Gnu + && sess.target.options.abi == Abi::Llvm) { match sess.opts.cg.control_flow_guard { CFGuard::Disabled => {} @@ -669,7 +669,7 @@ pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&' /// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC. pub(crate) fn objc_abi_version(&self) -> u32 { assert!(self.tcx.sess.target.is_like_darwin); - if self.tcx.sess.target.arch == Arch::X86 && self.tcx.sess.target.os == "macos" { + if self.tcx.sess.target.arch == Arch::X86 && self.tcx.sess.target.os == Os::MacOs { // 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI"). 1 } else { @@ -710,7 +710,7 @@ pub(crate) fn add_objc_module_flags(&self) { }, ); - if self.tcx.sess.target.env == "sim" { + if self.tcx.sess.target.env == Env::Sim { llvm::add_module_flag_u32( self.llmod, llvm::ModuleFlagMergeBehavior::Error, @@ -963,7 +963,7 @@ pub(crate) fn eh_catch_typeinfo(&self) -> &'ll Value { return eh_catch_typeinfo; } let tcx = self.tcx; - assert!(self.sess().target.os == "emscripten"); + assert!(self.sess().target.os == Os::Emscripten); let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { Some(def_id) => self.get_static(def_id), _ => { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0626cb3f2f16..739b33f34962 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -18,6 +18,7 @@ use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; +use rustc_target::spec::Os; use tracing::debug; use crate::abi::FnAbiLlvmExt; @@ -681,7 +682,7 @@ fn catch_unwind_intrinsic<'ll, 'tcx>( codegen_msvc_try(bx, try_func, data, catch_func, dest); } else if wants_wasm_eh(bx.sess()) { codegen_wasm_try(bx, try_func, data, catch_func, dest); - } else if bx.sess().target.os == "emscripten" { + } else if bx.sess().target.os == Os::Emscripten { codegen_emcc_try(bx, try_func, data, catch_func, dest); } else { codegen_gnu_try(bx, try_func, data, catch_func, dest); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 16549f9aab81..ca64d96c2a33 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2025,7 +2025,7 @@ pub(crate) fn LLVMRustCreateRangeAttribute( ) -> &Attribute; // Operations on functions - pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value); + pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value); pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 3714049c3231..b498448417f5 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -15,7 +15,9 @@ use rustc_middle::bug; use rustc_session::Session; use rustc_session::config::{PrintKind, PrintRequest}; -use rustc_target::spec::{Arch, MergeFunctions, PanicStrategy, SmallDataThresholdSupport}; +use rustc_target::spec::{ + Abi, Arch, Env, MergeFunctions, Os, PanicStrategy, SmallDataThresholdSupport, +}; use smallvec::{SmallVec, smallvec}; use crate::back::write::create_informational_target_machine; @@ -104,7 +106,7 @@ fn llvm_arg_to_arg_name(full_arg: &str) -> &str { add("-wasm-enable-eh", false); } - if sess.target.os == "emscripten" + if sess.target.os == Os::Emscripten && !sess.opts.unstable_opts.emscripten_wasm_eh && sess.panic_strategy().unwinds() { @@ -351,9 +353,9 @@ pub(crate) fn target_config(sess: &Session) -> TargetConfig { /// Determine whether or not experimental float types are reliable based on known bugs. fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { let target_arch = &sess.target.arch; - let target_os = sess.target.options.os.as_ref(); - let target_env = sess.target.options.env.as_ref(); - let target_abi = sess.target.options.abi.as_ref(); + let target_os = &sess.target.options.os; + let target_env = &sess.target.options.env; + let target_abi = &sess.target.options.abi; let target_pointer_width = sess.target.pointer_width; let version = get_version(); let lt_20_1_1 = version < (20, 1, 1); @@ -371,7 +373,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // Selection failure (fixed in llvm21) (Arch::S390x, _) if lt_21_0_0 => false, // MinGW ABI bugs - (Arch::X86_64, "windows") if target_env == "gnu" && target_abi != "llvm" => false, + (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // Infinite recursion (Arch::CSky, _) => false, (Arch::Hexagon, _) if lt_21_0_0 => false, // (fixed in llvm21) @@ -403,7 +405,7 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // not fail if our compiler-builtins is linked. (fixed in llvm21) (Arch::X86, _) if lt_21_0_0 => false, // MinGW ABI bugs - (Arch::X86_64, "windows") if target_env == "gnu" && target_abi != "llvm" => false, + (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // There are no known problems on other platforms, so the only requirement is that symbols // are available. `compiler-builtins` provides all symbols required for core `f128` // support, so this should work for everything else. @@ -424,9 +426,9 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // (ld is 80-bit extended precision). // // musl does not implement the symbols required for f128 math at all. - _ if target_env == "musl" => false, + _ if *target_env == Env::Musl => false, (Arch::X86_64, _) => false, - (_, "linux") if target_pointer_width == 64 => true, + (_, Os::Linux) if target_pointer_width == 64 => true, _ => false, } && cfg.has_reliable_f128; } diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 115d96d9baf6..add25da025b2 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -7,7 +7,7 @@ }; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; -use rustc_target::spec::Arch; +use rustc_target::spec::{Abi, Arch}; use crate::builder::Builder; use crate::llvm::{Type, Value}; @@ -270,7 +270,7 @@ fn emit_powerpc_va_arg<'ll, 'tcx>( // Rust does not currently support any powerpc softfloat targets. let target = &bx.cx.tcx.sess.target; - let is_soft_float_abi = target.abi == "softfloat"; + let is_soft_float_abi = target.abi == Abi::SoftFloat; assert!(!is_soft_float_abi); // All instances of VaArgSafe are passed directly. diff --git a/compiler/rustc_codegen_ssa/src/back/apple.rs b/compiler/rustc_codegen_ssa/src/back/apple.rs index 3b29ddeadc7f..23808ade6c85 100644 --- a/compiler/rustc_codegen_ssa/src/back/apple.rs +++ b/compiler/rustc_codegen_ssa/src/back/apple.rs @@ -6,7 +6,7 @@ use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_session::Session; pub(super) use rustc_target::spec::apple::OSVersion; -use rustc_target::spec::{Arch, Target}; +use rustc_target::spec::{Arch, Env, Os, Target}; use tracing::debug; use crate::errors::{XcrunError, XcrunSdkPathWarning}; @@ -17,35 +17,35 @@ /// The canonical name of the desired SDK for a given target. pub(super) fn sdk_name(target: &Target) -> &'static str { - match (&*target.os, &*target.env) { - ("macos", "") => "MacOSX", - ("ios", "") => "iPhoneOS", - ("ios", "sim") => "iPhoneSimulator", + match (&target.os, &target.env) { + (Os::MacOs, Env::Unspecified) => "MacOSX", + (Os::IOs, Env::Unspecified) => "iPhoneOS", + (Os::IOs, Env::Sim) => "iPhoneSimulator", // Mac Catalyst uses the macOS SDK - ("ios", "macabi") => "MacOSX", - ("tvos", "") => "AppleTVOS", - ("tvos", "sim") => "AppleTVSimulator", - ("visionos", "") => "XROS", - ("visionos", "sim") => "XRSimulator", - ("watchos", "") => "WatchOS", - ("watchos", "sim") => "WatchSimulator", + (Os::IOs, Env::MacAbi) => "MacOSX", + (Os::TvOs, Env::Unspecified) => "AppleTVOS", + (Os::TvOs, Env::Sim) => "AppleTVSimulator", + (Os::VisionOs, Env::Unspecified) => "XROS", + (Os::VisionOs, Env::Sim) => "XRSimulator", + (Os::WatchOs, Env::Unspecified) => "WatchOS", + (Os::WatchOs, Env::Sim) => "WatchSimulator", (os, abi) => unreachable!("invalid os '{os}' / abi '{abi}' combination for Apple target"), } } pub(super) fn macho_platform(target: &Target) -> u32 { - match (&*target.os, &*target.env) { - ("macos", _) => object::macho::PLATFORM_MACOS, - ("ios", "macabi") => object::macho::PLATFORM_MACCATALYST, - ("ios", "sim") => object::macho::PLATFORM_IOSSIMULATOR, - ("ios", _) => object::macho::PLATFORM_IOS, - ("watchos", "sim") => object::macho::PLATFORM_WATCHOSSIMULATOR, - ("watchos", _) => object::macho::PLATFORM_WATCHOS, - ("tvos", "sim") => object::macho::PLATFORM_TVOSSIMULATOR, - ("tvos", _) => object::macho::PLATFORM_TVOS, - ("visionos", "sim") => object::macho::PLATFORM_XROSSIMULATOR, - ("visionos", _) => object::macho::PLATFORM_XROS, - _ => unreachable!("tried to get Mach-O platform for non-Apple target"), + match (&target.os, &target.env) { + (Os::MacOs, _) => object::macho::PLATFORM_MACOS, + (Os::IOs, Env::MacAbi) => object::macho::PLATFORM_MACCATALYST, + (Os::IOs, Env::Sim) => object::macho::PLATFORM_IOSSIMULATOR, + (Os::IOs, _) => object::macho::PLATFORM_IOS, + (Os::WatchOs, Env::Sim) => object::macho::PLATFORM_WATCHOSSIMULATOR, + (Os::WatchOs, _) => object::macho::PLATFORM_WATCHOS, + (Os::TvOs, Env::Sim) => object::macho::PLATFORM_TVOSSIMULATOR, + (Os::TvOs, _) => object::macho::PLATFORM_TVOS, + (Os::VisionOs, Env::Sim) => object::macho::PLATFORM_XROSSIMULATOR, + (Os::VisionOs, _) => object::macho::PLATFORM_XROS, + (os, env) => unreachable!("invalid os '{os}' / env '{env}' combination for Apple target"), } } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c5724c20b216..820f7ba4a6f2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -46,9 +46,9 @@ use rustc_span::Symbol; use rustc_target::spec::crt_objects::CrtObjects; use rustc_target::spec::{ - BinaryFormat, Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, - LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, RelocModel, RelroLevel, SanitizerSet, - SplitDebuginfo, + Abi, BinaryFormat, Cc, Env, LinkOutputKind, LinkSelfContainedComponents, + LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel, + RelroLevel, SanitizerSet, SplitDebuginfo, }; use tracing::{debug, info, warn}; @@ -1407,11 +1407,9 @@ fn adjust_flavor_to_features( Some(LinkerFlavorCli::Llbc) => Some(LinkerFlavor::Llbc), Some(LinkerFlavorCli::Ptx) => Some(LinkerFlavor::Ptx), // The linker flavors that corresponds to targets needs logic that keeps the base LinkerFlavor - _ => sess - .opts - .cg - .linker_flavor - .map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)), + linker_flavor => { + linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)) + } }; if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor, features) { return ret; @@ -1819,7 +1817,7 @@ fn self_contained_components( LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), LinkSelfContainedDefault::InferredForMingw => { sess.host == sess.target - && sess.target.vendor != "uwp" + && sess.target.abi != Abi::Uwp && detect_self_contained_mingw(sess, linker) } } @@ -1845,7 +1843,7 @@ fn add_pre_link_objects( let empty = Default::default(); let objects = if self_contained { &opts.pre_link_objects_self_contained - } else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) { + } else if !(sess.target.os == Os::Fuchsia && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) { &opts.pre_link_objects } else { &empty @@ -2496,7 +2494,7 @@ fn add_order_independent_options( let apple_sdk_root = add_apple_sdk(cmd, sess, flavor); - if sess.target.os == "fuchsia" + if sess.target.os == Os::Fuchsia && crate_type == CrateType::Executable && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) { @@ -2515,7 +2513,7 @@ fn add_order_independent_options( cmd.no_crt_objects(); } - if sess.target.os == "emscripten" { + if sess.target.os == Os::Emscripten { cmd.cc_arg(if sess.opts.unstable_opts.emscripten_wasm_eh { "-fwasm-exceptions" } else if sess.panic_strategy().unwinds() { @@ -3070,8 +3068,8 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // `sess.target.arch` (`target_arch`) is not detailed enough. let llvm_arch = sess.target.llvm_target.split_once('-').expect("LLVM target must have arch").0; - let target_os = &*sess.target.os; - let target_env = &*sess.target.env; + let target_os = &sess.target.os; + let target_env = &sess.target.env; // The architecture name to forward to the linker. // @@ -3123,12 +3121,12 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // > - xros-simulator // > - driverkit let platform_name = match (target_os, target_env) { - (os, "") => os, - ("ios", "macabi") => "mac-catalyst", - ("ios", "sim") => "ios-simulator", - ("tvos", "sim") => "tvos-simulator", - ("watchos", "sim") => "watchos-simulator", - ("visionos", "sim") => "visionos-simulator", + (os, Env::Unspecified) => os.desc(), + (Os::IOs, Env::MacAbi) => "mac-catalyst", + (Os::IOs, Env::Sim) => "ios-simulator", + (Os::TvOs, Env::Sim) => "tvos-simulator", + (Os::WatchOs, Env::Sim) => "watchos-simulator", + (Os::VisionOs, Env::Sim) => "visionos-simulator", _ => bug!("invalid OS/env combination for Apple target: {target_os}, {target_env}"), }; @@ -3192,7 +3190,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo // fairly safely use `-target`. See also the following, where it is // made explicit that the recommendation by LLVM developers is to use // `-target`: - if target_os == "macos" { + if *target_os == Os::MacOs { // `-arch` communicates the architecture. // // CC forwards the `-arch` to the linker, so we use the same value diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index eb2740d59b4b..f4225b2d764b 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; -use rustc_target::spec::{Arch, Cc, LinkOutputKind, LinkerFlavor, Lld}; +use rustc_target::spec::{Abi, Arch, Cc, LinkOutputKind, LinkerFlavor, Lld, Os}; use tracing::{debug, warn}; use super::command::Command; @@ -83,7 +83,7 @@ pub(crate) fn get_linker<'a>( // To comply with the Windows App Certification Kit, // MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc). let t = &sess.target; - if matches!(flavor, LinkerFlavor::Msvc(..)) && t.vendor == "uwp" { + if matches!(flavor, LinkerFlavor::Msvc(..)) && t.abi == Abi::Uwp { if let Some(ref tool) = msvc_tool { let original_path = tool.path(); if let Some(root_lib_path) = original_path.ancestors().nth(4) { @@ -134,12 +134,12 @@ pub(crate) fn get_linker<'a>( // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction // to the linker args construction. - assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp"); + assert!(cmd.get_args().is_empty() || sess.target.abi == Abi::Uwp); match flavor { - LinkerFlavor::Unix(Cc::No) if sess.target.os == "l4re" => { + LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::L4Re => { Box::new(L4Bender::new(cmd, sess)) as Box } - LinkerFlavor::Unix(Cc::No) if sess.target.os == "aix" => { + LinkerFlavor::Unix(Cc::No) if sess.target.os == Os::Aix => { Box::new(AixLinker::new(cmd, sess)) as Box } LinkerFlavor::WasmLld(Cc::No) => Box::new(WasmLd::new(cmd, sess)) as Box, @@ -573,7 +573,7 @@ fn set_output_kind( // any `#[link]` attributes in the `libc` crate, see #72782 for details. // FIXME: Switch to using `#[link]` attributes in the `libc` crate // similarly to other targets. - if self.sess.target.os == "vxworks" + if self.sess.target.os == Os::VxWorks && matches!( output_kind, LinkOutputKind::StaticNoPicExe @@ -595,7 +595,7 @@ fn set_output_kind( } fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool) { - if self.sess.target.os == "illumos" && name == "c" { + if self.sess.target.os == Os::Illumos && name == "c" { // libc will be added via late_link_args on illumos so that it will // appear last in the library search order. // FIXME: This should be replaced by a more complete and generic @@ -1439,7 +1439,7 @@ fn export_symbols( // symbols explicitly passed via the `--export` flags above and hides all // others. Various bits and pieces of wasm32-unknown-unknown tooling use // this, so be sure these symbols make their way out of the linker as well. - if self.sess.target.os == "unknown" || self.sess.target.os == "none" { + if matches!(self.sess.target.os, Os::Unknown | Os::None) { self.link_args(&["--export=__heap_base", "--export=__data_end"]); } } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 10aaadd5688a..6dff79374f20 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -20,7 +20,7 @@ use rustc_middle::bug; use rustc_session::Session; use rustc_span::sym; -use rustc_target::spec::{RelocModel, Target, ef_avr_arch}; +use rustc_target::spec::{Abi, Os, RelocModel, Target, ef_avr_arch}; use tracing::debug; use super::apple; @@ -260,10 +260,10 @@ pub(crate) fn create_object_file(sess: &Session) -> Option u8 { - match sess.target.options.os.as_ref() { - "hermit" => elf::ELFOSABI_STANDALONE, - "freebsd" => elf::ELFOSABI_FREEBSD, - "solaris" => elf::ELFOSABI_SOLARIS, + match sess.target.options.os { + Os::Hermit => elf::ELFOSABI_STANDALONE, + Os::FreeBsd => elf::ELFOSABI_FREEBSD, + Os::Solaris => elf::ELFOSABI_SOLARIS, _ => elf::ELFOSABI_NONE, } } @@ -379,11 +379,11 @@ pub(super) fn elf_e_flags(architecture: Architecture, sess: &Session) -> u32 { } } Architecture::Csky => { - let e_flags = match sess.target.options.abi.as_ref() { - "abiv2" => elf::EF_CSKY_ABIV2, - _ => elf::EF_CSKY_ABIV1, - }; - e_flags + if matches!(sess.target.options.abi, Abi::AbiV2) { + elf::EF_CSKY_ABIV2 + } else { + elf::EF_CSKY_ABIV1 + } } Architecture::PowerPc64 => { const EF_PPC64_ABI_UNKNOWN: u32 = 0; diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 6fa8725a7871..a80976fad02a 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -17,7 +17,7 @@ use rustc_middle::util::Providers; use rustc_session::config::{CrateType, OomStrategy}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::{Arch, TlsModel}; +use rustc_target::spec::{Arch, Os, TlsModel}; use tracing::debug; use crate::back::symbol_export; @@ -711,7 +711,7 @@ pub(crate) fn extend_exported_symbols<'tcx>( ) { let (callconv, _) = calling_convention_for_symbol(tcx, symbol); - if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != "amdhsa" { + if callconv != CanonAbi::GpuKernel || tcx.sess.target.os != Os::AmdHsa { return; } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index ed2815b06c18..414e9ce1c821 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -33,7 +33,7 @@ use rustc_session::config::{self, CrateType, EntryFnType}; use rustc_span::{DUMMY_SP, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::spec::Arch; +use rustc_target::spec::{Arch, Os}; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; @@ -366,7 +366,7 @@ pub(crate) fn build_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // us pub fn wants_wasm_eh(sess: &Session) -> bool { sess.target.is_like_wasm - && (sess.target.os != "emscripten" || sess.opts.unstable_opts.emscripten_wasm_eh) + && (sess.target.os != Os::Emscripten || sess.opts.unstable_opts.emscripten_wasm_eh) } /// Returns `true` if this session's target will use SEH-based unwinding. @@ -500,7 +500,7 @@ fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) -> Bx::Function { // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, or // `usize efi_main(void *handle, void *system_table)` depending on the target. - let llfty = if cx.sess().target.os.contains("uefi") { + let llfty = if cx.sess().target.os == Os::Uefi { cx.type_func(&[cx.type_ptr(), cx.type_ptr()], cx.type_isize()) } else if cx.sess().target.main_needs_argc_argv { cx.type_func(&[cx.type_int(), cx.type_ptr()], cx.type_int()) @@ -562,7 +562,7 @@ fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }; let result = bx.call(start_ty, None, None, start_fn, &args, None, instance); - if cx.sess().target.os.contains("uefi") { + if cx.sess().target.os == Os::Uefi { bx.ret(result); } else { let cast = bx.intcast(result, cx.type_int(), true); @@ -576,7 +576,7 @@ fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// Obtain the `argc` and `argv` values to pass to the rust start function /// (i.e., the "start" lang item). 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") { + if bx.cx().sess().target.os == Os::Uefi { // Params for UEFI let param_handle = bx.get_param(0); let param_system_table = bx.get_param(1); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 720f8061c4e6..fd3d7d2a3ded 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -16,6 +16,7 @@ use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::{Ident, Span, sym}; +use rustc_target::spec::Os; use crate::errors; use crate::target_features::{ @@ -258,7 +259,7 @@ fn process_builtin_attrs( UsedBy::Compiler => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_COMPILER, UsedBy::Linker => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER, UsedBy::Default => { - let used_form = if tcx.sess.target.os == "illumos" { + let used_form = if tcx.sess.target.os == Os::Illumos { // illumos' `ld` doesn't support a section header that would represent // `#[used(linker)]`, see // https://github.com/rust-lang/rust/issues/146169. For that target, @@ -468,16 +469,18 @@ fn check_result( }) } - // warn for nonblocking async fn. + // warn for nonblocking async functions, blocks and closures. // This doesn't behave as expected, because the executor can run blocking code without the sanitizer noticing. if codegen_fn_attrs.sanitizers.rtsan_setting == RtsanSetting::Nonblocking && let Some(sanitize_span) = interesting_spans.sanitize - // async function - && (tcx.asyncness(did).is_async() || (tcx.is_closure_like(did.into()) + // async fn + && (tcx.asyncness(did).is_async() // async block - && (tcx.coroutine_is_async(did.into()) - // async closure - || tcx.coroutine_is_async(tcx.coroutine_for_closure(did))))) + || tcx.is_coroutine(did.into()) + // async closure + || (tcx.is_closure_like(did.into()) + && tcx.hir_node_by_def_id(did).expect_closure().kind + != rustc_hir::ClosureKind::Closure)) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint( diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 08e2f3559533..89a3f8061a8c 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -7,7 +7,7 @@ use rustc_middle::{bug, mir, span_bug}; use rustc_session::cstore::{DllCallingConvention, DllImport}; use rustc_span::Span; -use rustc_target::spec::Target; +use rustc_target::spec::{Abi, Env, Os, Target}; use crate::traits::*; @@ -171,7 +171,7 @@ pub fn asm_const_to_str<'tcx>( } pub fn is_mingw_gnu_toolchain(target: &Target) -> bool { - target.vendor == "pc" && target.os == "windows" && target.env == "gnu" && target.abi.is_empty() + target.os == Os::Windows && target.env == Env::Gnu && target.abi == Abi::Unspecified } pub fn i686_decorated_name( diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 323e1cefd586..bac3a9da48d9 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1498,14 +1498,10 @@ pub fn mem_copy_repeatedly( // Prepare getting source provenance. let src_bytes = src_alloc.get_bytes_unchecked(src_range).as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation - // first copy the provenance to a temporary buffer, because - // `get_bytes_mut` will clear the provenance, which is correct, - // since we don't want to keep any provenance at the target. - // This will also error if copying partial provenance is not supported. - let provenance = src_alloc - .provenance() - .prepare_copy(src_range, self) - .map_err(|e| e.to_interp_error(src_alloc_id))?; + // First copy the provenance to a temporary buffer, because + // `get_bytes_unchecked_for_overwrite_ptr` will clear the provenance (in preparation for + // inserting the new provenance), and that can overlap with the source range. + let provenance = src_alloc.provenance_prepare_copy(src_range, self); // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index cd34892f029c..a409c7fad417 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -704,6 +704,7 @@ fn write_immediate_to_mplace_no_validate( // wrong type. let tcx = *self.tcx; + let will_later_validate = M::enforce_validity(self, layout); let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else { // zero-sized access return interp_ok(()); @@ -714,23 +715,31 @@ fn write_immediate_to_mplace_no_validate( alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { - let BackendRepr::ScalarPair(a, b) = layout.backend_repr else { + let BackendRepr::ScalarPair(_a, b) = layout.backend_repr else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", layout ) }; - let b_offset = a.size(&tcx).align_to(b.align(&tcx).abi); + let a_size = a_val.size(); + let b_offset = a_size.align_to(b.align(&tcx).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, // but that does not work: We could be a newtype around a pair, then the // fields do not match the `ScalarPair` components. - alloc.write_scalar(alloc_range(Size::ZERO, a_val.size()), a_val)?; + // In preparation, if we do *not* later reset the padding, we clear the entire + // destination now to ensure that no stray pointer fragments are being + // preserved (see ). + // We can skip this if there is no padding (e.g. for wide pointers). + if !will_later_validate && a_size + b_val.size() != layout.size { + alloc.write_uninit_full(); + } + + alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?; alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; - // We don't have to reset padding here, `write_immediate` will anyway do a validation run. } Immediate::Uninit => alloc.write_uninit_full(), } diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index 2be9ba292f97..0ef3bb319ff8 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -53,13 +53,13 @@ impl !DynSend for std::env::VarsOs {} macro_rules! already_send { ($([$ty: ty])*) => { - $(unsafe impl DynSend for $ty where $ty: Send {})* + $(unsafe impl DynSend for $ty where Self: Send {})* }; } // These structures are already `Send`. already_send!( - [std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File] + [std::backtrace::Backtrace][std::io::Stdout][std::io::Stderr][std::io::Error][std::fs::File][std::panic::Location<'_>] [rustc_arena::DroplessArena][jobserver_crate::Client][jobserver_crate::HelperThread] [crate::memmap::Mmap][crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] ); @@ -127,14 +127,14 @@ impl !DynSync for std::env::VarsOs {} macro_rules! already_sync { ($([$ty: ty])*) => { - $(unsafe impl DynSync for $ty where $ty: Sync {})* + $(unsafe impl DynSync for $ty where Self: Sync {})* }; } // These structures are already `Sync`. already_sync!( [std::sync::atomic::AtomicBool][std::sync::atomic::AtomicUsize][std::sync::atomic::AtomicU8] - [std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File] + [std::sync::atomic::AtomicU32][std::backtrace::Backtrace][std::io::Error][std::fs::File][std::panic::Location<'_>] [jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap] [crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice] ); diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index 0d79d98b5d96..afa9bc36f2c5 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -1,5 +1,5 @@ use crate::stable_hasher::{HashStable, StableHasher}; -use crate::sync::{MappedReadGuard, ReadGuard, RwLock}; +use crate::sync::{MappedReadGuard, MappedWriteGuard, ReadGuard, RwLock, WriteGuard}; /// The `Steal` struct is intended to used as the value for a query. /// Specifically, we sometimes have queries (*cough* MIR *cough*) @@ -40,9 +40,17 @@ pub fn borrow(&self) -> MappedReadGuard<'_, T> { ReadGuard::map(borrow, |opt| opt.as_ref().unwrap()) } + /// An escape hatch for rustc drivers to mutate `Steal` caches. + /// + /// Use at your own risk. This can badly break incremental compilation + /// and anything else that relies on the immutability of query caches. #[track_caller] - pub fn get_mut(&mut self) -> &mut T { - self.value.get_mut().as_mut().expect("attempt to read from stolen value") + pub fn risky_hack_borrow_mut(&self) -> MappedWriteGuard<'_, T> { + let borrow = self.value.borrow_mut(); + if borrow.is_none() { + panic!("attempted to read from stolen value: {}", std::any::type_name::()); + } + WriteGuard::map(borrow, |opt| opt.as_mut().unwrap()) } #[track_caller] diff --git a/compiler/rustc_driver_impl/src/signal_handler.rs b/compiler/rustc_driver_impl/src/signal_handler.rs index e7bc57c9749b..3d5df19bf67e 100644 --- a/compiler/rustc_driver_impl/src/signal_handler.rs +++ b/compiler/rustc_driver_impl/src/signal_handler.rs @@ -152,7 +152,8 @@ pub(super) fn install() { libc::sigaltstack(&alt_stack, ptr::null_mut()); let mut sa: libc::sigaction = mem::zeroed(); - sa.sa_sigaction = print_stack_trace as libc::sighandler_t; + sa.sa_sigaction = + print_stack_trace as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t; sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK; libc::sigemptyset(&mut sa.sa_mask); for (signum, _signame) in KILL_SIGNALS { diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 6606092e421e..b3f76732a602 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -annotate-snippets = "0.12.8" +annotate-snippets = "0.12.9" anstream = "0.6.20" anstyle = "1.0.13" derive_setters = "0.1.6" diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 5d22a8b8e30a..5b1fffd21d18 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -350,22 +350,27 @@ fn emit_messages_default( "all spans must be disjoint", ); + let lo = subst.parts.iter().map(|part| part.span.lo()).min()?; + let lo_file = sm.lookup_source_file(lo); + let hi = subst.parts.iter().map(|part| part.span.hi()).max()?; + let hi_file = sm.lookup_source_file(hi); + + // The different spans might belong to different contexts, if so ignore suggestion. + if lo_file.stable_id != hi_file.stable_id { + return None; + } + + // We can't splice anything if the source is unavailable. + if !sm.ensure_source_file_source_present(&lo_file) { + return None; + } + // Account for cases where we are suggesting the same code that's already // there. This shouldn't happen often, but in some cases for multipart // suggestions it's much easier to handle it here than in the origin. subst.parts.retain(|p| is_different(sm, &p.snippet, p.span)); - let item_span = subst.parts.first()?; - let file = sm.lookup_source_file(item_span.span.lo()); - if should_show_source_code( - &self.ignored_directories_in_source_blocks, - sm, - &file, - ) { - Some(subst) - } else { - None - } + if subst.parts.is_empty() { None } else { Some(subst) } }) .collect::>(); @@ -745,14 +750,20 @@ fn shrink_file( ) -> Option<(Span, String, usize)> { let lo_byte = spans.iter().map(|s| s.lo()).min()?; let lo_loc = sm.lookup_char_pos(lo_byte); - let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start; let hi_byte = spans.iter().map(|s| s.hi()).max()?; let hi_loc = sm.lookup_char_pos(hi_byte); - let hi = lo_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end; + + if lo_loc.file.stable_id != hi_loc.file.stable_id { + // this may happen when spans cross file boundaries due to macro expansion. + return None; + } + + let lo = lo_loc.file.line_bounds(lo_loc.line.saturating_sub(1)).start; + let hi = hi_loc.file.line_bounds(hi_loc.line.saturating_sub(1)).end; let bounding_span = Span::with_root_ctxt(lo, hi); - let source = sm.span_to_snippet(bounding_span).unwrap_or_default(); + let source = sm.span_to_snippet(bounding_span).ok()?; let offset_line = sm.doctest_offset_line(file_name, lo_loc.line); Some((bounding_span, source, offset_line)) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 81a7ee1ff45f..4cab7e407eec 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -1704,7 +1704,7 @@ fn emit_messages_default_inner( } // print out the span location and spacer before we print the annotated source // to do this, we need to know if this span will be primary - let is_primary = primary_lo.file.name == annotated_file.file.name; + let is_primary = primary_lo.file.stable_id == annotated_file.file.stable_id; if is_primary { let loc = primary_lo.clone(); if !self.short_message { @@ -2322,11 +2322,6 @@ fn emit_suggestion_default( show_code_change { for part in parts { - let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) { - snippet - } else { - String::new() - }; let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display; let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display; @@ -2402,7 +2397,7 @@ fn emit_suggestion_default( // LL - REMOVED <- row_num - 2 - (newlines - first_i - 1) // LL + NEWER // | <- row_num - + let snippet = sm.span_to_snippet(part.span).unwrap_or_default(); let newlines = snippet.lines().count(); if newlines > 0 && row_num > newlines { // Account for removals where the part being removed spans multiple @@ -3108,7 +3103,7 @@ fn add_annotation_to_file( ) { for slot in file_vec.iter_mut() { // Look through each of our files for the one we're adding to - if slot.file.name == file.name { + if slot.file.stable_id == file.stable_id { // See if we already have a line for it for line_slot in &mut slot.lines { if line_slot.line_index == line_index { @@ -3471,14 +3466,9 @@ fn drop(&mut self) { pub fn stderr_destination(color: ColorConfig) -> Destination { let buffer_writer = std::io::stderr(); - let choice = color.to_color_choice(); // We need to resolve `ColorChoice::Auto` before `Box`ing since // `ColorChoice::Auto` on `dyn Write` will always resolve to `Never` - let choice = if matches!(choice, ColorChoice::Auto) { - AutoStream::choice(&buffer_writer) - } else { - choice - }; + let choice = get_stderr_color_choice(color, &buffer_writer); // On Windows we'll be performing global synchronization on the entire // system for emitting rustc errors, so there's no need to buffer // anything. @@ -3493,6 +3483,11 @@ pub fn stderr_destination(color: ColorConfig) -> Destination { } } +pub fn get_stderr_color_choice(color: ColorConfig, stderr: &std::io::Stderr) -> ColorChoice { + let choice = color.to_color_choice(); + if matches!(choice, ColorChoice::Auto) { AutoStream::choice(stderr) } else { choice } +} + /// On Windows, BRIGHT_BLUE is hard to read on black. Use cyan instead. /// /// See #36178. diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 810a5a21a055..946f17943fe3 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1099,6 +1099,9 @@ pub fn expn_data( pub struct DeriveResolution { pub path: ast::Path, pub item: Annotatable, + // FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the + // `Arc` will be used if the FIXME in `MacroExpander::fully_expand_fragment` + // is completed. pub exts: Option>, pub is_const: bool, } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 276490bc0c9d..1dbf7b472649 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -886,9 +886,6 @@ fn expand_invoc( } } } else if let SyntaxExtensionKind::NonMacroAttr = ext { - if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety { - self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute"); - } // `-Zmacro-stats` ignores these because they don't do any real expansion. self.cx.expanded_inert_attrs.mark(&attr); item.visit_attrs(|attrs| attrs.insert(pos, attr)); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 295573f44926..0e063011eea4 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -749,7 +749,7 @@ fn join(&mut self, first: Self::Span, second: Self::Span) -> Option let self_loc = self.psess().source_map().lookup_char_pos(first.lo()); let other_loc = self.psess().source_map().lookup_char_pos(second.lo()); - if self_loc.file.name != other_loc.file.name { + if self_loc.file.stable_id != other_loc.file.stable_id { return None; } diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 0ee4ad409e4b..5af47d51b711 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -215,6 +215,8 @@ macro_rules! declare_features { (accepted, extern_crate_self, "1.34.0", Some(56409)), /// Allows access to crate names passed via `--extern` through prelude. (accepted, extern_prelude, "1.30.0", Some(44660)), + /// Allows using `system` as a calling convention with varargs. + (accepted, extern_system_varargs, "CURRENT_RUSTC_VERSION", Some(136946)), /// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`. (accepted, f16c_target_feature, "1.68.0", Some(44839)), /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8397cd294e0a..26612fda5a7f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -410,6 +410,9 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows defining c-variadic naked functions with any extern ABI that is allowed + /// on c-variadic foreign functions. + (unstable, c_variadic_naked_functions, "CURRENT_RUSTC_VERSION", Some(148767)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. (unstable, cfg_contract_checks, "1.86.0", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. @@ -498,8 +501,6 @@ pub fn internal(&self, feature: Symbol) -> bool { (incomplete, explicit_tail_calls, "1.72.0", Some(112788)), /// Allows using `#[export_stable]` which indicates that an item is exportable. (incomplete, export_stable, "1.88.0", Some(139939)), - /// Allows using `system` as a calling convention with varargs. - (unstable, extern_system_varargs, "1.86.0", Some(136946)), /// Allows defining `extern type`s. (unstable, extern_types, "1.23.0", Some(43467)), /// Allow using 128-bit (quad precision) floating point numbers. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 7ddd71c34600..4ba64b240ac0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -194,6 +194,18 @@ pub enum CfgEntry { Version(Option, Span), } +impl CfgEntry { + pub fn span(&self) -> Span { + let (CfgEntry::All(_, span) + | CfgEntry::Any(_, span) + | CfgEntry::Not(_, span) + | CfgEntry::Bool(_, span) + | CfgEntry::NameValue { span, .. } + | CfgEntry::Version(_, span)) = self; + *span + } +} + /// Possible values for the `#[linkage]` attribute, allowing to specify the /// linkage type for a `MonoItem`. /// diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4cdfc752d989..786280c24c11 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1722,7 +1722,9 @@ fn walk_short_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) -> bool { match self.kind { Missing => unreachable!(), Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), + Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => { + s.walk_short_(it) + } Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), Slice(before, slice, after) => { @@ -1749,7 +1751,7 @@ fn walk_(&self, it: &mut impl FnMut(&Pat<'hir>) -> bool) { use PatKind::*; match self.kind { Missing | Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} - Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), + Box(s) | Deref(s) | Ref(s, _, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), Slice(before, slice, after) => { @@ -1938,7 +1940,7 @@ pub enum PatKind<'hir> { Deref(&'hir Pat<'hir>), /// A reference pattern (e.g., `&mut (a, b)`). - Ref(&'hir Pat<'hir>, Mutability), + Ref(&'hir Pat<'hir>, Pinnedness, Mutability), /// A literal, const block or path. Expr(&'hir PatExpr<'hir>), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9328ffad1c9a..2f45636a883d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -752,7 +752,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: } PatKind::Box(ref subpattern) | PatKind::Deref(ref subpattern) - | PatKind::Ref(ref subpattern, _) => { + | PatKind::Ref(ref subpattern, _, _) => { try_visit!(visitor.visit_pat(subpattern)); } PatKind::Binding(_, _hir_id, ident, ref optional_subpattern) => { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index e454ed58699c..0a6e491227d0 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -176,6 +176,7 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; UseCloned, sym::use_cloned, use_cloned_trait, Target::Trait, GenericRequirement::None; + TrivialClone, sym::trivial_clone, trivial_clone_trait, Target::Trait, GenericRequirement::None; Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the `DiscriminantKind` trait. @@ -328,9 +329,6 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { // Lang items needed for `format_args!()`. FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None; FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None; - FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None; - FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None; - FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None; ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); @@ -372,6 +370,7 @@ pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + ResidualIntoTryType, sym::into_try_type, into_try_type_fn, Target::Fn, GenericRequirement::None; CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index bac34454b3b7..4e605ef62519 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -516,12 +516,10 @@ fn resolve_local<'tcx>( if let Some(pat) = pat { if is_binding_pat(pat) { - visitor.scope_tree.record_rvalue_candidate( - expr.hir_id, - RvalueCandidate { - target: expr.hir_id.local_id, - lifetime: visitor.cx.var_parent, - }, + record_subexpr_extended_temp_scopes( + &mut visitor.scope_tree, + expr, + visitor.cx.var_parent, ); } } @@ -593,7 +591,7 @@ fn is_binding_pat(pat: &hir::Pat<'_>) -> bool { is_binding_pat(subpat) } - PatKind::Ref(_, _) + PatKind::Ref(_, _, _) | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Missing | PatKind::Wild @@ -604,7 +602,7 @@ fn is_binding_pat(pat: &hir::Pat<'_>) -> bool { } } - /// If `expr` matches the `E&` grammar, then records an extended rvalue scope as appropriate: + /// If `expr` matches the `E&` grammar, then records an extended temporary scope as appropriate: /// /// ```text /// E& = & ET @@ -627,10 +625,7 @@ fn record_rvalue_scope_if_borrow_expr<'tcx>( match expr.kind { hir::ExprKind::AddrOf(_, _, subexpr) => { record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); - visitor.scope_tree.record_rvalue_candidate( - subexpr.hir_id, - RvalueCandidate { target: subexpr.hir_id.local_id, lifetime: blk_id }, - ); + record_subexpr_extended_temp_scopes(&mut visitor.scope_tree, subexpr, blk_id); } hir::ExprKind::Struct(_, fields, _) => { for field in fields { @@ -687,6 +682,53 @@ fn record_rvalue_scope_if_borrow_expr<'tcx>( } } +/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by +/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that +/// case, the "temporary lifetime" of `expr` is extended to be the block enclosing the `let` +/// statement. +/// +/// More formally, if `expr` matches the grammar `ET`, record the temporary scope of the matching +/// `` as `lifetime`: +/// +/// ```text +/// ET = *ET +/// | ET[...] +/// | ET.f +/// | (ET) +/// | +/// ``` +/// +/// Note: ET is intended to match "rvalues or places based on rvalues". +fn record_subexpr_extended_temp_scopes( + scope_tree: &mut ScopeTree, + mut expr: &hir::Expr<'_>, + lifetime: Option, +) { + debug!(?expr, ?lifetime); + + loop { + // Note: give all the expressions matching `ET` with the + // extended temporary lifetime, not just the innermost rvalue, + // because in MIR building if we must compile e.g., `*rvalue()` + // into a temporary, we request the temporary scope of the + // outer expression. + + scope_tree.record_extended_temp_scope(expr.hir_id.local_id, lifetime); + + match expr.kind { + hir::ExprKind::AddrOf(_, _, subexpr) + | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) + | hir::ExprKind::Field(subexpr, _) + | hir::ExprKind::Index(subexpr, _, _) => { + expr = subexpr; + } + _ => { + return; + } + } + } +} + impl<'tcx> ScopeResolutionVisitor<'tcx> { /// Records the current parent (if any) as the parent of `child_scope`. fn record_child_scope(&mut self, child_scope: Scope) { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 79f3d7f2ff8f..c881c77f38f1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1487,11 +1487,8 @@ fn probe_inherent_assoc_item( match assoc_tag { // Don't attempt to look up inherent associated types when the feature is not // enabled. Theoretically it'd be fine to do so since we feature-gate their - // definition site. However, due to current limitations of the implementation - // (caused by us performing selection during HIR ty lowering instead of in the - // trait solver), IATs can lead to cycle errors (#108491) which mask the - // feature-gate error, needlessly confusing users who use IATs by accident - // (#113265). + // definition site. However, the current implementation of inherent associated + // items is somewhat brittle, so let's not run it by default. ty::AssocTag::Type => return Ok(None), ty::AssocTag::Const => { // We also gate the mgca codepath for type-level uses of inherent consts @@ -1520,9 +1517,18 @@ fn probe_inherent_assoc_item( }) .collect(); + // At the moment, we actually bail out with a hard error if the selection of an inherent + // associated item fails (see below). This means we never consider trait associated items + // as potential fallback candidates (#142006). To temporarily mask that issue, let's not + // select at all if there are no early inherent candidates. + if candidates.is_empty() { + return Ok(None); + } + let (applicable_candidates, fulfillment_errors) = self.select_inherent_assoc_candidates(span, self_ty, candidates.clone()); + // FIXME(#142006): Don't eagerly error here, there might be applicable trait candidates. let InherentAssocCandidate { impl_, assoc_item, scope: def_scope } = match &applicable_candidates[..] { &[] => Err(self.report_unresolved_inherent_assoc_item( @@ -1543,6 +1549,8 @@ fn probe_inherent_assoc_item( )), }?; + // FIXME(#142006): Don't eagerly validate here, there might be trait candidates that are + // accessible (visible and stable) contrary to the inherent candidate. self.check_assoc_item(assoc_item, name, def_scope, block, span); // FIXME(fmease): Currently creating throwaway `parent_args` to please diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index cf38957bf24a..9c7680b92120 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -44,7 +44,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -/// The first pass over the crate simply builds up the set of inferreds. +// The first pass over the crate simply builds up the set of inferreds. pub(crate) struct TermsContext<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 2ff651df0d3d..308e604deb40 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2028,9 +2028,15 @@ fn print_pat(&mut self, pat: &hir::Pat<'_>) { self.print_pat(inner); self.pclose(); } - PatKind::Ref(inner, mutbl) => { + PatKind::Ref(inner, pinned, mutbl) => { let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.word("&"); + if pinned.is_pinned() { + self.word("pin "); + if mutbl.is_not() { + self.word("const "); + } + } self.word(mutbl.prefix_str()); if is_range_inner { self.popen(); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e533ee78cc82..d7df23f92f05 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -523,7 +523,7 @@ pub(super) fn pat_guaranteed_to_constitute_read_for_never(&self, pat: &hir::Pat< | hir::PatKind::TupleStruct(_, _, _) | hir::PatKind::Tuple(_, _) | hir::PatKind::Box(_) - | hir::PatKind::Ref(_, _) + | hir::PatKind::Ref(_, _, _) | hir::PatKind::Deref(_) | hir::PatKind::Expr(_) | hir::PatKind::Range(_, _, _) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index b9f1463d8007..561230c193ce 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -1231,7 +1231,7 @@ fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> Result, Cx::Error> { debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); return Ok(first_adjust.source); } - } else if let PatKind::Ref(subpat, _) = pat.kind + } else if let PatKind::Ref(subpat, _, _) = pat.kind && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) { return self.pat_ty_adjusted(subpat); @@ -1817,13 +1817,13 @@ fn cat_pattern( self.cat_pattern(place_with_id, subpat, op)?; } - PatKind::Ref(subpat, _) + PatKind::Ref(subpat, _, _) if self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) => { self.cat_pattern(place_with_id, subpat, op)?; } - PatKind::Box(subpat) | PatKind::Ref(subpat, _) => { + PatKind::Box(subpat) | PatKind::Ref(subpat, _, _) => { // box p1, &p1, &mut p1. we can ignore the mutability of // PatKind::Ref since that information is already contained // in the type. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 38586c844445..da719e615fd7 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -40,7 +40,7 @@ use crate::callee::{self, DeferredCallResolution}; use crate::errors::{self, CtorIsPrivate}; use crate::method::{self, MethodCallee}; -use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, rvalue_scopes}; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Produces warning on the given node, if the current point in the @@ -604,13 +604,6 @@ pub(crate) fn field_ty( self.normalize(span, field.ty(self.tcx, args)) } - pub(crate) fn resolve_rvalue_scopes(&self, def_id: DefId) { - let scope_tree = self.tcx.region_scope_tree(def_id); - let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, scope_tree, def_id) }; - let mut typeck_results = self.typeck_results.borrow_mut(); - typeck_results.rvalue_scopes = rvalue_scopes; - } - /// Drain all obligations that are stalled on coroutines defined in this body. #[instrument(level = "debug", skip(self))] pub(crate) fn drain_stalled_coroutine_obligations(&self) { diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index c0cd23be6909..6460bd72c797 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -7,7 +7,7 @@ use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::asm::{ InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, }; @@ -27,6 +27,7 @@ enum NonAsmTypeReason<'tcx> { InvalidElement(DefId, Ty<'tcx>), NotSizedPtr(Ty<'tcx>), EmptySIMDArray(Ty<'tcx>), + Tainted(ErrorGuaranteed), } impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { @@ -93,6 +94,14 @@ fn get_asm_ty( } } ty::Adt(adt, args) if adt.repr().simd() => { + if !adt.is_struct() { + let guar = self.fcx.dcx().span_delayed_bug( + span, + format!("repr(simd) should only be used on structs, got {}", adt.descr()), + ); + return Err(NonAsmTypeReason::Tainted(guar)); + } + let fields = &adt.non_enum_variant().fields; if fields.is_empty() { return Err(NonAsmTypeReason::EmptySIMDArray(ty)); @@ -234,6 +243,9 @@ fn check_asm_operand_type( let msg = format!("use of empty SIMD vector `{ty}`"); self.fcx.dcx().struct_span_err(expr.span, msg).emit(); } + NonAsmTypeReason::Tainted(_error_guard) => { + // An error has already been reported. + } } return None; } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index b4264e999331..d090eb633828 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -37,7 +37,6 @@ mod opaque_types; mod pat; mod place_op; -mod rvalue_scopes; mod typeck_root_ctxt; mod upvar; mod writeback; @@ -237,9 +236,6 @@ fn typeck_with_inspect<'tcx>( // because they don't constrain other type variables. fcx.closure_analyze(body); assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the coroutine analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { let ty = fcx.normalize(span, ty); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 7d6982cb024c..6beaac9d3828 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1079,6 +1079,7 @@ fn report_no_match_method_error( } self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected); + self.suggest_bounds_for_range_to_method(&mut err, source, item_ident); err.emit() } @@ -2610,7 +2611,7 @@ fn report_failed_method_call_on_numerical_infer_var( let mut current_node = parent_node; while let Node::Pat(parent_pat) = current_node { - if let hir::PatKind::Ref(_, mutability) = parent_pat.kind { + if let hir::PatKind::Ref(_, _, mutability) = parent_pat.kind { ref_muts.push(mutability); current_node = self.tcx.parent_hir_node(parent_pat.hir_id); } else { @@ -3260,6 +3261,71 @@ fn note_derefed_ty_has_method( } } + fn suggest_bounds_for_range_to_method( + &self, + err: &mut Diag<'_>, + source: SelfSource<'tcx>, + item_ident: Ident, + ) { + let SelfSource::MethodCall(rcvr_expr) = source else { return }; + let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind else { return }; + let Some(lang_item) = self.tcx.qpath_lang_item(*qpath) else { + return; + }; + let is_inclusive = match lang_item { + hir::LangItem::RangeTo => false, + hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true, + _ => return, + }; + + let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) else { return }; + let Some(_) = self + .tcx + .associated_items(iterator_trait) + .filter_by_name_unhygienic(item_ident.name) + .next() + else { + return; + }; + + let source_map = self.tcx.sess.source_map(); + let range_type = if is_inclusive { "RangeInclusive" } else { "Range" }; + let Some(end_field) = fields.iter().find(|f| f.ident.name == rustc_span::sym::end) else { + return; + }; + + let element_ty = self.typeck_results.borrow().expr_ty_opt(end_field.expr); + let is_integral = element_ty.is_some_and(|ty| ty.is_integral()); + let end_is_negative = is_integral + && matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _)); + + let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) else { return }; + + let offset = snippet + .chars() + .take_while(|&c| c == '(' || c.is_whitespace()) + .map(|c| c.len_utf8()) + .sum::(); + + let insert_span = rcvr_expr + .span + .with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32)) + .shrink_to_lo(); + + let (value, appl) = if is_integral && !end_is_negative { + ("0", Applicability::MachineApplicable) + } else { + ("/* start */", Applicability::HasPlaceholders) + }; + + err.span_suggestion_verbose( + insert_span, + format!("consider using a bounded `{range_type}` by adding a concrete starting value"), + value, + appl, + ); + } + /// Print out the type for use in value namespace. fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { match ty.kind() { @@ -4212,7 +4278,7 @@ fn detect_and_explain_multiple_crate_versions_of_trait_item( err: &mut Diag<'_>, item_def_id: DefId, hir_id: hir::HirId, - rcvr_ty: Option>, + rcvr_ty: Option>, ) -> bool { let hir_id = self.tcx.parent_hir_id(hir_id); let Some(traits) = self.tcx.in_scope_traits(hir_id) else { return false }; @@ -4223,49 +4289,16 @@ fn detect_and_explain_multiple_crate_versions_of_trait_item( if !self.tcx.is_trait(trait_def_id) { return false; } - let krate = self.tcx.crate_name(trait_def_id.krate); - let name = self.tcx.item_name(trait_def_id); - let candidates: Vec<_> = traits - .iter() - .filter(|c| { - c.def_id.krate != trait_def_id.krate - && self.tcx.crate_name(c.def_id.krate) == krate - && self.tcx.item_name(c.def_id) == name - }) - .map(|c| (c.def_id, c.import_ids.get(0).cloned())) - .collect(); - if candidates.is_empty() { + let hir::Node::Expr(rcvr) = self.tcx.hir_node(hir_id) else { return false; - } - let item_span = self.tcx.def_span(item_def_id); - let msg = format!( - "there are multiple different versions of crate `{krate}` in the dependency graph", - ); - let trait_span = self.tcx.def_span(trait_def_id); - let mut multi_span: MultiSpan = trait_span.into(); - multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string()); - let descr = self.tcx.associated_item(item_def_id).descr(); - let rcvr_ty = - rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string()); - multi_span - .push_span_label(item_span, format!("the {descr} is available for {rcvr_ty} here")); - for (def_id, import_def_id) in candidates { - if let Some(import_def_id) = import_def_id { - multi_span.push_span_label( - self.tcx.def_span(import_def_id), - format!( - "`{name}` imported here doesn't correspond to the right version of crate \ - `{krate}`", - ), - ); - } - multi_span.push_span_label( - self.tcx.def_span(def_id), - "this is the trait that was imported".to_string(), - ); - } - err.span_note(multi_span, msg); - true + }; + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, rcvr_ty.into_iter()); + let trait_pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + polarity: ty::PredicatePolarity::Positive, + }); + let obligation = Obligation::new(self.tcx, self.misc(rcvr.span), self.param_env, trait_ref); + self.err_ctxt().note_different_trait_with_same_name(err, &obligation, trait_pred) } /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 88f6077c2ef5..0e9ff962435f 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -91,6 +91,7 @@ struct TopInfo<'tcx> { #[derive(Copy, Clone)] struct PatInfo<'tcx> { binding_mode: ByRef, + max_pinnedness: PinnednessCap, max_ref_mutbl: MutblCap, top_info: TopInfo<'tcx>, decl_origin: Option>, @@ -241,6 +242,19 @@ fn as_mutbl(self) -> Mutability { } } +/// `ref` or `ref mut` bindings (not pinned, explicitly or match-ergonomics) are only allowed behind +/// an `&pin` reference if the binding's type is `Unpin`. +/// +/// Normally, the borrow checker enforces this (not implemented yet), but we track it here for better +/// diagnostics. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PinnednessCap { + /// No restriction on pinnedness. + Not, + /// Pinnedness restricted to pinned. + Pinned, +} + /// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references? /// /// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e. @@ -374,6 +388,7 @@ pub(crate) fn check_pat_top( let top_info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id }; let pat_info = PatInfo { binding_mode: ByRef::No, + max_pinnedness: PinnednessCap::Not, max_ref_mutbl: MutblCap::Mut, top_info, decl_origin, @@ -489,22 +504,6 @@ fn check_pat_inner( let old_pat_info = pat_info; let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info }; - let adjust_binding_mode = |inner_pinnedness, inner_mutability| { - match pat_info.binding_mode { - // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` - // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or - // `&pin mut`). - ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability), - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - // Pinnedness is preserved. - ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability), - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - // Pinnedness is preserved. - ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not), - } - }; - match pat.kind { // Peel off a `&` or `&mut`from the scrutinee type. See the examples in // `tests/ui/rfcs/rfc-2005-default-binding-mode`. @@ -524,19 +523,10 @@ fn check_pat_inner( .or_default() .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected }); - let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability); - - let mut max_ref_mutbl = pat_info.max_ref_mutbl; - if self.downgrade_mut_inside_shared() { - binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); - } - if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) { - max_ref_mutbl = MutblCap::Not; - } - debug!("default binding mode is now {:?}", binding_mode); - // Use the old pat info to keep `current_depth` to its old value. - let new_pat_info = PatInfo { binding_mode, max_ref_mutbl, ..old_pat_info }; + let new_pat_info = + self.adjust_pat_info(Pinnedness::Not, inner_mutability, old_pat_info); + // Recurse with the new expected type. self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) } @@ -569,20 +559,9 @@ fn check_pat_inner( }); } - let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability); - // If the pinnedness is `Not`, it means the pattern is unpinned - // and thus requires an `Unpin` bound. - if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) { - self.register_bound( - inner_ty, - self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), - self.misc(pat.span), - ) - } - debug!("default binding mode is now {:?}", binding_mode); - // Use the old pat info to keep `current_depth` to its old value. - let new_pat_info = PatInfo { binding_mode, ..old_pat_info }; + let new_pat_info = + self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info); self.check_deref_pattern( pat, @@ -689,13 +668,62 @@ fn check_pat_inner( } PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, pat_info), PatKind::Deref(inner) => self.check_pat_deref(pat.span, inner, expected, pat_info), - PatKind::Ref(inner, mutbl) => self.check_pat_ref(pat, inner, mutbl, expected, pat_info), + PatKind::Ref(inner, pinned, mutbl) => { + self.check_pat_ref(pat, inner, pinned, mutbl, expected, pat_info) + } PatKind::Slice(before, slice, after) => { self.check_pat_slice(pat.span, before, slice, after, expected, pat_info) } } } + fn adjust_pat_info( + &self, + inner_pinnedness: Pinnedness, + inner_mutability: Mutability, + pat_info: PatInfo<'tcx>, + ) -> PatInfo<'tcx> { + let mut binding_mode = match pat_info.binding_mode { + // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const` + // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or + // `&pin mut`). + ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability), + ByRef::Yes(pinnedness, mutability) => { + let pinnedness = match pinnedness { + // When `ref`, stay a `ref` (on `&`) or downgrade to `ref pin` (on `&pin`). + Pinnedness::Not => inner_pinnedness, + // When `ref pin`, stay a `ref pin`. + // This is because we cannot get an `&mut T` from `&mut &pin mut T` unless `T: Unpin`. + // Note that `&T` and `&mut T` are `Unpin`, which implies + // `& &pin const T` <-> `&pin const &T` and `&mut &pin mut T` <-> `&pin mut &mut T` + // (i.e. mutually coercible). + Pinnedness::Pinned => Pinnedness::Pinned, + }; + + let mutability = match mutability { + // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). + Mutability::Mut => inner_mutability, + // Once a `ref`, always a `ref`. + // This is because a `& &mut` cannot mutate the underlying value. + Mutability::Not => Mutability::Not, + }; + ByRef::Yes(pinnedness, mutability) + } + }; + + let PatInfo { mut max_ref_mutbl, mut max_pinnedness, .. } = pat_info; + if self.downgrade_mut_inside_shared() { + binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl()); + } + match binding_mode { + ByRef::Yes(_, Mutability::Not) => max_ref_mutbl = MutblCap::Not, + ByRef::Yes(Pinnedness::Pinned, _) => max_pinnedness = PinnednessCap::Pinned, + _ => {} + } + debug!("default binding mode is now {:?}", binding_mode); + PatInfo { binding_mode, max_pinnedness, max_ref_mutbl, ..pat_info } + } + fn check_deref_pattern( &self, pat: &'tcx Pat<'tcx>, @@ -1195,6 +1223,18 @@ fn check_pat_ident( } }; + // If there exists a pinned reference in the pattern but the binding is not pinned, + // it means the binding is unpinned and thus requires an `Unpin` bound. + if pat_info.max_pinnedness == PinnednessCap::Pinned + && matches!(bm.0, ByRef::Yes(Pinnedness::Not, _)) + { + self.register_bound( + expected, + self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span), + self.misc(pat.span), + ) + } + if matches!(bm.0, ByRef::Yes(_, Mutability::Mut)) && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl { @@ -1223,22 +1263,20 @@ fn check_pat_ident( let local_ty = self.local_ty(pat.span, pat.hir_id); let eq_ty = match bm.0 { - ByRef::Yes(Pinnedness::Not, mutbl) => { + ByRef::Yes(pinnedness, mutbl) => { // If the binding is like `ref x | ref mut x`, // then `x` is assigned a value of type `&M T` where M is the // mutability and T is the expected type. // + // Under pin ergonomics, if the binding is like `ref pin const|mut x`, + // then `x` is assigned a value of type `&pin M T` where M is the + // mutability and T is the expected type. + // // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` // is required. However, we use equality, which is stronger. // See (note_1) for an explanation. - self.new_ref_ty(pat.span, mutbl, expected) + self.new_ref_ty(pat.span, pinnedness, mutbl, expected) } - // Wrapping the type into `Pin` if the binding is like `ref pin const|mut x` - ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt( - self.tcx, - self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)), - self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]), - ), // Otherwise, the type of x is the expected type `T`. ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). }; @@ -1331,18 +1369,16 @@ fn suggest_adding_missing_ref_or_removing_ref( } /// Precondition: pat is a `Ref(_)` pattern + // FIXME(pin_ergonomics): add suggestions for `&pin mut` or `&pin const` patterns fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { let tcx = self.tcx; - if let PatKind::Ref(inner, mutbl) = pat.kind + if let PatKind::Ref(inner, pinned, mutbl) = pat.kind && let PatKind::Binding(_, _, binding, ..) = inner.kind { let binding_parent = tcx.parent_hir_node(pat.hir_id); debug!(?inner, ?pat, ?binding_parent); - let mutability = match mutbl { - ast::Mutability::Mut => "mut", - ast::Mutability::Not => "", - }; + let pin_and_mut = pinned.prefix_str(mutbl).trim_end(); let mut_var_suggestion = 'block: { if mutbl.is_not() { @@ -1392,7 +1428,7 @@ fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { // so we don't suggest moving something to the type that does not exist hir::Node::Param(hir::Param { ty_span, pat, .. }) if pat.span != *ty_span => { err.multipart_suggestion_verbose( - format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"), + format!("to take parameter `{binding}` by reference, move `&{pin_and_mut}` to the type"), vec![ (pat.span.until(inner.span), "".to_owned()), (ty_span.shrink_to_lo(), mutbl.ref_prefix_str().to_owned()), @@ -1406,13 +1442,13 @@ fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { } hir::Node::Pat(pt) if let PatKind::TupleStruct(_, pat_arr, _) = pt.kind => { for i in pat_arr.iter() { - if let PatKind::Ref(the_ref, _) = i.kind + if let PatKind::Ref(the_ref, _, _) = i.kind && let PatKind::Binding(mt, _, ident, _) = the_ref.kind { let BindingMode(_, mtblty) = mt; err.span_suggestion_verbose( i.span, - format!("consider removing `&{mutability}` from the pattern"), + format!("consider removing `&{pin_and_mut}` from the pattern"), mtblty.prefix_str().to_string() + &ident.name.to_string(), Applicability::MaybeIncorrect, ); @@ -1426,7 +1462,7 @@ fn borrow_pat_suggestion(&self, err: &mut Diag<'_>, pat: &Pat<'_>) { // rely on match ergonomics or it might be nested `&&pat` err.span_suggestion_verbose( pat.span.until(inner.span), - format!("consider removing `&{mutability}` from the pattern"), + format!("consider removing `&{pin_and_mut}` from the pattern"), "", Applicability::MaybeIncorrect, ); @@ -2677,6 +2713,7 @@ fn check_pat_ref( &self, pat: &'tcx Pat<'tcx>, inner: &'tcx Pat<'tcx>, + pat_pinned: Pinnedness, pat_mutbl: Mutability, mut expected: Ty<'tcx>, mut pat_info: PatInfo<'tcx>, @@ -2699,9 +2736,7 @@ fn check_pat_ref( // Determine whether we're consuming an inherited reference and resetting the default // binding mode, based on edition and enabled experimental features. if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode - // FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here - // should be adjusted to `pat_pin == inh_pin` - && (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not) + && pat_pinned == inh_pin { match self.ref_pat_matches_inherited_ref(pat.span.edition()) { InheritedRefMatchRule::EatOuter => { @@ -2821,21 +2856,24 @@ fn check_pat_ref( // to avoid creating needless variables. This also helps with // the bad interactions of the given hack detailed in (note_1). debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) - if (ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) - || r_mutbl == pat_mutbl => + match expected.maybe_pinned_ref() { + Some((r_ty, r_pinned, r_mutbl)) + if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl) + || r_mutbl == pat_mutbl) + && pat_pinned == r_pinned => { if r_mutbl == Mutability::Not { pat_info.max_ref_mutbl = MutblCap::Not; } + if r_pinned == Pinnedness::Pinned { + pat_info.max_pinnedness = PinnednessCap::Pinned; + } (expected, r_ty) } - _ => { let inner_ty = self.next_ty_var(inner.span); - let ref_ty = self.new_ref_ty(pat.span, pat_mutbl, inner_ty); + let ref_ty = self.new_ref_ty(pat.span, pat_pinned, pat_mutbl, inner_ty); debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty); let err = self.demand_eqtype_pat_diag( pat.span, @@ -2864,10 +2902,29 @@ fn check_pat_ref( ref_ty } - /// Create a reference type with a fresh region variable. - fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { + /// Create a reference or pinned reference type with a fresh region variable. + fn new_ref_ty( + &self, + span: Span, + pinnedness: Pinnedness, + mutbl: Mutability, + ty: Ty<'tcx>, + ) -> Ty<'tcx> { let region = self.next_region_var(RegionVariableOrigin::PatternRegion(span)); - Ty::new_ref(self.tcx, region, ty, mutbl) + let ref_ty = Ty::new_ref(self.tcx, region, ty, mutbl); + if pinnedness.is_pinned() { + return self.new_pinned_ty(span, ref_ty); + } + ref_ty + } + + /// Create a pinned type. + fn new_pinned_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + Ty::new_adt( + self.tcx, + self.tcx.adt_def(self.tcx.require_lang_item(LangItem::Pin, span)), + self.tcx.mk_args(&[ty.into()]), + ) } fn error_inherited_ref_mutability_mismatch( diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs deleted file mode 100644 index 973dc7141e64..000000000000 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ /dev/null @@ -1,79 +0,0 @@ -use hir::Node; -use hir::def_id::DefId; -use rustc_hir as hir; -use rustc_middle::bug; -use rustc_middle::middle::region::{RvalueCandidate, Scope, ScopeTree}; -use rustc_middle::ty::RvalueScopes; -use tracing::debug; - -use super::FnCtxt; - -/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by -/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that -/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let` -/// statement. -/// -/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching -/// `` as `blk_id`: -/// -/// ```text -/// ET = *ET -/// | ET[...] -/// | ET.f -/// | (ET) -/// | -/// ``` -/// -/// Note: ET is intended to match "rvalues or places based on rvalues". -fn record_rvalue_scope_rec( - rvalue_scopes: &mut RvalueScopes, - mut expr: &hir::Expr<'_>, - lifetime: Option, -) { - loop { - // Note: give all the expressions matching `ET` with the - // extended temporary lifetime, not just the innermost rvalue, - // because in codegen if we must compile e.g., `*rvalue()` - // into a temporary, we request the temporary scope of the - // outer expression. - - rvalue_scopes.record_rvalue_scope(expr.hir_id.local_id, lifetime); - - match expr.kind { - hir::ExprKind::AddrOf(_, _, subexpr) - | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) - | hir::ExprKind::Field(subexpr, _) - | hir::ExprKind::Index(subexpr, _, _) => { - expr = subexpr; - } - _ => { - return; - } - } - } -} -fn record_rvalue_scope( - rvalue_scopes: &mut RvalueScopes, - expr: &hir::Expr<'_>, - candidate: &RvalueCandidate, -) { - debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})"); - record_rvalue_scope_rec(rvalue_scopes, expr, candidate.lifetime) - // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments -} - -pub(crate) fn resolve_rvalue_scopes<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - scope_tree: &'a ScopeTree, - def_id: DefId, -) -> RvalueScopes { - let tcx = &fcx.tcx; - let mut rvalue_scopes = RvalueScopes::new(); - debug!("start resolving rvalue scopes, def_id={def_id:?}"); - debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates); - for (&hir_id, candidate) in &scope_tree.rvalue_candidates { - let Node::Expr(expr) = tcx.hir_node(hir_id) else { bug!("hir node does not exist") }; - record_rvalue_scope(&mut rvalue_scopes, expr, candidate); - } - rvalue_scopes -} diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 3235aa396df4..ec4e7cd6c82a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -79,9 +79,6 @@ pub(crate) fn resolve_type_vars_in_body( wbcx.visit_offset_of_container_types(); wbcx.visit_potentially_region_dependent_goals(); - wbcx.typeck_results.rvalue_scopes = - mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); - let used_trait_imports = mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports); debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e68db4f44ca4..ddfec9f886a6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -596,7 +596,9 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P .map(|fmap| { ( escape_dep_filename(&fmap.name.prefer_local().to_string()), - fmap.source_len.0 as u64, + // This needs to be unnormalized, + // as external tools wouldn't know how rustc normalizes them + fmap.unnormalized_source_len as u64, fmap.checksum_hash, ) }) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index ade7ec38fb35..2e100a6215c0 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -252,7 +252,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler") + QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler") }); break_query_cycles(query_map, ®istry); }) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 8aa90c070acd..c996181354b9 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -265,6 +265,9 @@ lint_forgetting_copy_types = calls to `std::mem::forget` with a value that imple lint_forgetting_references = calls to `std::mem::forget` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` +lint_function_casts_as_integer = direct cast of function item into an integer + .cast_as_fn = first cast to a pointer `as *const ()` + lint_hidden_glob_reexport = private item shadows public glob re-export .note_glob_reexport = the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here .note_private_item = but the private item here shadows it diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 83de7d389231..4e9ecb9006ab 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1705,7 +1705,7 @@ fn matches_ellipsis_pat(pat: &ast::Pat) -> Option<(Option<&Expr>, &Expr, Span)> } let (parentheses, endpoints) = match &pat.kind { - PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(subpat)), + PatKind::Ref(subpat, _, _) => (true, matches_ellipsis_pat(subpat)), _ => (false, matches_ellipsis_pat(pat)), }; diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index dff1fc436702..6cb5aea79840 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -5,7 +5,7 @@ //! syntactical lints. use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; -use rustc_ast::{self as ast, HasAttrs}; +use rustc_ast::{self as ast, AttrVec, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; use rustc_feature::Features; @@ -135,7 +135,7 @@ fn visit_stmt(&mut self, s: &'ast ast::Stmt) { }); } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) { lint_callback!(self, check_fn, fk, span, id); ast_visit::walk_fn(self, fk); } diff --git a/compiler/rustc_lint/src/function_cast_as_integer.rs b/compiler/rustc_lint/src/function_cast_as_integer.rs new file mode 100644 index 000000000000..2a4ff1f63db5 --- /dev/null +++ b/compiler/rustc_lint/src/function_cast_as_integer.rs @@ -0,0 +1,63 @@ +use rustc_hir as hir; +use rustc_middle::ty; +use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_span::BytePos; + +use crate::lints::{FunctionCastsAsIntegerDiag, FunctionCastsAsIntegerSugg}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `function_casts_as_integer` lint detects cases where a function item is cast + /// to an integer. + /// + /// ### Example + /// + /// ```rust + /// fn foo() {} + /// let x = foo as usize; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When casting a function item to an integer, it implicitly creates a + /// function pointer that will in turn be cast to an integer. By making + /// it explicit, it improves readability of the code and prevents bugs. + pub FUNCTION_CASTS_AS_INTEGER, + Warn, + "casting a function into an integer", +} + +declare_lint_pass!( + /// Lint for casts of functions into integers. + FunctionCastsAsInteger => [FUNCTION_CASTS_AS_INTEGER] +); + +impl<'tcx> LateLintPass<'tcx> for FunctionCastsAsInteger { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_expr) = expr.kind else { return }; + let cast_to_ty = cx.typeck_results().expr_ty(expr); + // Casting to a function (pointer?), so all good. + // + // Normally, only casts to integers is possible, but if it ever changed, this condition + // will likely need to be updated. + if matches!(cast_to_ty.kind(), ty::FnDef(..) | ty::FnPtr(..) | ty::RawPtr(..)) { + return; + } + let cast_from_ty = cx.typeck_results().expr_ty(cast_from_expr); + if matches!(cast_from_ty.kind(), ty::FnDef(..)) { + cx.tcx.emit_node_span_lint( + FUNCTION_CASTS_AS_INTEGER, + expr.hir_id, + cast_to_expr.span.with_lo(cast_from_expr.span.hi() + BytePos(1)), + FunctionCastsAsIntegerDiag { + sugg: FunctionCastsAsIntegerSugg { + suggestion: cast_from_expr.span.shrink_to_hi(), + cast_to_ty, + }, + }, + ); + } + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 8a83434e10c1..7c3a81e89130 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,6 +45,7 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; +mod function_cast_as_integer; mod if_let_rescope; mod impl_trait_overcaptures; mod internal; @@ -89,6 +90,7 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; +use function_cast_as_integer::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; use internal::*; @@ -241,6 +243,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { IfLetRescope: IfLetRescope::default(), StaticMutRefs: StaticMutRefs, UnqualifiedLocalImports: UnqualifiedLocalImports, + FunctionCastsAsInteger: FunctionCastsAsInteger, CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, ] diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c55f2b9dd6f2..dc50f61f741d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3019,6 +3019,26 @@ pub(crate) struct ReservedMultihash { pub suggestion: Span, } +#[derive(LintDiagnostic)] +#[diag(lint_function_casts_as_integer)] +pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { + #[subdiagnostic] + pub(crate) sugg: FunctionCastsAsIntegerSugg<'tcx>, +} + +#[derive(Subdiagnostic)] +#[suggestion( + lint_cast_as_fn, + code = " as *const ()", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct FunctionCastsAsIntegerSugg<'tcx> { + #[primary_span] + pub suggestion: Span, + pub cast_to_ty: Ty<'tcx>, +} + #[derive(Debug)] pub(crate) struct MismatchedLifetimeSyntaxes { pub inputs: LifetimeSyntaxCategories>, diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 191eb721b347..ae145543e70d 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -55,10 +55,10 @@ fn check_fn( /// Each `check` method checks a single syntax node, and should not /// invoke methods recursively (unlike `Visitor`). By default they /// do nothing. -// +/// // FIXME: eliminate the duplication with `Visitor`. But this also // contains a few lint-specific methods with no equivalent in `Visitor`. - +// macro_rules! declare_late_lint_pass { ([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( pub trait LateLintPass<'tcx>: LintPass { diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs index 2c8839708669..9e38ea6b685b 100644 --- a/compiler/rustc_lint/src/types/improper_ctypes.rs +++ b/compiler/rustc_lint/src/types/improper_ctypes.rs @@ -16,6 +16,7 @@ use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; +use rustc_target::spec::Os; use tracing::debug; use super::repr_nullable_ptr; @@ -177,7 +178,7 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool { /// the Power alignment Rule (see the `check_struct_for_power_alignment` function). fn check_arg_for_power_alignment<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { let tcx = cx.tcx; - assert!(tcx.sess.target.os == "aix"); + assert!(tcx.sess.target.os == Os::Aix); // Structs (under repr(C)) follow the power alignment rule if: // - the first field of the struct is a floating-point type that // is greater than 4-bytes, or @@ -222,7 +223,7 @@ fn check_struct_for_power_alignment<'tcx>( let tcx = cx.tcx; // Only consider structs (not enums or unions) on AIX. - if tcx.sess.target.os != "aix" || !adt_def.is_struct() { + if tcx.sess.target.os != Os::Aix || !adt_def.is_struct() { return; } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 40df5ecb0f3c..4842615409f3 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1307,7 +1307,8 @@ fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. - Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), + // FIXME(pin_ergonomics): check pinned patterns + Ref(p, _, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7e762a878078..899632b9d4b7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -144,6 +144,7 @@ UNUSED_UNSAFE, UNUSED_VARIABLES, USELESS_DEPRECATED, + VARARGS_WITHOUT_PATTERN, WARNINGS, // tidy-alphabetical-end ] @@ -5295,3 +5296,50 @@ report_in_deps: false, }; } + +declare_lint! { + /// The `varargs_without_pattern` lint detects when `...` is used as an argument to a + /// non-foreign function without any pattern being specified. + /// + /// ### Example + /// + /// ```rust + /// // Using `...` in non-foreign function definitions is unstable, however stability is + /// // currently only checked after attributes are expanded, so using `#[cfg(false)]` here will + /// // allow this to compile on stable Rust. + /// #[cfg(false)] + /// fn foo(...) { + /// + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Patterns are currently required for all non-`...` arguments in function definitions (with + /// some exceptions in the 2015 edition). Requiring `...` arguments to have patterns in + /// non-foreign function definitions makes the language more consistent, and removes a source of + /// confusion for the unstable C variadic feature. `...` arguments without a pattern are already + /// stable and widely used in foreign function definitions; this lint only affects non-foreign + /// function definitions. + /// + /// Using `...` (C varargs) in a non-foreign function definition is currently unstable. However, + /// stability checking for the `...` syntax in non-foreign function definitions is currently + /// implemented after attributes have been expanded, meaning that if the attribute removes the + /// use of the unstable syntax (e.g. `#[cfg(false)]`, or a procedural macro), the code will + /// compile on stable Rust; this is the only situation where this lint affects code that + /// compiles on stable Rust. + /// + /// This is a [future-incompatible] lint to transition this to a hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub VARARGS_WITHOUT_PATTERN, + Warn, + "detects usage of `...` arguments without a pattern in non-foreign items", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #145544 ", + report_in_deps: false, + }; +} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index eab65f92e293..8823c8392282 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) { llvm::PrintStatistics(OS); } -extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn, - LLVMValueRef NewFn) { - llvm::Module *module = llvm::unwrap(M); +extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) { llvm::Function *oldFn = llvm::unwrap(OldFn); llvm::Function *newFn = llvm::unwrap(NewFn); diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 291f5c65dfa2..250657bc6806 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -15,7 +15,7 @@ use rustc_session::search_paths::PathKind; use rustc_span::Symbol; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_target::spec::{Arch, BinaryFormat, LinkSelfContainedComponents}; +use rustc_target::spec::{Abi, Arch, BinaryFormat, Env, LinkSelfContainedComponents, Os}; use crate::errors; @@ -67,9 +67,9 @@ pub fn walk_native_lib_search_dirs( // FIXME: On AIX this also has the side-effect of making the list of library search paths // non-empty, which is needed or the linker may decide to record the LIBPATH env, if // defined, as the search path instead of appending the default search paths. - if sess.target.vendor == "fortanix" - || sess.target.os == "linux" - || sess.target.os == "fuchsia" + if sess.target.abi == Abi::Fortanix + || sess.target.os == Os::Linux + || sess.target.os == Os::Fuchsia || sess.target.is_like_aix || sess.target.is_like_darwin && !sess.sanitizers().is_empty() { @@ -79,7 +79,7 @@ pub fn walk_native_lib_search_dirs( // Mac Catalyst uses the macOS SDK, but to link to iOS-specific frameworks // we must have the support library stubs in the library search path (#121430). if let Some(sdk_root) = apple_sdk_root - && sess.target.env == "macabi" + && sess.target.env == Env::MacAbi { f(&sdk_root.join("System/iOSSupport/usr/lib"), false)?; f(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"), true)?; diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 808d9fbbc2ce..6c796b3a9c8c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1744,7 +1744,8 @@ fn filter<'a>( src_hash, checksum_hash, start_pos: original_start_pos, - source_len, + normalized_source_len, + unnormalized_source_len, lines, multibyte_chars, normalized_pos, @@ -1804,7 +1805,8 @@ fn filter<'a>( src_hash, checksum_hash, stable_id, - source_len.to_u32(), + normalized_source_len.to_u32(), + unnormalized_source_len, self.cnum, lines, multibyte_chars, @@ -1817,9 +1819,9 @@ fn filter<'a>( translated (start_pos {:?} source_len {:?})", local_version.name, original_start_pos, - source_len, + normalized_source_len, local_version.start_pos, - local_version.source_len + local_version.normalized_source_len ); ImportedSourceFile { diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 8d4385a2fd32..ef8326fd038e 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -51,6 +51,7 @@ #![feature(negative_impls)] #![feature(never_type)] #![feature(ptr_alignment_type)] +#![feature(range_bounds_is_empty)] #![feature(rustc_attrs)] #![feature(sized_hierarchy)] #![feature(try_blocks)] diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 3ed8d9a36e10..990ed8f48fb8 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::UnordMap; use rustc_hir as hir; -use rustc_hir::{HirId, HirIdMap, Node}; +use rustc_hir::{HirId, ItemLocalMap, Node}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::{DUMMY_SP, Span}; use tracing::debug; @@ -221,12 +221,12 @@ pub struct ScopeTree { /// variable is declared. var_map: FxIndexMap, - /// Identifies expressions which, if captured into a temporary, ought to - /// have a temporary whose lifetime extends to the end of the enclosing *block*, - /// and not the enclosing *statement*. Expressions that are not present in this - /// table are not rvalue candidates. The set of rvalue candidates is computed - /// during type check based on a traversal of the AST. - pub rvalue_candidates: HirIdMap, + /// Tracks expressions with extended temporary scopes, based on the syntactic rules for + /// temporary lifetime extension. Further details may be found in + /// `rustc_hir_analysis::check::region` and in the [Reference]. + /// + /// [Reference]: https://doc.rust-lang.org/nightly/reference/destructors.html#temporary-lifetime-extension + extended_temp_scopes: ItemLocalMap>, /// Backwards incompatible scoping that will be introduced in future editions. /// This information is used later for linting to identify locals and @@ -234,14 +234,15 @@ pub struct ScopeTree { pub backwards_incompatible_scope: UnordMap, } -/// See the `rvalue_candidates` field for more information on rvalue -/// candidates in general. -/// The `lifetime` field is None to indicate that certain expressions escape -/// into 'static and should have no local cleanup scope. -#[derive(Debug, Copy, Clone, HashStable)] -pub struct RvalueCandidate { - pub target: hir::ItemLocalId, - pub lifetime: Option, +/// Temporary lifetime information for expressions, used when lowering to MIR. +#[derive(Clone, Copy, Debug, HashStable)] +pub struct TempLifetime { + /// The scope in which a temporary should be dropped. If `None`, no drop is scheduled; this is + /// the case for lifetime-extended temporaries extended by a const/static item or const block. + pub temp_lifetime: Option, + /// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition. + /// If `None`, then no changes are expected, or lints are disabled. + pub backwards_incompatible: Option, } impl ScopeTree { @@ -260,12 +261,13 @@ pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) { self.var_map.insert(var, lifetime); } - pub fn record_rvalue_candidate(&mut self, var: HirId, candidate: RvalueCandidate) { - debug!("record_rvalue_candidate(var={var:?}, candidate={candidate:?})"); - if let Some(lifetime) = &candidate.lifetime { - assert!(var.local_id != lifetime.local_id) + /// Make an association between a sub-expression and an extended lifetime + pub fn record_extended_temp_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { + debug!(?var, ?lifetime); + if let Some(lifetime) = lifetime { + assert!(var != lifetime.local_id); } - self.rvalue_candidates.insert(var, candidate); + self.extended_temp_scopes.insert(var, lifetime); } /// Returns the narrowest scope that encloses `id`, if any. @@ -337,4 +339,20 @@ pub fn default_temporary_scope(&self, inner: Scope) -> (Scope, Option) { span_bug!(ty::tls::with(|tcx| inner.span(tcx, self)), "no enclosing temporary scope") } + + /// Returns the scope when the temp created by `expr_id` will be cleaned up. + /// It also emits a lint on potential backwards incompatible change to the temporary scope + /// which is *for now* always shortening. + pub fn temporary_scope(&self, expr_id: hir::ItemLocalId) -> TempLifetime { + // Check for a designated extended temporary scope. + if let Some(&s) = self.extended_temp_scopes.get(&expr_id) { + debug!("temporary_scope({expr_id:?}) = {s:?} [custom]"); + return TempLifetime { temp_lifetime: s, backwards_incompatible: None }; + } + + // Otherwise, locate the innermost terminating scope. + let (scope, backwards_incompatible) = + self.default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node }); + TempLifetime { temp_lifetime: Some(scope), backwards_incompatible } + } } diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 5764a9c84eea..6985cc7ddcfa 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -16,7 +16,7 @@ /////////////////////////////////////////////////////////////////////////// /// Evaluated Constants - +/// /// Represents the result of const evaluation via the `eval_to_allocation` query. /// Not to be confused with `ConstAllocation`, which directly refers to the underlying data! /// Here we indirect via an `AllocId`. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 8e603ce1b911..1cfe5219997b 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -19,9 +19,9 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use super::{ - AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, - PointerArithmetic, Provenance, ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, - UndefinedBehaviorInfo, UnsupportedOpInfo, interp_ok, read_target_uint, write_target_uint, + AllocId, BadBytesAccess, CtfeProvenance, InterpErrorKind, InterpResult, Pointer, Provenance, + ResourceExhaustionInfo, Scalar, ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, + interp_ok, read_target_uint, write_target_uint, }; use crate::ty; @@ -601,14 +601,13 @@ pub fn get_bytes_strip_provenance( })?; if !Prov::OFFSET_IS_ADDR && !self.provenance.range_empty(range, cx) { // Find the provenance. - let (offset, _prov) = self + let (prov_range, _prov) = self .provenance - .range_ptrs_get(range, cx) - .first() - .copied() + .get_range(range, cx) + .next() .expect("there must be provenance somewhere here"); - let start = offset.max(range.start); // the pointer might begin before `range`! - let end = (offset + cx.pointer_size()).min(range.end()); // the pointer might end after `range`! + let start = prov_range.start.max(range.start); // the pointer might begin before `range`! + let end = prov_range.end().min(range.end()); // the pointer might end after `range`! return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess { access: range, bad: AllocRange::from(start..end), @@ -630,7 +629,7 @@ pub fn get_bytes_unchecked_for_overwrite( range: AllocRange, ) -> &mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx); + self.provenance.clear(range, &self.bytes, cx); &mut self.bytes[range.start.bytes_usize()..range.end().bytes_usize()] } @@ -643,7 +642,7 @@ pub fn get_bytes_unchecked_for_overwrite_ptr( range: AllocRange, ) -> *mut [u8] { self.mark_init(range, true); - self.provenance.clear(range, cx); + self.provenance.clear(range, &self.bytes, cx); assert!(range.end().bytes_usize() <= self.bytes.len()); // need to do our own bounds-check // Crucially, we go via `AllocBytes::as_mut_ptr`, not `AllocBytes::deref_mut`. @@ -711,57 +710,14 @@ pub fn read_scalar( if read_provenance { assert_eq!(range.size, cx.data_layout().pointer_size()); - // When reading data with provenance, the easy case is finding provenance exactly where we - // are reading, then we can put data and provenance back together and return that. - if let Some(prov) = self.provenance.get_ptr(range.start) { - // Now we can return the bits, with their appropriate provenance. + if let Some(prov) = self.provenance.read_ptr(range.start, cx)? { + // Assemble the bits with their provenance. let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_pointer(ptr, cx)); + Ok(Scalar::from_pointer(ptr, cx)) + } else { + // Return raw bits without provenance. + Ok(Scalar::from_uint(bits, range.size)) } - // The other easy case is total absence of provenance. - if self.provenance.range_empty(range, cx) { - return Ok(Scalar::from_uint(bits, range.size)); - } - // If we get here, we have to check per-byte provenance, and join them together. - let prov = 'prov: { - if !Prov::OFFSET_IS_ADDR { - // FIXME(#146291): We need to ensure that we don't mix different pointers with - // the same provenance. - return Err(AllocError::ReadPartialPointer(range.start)); - } - // Initialize with first fragment. Must have index 0. - let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { - break 'prov None; - }; - // Update with the remaining fragments. - for offset in Size::from_bytes(1)..range.size { - // Ensure there is provenance here and it has the right index. - let Some((frag_prov, frag_idx)) = - self.provenance.get_byte(range.start + offset, cx) - else { - break 'prov None; - }; - // Wildcard provenance is allowed to come with any index (this is needed - // for Miri's native-lib mode to work). - if u64::from(frag_idx) != offset.bytes() && Some(frag_prov) != Prov::WILDCARD { - break 'prov None; - } - // Merge this byte's provenance with the previous ones. - joint_prov = match Prov::join(joint_prov, frag_prov) { - Some(prov) => prov, - None => break 'prov None, - }; - } - break 'prov Some(joint_prov); - }; - if prov.is_none() && !Prov::OFFSET_IS_ADDR { - // There are some bytes with provenance here but overall the provenance does not add up. - // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we must error. - return Err(AllocError::ReadPartialPointer(range.start)); - } - // We can use this provenance. - let ptr = Pointer::new(prov, Size::from_bytes(bits)); - return Ok(Scalar::from_maybe_pointer(ptr, cx)); } else { // We are *not* reading a pointer. // If we can just ignore provenance or there is none, that's easy. @@ -816,7 +772,7 @@ pub fn write_scalar( /// Write "uninit" to the given memory range. pub fn write_uninit(&mut self, cx: &impl HasDataLayout, range: AllocRange) { self.mark_init(range, false); - self.provenance.clear(range, cx); + self.provenance.clear(range, &self.bytes, cx); } /// Mark all bytes in the given range as initialised and reset the provenance @@ -831,21 +787,28 @@ pub fn process_native_write(&mut self, cx: &impl HasDataLayout, range: Option bool { self.provenance.merge_bytes(cx) } + pub fn provenance_prepare_copy( + &self, + range: AllocRange, + cx: &impl HasDataLayout, + ) -> ProvenanceCopy { + self.provenance.prepare_copy(range, &self.bytes, cx) + } + /// Applies a previously prepared provenance copy. - /// The affected range, as defined in the parameters to `provenance().prepare_copy` is expected - /// to be clear of provenance. + /// The affected range is expected to be clear of provenance. /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 67baf63bbfad..9d38f3ebb157 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -2,7 +2,7 @@ //! representation for the common case where PTR_SIZE consecutive bytes have the same provenance. use std::cmp; -use std::ops::Range; +use std::ops::{Range, RangeBounds}; use rustc_abi::{HasDataLayout, Size}; use rustc_data_structures::sorted_map::SortedMap; @@ -13,6 +13,23 @@ use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; use crate::mir::interpret::{AllocError, AllocResult}; +/// A pointer fragment represents one byte of a pointer. +/// If the bytes are re-assembled in their original order, the pointer can be used again. +/// Wildcard provenance is allowed to have index 0 everywhere. +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +#[derive(HashStable)] +pub struct PointerFrag { + /// The position of this fragment inside the pointer (in `0..8`). + pub idx: u8, + /// The provenance of the pointer this is a fragment of. + pub prov: Prov, + /// The raw bytes of the pointer this is a fragment of. + /// This is taken as a direct subslice of the raw allocation data, so we don't have to worry + /// about endianness. If the pointer size is less than 8, only the first N bytes of this are + /// ever non-zero. + pub bytes: [u8; 8], +} + /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(HashStable)] @@ -21,10 +38,7 @@ pub struct ProvenanceMap { /// bytes. Two entries in this map are always at least a pointer size apart. ptrs: SortedMap, /// This stores byte-sized provenance fragments. - /// The `u8` indicates the position of this byte inside its original pointer. - /// If the bytes are re-assembled in their original order, the pointer can be used again. - /// Wildcard provenance is allowed to have index 0 everywhere. - bytes: Option>>, + bytes: Option>>>, } // These impls are generic over `Prov` since `CtfeProvenance` is only decodable/encodable @@ -49,7 +63,7 @@ pub fn new() -> Self { } /// The caller must guarantee that the given provenance list is already sorted - /// by address and contain no duplicates. + /// by offset and contain no duplicates. pub fn from_presorted_ptrs(r: Vec<(Size, Prov)>) -> Self { ProvenanceMap { ptrs: SortedMap::from_presorted_elements(r), bytes: None } } @@ -80,11 +94,7 @@ fn adjusted_range_ptrs(range: AllocRange, cx: &impl HasDataLayout) -> Range &[(Size, Prov)] { + fn range_ptrs_get(&self, range: AllocRange, cx: &impl HasDataLayout) -> &[(Size, Prov)] { self.ptrs.range(Self::adjusted_range_ptrs(range, cx)) } @@ -93,8 +103,14 @@ fn range_ptrs_is_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> boo self.ptrs.range_is_empty(Self::adjusted_range_ptrs(range, cx)) } + /// Check if there is ptr-sized provenance at the given index. + /// Does not mean anything for bytewise provenance! But can be useful as an optimization. + pub fn get_ptr(&self, offset: Size) -> Option { + self.ptrs.get(&offset).copied() + } + /// Returns all byte-wise provenance in the given range. - fn range_bytes_get(&self, range: AllocRange) -> &[(Size, (Prov, u8))] { + fn range_bytes_get(&self, range: AllocRange) -> &[(Size, PointerFrag)] { if let Some(bytes) = self.bytes.as_ref() { bytes.range(range.start..range.end()) } else { @@ -107,68 +123,126 @@ fn range_bytes_is_empty(&self, range: AllocRange) -> bool { self.bytes.as_ref().is_none_or(|bytes| bytes.range_is_empty(range.start..range.end())) } - /// Get the provenance of a single byte. - pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<(Prov, u8)> { - let prov = self.range_ptrs_get(alloc_range(offset, Size::from_bytes(1)), cx); - debug_assert!(prov.len() <= 1); - if let Some(entry) = prov.first() { - // If it overlaps with this byte, it is on this byte. - debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); - Some((entry.1, (offset - entry.0).bytes() as u8)) - } else { - // Look up per-byte provenance. - self.bytes.as_ref().and_then(|b| b.get(&offset).copied()) - } + /// Get the provenance of a single byte. Must only be called if there is no + /// pointer-sized provenance here. + pub fn get_byte(&self, offset: Size, cx: &impl HasDataLayout) -> Option<&PointerFrag> { + debug_assert!(self.range_ptrs_is_empty(alloc_range(offset, Size::from_bytes(1)), cx)); + self.bytes.as_ref().and_then(|b| b.get(&offset)) } /// Gets the provenances of all bytes (including from pointers) in a range. pub fn get_range( &self, - cx: &impl HasDataLayout, range: AllocRange, - ) -> impl Iterator { - let ptr_provs = self.range_ptrs_get(range, cx).iter().map(|(_, p)| *p); - let byte_provs = self.range_bytes_get(range).iter().map(|(_, (p, _))| *p); + cx: &impl HasDataLayout, + ) -> impl Iterator { + let ptr_size = cx.data_layout().pointer_size(); + let ptr_provs = self + .range_ptrs_get(range, cx) + .iter() + .map(move |(offset, p)| (alloc_range(*offset, ptr_size), *p)); + let byte_provs = self + .range_bytes_get(range) + .iter() + .map(move |(offset, frag)| (alloc_range(*offset, Size::from_bytes(1)), frag.prov)); ptr_provs.chain(byte_provs) } /// Attempt to merge per-byte provenance back into ptr chunks, if the right fragments - /// sit next to each other. Return `false` is that is not possible due to partial pointers. + /// sit next to each other. Return `false` if that is not possible due to partial pointers. pub fn merge_bytes(&mut self, cx: &impl HasDataLayout) -> bool { let Some(bytes) = self.bytes.as_deref_mut() else { return true; }; - if !Prov::OFFSET_IS_ADDR { - // FIXME(#146291): We need to ensure that we don't mix different pointers with - // the same provenance. - return false; - } let ptr_size = cx.data_layout().pointer_size(); - while let Some((offset, (prov, _))) = bytes.iter().next().copied() { + while let Some((offset, first_frag)) = bytes.iter().next() { + let offset = *offset; // Check if this fragment starts a pointer. let range = offset..offset + ptr_size; let frags = bytes.range(range.clone()); if frags.len() != ptr_size.bytes_usize() { + // We can't merge this one, no point in trying to merge the rest. return false; } - for (idx, (_offset, (frag_prov, frag_idx))) in frags.iter().copied().enumerate() { - if frag_prov != prov || frag_idx != idx as u8 { + for (idx, (_offset, frag)) in frags.iter().enumerate() { + if !(frag.prov == first_frag.prov + && frag.bytes == first_frag.bytes + && frag.idx == idx as u8) + { return false; } } // Looks like a pointer! Move it over to the ptr provenance map. + self.ptrs.insert(offset, first_frag.prov); bytes.remove_range(range); - self.ptrs.insert(offset, prov); } // We managed to convert everything into whole pointers. self.bytes = None; true } - /// Check if there is ptr-sized provenance at the given index. - /// Does not mean anything for bytewise provenance! But can be useful as an optimization. - pub fn get_ptr(&self, offset: Size) -> Option { - self.ptrs.get(&offset).copied() + /// Try to read a pointer from the given location, possibly by loading from many per-byte + /// provenances. + pub fn read_ptr(&self, offset: Size, cx: &impl HasDataLayout) -> AllocResult> { + // If there is pointer-sized provenance exactly here, we can just return that. + if let Some(prov) = self.get_ptr(offset) { + return Ok(Some(prov)); + } + // The other easy case is total absence of provenance, that also always works. + let range = alloc_range(offset, cx.data_layout().pointer_size()); + let no_ptrs = self.range_ptrs_is_empty(range, cx); + if no_ptrs && self.range_bytes_is_empty(range) { + return Ok(None); + } + // If we get here, we have to check whether we can merge per-byte provenance. + let prov = 'prov: { + // If there is any ptr-sized provenance overlapping with this range, + // this is definitely mixing multiple pointers and we can bail. + if !no_ptrs { + break 'prov None; + } + // Scan all fragments, and ensure their indices, provenance, and bytes match. + // However, we have to ignore wildcard fragments for this (this is needed for Miri's + // native-lib mode). Therefore, we will only know the expected provenance and bytes + // once we find the first non-wildcard fragment. + let mut expected = None; + for idx in Size::ZERO..range.size { + // Ensure there is provenance here. + let Some(frag) = self.get_byte(offset + idx, cx) else { + break 'prov None; + }; + // If this is wildcard provenance, ignore this fragment. + if Some(frag.prov) == Prov::WILDCARD { + continue; + } + // For non-wildcard fragments, the index must match. + if u64::from(frag.idx) != idx.bytes() { + break 'prov None; + } + // If there are expectations registered, check them. + // If not, record this fragment as setting the expectations. + match expected { + Some(expected) => { + if (frag.prov, frag.bytes) != expected { + break 'prov None; + } + } + None => { + expected = Some((frag.prov, frag.bytes)); + } + } + } + // The final provenance is the expected one we found along the way, or wildcard if + // we didn't find any. + Some(expected.map(|(prov, _addr)| prov).or_else(|| Prov::WILDCARD).unwrap()) + }; + if prov.is_none() && !Prov::OFFSET_IS_ADDR { + // There are some bytes with provenance here but overall the provenance does not add up. + // We need `OFFSET_IS_ADDR` to fall back to no-provenance here; without that option, we + // must error. + return Err(AllocError::ReadPartialPointer(offset)); + } + Ok(prov) } /// Returns whether this allocation has provenance overlapping with the given range. @@ -182,8 +256,8 @@ pub fn range_empty(&self, range: AllocRange, cx: &impl HasDataLayout) -> bool { /// Yields all the provenances stored in this map. pub fn provenances(&self) -> impl Iterator { - let bytes = self.bytes.iter().flat_map(|b| b.values().map(|(p, _i)| p)); - self.ptrs.values().chain(bytes).copied() + let bytes = self.bytes.iter().flat_map(|b| b.values().map(|frag| frag.prov)); + self.ptrs.values().copied().chain(bytes) } pub fn insert_ptr(&mut self, offset: Size, prov: Prov, cx: &impl HasDataLayout) { @@ -191,9 +265,37 @@ pub fn insert_ptr(&mut self, offset: Size, prov: Prov, cx: &impl HasDataLayout) self.ptrs.insert(offset, prov); } + /// Returns an iterator that yields the fragments of this pointer whose absolute positions are + /// inside `pos_range`. + fn ptr_fragments( + pos_range: impl RangeBounds, + ptr_pos: Size, + prov: Prov, + data_bytes: &[u8], + ptr_size: Size, + ) -> impl Iterator)> { + if pos_range.is_empty() { + return either::Left(std::iter::empty()); + } + // Read ptr_size many bytes starting at ptr_pos. + let mut bytes = [0u8; 8]; + (&mut bytes[..ptr_size.bytes_usize()]) + .copy_from_slice(&data_bytes[ptr_pos.bytes_usize()..][..ptr_size.bytes_usize()]); + // Yield the fragments of this pointer. + either::Right( + (ptr_pos..ptr_pos + ptr_size).filter(move |pos| pos_range.contains(pos)).map( + move |pos| (pos, PointerFrag { idx: (pos - ptr_pos).bytes() as u8, bytes, prov }), + ), + ) + } + /// Removes all provenance inside the given range. - /// If there is provenance overlapping with the edges, might result in an error. - pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { + #[allow(irrefutable_let_patterns)] // these actually make the code more clear + pub fn clear(&mut self, range: AllocRange, data_bytes: &[u8], cx: &impl HasDataLayout) { + if range.size == Size::ZERO { + return; + } + let start = range.start; let end = range.end(); // Clear the bytewise part -- this is easy. @@ -201,46 +303,42 @@ pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { bytes.remove_range(start..end); } + // Find all provenance overlapping the given range. + let ptrs_range = Self::adjusted_range_ptrs(range, cx); + if self.ptrs.range_is_empty(ptrs_range.clone()) { + // No provenance in this range, we are done. This is the common case. + return; + } let pointer_size = cx.data_layout().pointer_size(); - // For the ptr-sized part, find the first (inclusive) and last (exclusive) byte of - // provenance that overlaps with the given range. - let (first, last) = { - // Find all provenance overlapping the given range. - if self.range_ptrs_is_empty(range, cx) { - // No provenance in this range, we are done. This is the common case. - return; - } - - // This redoes some of the work of `range_get_ptrs_is_empty`, but this path is much - // colder than the early return above, so it's worth it. - let provenance = self.range_ptrs_get(range, cx); - (provenance.first().unwrap().0, provenance.last().unwrap().0 + pointer_size) - }; + // This redoes some of the work of `range_is_empty`, but this path is much + // colder than the early return above, so it's worth it. + let ptrs = self.ptrs.range(ptrs_range.clone()); // We need to handle clearing the provenance from parts of a pointer. - if first < start { + if let &(first, prov) = ptrs.first().unwrap() + && first < start + { // Insert the remaining part in the bytewise provenance. - let prov = self.ptrs[&first]; let bytes = self.bytes.get_or_insert_with(Box::default); - for offset in first..start { - bytes.insert(offset, (prov, (offset - first).bytes() as u8)); + for (pos, frag) in Self::ptr_fragments(..start, first, prov, data_bytes, pointer_size) { + bytes.insert(pos, frag); } } - if last > end { - let begin_of_last = last - pointer_size; + if let &(last, prov) = ptrs.last().unwrap() + && last + pointer_size > end + { // Insert the remaining part in the bytewise provenance. - let prov = self.ptrs[&begin_of_last]; let bytes = self.bytes.get_or_insert_with(Box::default); - for offset in end..last { - bytes.insert(offset, (prov, (offset - begin_of_last).bytes() as u8)); + for (pos, frag) in Self::ptr_fragments(end.., last, prov, data_bytes, pointer_size) { + bytes.insert(pos, frag); } } // Forget all the provenance. // Since provenance do not overlap, we know that removing until `last` (exclusive) is fine, // i.e., this will not remove any other provenance just after the ones we care about. - self.ptrs.remove_range(first..last); + self.ptrs.remove_range(ptrs_range); } /// Overwrites all provenance in the given range with wildcard provenance. @@ -248,30 +346,25 @@ pub fn clear(&mut self, range: AllocRange, cx: &impl HasDataLayout) { /// bytewise on their remaining bytes. /// /// Provided for usage in Miri and panics otherwise. - pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { + pub fn write_wildcards( + &mut self, + cx: &impl HasDataLayout, + data_bytes: &[u8], + range: AllocRange, + ) { let wildcard = Prov::WILDCARD.unwrap(); + // Clear existing provenance in this range. + self.clear(range, data_bytes, cx); + + // Make everything in the range wildcards. let bytes = self.bytes.get_or_insert_with(Box::default); - - // Remove pointer provenances that overlap with the range, then readd the edge ones bytewise. - let ptr_range = Self::adjusted_range_ptrs(range, cx); - let ptrs = self.ptrs.range(ptr_range.clone()); - if let Some((offset, prov)) = ptrs.first().copied() { - for byte_ofs in offset..range.start { - bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); - } - } - if let Some((offset, prov)) = ptrs.last().copied() { - for byte_ofs in range.end()..offset + cx.data_layout().pointer_size() { - bytes.insert(byte_ofs, (prov, (byte_ofs - offset).bytes() as u8)); - } - } - self.ptrs.remove_range(ptr_range); - - // Overwrite bytewise provenance. for offset in range.start..range.end() { - // The fragment index does not matter for wildcard provenance. - bytes.insert(offset, (wildcard, 0)); + // The fragment index and bytes do not matter for wildcard provenance. + bytes.insert( + offset, + PointerFrag { prov: wildcard, idx: Default::default(), bytes: Default::default() }, + ); } } } @@ -281,15 +374,16 @@ pub fn write_wildcards(&mut self, cx: &impl HasDataLayout, range: AllocRange) { /// Offsets are relative to the beginning of the copied range. pub struct ProvenanceCopy { ptrs: Box<[(Size, Prov)]>, - bytes: Box<[(Size, (Prov, u8))]>, + bytes: Box<[(Size, PointerFrag)]>, } impl ProvenanceMap { pub fn prepare_copy( &self, range: AllocRange, + data_bytes: &[u8], cx: &impl HasDataLayout, - ) -> AllocResult> { + ) -> ProvenanceCopy { let shift_offset = move |offset| offset - range.start; let ptr_size = cx.data_layout().pointer_size(); @@ -312,14 +406,15 @@ pub fn prepare_copy( let end_overlap = self.range_ptrs_get(alloc_range(range.end(), Size::ZERO), cx).first(); // We only need to go here if there is some overlap or some bytewise provenance. if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { - let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); + let mut bytes: Vec<(Size, PointerFrag)> = Vec::new(); // First, if there is a part of a pointer at the start, add that. - if let Some(entry) = begin_overlap { - trace!("start overlapping entry: {entry:?}"); + if let Some(&(pos, prov)) = begin_overlap { // For really small copies, make sure we don't run off the end of the range. - let entry_end = cmp::min(entry.0 + ptr_size, range.end()); - for offset in range.start..entry_end { - bytes.push((shift_offset(offset), (entry.1, (offset - entry.0).bytes() as u8))); + let end = cmp::min(pos + ptr_size, range.end()); + for (pos, frag) in + Self::ptr_fragments(range.start..end, pos, prov, data_bytes, ptr_size) + { + bytes.push((shift_offset(pos), frag)); } } else { trace!("no start overlapping entry"); @@ -329,45 +424,35 @@ pub fn prepare_copy( bytes.extend( self.range_bytes_get(range) .iter() - .map(|&(offset, reloc)| (shift_offset(offset), reloc)), + .map(|(offset, frag)| (shift_offset(*offset), frag.clone())), ); // And finally possibly parts of a pointer at the end. - if let Some(entry) = end_overlap { - trace!("end overlapping entry: {entry:?}"); - // For really small copies, make sure we don't start before `range` does. - let entry_start = cmp::max(entry.0, range.start); - for offset in entry_start..range.end() { - if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { - // The last entry, if it exists, has a lower offset than us, so we - // can add it at the end and remain sorted. - bytes.push(( - shift_offset(offset), - (entry.1, (offset - entry.0).bytes() as u8), - )); - } else { - // There already is an entry for this offset in there! This can happen when the - // start and end range checks actually end up hitting the same pointer, so we - // already added this in the "pointer at the start" part above. - assert!(entry.0 <= range.start); - } + // We only have to go here if this is actually different than the begin_overlap. + if let Some(&(pos, prov)) = end_overlap + && begin_overlap.is_none_or(|(begin, _)| *begin != pos) + { + // If this was a really small copy, we'd have handled this in begin_overlap. + assert!(pos >= range.start); + for (pos, frag) in + Self::ptr_fragments(pos..range.end(), pos, prov, data_bytes, ptr_size) + { + let pos = shift_offset(pos); + // The last entry, if it exists, has a lower offset than us, so we + // can add it at the end and remain sorted. + debug_assert!(bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < pos)); + bytes.push((pos, frag)); } } else { trace!("no end overlapping entry"); } trace!("byte provenances: {bytes:?}"); - if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { - // FIXME(#146291): We need to ensure that we don't mix different pointers with - // the same provenance. - return Err(AllocError::ReadPartialPointer(range.start)); - } - // And again a buffer for the new list on the target side. bytes_box = bytes.into_boxed_slice(); } - Ok(ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box }) + ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box } } /// Applies a provenance copy. @@ -381,8 +466,8 @@ pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repe let chunk_len = copy.ptrs.len() as u64; self.ptrs.insert_presorted((0..chunk_len * repeat).map(|i| { let chunk = i / chunk_len; - let (offset, reloc) = copy.ptrs[(i % chunk_len) as usize]; - (shift_offset(chunk, offset), reloc) + let (offset, prov) = copy.ptrs[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), prov) })); } if !copy.bytes.is_empty() { @@ -390,8 +475,8 @@ pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repe self.bytes.get_or_insert_with(Box::default).insert_presorted( (0..chunk_len * repeat).map(|i| { let chunk = i / chunk_len; - let (offset, reloc) = copy.bytes[(i % chunk_len) as usize]; - (shift_offset(chunk, offset), reloc) + let (offset, frag) = ©.bytes[(i % chunk_len) as usize]; + (shift_offset(chunk, *offset), frag.clone()) }), ); } diff --git a/compiler/rustc_middle/src/mir/interpret/pointer.rs b/compiler/rustc_middle/src/mir/interpret/pointer.rs index 2aac8852b7e8..b6bcc87ff041 100644 --- a/compiler/rustc_middle/src/mir/interpret/pointer.rs +++ b/compiler/rustc_middle/src/mir/interpret/pointer.rs @@ -77,9 +77,6 @@ pub trait Provenance: Copy + PartialEq + fmt::Debug + 'static { /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`). /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.) fn get_alloc_id(self) -> Option; - - /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance. - fn join(left: Self, right: Self) -> Option; } /// The type of provenance in the compile-time interpreter. @@ -191,10 +188,6 @@ fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn get_alloc_id(self) -> Option { Some(self.alloc_id()) } - - fn join(left: Self, right: Self) -> Option { - if left == right { Some(left) } else { None } - } } // We also need this impl so that one can debug-print `Pointer` @@ -223,10 +216,6 @@ fn fmt(ptr: &Pointer, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn get_alloc_id(self) -> Option { Some(self) } - - fn join(_left: Self, _right: Self) -> Option { - unreachable!() - } } /// Represents a pointer in the Miri engine. diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 802adceb063e..a1f99de9165b 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -11,7 +11,7 @@ use either::Either; use polonius_engine::Atom; use rustc_abi::{FieldIdx, VariantIdx}; -pub use rustc_ast::Mutability; +pub use rustc_ast::{Mutability, Pinnedness}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::graph::dominators::Dominators; use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, ErrorGuaranteed, IntoDiagArg}; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index e31b110ab265..82cab107819f 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1791,7 +1791,7 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( ascii.push('╼'); i += ptr_size; } - } else if let Some((prov, idx)) = alloc.provenance().get_byte(i, &tcx) { + } else if let Some(frag) = alloc.provenance().get_byte(i, &tcx) { // Memory with provenance must be defined assert!( alloc.init_mask().is_range_initialized(alloc_range(i, Size::from_bytes(1))).is_ok() @@ -1801,7 +1801,8 @@ pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>( // Format is similar to "oversized" above. let j = i.bytes_usize(); let c = alloc.inspect_with_uninit_and_ptr_outside_interpreter(j..j + 1)[0]; - write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼")?; + // FIXME: Find a way to print `frag.offset` that does not look terrible... + write!(w, "╾{c:02x}{prov:#?} (ptr fragment {idx})╼", prov = frag.prov, idx = frag.idx)?; i += Size::from_bytes(1); } else if alloc .init_mask() diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index e72ed78d07e6..4f38ef017f49 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -256,25 +256,15 @@ pub struct Expr<'tcx> { /// The type of this expression pub ty: Ty<'tcx>, - /// The lifetime of this expression if it should be spilled into a - /// temporary - pub temp_lifetime: TempLifetime, + /// The id of the HIR expression whose [temporary scope] should be used for this expression. + /// + /// [temporary scope]: https://doc.rust-lang.org/reference/destructors.html#temporary-scopes + pub temp_scope_id: hir::ItemLocalId, /// span of the expression in the source pub span: Span, } -/// Temporary lifetime information for THIR expressions -#[derive(Clone, Copy, Debug, HashStable)] -pub struct TempLifetime { - /// Lifetime for temporaries as expected. - /// This should be `None` in a constant context. - pub temp_lifetime: Option, - /// If `Some(lt)`, indicates that the lifetime of this temporary will change to `lt` in a future edition. - /// If `None`, then no changes are expected, or lints are disabled. - pub backwards_incompatible: Option, -} - #[derive(Clone, Debug, HashStable)] pub enum ExprKind<'tcx> { /// `Scope`s are used to explicitly mark destruction scopes, @@ -1127,7 +1117,7 @@ mod size_asserts { use super::*; // tidy-alphabetical-start static_assert_size!(Block, 48); - static_assert_size!(Expr<'_>, 72); + static_assert_size!(Expr<'_>, 64); static_assert_size!(ExprKind<'_>, 40); static_assert_size!(Pat<'_>, 64); static_assert_size!(PatKind<'_>, 48); diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index dcfa6c4db327..2ed506767397 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -45,7 +45,7 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( expr: &'thir Expr<'tcx>, ) { use ExprKind::*; - let Expr { kind, ty: _, temp_lifetime: _, span: _ } = expr; + let Expr { kind, ty: _, temp_scope_id: _, span: _ } = expr; match *kind { Scope { value, region_scope: _, lint_level: _ } => { visitor.visit_expr(&visitor.thir()[value]) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ae83e98f37d2..d7c161a6a609 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -856,6 +856,7 @@ fn $to_solver(lang_item: LangItem) -> Option<$solver_ty> { PointeeTrait, Sized, TransmuteTrait, + TrivialClone, Tuple, Unpin, Unsize, diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index faa8420c20d0..5eb8f1713a13 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -97,7 +97,6 @@ BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid, }; -pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::{ AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst, @@ -156,7 +155,6 @@ mod opaque_types; mod predicate; mod region; -mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] mod sty; diff --git a/compiler/rustc_middle/src/ty/rvalue_scopes.rs b/compiler/rustc_middle/src/ty/rvalue_scopes.rs deleted file mode 100644 index df4e29d45754..000000000000 --- a/compiler/rustc_middle/src/ty/rvalue_scopes.rs +++ /dev/null @@ -1,48 +0,0 @@ -use rustc_hir as hir; -use rustc_hir::ItemLocalMap; -use rustc_macros::{HashStable, TyDecodable, TyEncodable}; -use tracing::debug; - -use crate::middle::region::{Scope, ScopeData, ScopeTree}; - -/// `RvalueScopes` is a mapping from sub-expressions to _extended_ lifetime as determined by -/// rules laid out in `rustc_hir_analysis::check::rvalue_scopes`. -#[derive(TyEncodable, TyDecodable, Clone, Debug, Default, Eq, PartialEq, HashStable)] -pub struct RvalueScopes { - map: ItemLocalMap>, -} - -impl RvalueScopes { - pub fn new() -> Self { - Self { map: <_>::default() } - } - - /// Returns the scope when the temp created by `expr_id` will be cleaned up. - /// It also emits a lint on potential backwards incompatible change to the temporary scope - /// which is *for now* always shortening. - pub fn temporary_scope( - &self, - region_scope_tree: &ScopeTree, - expr_id: hir::ItemLocalId, - ) -> (Option, Option) { - // Check for a designated rvalue scope. - if let Some(&s) = self.map.get(&expr_id) { - debug!("temporary_scope({expr_id:?}) = {s:?} [custom]"); - return (s, None); - } - - // Otherwise, locate the innermost terminating scope. - let (scope, backward_incompatible) = region_scope_tree - .default_temporary_scope(Scope { local_id: expr_id, data: ScopeData::Node }); - (Some(scope), backward_incompatible) - } - - /// Make an association between a sub-expression and an extended lifetime - pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option) { - debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})"); - if let Some(lifetime) = lifetime { - assert!(var != lifetime.local_id); - } - self.map.insert(var, lifetime); - } -} diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 953c806658ae..d8f16ef5561d 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1366,6 +1366,19 @@ pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> { None } + pub fn maybe_pinned_ref(self) -> Option<(Ty<'tcx>, ty::Pinnedness, ty::Mutability)> { + match *self.kind() { + Adt(def, args) + if def.is_pin() + && let ty::Ref(_, ty, mutbl) = *args.type_at(0).kind() => + { + Some((ty, ty::Pinnedness::Pinned, mutbl)) + } + ty::Ref(_, ty, mutbl) => Some((ty, ty::Pinnedness::Not, mutbl)), + _ => None, + } + } + /// Panics if called on any type other than `Box`. pub fn expect_boxed_ty(self) -> Ty<'tcx> { self.boxed_ty() diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 5300be58f04e..55b738a0a34f 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -18,7 +18,6 @@ use rustc_session::Session; use rustc_span::Span; -use super::RvalueScopes; use crate::hir::place::Place as HirPlace; use crate::infer::canonical::Canonical; use crate::mir::FakeReadCause; @@ -198,11 +197,6 @@ pub struct TypeckResults<'tcx> { /// issue by fake reading `t`. pub closure_fake_reads: LocalDefIdMap, FakeReadCause, HirId)>>, - /// Tracks the rvalue scoping rules which defines finer scoping for rvalue expressions - /// by applying extended parameter rules. - /// Details may be found in `rustc_hir_analysis::check::rvalue_scopes`. - pub rvalue_scopes: RvalueScopes, - /// Stores the predicates that apply on coroutine witness types. /// formatting modified file tests/ui/coroutine/retain-resume-ref.rs pub coroutine_stalled_predicates: FxIndexSet<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>, @@ -254,7 +248,6 @@ pub fn new(hir_owner: OwnerId) -> TypeckResults<'tcx> { hidden_types: Default::default(), closure_min_captures: Default::default(), closure_fake_reads: Default::default(), - rvalue_scopes: Default::default(), coroutine_stalled_predicates: Default::default(), potentially_region_dependent_goals: Default::default(), closure_size_eval: Default::default(), diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a4422abc6883..782ea3906ef1 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -607,9 +607,9 @@ pub fn uses_unique_generic_params( /// have the same `DefKind`. /// /// Note that closures have a `DefId`, but the closure *expression* also has a - // `HirId` that is located within the context where the closure appears (and, sadly, - // a corresponding `NodeId`, since those are not yet phased out). The parent of - // the closure's `DefId` will also be the context where it appears. + /// `HirId` that is located within the context where the closure appears (and, sadly, + /// a corresponding `NodeId`, since those are not yet phased out). The parent of + /// the closure's `DefId` will also be the context where it appears. pub fn is_closure_like(self, def_id: DefId) -> bool { matches!(self.def_kind(def_id), DefKind::Closure) } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index 0e0c7a7fa4f0..eb0546cd0e37 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -21,7 +21,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(crate) fn as_constant(&mut self, expr: &Expr<'tcx>) -> ConstOperand<'tcx> { let this = self; let tcx = this.tcx; - let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; + let Expr { ty, temp_scope_id: _, span, ref kind } = *expr; match kind { ExprKind::Scope { region_scope: _, lint_level: _, value } => { this.as_constant(&this.thir[*value]) @@ -46,7 +46,7 @@ pub(crate) fn as_constant_inner<'tcx>( push_cuta: impl FnMut(&Box>) -> Option, tcx: TyCtxt<'tcx>, ) -> ConstOperand<'tcx> { - let Expr { ty, temp_lifetime: _, span, ref kind } = *expr; + let Expr { ty, temp_scope_id: _, span, ref kind } = *expr; match *kind { ExprKind::Literal { lit, neg } => { let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg }); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs index 6a4222239904..39ddf0edf5d2 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_operand.rs @@ -1,5 +1,6 @@ //! See docs in build/expr/mod.rs +use rustc_middle::middle::region::TempLifetime; use rustc_middle::mir::*; use rustc_middle::thir::*; use tracing::{debug, instrument}; diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 1b143f37a585..6c5356228338 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -503,10 +503,9 @@ fn expr_as_place( block.and(place_builder) } ExprKind::ValueTypeAscription { source, ref user_ty, user_ty_span } => { - let source_expr = &this.thir[source]; - let temp = unpack!( - block = this.as_temp(block, source_expr.temp_lifetime, source, mutability) - ); + let temp_lifetime = + this.region_scope_tree.temporary_scope(this.thir[source].temp_scope_id); + let temp = unpack!(block = this.as_temp(block, temp_lifetime, source, mutability)); if let Some(user_ty) = user_ty { let ty_source_info = this.source_info(user_ty_span); let annotation_index = @@ -539,10 +538,9 @@ fn expr_as_place( block.and(place_builder.project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) } ExprKind::ValueUnwrapUnsafeBinder { source } => { - let source_expr = &this.thir[source]; - let temp = unpack!( - block = this.as_temp(block, source_expr.temp_lifetime, source, mutability) - ); + let temp_lifetime = + this.region_scope_tree.temporary_scope(this.thir[source].temp_scope_id); + let temp = unpack!(block = this.as_temp(block, temp_lifetime, source, mutability)); block.and(PlaceBuilder::from(temp).project(PlaceElem::UnwrapUnsafeBinder(expr.ty))) } @@ -590,8 +588,8 @@ fn expr_as_place( | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); - let temp = - unpack!(block = this.as_temp(block, expr.temp_lifetime, expr_id, mutability)); + let temp_lifetime = this.region_scope_tree.temporary_scope(expr.temp_scope_id); + let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) } } @@ -637,7 +635,10 @@ fn lower_index_expression( // Making this a *fresh* temporary means we do not have to worry about // the index changing later: Nothing will ever change this temporary. // The "retagging" transformation (for Stacked Borrows) relies on this. - let index_lifetime = self.thir[index].temp_lifetime; + // Using the enclosing temporary scope for the index ensures it will live past where this + // place is used. This lifetime may be larger than strictly necessary but it means we don't + // need to pass a scope for operands to `as_place`. + let index_lifetime = self.region_scope_tree.temporary_scope(self.thir[index].temp_scope_id); let idx = unpack!(block = self.as_temp(block, index_lifetime, index, Mutability::Not)); block = self.bounds_check(block, &base_place, idx, expr_span, source_info); diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index 552f8c66784e..91814bca76f1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; use rustc_middle::bug; -use rustc_middle::middle::region; +use rustc_middle::middle::region::{self, TempLifetime}; use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::*; use rustc_middle::thir::*; @@ -779,8 +779,8 @@ fn limit_capture_mutability( Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place), ); - // See the comment in `expr_as_temp` and on the `rvalue_scopes` field for why - // this can be `None`. + // This can be `None` if the expression's temporary scope was extended so that it can be + // borrowed by a `const` or `static`. In that case, it's never dropped. if let Some(temp_lifetime) = temp_lifetime { this.schedule_drop_storage_and_value(upvar_span, temp_lifetime, temp); } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs index b0ce3527d8b3..754ab0c0a16e 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_temp.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_temp.rs @@ -2,7 +2,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::HirId; -use rustc_middle::middle::region::{Scope, ScopeData}; +use rustc_middle::middle::region::{Scope, ScopeData, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::*; use tracing::{debug, instrument}; diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 675beceea14a..db0d3a449712 100644 --- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -1,4 +1,4 @@ -use rustc_middle::middle::region; +use rustc_middle::middle::region::{self, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::span_bug; use rustc_middle::thir::*; diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index af565ef63699..a0d54354a9c6 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -284,14 +284,12 @@ pub(super) fn for_pattern( } PatKind::Deref { ref subpattern } - | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => { - if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) { - // Only deref patterns on boxes can be lowered using a built-in deref. - debug_assert!(pattern.ty.is_box()); - } - + | PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } + if let Some(ref_ty) = pattern.ty.pinned_ty() + && ref_ty.is_ref() => + { MatchPairTree::for_pattern( - place_builder.deref(), + place_builder.field(FieldIdx::ZERO, ref_ty).deref(), subpattern, cx, &mut subpairs, @@ -300,12 +298,14 @@ pub(super) fn for_pattern( None } - PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => { - let Some(ref_ty) = pattern.ty.pinned_ty() else { - rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty); - }; + PatKind::DerefPattern { borrow: ByRef::Yes(Pinnedness::Pinned, _), .. } => { + rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty) + } + + PatKind::Deref { ref subpattern } + | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => { MatchPairTree::for_pattern( - place_builder.field(FieldIdx::ZERO, ref_ty).deref(), + place_builder.deref(), subpattern, cx, &mut subpairs, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 96c58dc14e8c..03a4256add84 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness}; -use rustc_middle::middle::region; +use rustc_middle::middle::region::{self, TempLifetime}; use rustc_middle::mir::*; use rustc_middle::thir::{self, *}; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind}; diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 9115c17f3752..b8547e288027 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -135,6 +135,10 @@ fn check_tail_call(&mut self, call: &Expr<'_>, expr: &Expr<'_>) { self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); } + if !callee_sig.abi.supports_guaranteed_tail_call() { + self.report_unsupported_abi(expr.span, callee_sig.abi); + } + // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. // e.g. // ``` @@ -358,6 +362,16 @@ fn report_abi_mismatch(&mut self, sp: Span, caller_abi: ExternAbi, callee_abi: E self.found_errors = Err(err); } + fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "ABI does not support guaranteed tail calls") + .with_note(format!("`become` is not supported for `extern {callee_abi}` functions")) + .emit(); + self.found_errors = Err(err); + } + fn report_signature_mismatch( &mut self, sp: Span, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8ce0b73e47e3..59efd805b30e 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -71,7 +71,7 @@ pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> E // Finally, wrap this up in the expr's scope. expr = Expr { - temp_lifetime: expr.temp_lifetime, + temp_scope_id: expr_scope.local_id, ty: expr.ty, span: hir_expr.span, kind: ExprKind::Scope { @@ -93,7 +93,7 @@ fn apply_adjustment( adjustment: &Adjustment<'tcx>, mut span: Span, ) -> Expr<'tcx> { - let Expr { temp_lifetime, .. } = expr; + let Expr { temp_scope_id, .. } = expr; // Adjust the span from the block, to the last expression of the // block. This is a better span when returning a mutable reference @@ -152,7 +152,7 @@ fn apply_adjustment( Ty::new_fn_def(self.tcx, call_def_id, self.tcx.mk_args(&[expr.ty.into()])); expr = Expr { - temp_lifetime, + temp_scope_id, ty: Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, expr.ty, deref.mutbl), span, kind: ExprKind::Borrow { @@ -199,13 +199,13 @@ fn apply_adjustment( variant_index: FIRST_VARIANT, name: FieldIdx::ZERO, }; - let arg = Expr { temp_lifetime, ty: pin_ty, span, kind: pointer_target }; + let arg = Expr { temp_scope_id, 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, + temp_scope_id, ty: ptr_target_ty, span, kind: expr, @@ -219,7 +219,7 @@ fn apply_adjustment( let new_pin_target = Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, ptr_target_ty, mutbl); let expr = self.thir.exprs.push(Expr { - temp_lifetime, + temp_scope_id, ty: new_pin_target, span, kind: ExprKind::Borrow { borrow_kind, arg }, @@ -242,7 +242,7 @@ fn apply_adjustment( } }; - Expr { temp_lifetime, ty: adjustment.target, span, kind } + Expr { temp_scope_id, ty: adjustment.target, span, kind } } /// Lowers a cast expression. @@ -251,7 +251,7 @@ fn apply_adjustment( fn mirror_expr_cast( &mut self, source: &'tcx hir::Expr<'tcx>, - temp_lifetime: TempLifetime, + temp_scope_id: hir::ItemLocalId, span: Span, ) -> ExprKind<'tcx> { let tcx = self.tcx; @@ -309,7 +309,7 @@ fn mirror_expr_cast( ); } let kind = ExprKind::NonHirLiteral { lit, user_ty: None }; - let offset = self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind }); + let offset = self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind }); let source = match discr_did { // in case we are offsetting from a computed discriminant @@ -317,9 +317,9 @@ fn mirror_expr_cast( Some(did) => { let kind = ExprKind::NamedConst { def_id: did, args, user_ty: None }; let lhs = - self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind }); + self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind }); let bin = ExprKind::Binary { op: BinOp::Add, lhs, rhs: offset }; - self.thir.exprs.push(Expr { temp_lifetime, ty: discr_ty, span, kind: bin }) + self.thir.exprs.push(Expr { temp_scope_id, ty: discr_ty, span, kind: bin }) } None => offset, }; @@ -336,8 +336,6 @@ fn mirror_expr_cast( fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> { let tcx = self.tcx; let expr_ty = self.typeck_results.expr_ty(expr); - let (temp_lifetime, backwards_incompatible) = - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = match expr.kind { // Here comes the interesting stuff: @@ -372,7 +370,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> let arg_tys = args.iter().map(|e| self.typeck_results.expr_ty_adjusted(e)); let tupled_args = Expr { ty: Ty::new_tup_from_iter(tcx, arg_tys), - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, span: expr.span, kind: ExprKind::Tuple { fields: self.mirror_exprs(args) }, }; @@ -398,7 +396,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> } let value = &args[0]; return Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, ty: expr_ty, span: expr.span, kind: ExprKind::Box { value: self.mirror_expr(value) }, @@ -502,18 +500,15 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> expr: Some(arg), safety_mode: BlockSafety::Safe, }); - let (temp_lifetime, backwards_incompatible) = self - .rvalue_scopes - .temporary_scope(self.region_scope_tree, arg_expr.hir_id.local_id); arg = self.thir.exprs.push(Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: arg_expr.hir_id.local_id, ty: arg_ty, span: arg_expr.span, kind: ExprKind::Block { block }, }); } let expr = self.thir.exprs.push(Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, ty, span: expr.span, kind: ExprKind::Borrow { borrow_kind: mutbl.to_borrow_kind(), arg }, @@ -996,13 +991,10 @@ fn local( } } else { let block_ty = self.typeck_results.node_type(body.hir_id); - let (temp_lifetime, backwards_incompatible) = self - .rvalue_scopes - .temporary_scope(self.region_scope_tree, body.hir_id.local_id); let block = self.mirror_block(body); let body = self.thir.exprs.push(Expr { ty: block_ty, - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: body.hir_id.local_id, span: self.thir[block].span, kind: ExprKind::Block { block }, }); @@ -1024,17 +1016,13 @@ fn local( expr, cast_ty.hir_id, user_ty, ); - let cast = self.mirror_expr_cast( - source, - TempLifetime { temp_lifetime, backwards_incompatible }, - expr.span, - ); + let cast = self.mirror_expr_cast(source, expr.hir_id.local_id, expr.span); if let Some(user_ty) = user_ty { // NOTE: Creating a new Expr and wrapping a Cast inside of it may be // inefficient, revisit this when performance becomes an issue. let cast_expr = self.thir.exprs.push(Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, ty: expr_ty, span: expr.span, kind: cast, @@ -1093,12 +1081,7 @@ fn local( hir::ExprKind::Err(_) => unreachable!("cannot lower a `hir::ExprKind::Err` to THIR"), }; - Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, - ty: expr_ty, - span: expr.span, - kind, - } + Expr { temp_scope_id: expr.hir_id.local_id, ty: expr_ty, span: expr.span, kind } } fn user_args_applied_to_res( @@ -1142,8 +1125,6 @@ fn method_callee( span: Span, overloaded_callee: Option>, ) -> Expr<'tcx> { - let (temp_lifetime, backwards_incompatible) = - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let (ty, user_ty) = match overloaded_callee { Some(fn_def) => (fn_def, None), None => { @@ -1160,7 +1141,7 @@ fn method_callee( } }; Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, ty, span, kind: ExprKind::ZstLiteral { user_ty }, @@ -1237,9 +1218,6 @@ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKi Res::Def(DefKind::Static { .. }, id) => { // this is &raw for extern static or static mut, and & for other statics let ty = self.tcx.static_ptr_ty(id, self.typing_env); - let (temp_lifetime, backwards_incompatible) = self - .rvalue_scopes - .temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let kind = if self.tcx.is_thread_local_static(id) { ExprKind::ThreadLocalRef(id) } else { @@ -1249,7 +1227,7 @@ fn convert_path_expr(&mut self, expr: &'tcx hir::Expr<'tcx>, res: Res) -> ExprKi ExprKind::Deref { arg: self.thir.exprs.push(Expr { ty, - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, span: expr.span, kind, }), @@ -1320,13 +1298,11 @@ fn overloaded_place( // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type - let (temp_lifetime, backwards_incompatible) = - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id); let fun = self.method_callee(expr, span, overloaded_callee); let fun = self.thir.exprs.push(fun); let fun_ty = self.thir[fun].ty; let ref_expr = self.thir.exprs.push(Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id: expr.hir_id.local_id, ty: ref_ty, span, kind: ExprKind::Call { ty: fun_ty, fun, args, from_hir_call: false, fn_span: span }, @@ -1341,9 +1317,7 @@ fn convert_captured_hir_place( closure_expr: &'tcx hir::Expr<'tcx>, place: HirPlace<'tcx>, ) -> Expr<'tcx> { - let (temp_lifetime, backwards_incompatible) = self - .rvalue_scopes - .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); + let temp_scope_id = closure_expr.hir_id.local_id; let var_ty = place.base_ty; // The result of capture analysis in `rustc_hir_typeck/src/upvar.rs` represents a captured path @@ -1357,7 +1331,7 @@ fn convert_captured_hir_place( }; let mut captured_place_expr = Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id, ty: var_ty, span: closure_expr.span, kind: self.convert_var(var_hir_id), @@ -1385,12 +1359,8 @@ fn convert_captured_hir_place( } }; - captured_place_expr = Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, - ty: proj.ty, - span: closure_expr.span, - kind, - }; + captured_place_expr = + Expr { temp_scope_id, ty: proj.ty, span: closure_expr.span, kind }; } captured_place_expr @@ -1405,9 +1375,7 @@ fn capture_upvar( let upvar_capture = captured_place.info.capture_kind; let captured_place_expr = self.convert_captured_hir_place(closure_expr, captured_place.place.clone()); - let (temp_lifetime, backwards_incompatible) = self - .rvalue_scopes - .temporary_scope(self.region_scope_tree, closure_expr.hir_id.local_id); + let temp_scope_id = closure_expr.hir_id.local_id; match upvar_capture { ty::UpvarCapture::ByValue => captured_place_expr, @@ -1416,7 +1384,7 @@ fn capture_upvar( let expr_id = self.thir.exprs.push(captured_place_expr); Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id, ty: upvar_ty, span: closure_expr.span, kind: ExprKind::ByUse { expr: expr_id, span }, @@ -1433,7 +1401,7 @@ fn capture_upvar( } }; Expr { - temp_lifetime: TempLifetime { temp_lifetime, backwards_incompatible }, + temp_scope_id, ty: upvar_ty, span: closure_expr.span, kind: ExprKind::Borrow { diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 9657c4dc839d..d26dfac0c2ab 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -10,9 +10,8 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, HirId, find_attr}; use rustc_middle::bug; -use rustc_middle::middle::region; use rustc_middle::thir::*; -use rustc_middle::ty::{self, RvalueScopes, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt}; use tracing::instrument; use crate::thir::pattern::pat_from_hir; @@ -60,9 +59,7 @@ struct ThirBuildCx<'tcx> { typing_env: ty::TypingEnv<'tcx>, - region_scope_tree: &'tcx region::ScopeTree, typeck_results: &'tcx ty::TypeckResults<'tcx>, - rvalue_scopes: &'tcx RvalueScopes, /// False to indicate that adjustments should not be applied. Only used for `custom_mir` apply_adjustments: bool, @@ -107,9 +104,7 @@ fn new(tcx: TyCtxt<'tcx>, def: LocalDefId) -> Self { // FIXME(#132279): We're in a body, we should use a typing // mode which reveals the opaque types defined by that body. typing_env: ty::TypingEnv::non_body_analysis(tcx, def), - region_scope_tree: tcx.region_scope_tree(def), typeck_results, - rvalue_scopes: &typeck_results.rvalue_scopes, body_owner: def.to_def_id(), apply_adjustments: !find_attr!(tcx.hir_attrs(hir_id), AttributeKind::CustomMir(..) => ()).is_some(), diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 404f410e2219..8531c5f39f84 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -97,7 +97,7 @@ fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { // adjustments in *reverse order* (last-in-first-out, so that the last `Deref` inserted // gets the least-dereferenced type). let unadjusted_pat = match pat.kind { - hir::PatKind::Ref(inner, _) + hir::PatKind::Ref(inner, _, _) if self.typeck_results.skipped_ref_pats().contains(pat.hir_id) => { self.lower_pattern(inner) @@ -319,7 +319,7 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box { + hir::PatKind::Ref(subpattern, _, _) => { // Track the default binding mode for the Rust 2024 migration suggestion. let opt_old_mode_span = self.rust_2024_migration.as_mut().and_then(|s| s.visit_explicit_deref()); @@ -370,10 +370,6 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box { - debug_assert!( - self.tcx.features().pin_ergonomics(), - "`pin_ergonomics` must be enabled to have a by-pin-ref binding" - ); ty = rty; } hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => { diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index e0a5c18a2eed..655cd5f5dc95 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -183,10 +183,10 @@ fn print_stmt(&mut self, stmt_id: StmtId, depth_lvl: usize) { } fn print_expr(&mut self, expr: ExprId, depth_lvl: usize) { - let Expr { ty, temp_lifetime, span, kind } = &self.thir[expr]; + let Expr { ty, temp_scope_id, span, kind } = &self.thir[expr]; print_indented!(self, "Expr {", depth_lvl); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1); - print_indented!(self, format!("temp_lifetime: {:?}", temp_lifetime), depth_lvl + 1); + print_indented!(self, format!("temp_scope_id: {:?}", temp_scope_id), depth_lvl + 1); print_indented!(self, format!("span: {:?}", span), depth_lvl + 1); print_indented!(self, "kind: ", depth_lvl + 1); self.print_expr_kind(kind, depth_lvl + 2); diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 851bbaeed48e..827df27da52c 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -1,7 +1,12 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; +use rustc_middle::mir; use rustc_middle::mir::coverage::BasicCoverageBlock; use rustc_span::{ExpnId, ExpnKind, Span}; +use crate::coverage::from_mir; +use crate::coverage::graph::CoverageGraph; +use crate::coverage::hir_info::ExtractedHirInfo; + #[derive(Clone, Copy, Debug)] pub(crate) struct SpanWithBcb { pub(crate) span: Span, @@ -70,6 +75,10 @@ pub(crate) struct ExpnNode { pub(crate) spans: Vec, /// Expansions whose call-site is in this expansion. pub(crate) child_expn_ids: FxIndexSet, + + /// Hole spans belonging to this expansion, to be carved out from the + /// code spans during span refinement. + pub(crate) hole_spans: Vec, } impl ExpnNode { @@ -88,17 +97,27 @@ fn new(expn_id: ExpnId) -> Self { spans: vec![], child_expn_ids: FxIndexSet::default(), + + hole_spans: vec![], } } } -/// Given a collection of span/BCB pairs from potentially-different syntax contexts, +/// Extracts raw span/BCB pairs from potentially-different syntax contexts, and /// arranges them into an "expansion tree" based on their expansion call-sites. -pub(crate) fn build_expn_tree(spans: impl IntoIterator) -> ExpnTree { +pub(crate) fn build_expn_tree( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + graph: &CoverageGraph, +) -> ExpnTree { + let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); + let mut nodes = FxIndexMap::default(); let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id); - for span_with_bcb in spans { + for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans { + let span_with_bcb = SpanWithBcb { span: raw_span, bcb }; + // Create a node for this span's enclosing expansion, and add the span to it. let expn_id = span_with_bcb.span.ctxt().outer_expn(); let node = nodes.entry(expn_id).or_insert_with_key(new_node); @@ -123,5 +142,13 @@ pub(crate) fn build_expn_tree(spans: impl IntoIterator) -> E } } + // Associate each hole span (extracted from HIR) with its corresponding + // expansion tree node. + for &hole_span in &hir_info.hole_spans { + let expn_id = hole_span.ctxt().outer_expn(); + let Some(node) = nodes.get_mut(&expn_id) else { continue }; + node.hole_spans.push(hole_span); + } + ExpnTree { nodes } } diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/from_mir.rs similarity index 94% rename from compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs rename to compiler/rustc_mir_transform/src/coverage/from_mir.rs index c096f1e2632c..e1623f590a8e 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/from_mir.rs @@ -142,19 +142,3 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), } } - -#[derive(Debug)] -pub(crate) struct Hole { - pub(crate) span: Span, -} - -impl Hole { - pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { - if !self.span.overlaps_or_adjacent(other.span) { - return false; - } - - self.span = self.span.to(other.span); - true - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 8dbe564f5174..5347e1150e5d 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; +use crate::coverage::expansion; use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::extract_refined_covspans; @@ -23,10 +24,12 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> ExtractedMappings { + let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph); + let mut mappings = vec![]; // Extract ordinary code mappings from MIR statement/terminator spans. - extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings); + extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings); extract_branch_mappings(mir_body, hir_info, graph, &mut mappings); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 08c7d346009c..cc9d7c800a96 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -9,6 +9,7 @@ mod counters; mod expansion; +mod from_mir; mod graph; mod hir_info; mod mappings; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 325935ee8468..2a06b409e8c2 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,22 +1,18 @@ -use rustc_middle::mir; use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB}; use rustc_middle::ty::TyCtxt; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span}; use tracing::instrument; -use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb}; +use crate::coverage::expansion::{ExpnTree, SpanWithBcb}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::hir_info::ExtractedHirInfo; -use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir}; - -mod from_mir; pub(super) fn extract_refined_covspans<'tcx>( tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, + expn_tree: &ExpnTree, mappings: &mut Vec, ) { if hir_info.is_async_fn { @@ -32,22 +28,32 @@ pub(super) fn extract_refined_covspans<'tcx>( let &ExtractedHirInfo { body_span, .. } = hir_info; - let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); - // Use the raw spans to build a tree of expansions for this function. - let expn_tree = expansion::build_expn_tree( - raw_spans - .into_iter() - .map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }), - ); + // If there somehow isn't an expansion tree node corresponding to the + // body span, return now and don't create any mappings. + let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) else { return }; let mut covspans = vec![]; - let mut push_covspan = |covspan: Covspan| { + + for &SpanWithBcb { span, bcb } in &node.spans { + covspans.push(Covspan { span, bcb }); + } + + // For each expansion with its call-site in the body span, try to + // distill a corresponding covspan. + for &child_expn_id in &node.child_expn_ids { + if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) + { + covspans.push(covspan); + } + } + + covspans.retain(|covspan: &Covspan| { let covspan_span = covspan.span; // Discard any spans not contained within the function body span. // Also discard any spans that fill the entire body, because they tend // to represent compiler-inserted code, e.g. implicitly returning `()`. if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) { - return; + return false; } // Each pushed covspan should have the same context as the body span. @@ -57,27 +63,11 @@ pub(super) fn extract_refined_covspans<'tcx>( false, "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" ); - return; + return false; } - covspans.push(covspan); - }; - - if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) { - for &SpanWithBcb { span, bcb } in &node.spans { - push_covspan(Covspan { span, bcb }); - } - - // For each expansion with its call-site in the body span, try to - // distill a corresponding covspan. - for &child_expn_id in &node.child_expn_ids { - if let Some(covspan) = - single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) - { - push_covspan(covspan); - } - } - } + true + }); // Only proceed if we found at least one usable span. if covspans.is_empty() { @@ -107,14 +97,8 @@ pub(super) fn extract_refined_covspans<'tcx>( covspans.dedup_by(|b, a| a.span.source_equal(b.span)); // Sort the holes, and merge overlapping/adjacent holes. - let mut holes = hir_info - .hole_spans - .iter() - .copied() - // Discard any holes that aren't directly visible within the body span. - .filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span)) - .map(|span| Hole { span }) - .collect::>(); + let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::>(); + holes.sort_by(|a, b| compare_spans(a.span, b.span)); holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); @@ -295,3 +279,19 @@ fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option { }) .ok()? } + +#[derive(Debug)] +struct Hole { + span: Span, +} + +impl Hole { + fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { + if !self.span.overlaps_or_adjacent(other.span) { + return false; + } + + self.span = self.span.to(other.span); + true + } +} diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 221593d6eadb..57c36a0030aa 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -547,9 +547,11 @@ fn assemble_builtin_impl_candidates>( Some(SolverTraitLangItem::PointeeSized) => { unreachable!("`PointeeSized` is removed during lowering"); } - Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => { - G::consider_builtin_copy_clone_candidate(self, goal) - } + Some( + SolverTraitLangItem::Copy + | SolverTraitLangItem::Clone + | SolverTraitLangItem::TrivialClone, + ) => G::consider_builtin_copy_clone_candidate(self, goal), Some(SolverTraitLangItem::Fn) => { G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::Fn) } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 110cc30e740b..8da4289bf185 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -235,8 +235,9 @@ fn consider_impl_candidate( // See . ecx.try_evaluate_added_goals()?; - // Add GAT where clauses from the trait's definition. - // FIXME: We don't need these, since these are the type's own WF obligations. + // Add GAT where clauses from the trait's definition. This is necessary + // for soundness until we properly handle implied bounds on binders, + // see tests/ui/generic-associated-types/must-prove-where-clauses-on-norm.rs. ecx.add_goals( GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 1862900077f0..c74333f5655a 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -1004,6 +1004,9 @@ parse_use_if_else = use an `if-else` expression instead parse_use_let_not_auto = write `let` instead of `auto` to introduce a new variable parse_use_let_not_var = write `let` instead of `var` to introduce a new variable +parse_varargs_without_pattern = missing pattern for `...` argument + .suggestion = name the argument, or use `_` to continue ignoring it + parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by an item .label = the visibility .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a35c5c304d95..bde179c9438f 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3645,3 +3645,10 @@ fn add_to_diag(self, diag: &mut Diag<'_, G>) { } } } + +#[derive(LintDiagnostic)] +#[diag(parse_varargs_without_pattern)] +pub(crate) struct VarargsWithoutPattern { + #[suggestion(code = "_: ...", applicability = "machine-applicable")] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 5725f4c36679..63109c7ba5cb 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -201,7 +201,7 @@ fn annotate_following_item_if_applicable( AttrWrapper::empty(), true, false, - FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }, + FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, ForceCollect::No, ) { Ok(Some(item)) => { diff --git a/compiler/rustc_parse/src/parser/cfg_select.rs b/compiler/rustc_parse/src/parser/cfg_select.rs index 08a71db4de85..9692d2c70893 100644 --- a/compiler/rustc_parse/src/parser/cfg_select.rs +++ b/compiler/rustc_parse/src/parser/cfg_select.rs @@ -1,83 +1,34 @@ -use rustc_ast::token::Token; +use rustc_ast::token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::classify; -use rustc_ast::{MetaItemInner, token}; use rustc_errors::PResult; -use rustc_span::Span; use crate::exp; use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos}; -pub enum CfgSelectPredicate { - Cfg(MetaItemInner), - Wildcard(Token), -} - -#[derive(Default)] -pub struct CfgSelectBranches { - /// All the conditional branches. - pub reachable: Vec<(MetaItemInner, TokenStream, Span)>, - /// The first wildcard `_ => { ... }` branch. - pub wildcard: Option<(Token, TokenStream, Span)>, - /// All branches after the first wildcard, including further wildcards. - /// These branches are kept for formatting. - pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, -} - -/// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an -/// expression followed by a comma (and strip the comma). -fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> { - if p.token == token::OpenBrace { - // Strip the outer '{' and '}'. - match p.parse_token_tree() { - TokenTree::Token(..) => unreachable!("because of the expect above"), - TokenTree::Delimited(.., tts) => return Ok(tts), - } - } - let expr = p.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { - p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty()) - .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No)) - })?; - if !classify::expr_is_complete(&expr) && p.token != token::CloseBrace && p.token != token::Eof { - p.expect(exp!(Comma))?; - } else { - let _ = p.eat(exp!(Comma)); - } - Ok(TokenStream::from_ast(&expr)) -} - -pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> { - let mut branches = CfgSelectBranches::default(); - - while p.token != token::Eof { - if p.eat_keyword(exp!(Underscore)) { - let underscore = p.prev_token; - p.expect(exp!(FatArrow))?; - - let tts = parse_token_tree(p)?; - let span = underscore.span.to(p.token.span); - - match branches.wildcard { - None => branches.wildcard = Some((underscore, tts, span)), - Some(_) => { - branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span)) - } +impl<'a> Parser<'a> { + /// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an + /// expression followed by a comma (and strip the comma). + pub fn parse_delimited_token_tree(&mut self) -> PResult<'a, TokenStream> { + if self.token == token::OpenBrace { + // Strip the outer '{' and '}'. + match self.parse_token_tree() { + TokenTree::Token(..) => unreachable!("because of the expect above"), + TokenTree::Delimited(.., tts) => return Ok(tts), } + } + let expr = self.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { + p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty()) + .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No)) + })?; + if !classify::expr_is_complete(&expr) + && self.token != token::CloseBrace + && self.token != token::Eof + { + self.expect(exp!(Comma))?; } else { - let meta_item = p.parse_meta_item_inner()?; - p.expect(exp!(FatArrow))?; - - let tts = parse_token_tree(p)?; - let span = meta_item.span().to(p.token.span); - - match branches.wildcard { - None => branches.reachable.push((meta_item, tts, span)), - Some(_) => { - branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span)) - } - } + let _ = self.eat(exp!(Comma)); } + Ok(TokenStream::from_ast(&expr)) } - - Ok(branches) } diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index bc08faabece4..bd495f6ec1ac 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -45,6 +45,7 @@ }; use crate::parser::FnContext; use crate::parser::attr::InnerAttrPolicy; +use crate::parser::item::IsDotDotDot; use crate::{exp, fluent_generated as fluent}; /// Creates a placeholder argument. @@ -2284,7 +2285,7 @@ pub(super) fn parameter_without_type( let maybe_emit_anon_params_note = |this: &mut Self, err: &mut Diag<'_>| { let ed = this.token.span.with_neighbor(this.prev_token.span).edition(); if matches!(fn_parse_mode.context, crate::parser::item::FnContext::Trait) - && (fn_parse_mode.req_name)(ed) + && (fn_parse_mode.req_name)(ed, IsDotDotDot::No) { err.note("anonymous parameters are removed in the 2018 edition (see RFC 1685)"); } @@ -2301,10 +2302,10 @@ pub(super) fn parameter_without_type( pat.span.shrink_to_hi(), pat.span.shrink_to_lo(), ), - PatKind::Ref(ref inner_pat, _) + PatKind::Ref(ref inner_pat, _, _) // Fix suggestions for multi-reference `self` parameters (e.g. `&&&self`) // cc: https://github.com/rust-lang/rust/pull/146305 - if let PatKind::Ref(_, _) = &inner_pat.kind + if let PatKind::Ref(_, _, _) = &inner_pat.kind && let PatKind::Path(_, path) = &pat.peel_refs().kind && let [a, ..] = path.segments.as_slice() && a.ident.name == kw::SelfLower => @@ -2312,7 +2313,7 @@ pub(super) fn parameter_without_type( let mut inner = inner_pat; let mut span_vec = vec![pat.span]; - while let PatKind::Ref(ref inner_type, _) = inner.kind { + while let PatKind::Ref(ref inner_type, _, _) = inner.kind { inner = inner_type; span_vec.push(inner.span.shrink_to_lo()); } @@ -2334,10 +2335,10 @@ pub(super) fn parameter_without_type( return None; } // Also catches `fn foo(&a)`. - PatKind::Ref(ref inner_pat, mutab) + PatKind::Ref(ref inner_pat, pinned, mutab) if let PatKind::Ident(_, ident, _) = inner_pat.clone().kind => { - let mutab = mutab.prefix_str(); + let mutab = pinned.prefix_str(mutab); ( ident, "self: ", diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e7c89f079622..9e7d4bca37d0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -863,14 +863,15 @@ fn parse_borrow_modifiers(&mut self) -> (ast::BorrowKind, ast::Mutability) { assert!(found_raw); let mutability = self.parse_const_or_mut().unwrap(); (ast::BorrowKind::Raw, mutability) - } else if let Some((ast::Pinnedness::Pinned, mutbl)) = self.parse_pin_and_mut() { - // `pin [ const | mut ]`. - // `pin` has been gated in `self.parse_pin_and_mut()` so we don't - // need to gate it here. - (ast::BorrowKind::Pin, mutbl) } else { - // `mut?` - (ast::BorrowKind::Ref, self.parse_mutability()) + match self.parse_pin_and_mut() { + // `mut?` + (ast::Pinnedness::Not, mutbl) => (ast::BorrowKind::Ref, mutbl), + // `pin [ const | mut ]`. + // `pin` has been gated in `self.parse_pin_and_mut()` so we don't + // need to gate it here. + (ast::Pinnedness::Pinned, mutbl) => (ast::BorrowKind::Pin, mutbl), + } } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 599ec80e0a38..a4fadb029f5c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -12,6 +12,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::codes::*; use rustc_errors::{Applicability, PResult, StashKey, struct_span_code_err}; +use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN; use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, source_map, sym}; @@ -119,7 +120,7 @@ pub fn parse_mod( impl<'a> Parser<'a> { pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option>> { let fn_parse_mode = - FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }; + FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }; self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(Box::new)) } @@ -976,7 +977,7 @@ pub fn parse_impl_item( force_collect: ForceCollect, ) -> PResult<'a, Option>>> { let fn_parse_mode = - FnParseMode { req_name: |_| true, context: FnContext::Impl, req_body: true }; + FnParseMode { req_name: |_, _| true, context: FnContext::Impl, req_body: true }; self.parse_assoc_item(fn_parse_mode, force_collect) } @@ -985,7 +986,7 @@ pub fn parse_trait_item( force_collect: ForceCollect, ) -> PResult<'a, Option>>> { let fn_parse_mode = FnParseMode { - req_name: |edition| edition >= Edition::Edition2018, + req_name: |edition, _| edition >= Edition::Edition2018, context: FnContext::Trait, req_body: false, }; @@ -1245,8 +1246,11 @@ pub fn parse_foreign_item( &mut self, force_collect: ForceCollect, ) -> PResult<'a, Option>>> { - let fn_parse_mode = - FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: false }; + let fn_parse_mode = FnParseMode { + req_name: |_, is_dot_dot_dot| is_dot_dot_dot == IsDotDotDot::No, + context: FnContext::Free, + req_body: false, + }; Ok(self.parse_item_(fn_parse_mode, force_collect)?.map( |Item { attrs, id, span, vis, kind, tokens }| { let kind = match ForeignItemKind::try_from(kind) { @@ -2133,7 +2137,7 @@ fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> { Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited, tokens: None }; // We use `parse_fn` to get a span for the function let fn_parse_mode = - FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }; + FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }; match self.parse_fn( &mut AttrVec::new(), fn_parse_mode, @@ -2366,8 +2370,16 @@ fn recover_nested_adt_item(&mut self, keyword: Symbol) -> PResult<'a, bool> { /// The function decides if, per-parameter `p`, `p` must have a pattern or just a type. /// /// This function pointer accepts an edition, because in edition 2015, trait declarations -/// were allowed to omit parameter names. In 2018, they became required. -type ReqName = fn(Edition) -> bool; +/// were allowed to omit parameter names. In 2018, they became required. It also accepts an +/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are +/// allowed to omit the name of the `...` but regular function items are not. +type ReqName = fn(Edition, IsDotDotDot) -> bool; + +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum IsDotDotDot { + Yes, + No, +} /// Parsing configuration for functions. /// @@ -2400,6 +2412,9 @@ pub(crate) struct FnParseMode { /// to true. /// * The span is from Edition 2015. In particular, you can get a /// 2015 span inside a 2021 crate using macros. + /// + /// Or if `IsDotDotDot::Yes`, this function will also return `false` if the item being parsed + /// is inside an `extern` block. pub(super) req_name: ReqName, /// The context in which this function is parsed, used for diagnostics. /// This indicates the fn is a free function or method and so on. @@ -3049,11 +3064,25 @@ pub(super) fn parse_param_general( return Ok((res?, Trailing::No, UsePreAttrPos::No)); } - let is_name_required = match this.token.kind { - token::DotDotDot => false, - _ => (fn_parse_mode.req_name)( - this.token.span.with_neighbor(this.prev_token.span).edition(), - ), + let is_dot_dot_dot = if this.token.kind == token::DotDotDot { + IsDotDotDot::Yes + } else { + IsDotDotDot::No + }; + let is_name_required = (fn_parse_mode.req_name)( + this.token.span.with_neighbor(this.prev_token.span).edition(), + is_dot_dot_dot, + ); + let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes { + this.psess.buffer_lint( + VARARGS_WITHOUT_PATTERN, + this.token.span, + ast::CRATE_NODE_ID, + errors::VarargsWithoutPattern { span: this.token.span }, + ); + false + } else { + is_name_required }; let (pat, ty) = if is_name_required || this.is_named_param() { debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 30870c810942..203a93b52012 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Pinnedness, Recovered, - Safety, StrLit, Visibility, VisibilityKind, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, + Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1315,11 +1315,11 @@ fn parse_mutability(&mut self) -> Mutability { if self.eat_keyword(exp!(Mut)) { Mutability::Mut } else { Mutability::Not } } - /// Parses reference binding mode (`ref`, `ref mut`, or nothing). + /// Parses reference binding mode (`ref`, `ref mut`, `ref pin const`, `ref pin mut`, or nothing). fn parse_byref(&mut self) -> ByRef { if self.eat_keyword(exp!(Ref)) { - // FIXME(pin_ergonomics): support `ref pin const|mut` bindings - ByRef::Yes(Pinnedness::Not, self.parse_mutability()) + let (pinnedness, mutability) = self.parse_pin_and_mut(); + ByRef::Yes(pinnedness, mutability) } else { ByRef::No } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index f964ecb90326..0e9796c04c8a 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -7,8 +7,7 @@ use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, - Pat, PatField, PatFieldsRest, PatKind, Path, Pinnedness, QSelf, RangeEnd, RangeSyntax, Stmt, - StmtKind, + Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -661,7 +660,7 @@ fn visit_pat(&mut self, p: &'a Pat) -> Self::Result { // Sub-patterns // FIXME: this doesn't work with recursive subpats (`&mut &mut `) - PatKind::Box(subpat) | PatKind::Ref(subpat, _) + PatKind::Box(subpat) | PatKind::Ref(subpat, _, _) if matches!(subpat.kind, PatKind::Err(_) | PatKind::Expr(_)) => { self.maybe_add_suggestions_then_emit(subpat.span, p.span, false) @@ -777,11 +776,10 @@ fn parse_pat_with_range_pat( self.bump(); self.dcx().emit_err(SwitchRefBoxOrder { span }); } - // Parse ref ident @ pat / ref mut ident @ pat - let mutbl = self.parse_mutability(); + // Parse ref ident @ pat / ref mut ident @ pat / ref pin const|mut ident @ pat + let (pinned, mutbl) = self.parse_pin_and_mut(); self.parse_pat_ident( - // FIXME(pin_ergonomics): support `ref pin const|mut` bindings - BindingMode(ByRef::Yes(Pinnedness::Not, mutbl), Mutability::Not), + BindingMode(ByRef::Yes(pinned, mutbl), Mutability::Not), syntax_loc, )? } else if self.eat_keyword(exp!(Box)) { @@ -982,7 +980,7 @@ fn ban_pat_range_if_ambiguous(&self, pat: &Pat) { }); } - /// Parse `&pat` / `&mut pat`. + /// Parse `&pat` / `&mut pat` / `&pin const pat` / `&pin mut pat`. fn parse_pat_deref(&mut self, expected: Option) -> PResult<'a, PatKind> { self.expect_and()?; if let Some((lifetime, _)) = self.token.lifetime() { @@ -995,9 +993,9 @@ fn parse_pat_deref(&mut self, expected: Option) -> PResult<'a, PatKind }); } - let mutbl = self.parse_mutability(); + let (pinned, mutbl) = self.parse_pin_and_mut(); let subpat = self.parse_pat_with_range_pat(false, expected, None)?; - Ok(PatKind::Ref(Box::new(subpat), mutbl)) + Ok(PatKind::Ref(Box::new(subpat), pinned, mutbl)) } /// Parse a tuple or parenthesis pattern. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index f7757921cd42..437f6da67b74 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -404,7 +404,7 @@ pub(super) fn parse_path_segment( // Inside parenthesized type arguments, we want types only, not names. let mode = FnParseMode { context: FnContext::Free, - req_name: |_| false, + req_name: |_, _| false, req_body: false, }; let param = p.parse_param_general(&mode, false, false); diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ad5ab6e6b779..3fe8971f3d6c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -154,7 +154,7 @@ pub fn parse_stmt_without_recovery( attrs.clone(), // FIXME: unwanted clone of attrs false, true, - FnParseMode { req_name: |_| true, context: FnContext::Free, req_body: true }, + FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true }, force_collect, )? { self.mk_stmt(lo.to(item.span), StmtKind::Item(Box::new(item))) diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 4c4f09ab3ea8..08c5b06575cd 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -142,6 +142,7 @@ pub enum TokenType { SymNull, SymOptions, SymOut, + SymPin, SymPreservesFlags, SymPure, SymReadonly, @@ -568,6 +569,7 @@ macro_rules! exp { (Null) => { exp!(@sym, null, SymNull) }; (Options) => { exp!(@sym, options, SymOptions) }; (Out) => { exp!(@sym, out, SymOut) }; + (Pin) => { exp!(@sym, pin, SymPin) }; (PreservesFlags) => { exp!(@sym, preserves_flags, SymPreservesFlags) }; (Pure) => { exp!(@sym, pure, SymPure) }; (Readonly) => { exp!(@sym, readonly, SymReadonly) }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 39d8a1dd9e2d..df69adce142b 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -728,10 +728,7 @@ fn maybe_recover_array_ty_without_semi(&mut self, elt_ty: Box) -> PResult<'a fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { let and_span = self.prev_token.span; let mut opt_lifetime = self.check_lifetime().then(|| self.expect_lifetime()); - let (pinned, mut mutbl) = match self.parse_pin_and_mut() { - Some(pin_mut) => pin_mut, - None => (Pinnedness::Not, self.parse_mutability()), - }; + let (pinned, mut mutbl) = self.parse_pin_and_mut(); if self.token.is_lifetime() && mutbl == Mutability::Mut && opt_lifetime.is_none() { // A lifetime is invalid here: it would be part of a bare trait bound, which requires // it to be followed by a plus, but we disallow plus in the pointee type. @@ -773,28 +770,17 @@ fn parse_borrowed_pointee(&mut self) -> PResult<'a, TyKind> { }) } - /// Parses `pin` and `mut` annotations on references. + /// Parses `pin` and `mut` annotations on references, patterns, or borrow modifiers. /// - /// It must be either `pin const` or `pin mut`. - pub(crate) fn parse_pin_and_mut(&mut self) -> Option<(Pinnedness, Mutability)> { - if self.token.is_ident_named(sym::pin) { - let result = self.look_ahead(1, |token| { - if token.is_keyword(kw::Const) { - Some((Pinnedness::Pinned, Mutability::Not)) - } else if token.is_keyword(kw::Mut) { - Some((Pinnedness::Pinned, Mutability::Mut)) - } else { - None - } - }); - if result.is_some() { - self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); - self.bump(); - self.bump(); - } - result + /// It must be either `pin const`, `pin mut`, `mut`, or nothing (immutable). + pub(crate) fn parse_pin_and_mut(&mut self) -> (Pinnedness, Mutability) { + if self.token.is_ident_named(sym::pin) && self.look_ahead(1, Token::is_mutability) { + self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span); + assert!(self.eat_keyword(exp!(Pin))); + let mutbl = self.parse_const_or_mut().unwrap(); + (Pinnedness::Pinned, mutbl) } else { - None + (Pinnedness::Not, self.parse_mutability()) } } @@ -838,7 +824,7 @@ fn parse_ty_fn_ptr( self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let mode = crate::parser::item::FnParseMode { - req_name: |_| false, + req_name: |_, _| false, context: FnContext::Free, req_body: false, }; @@ -1394,7 +1380,8 @@ fn recover_path_from_fn(&mut self) -> Option { self.bump(); let args_lo = self.token.span; let snapshot = self.create_snapshot_for_diagnostic(); - let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false }; + let mode = + FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false }; match self.parse_fn_decl(&mode, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { Ok(decl) => { self.dcx().emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); @@ -1485,7 +1472,8 @@ fn recover_fn_trait_with_lifetime_params( // Parse `(T, U) -> R`. let inputs_lo = self.token.span; - let mode = FnParseMode { req_name: |_| false, context: FnContext::Free, req_body: false }; + let mode = + FnParseMode { req_name: |_, _| false, context: FnContext::Free, req_body: false }; let inputs: ThinVec<_> = self.parse_fn_params(&mode)?.into_iter().map(|input| input.ty).collect(); let inputs_span = inputs_lo.to(self.prev_token.span); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index c668bf0733d1..b33ccbae0e69 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -219,7 +219,6 @@ passes_doc_test_unknown_passes = unknown `doc` attribute `{$path}` .note = `doc` attribute `{$path}` no longer functions; see issue #44136 .label = no longer functions - .help = you may want to use `doc(document_private_items)` .no_op_note = `doc({$path})` is now a no-op passes_doc_test_unknown_plugins = diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f5023646b191..ed4cf77d294f 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -347,7 +347,6 @@ pub(crate) struct DocTestUnknownSpotlight { #[derive(LintDiagnostic)] #[diag(passes_doc_test_unknown_passes)] #[note] -#[help] #[note(passes_no_op_note)] pub(crate) struct DocTestUnknownPasses { pub path: String, diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index c83610da1aad..fe50b730256b 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -3,7 +3,7 @@ // completely accurate (some things might be counted twice, others missed). use rustc_ast::visit::BoundKind; -use rustc_ast::{self as ast, NodeId, visit as ast_visit}; +use rustc_ast::{self as ast, AttrVec, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thousands::usize_with_underscores; use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; @@ -709,7 +709,7 @@ fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { ast_visit::walk_where_predicate(self, p) } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: &AttrVec, _: Span, _: NodeId) { self.record("FnDecl", None, fk.decl()); ast_visit::walk_fn(self, fk) } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 93d164e7d01f..26cd9e7f44ff 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -8,6 +8,7 @@ use rustc_middle::middle::lang_items::required; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; +use rustc_target::spec::Os; use crate::errors::{ MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem, @@ -26,7 +27,7 @@ pub(crate) fn check_crate( if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } - if tcx.sess.target.os == "emscripten" + if tcx.sess.target.os == Os::Emscripten && items.eh_catch_typeinfo().is_none() && !tcx.sess.opts.unstable_opts.emscripten_wasm_eh { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 8df34ee941b2..df86233c2b05 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -191,7 +191,18 @@ pub(crate) fn variant_sub_tys( variant.fields.iter().map(move |field| { let ty = field.ty(self.tcx, args); // `field.ty()` doesn't normalize after instantiating. - let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty); + let ty = + self.tcx.try_normalize_erasing_regions(self.typing_env, ty).unwrap_or_else(|e| { + self.tcx.dcx().span_delayed_bug( + self.scrut_span, + format!( + "Failed to normalize {:?} in typing_env={:?} while getting variant sub tys for {ty:?}", + e.get_type_for_failure(), + self.typing_env, + ), + ); + ty + }); let ty = self.reveal_opaque_ty(ty); (field, ty) }) @@ -462,8 +473,12 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { PatKind::Deref { subpattern } => { fields = vec![self.lower_pat(subpattern).at_index(0)]; arity = 1; - ctor = match ty.kind() { - ty::Ref(..) => Ref, + ctor = match ty.pinned_ref() { + None if ty.is_ref() => Ref, + Some((inner_ty, _)) => { + self.internal_state.has_lowered_deref_pat.set(true); + DerefPattern(RevealedTy(inner_ty)) + } _ => span_bug!( pat.span, "pattern has unexpected type: pat: {:?}, ty: {:?}", diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4e601a6c5944..39b6fac4ebc0 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -88,17 +88,25 @@ fn current_query_job(self) -> Option { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a query map representing active query jobs. - /// It returns an incomplete map as an error if it fails - /// to take locks. + /// Returns a map of currently active query jobs. + /// + /// If `require_complete` is `true`, this function locks all shards of the + /// query results to produce a complete map, which always returns `Ok`. + /// Otherwise, it may return an incomplete map as an error if any shard + /// lock cannot be acquired. + /// + /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, + /// especially when called from within a deadlock handler, unless a + /// complete map is needed and no deadlock is possible at this call site. fn collect_active_jobs( self, + require_complete: bool, ) -> Result>, QueryMap>> { let mut jobs = QueryMap::default(); let mut complete = true; - for collect in super::TRY_COLLECT_ACTIVE_JOBS.iter() { - if collect(self.tcx, &mut jobs).is_none() { + for collect in super::COLLECT_ACTIVE_JOBS.iter() { + if collect(self.tcx, &mut jobs, require_complete).is_none() { complete = false; } } @@ -163,11 +171,7 @@ fn start_query( } fn depth_limit_error(self, job: QueryJobId) { - // FIXME: `collect_active_jobs` expects no locks to be held, which doesn't hold for this call. - let query_map = match self.collect_active_jobs() { - Ok(query_map) => query_map, - Err(query_map) => query_map, - }; + let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); let (info, depth) = job.find_dep_kind_root(query_map); let suggested_limit = match self.recursion_limit() { @@ -731,19 +735,21 @@ fn restore(value: >>::Value) -> Self } } - pub(crate) fn try_collect_active_jobs<'tcx>( + pub(crate) fn collect_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, qmap: &mut QueryMap>, + require_complete: bool, ) -> Option<()> { let make_query = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; - let res = tcx.query_system.states.$name.try_collect_active_jobs( + let res = tcx.query_system.states.$name.collect_active_jobs( tcx, make_query, qmap, + require_complete, ); // this can be called during unwinding, and the function has a `try_`-prefix, so // don't `unwrap()` here, just manually check for `None` and do best-effort error @@ -814,10 +820,10 @@ pub fn dynamic_queries<'tcx>() -> DynamicQueries<'tcx> { // These arrays are used for iteration and can't be indexed by `DepKind`. - const TRY_COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>) -> Option<()> + const COLLECT_ACTIVE_JOBS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>, bool) -> Option<()> ] = - &[$(query_impl::$name::try_collect_active_jobs),*]; + &[$(query_impl::$name::collect_active_jobs),*]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 044b97c2fea1..118229ffc990 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -54,7 +54,8 @@ fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHas checksum_hash: _, external_src: _, start_pos: _, - source_len: _, + normalized_source_len: _, + unnormalized_source_len: _, lines: _, ref multibyte_chars, ref normalized_pos, diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index fd1ea997ebe5..7d9b594d501f 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -616,7 +616,7 @@ pub fn print_query_stack( let mut count_total = 0; // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs() { + let query_map = match qcx.collect_active_jobs(false) { Ok(query_map) => query_map, Err(query_map) => query_map, }; diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 855769dacc3e..ce3456d532e6 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -161,7 +161,10 @@ pub trait QueryContext: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self) -> Result, QueryMap>; + fn collect_active_jobs( + self, + require_complete: bool, + ) -> Result, QueryMap>; fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index e74de5edc42d..dea47c8fa787 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,10 +7,12 @@ use std::hash::Hash; use std::mem; +use hashbrown::HashTable; use hashbrown::hash_table::Entry; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::sync::LockGuard; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; @@ -63,22 +65,33 @@ pub fn all_inactive(&self) -> bool { self.active.lock_shards().all(|shard| shard.is_empty()) } - pub fn try_collect_active_jobs( + pub fn collect_active_jobs( &self, qcx: Qcx, make_query: fn(Qcx, K) -> QueryStackFrame, jobs: &mut QueryMap, + require_complete: bool, ) -> Option<()> { let mut active = Vec::new(); - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - for shard in self.active.try_lock_shards() { - for (k, v) in shard?.iter() { + let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| { + for (k, v) in iter.iter() { if let QueryResult::Started(ref job) = *v { - active.push((*k, (*job).clone())); + active.push((*k, job.clone())); } } + }; + + if require_complete { + for shard in self.active.lock_shards() { + collect(shard); + } + } else { + // We use try_lock_shards here since we are called from the + // deadlock handler, and this shouldn't be locked. + for shard in self.active.try_lock_shards() { + collect(shard?); + } } // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call @@ -271,7 +284,7 @@ fn cycle_error( { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries"); + let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift(qcx)), None) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 886ffcffbb4d..cd12d5ad10cf 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1358,7 +1358,7 @@ fn visit_item(&mut self, item: &'a Item) { // Visit attributes after items for backward compatibility. // This way they can use `macro_rules` defined later. self.visit_vis(&item.vis); - item.kind.walk(item.span, item.id, &item.vis, (), self); + item.kind.walk(&item.attrs, item.span, item.id, &item.vis, (), self); visit::walk_list!(self, visit_attribute, &item.attrs); } _ => visit::walk_item(self, item), diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 14538df8187d..ea64a1e6c64d 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -193,7 +193,7 @@ fn visit_item(&mut self, i: &'a Item) { }); } - fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) { match fn_kind { FnKind::Fn( _ctxt, diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f3f0a74d03bc..8ecae07dea67 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -458,14 +458,11 @@ struct Flags: u8 { let mut result = Err(Determinacy::Determined); for derive in parent_scope.derives { let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; - match this.reborrow().resolve_macro_path( + match this.reborrow().resolve_derive_macro_path( derive, - MacroKind::Derive, parent_scope, - true, force, ignore_import, - None, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 16cbfe3b3c74..a92bc77b5b50 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1034,7 +1034,7 @@ fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) { } } } - fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'ast>, _: &AttrVec, sp: Span, fn_id: NodeId) { let previous_value = self.diag_metadata.current_function; match fn_kind { // Bail if the function is foreign, and thus cannot validly have @@ -2916,9 +2916,9 @@ fn resolve_item(&mut self, item: &'ast Item) { } } - fn with_generic_param_rib<'c, F>( - &'c mut self, - params: &'c [GenericParam], + fn with_generic_param_rib( + &mut self, + params: &[GenericParam], kind: RibKind<'ra>, binder: NodeId, generics_kind: LifetimeBinderKind, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index ad3493d93e80..0b4c52d68b6f 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3592,8 +3592,7 @@ fn add_missing_lifetime_specifiers_label( && (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) { - let pre = if lt.kind == MissingLifetimeKind::Ampersand - && let Some((kind, _span)) = self.diag_metadata.current_function + let pre = if let Some((kind, _span)) = self.diag_metadata.current_function && let FnKind::Fn(_, _, ast::Fn { sig, .. }) = kind && !sig.decl.inputs.is_empty() && let sugg = sig @@ -3623,10 +3622,12 @@ fn add_missing_lifetime_specifiers_label( } else { ("one of the", "s") }; + let dotdotdot = + if lt.kind == MissingLifetimeKind::Ampersand { "..." } else { "" }; err.multipart_suggestion_verbose( format!( "instead, you are more likely to want to change {the} \ - argument{s} to be borrowed...", + argument{s} to be borrowed{dotdotdot}", ), sugg, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4a5894c9ffa8..828759e2472f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -384,6 +384,7 @@ fn resolve_derives( // is applied, so they have to be produced by the container's expansion rather // than by individual derives. // - Derives in the container need to know whether one of them is a built-in `Copy`. + // (But see the comment mentioning #124794 below.) // Temporarily take the data to avoid borrow checker conflicts. let mut derive_data = mem::take(&mut self.derive_data); let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData { @@ -395,14 +396,11 @@ fn resolve_derives( for (i, resolution) in entry.resolutions.iter_mut().enumerate() { if resolution.exts.is_none() { resolution.exts = Some( - match self.cm().resolve_macro_path( + match self.cm().resolve_derive_macro_path( &resolution.path, - MacroKind::Derive, &parent_scope, - true, force, None, - None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -440,7 +438,13 @@ fn resolve_derives( .collect(); self.helper_attrs.insert(expn_id, helper_attrs); // Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive - // has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`. + // has `Copy`, to support `#[derive(Copy, Clone)]`, `#[derive(Clone, Copy)]`, or + // `#[derive(Copy)] #[derive(Clone)]`. We do this because the code generated for + // `derive(Clone)` changes if `derive(Copy)` is also present. + // + // FIXME(#124794): unfortunately this doesn't work with `#[derive(Clone)] #[derive(Copy)]`. + // When the `Clone` impl is generated the `#[derive(Copy)]` hasn't been processed and + // `has_derive_copy` hasn't been set yet. if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) { self.containers_deriving_copy.insert(expn_id); } @@ -564,7 +568,6 @@ fn smart_resolve_macro_path( path, kind, parent_scope, - true, force, deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), @@ -706,26 +709,22 @@ fn smart_resolve_macro_path( Ok((ext, res)) } - pub(crate) fn resolve_macro_path<'r>( + pub(crate) fn resolve_derive_macro_path<'r>( self: CmResolver<'r, 'ra, 'tcx>, path: &ast::Path, - kind: MacroKind, parent_scope: &ParentScope<'ra>, - trace: bool, force: bool, ignore_import: Option>, - suggestion_span: Option, ) -> Result<(Option>, Res), Determinacy> { self.resolve_macro_or_delegation_path( path, - kind, + MacroKind::Derive, parent_scope, - trace, force, None, None, ignore_import, - suggestion_span, + None, ) } @@ -734,7 +733,6 @@ fn resolve_macro_or_delegation_path<'r>( ast_path: &ast::Path, kind: MacroKind, parent_scope: &ParentScope<'ra>, - trace: bool, force: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, @@ -773,16 +771,14 @@ fn resolve_macro_or_delegation_path<'r>( PathResult::Module(..) => unreachable!(), }; - if trace { - self.multi_segment_macro_resolutions.borrow_mut(&self).push(( - path, - path_span, - kind, - *parent_scope, - res.ok(), - ns, - )); - } + self.multi_segment_macro_resolutions.borrow_mut(&self).push(( + path, + path_span, + kind, + *parent_scope, + res.ok(), + ns, + )); self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span); res @@ -800,15 +796,13 @@ fn resolve_macro_or_delegation_path<'r>( return Err(Determinacy::Undetermined); } - if trace { - self.single_segment_macro_resolutions.borrow_mut(&self).push(( - path[0].ident, - kind, - *parent_scope, - binding.ok(), - suggestion_span, - )); - } + self.single_segment_macro_resolutions.borrow_mut(&self).push(( + path[0].ident, + kind, + *parent_scope, + binding.ok(), + suggestion_span, + )); let res = binding.map(|binding| binding.res()); self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span); diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 26ff87417ab0..cd0556de5c22 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -239,10 +239,10 @@ macro_rules! ins_sym { ins_none!(sym::sanitizer_cfi_normalize_integers); } - ins_str!(sym::target_abi, &sess.target.abi); + ins_sym!(sym::target_abi, sess.target.abi.desc_symbol()); ins_sym!(sym::target_arch, sess.target.arch.desc_symbol()); ins_str!(sym::target_endian, sess.target.endian.as_str()); - ins_str!(sym::target_env, &sess.target.env); + ins_sym!(sym::target_env, sess.target.env.desc_symbol()); for family in sess.target.families.as_ref() { ins_str!(sym::target_family, family); @@ -291,14 +291,14 @@ macro_rules! ins_sym { } } - ins_str!(sym::target_os, &sess.target.os); + ins_sym!(sym::target_os, sess.target.os.desc_symbol()); ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width)); if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) { ins_none!(sym::target_thread_local); } - ins_str!(sym::target_vendor, &sess.target.vendor); + ins_sym!(sym::target_vendor, sess.target.vendor_symbol()); // If the user wants a test runner, then add the test cfg. if sess.is_test_crate() { @@ -447,16 +447,16 @@ macro_rules! ins { }; for target in Target::builtins().chain(iter::once(current_target.clone())) { - values_target_abi.insert(Symbol::intern(&target.options.abi)); + values_target_abi.insert(target.options.abi.desc_symbol()); values_target_arch.insert(target.arch.desc_symbol()); values_target_endian.insert(Symbol::intern(target.options.endian.as_str())); - values_target_env.insert(Symbol::intern(&target.options.env)); + values_target_env.insert(target.options.env.desc_symbol()); values_target_family.extend( target.options.families.iter().map(|family| Symbol::intern(family)), ); - values_target_os.insert(Symbol::intern(&target.options.os)); + values_target_os.insert(target.options.os.desc_symbol()); values_target_pointer_width.insert(sym::integer(target.pointer_width)); - values_target_vendor.insert(Symbol::intern(&target.options.vendor)); + values_target_vendor.insert(target.vendor_symbol()); } } } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index f64fa86948c8..12b294e124c8 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -72,7 +72,7 @@ fn current_dll_path() -> Result { #[cfg(not(target_os = "aix"))] unsafe { - let addr = current_dll_path as usize as *mut _; + let addr = current_dll_path as fn() -> Result as *mut _; let mut info = std::mem::zeroed(); if libc::dladdr(addr, &mut info) == 0 { return Err("dladdr failed".into()); @@ -151,7 +151,10 @@ fn current_dll_path() -> Result { unsafe { GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - PCWSTR(current_dll_path as *mut u16), + PCWSTR( + current_dll_path as fn() -> Result + as *mut u16, + ), &mut module, ) } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9fb3c35d5ef7..f27b32de565d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -32,7 +32,7 @@ use rustc_span::{FileNameDisplayPreference, RealFileName, Span, Symbol}; use rustc_target::asm::InlineAsmArch; use rustc_target::spec::{ - Arch, CodeModel, DebuginfoKind, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, + Arch, CodeModel, DebuginfoKind, Os, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProtector, SymbolVisibility, Target, TargetTuple, TlsModel, apple, }; @@ -382,7 +382,7 @@ pub fn crt_static(&self, crate_type: Option) -> bool { } pub fn is_wasi_reactor(&self) -> bool { - self.target.options.os == "wasi" + self.target.options.os == Os::Wasi && matches!( self.opts.unstable_opts.wasi_exec_model, Some(config::WasiExecModel::Reactor) diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index afd4564f1b6f..2e03ccb1aa1a 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1723,8 +1723,10 @@ pub struct SourceFile { pub external_src: FreezeLock, /// The start position of this source in the `SourceMap`. pub start_pos: BytePos, - /// The byte length of this source. - pub source_len: RelativeBytePos, + /// The byte length of this source after normalization. + pub normalized_source_len: RelativeBytePos, + /// The byte length of this source before normalization. + pub unnormalized_source_len: u32, /// Locations of lines beginnings in the source code. pub lines: FreezeLock, /// Locations of multi-byte characters in the source code. @@ -1748,7 +1750,8 @@ fn clone(&self) -> Self { checksum_hash: self.checksum_hash, external_src: self.external_src.clone(), start_pos: self.start_pos, - source_len: self.source_len, + normalized_source_len: self.normalized_source_len, + unnormalized_source_len: self.unnormalized_source_len, lines: self.lines.clone(), multibyte_chars: self.multibyte_chars.clone(), normalized_pos: self.normalized_pos.clone(), @@ -1764,7 +1767,8 @@ fn encode(&self, s: &mut S) { self.src_hash.encode(s); self.checksum_hash.encode(s); // Do not encode `start_pos` as it's global state for this session. - self.source_len.encode(s); + self.normalized_source_len.encode(s); + self.unnormalized_source_len.encode(s); // We are always in `Lines` form by the time we reach here. assert!(self.lines.read().is_lines()); @@ -1837,7 +1841,8 @@ fn decode(d: &mut D) -> SourceFile { let name: FileName = Decodable::decode(d); let src_hash: SourceFileHash = Decodable::decode(d); let checksum_hash: Option = Decodable::decode(d); - let source_len: RelativeBytePos = Decodable::decode(d); + let normalized_source_len: RelativeBytePos = Decodable::decode(d); + let unnormalized_source_len = Decodable::decode(d); let lines = { let num_lines: u32 = Decodable::decode(d); if num_lines > 0 { @@ -1859,7 +1864,8 @@ fn decode(d: &mut D) -> SourceFile { SourceFile { name, start_pos: BytePos::from_u32(0), - source_len, + normalized_source_len, + unnormalized_source_len, src: None, src_hash, checksum_hash, @@ -1959,12 +1965,17 @@ pub fn new( SourceFileHash::new_in_memory(checksum_hash_kind, src.as_bytes()) } }); + // Capture the original source length before normalization. + let unnormalized_source_len = u32::try_from(src.len()).map_err(|_| OffsetOverflowError)?; + if unnormalized_source_len > Self::MAX_FILE_SIZE { + return Err(OffsetOverflowError); + } + let normalized_pos = normalize_src(&mut src); let stable_id = StableSourceFileId::from_filename_in_current_crate(&name); - let source_len = src.len(); - let source_len = u32::try_from(source_len).map_err(|_| OffsetOverflowError)?; - if source_len > Self::MAX_FILE_SIZE { + let normalized_source_len = u32::try_from(src.len()).map_err(|_| OffsetOverflowError)?; + if normalized_source_len > Self::MAX_FILE_SIZE { return Err(OffsetOverflowError); } @@ -1977,7 +1988,8 @@ pub fn new( checksum_hash, external_src: FreezeLock::frozen(ExternalSource::Unneeded), start_pos: BytePos::from_u32(0), - source_len: RelativeBytePos::from_u32(source_len), + normalized_source_len: RelativeBytePos::from_u32(normalized_source_len), + unnormalized_source_len, lines: FreezeLock::frozen(SourceFileLines::Lines(lines)), multibyte_chars, normalized_pos, @@ -2161,7 +2173,7 @@ pub fn relative_position(&self, pos: BytePos) -> RelativeBytePos { #[inline] pub fn end_position(&self) -> BytePos { - self.absolute_position(self.source_len) + self.absolute_position(self.normalized_source_len) } /// Finds the line containing the given position. The return value is the @@ -2197,7 +2209,7 @@ pub fn contains(&self, byte_pos: BytePos) -> bool { #[inline] pub fn is_empty(&self) -> bool { - self.source_len.to_u32() == 0 + self.normalized_source_len.to_u32() == 0 } /// Calculates the original byte position relative to the start of the file diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 166842e374b6..17de34c8436f 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -262,7 +262,7 @@ pub fn load_binary_file(&self, path: &Path) -> io::Result<(Arc<[u8]>, Span)> { bytes, Span::new( file.start_pos, - BytePos(file.start_pos.0 + file.source_len.0), + BytePos(file.start_pos.0 + file.normalized_source_len.0), SyntaxContext::root(), None, ), @@ -353,14 +353,15 @@ pub fn new_imported_source_file( src_hash: SourceFileHash, checksum_hash: Option, stable_id: StableSourceFileId, - source_len: u32, + normalized_source_len: u32, + unnormalized_source_len: u32, cnum: CrateNum, file_local_lines: FreezeLock, multibyte_chars: Vec, normalized_pos: Vec, metadata_index: u32, ) -> Arc { - let source_len = RelativeBytePos::from_u32(source_len); + let normalized_source_len = RelativeBytePos::from_u32(normalized_source_len); let source_file = SourceFile { name: filename, @@ -372,7 +373,8 @@ pub fn new_imported_source_file( metadata_index, }), start_pos: BytePos(0), - source_len, + normalized_source_len, + unnormalized_source_len, lines: file_local_lines, multibyte_chars, normalized_pos, @@ -566,7 +568,7 @@ pub fn span_to_source(&self, sp: Span, extract_source: F) -> Result end_index || end_index > source_len { return Err(SpanSnippetError::MalformedForSourcemap(MalformedSourceMapPositions { @@ -997,7 +999,7 @@ fn find_width_of_character_at_span(&self, sp: SpanData, forwards: bool) -> u32 { return 1; } - let source_len = local_begin.sf.source_len.to_usize(); + let source_len = local_begin.sf.normalized_source_len.to_usize(); debug!("source_len=`{:?}`", source_len); // Ensure indexes are also not malformed. if start_index > end_index || end_index > source_len - 1 { diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 589c2a363548..c919aacf6b5f 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -230,7 +230,8 @@ fn t10() { name, src_hash, checksum_hash, - source_len, + normalized_source_len, + unnormalized_source_len, lines, multibyte_chars, normalized_pos, @@ -243,7 +244,8 @@ fn t10() { src_hash, checksum_hash, stable_id, - source_len.to_u32(), + normalized_source_len.to_u32(), + unnormalized_source_len, CrateNum::ZERO, FreezeLock::new(lines.read().clone()), multibyte_chars, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0912bad459da..8ab818183306 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -372,6 +372,7 @@ ToString, TokenStream, Trait, + TrivialClone, Try, TryCaptureGeneric, TryCapturePrintable, @@ -611,6 +612,7 @@ c_str_literals, c_unwind, c_variadic, + c_variadic_naked_functions, c_void, call, call_mut, @@ -898,7 +900,6 @@ doc_primitive, doc_spotlight, doctest, - document_private_items, dotdot: "..", dotdot_in_tuple_patterns, dotdoteq_in_patterns, @@ -1095,10 +1096,7 @@ format_args_nl, format_argument, format_arguments, - format_count, format_macro, - format_placeholder, - format_unsafe_arg, framework, freeze, freeze_impls, @@ -1113,7 +1111,9 @@ from_output, from_residual, from_size_align_unchecked, + from_str, from_str_method, + from_str_nonconst, from_u16, from_usize, from_yeet, @@ -1259,6 +1259,7 @@ into_async_iter_into_iter, into_future, into_iter, + into_try_type, intra_doc_pointers, intrinsics, intrinsics_unaligned_volatile_load, @@ -2269,6 +2270,7 @@ transparent_enums, transparent_unions, trivial_bounds, + trivial_clone, truncf16, truncf32, truncf64, @@ -2279,6 +2281,7 @@ try_from_fn, try_into, try_trait_v2, + try_trait_v2_residual, try_update, tt, tuple, diff --git a/compiler/rustc_span/src/tests.rs b/compiler/rustc_span/src/tests.rs index ed1db3446342..64c40e611625 100644 --- a/compiler/rustc_span/src/tests.rs +++ b/compiler/rustc_span/src/tests.rs @@ -103,3 +103,17 @@ fn test_trim() { assert_eq!(span(well_before, before).trim_start(other), None); } + +#[test] +fn test_unnormalized_source_length() { + let source = "\u{feff}hello\r\nferries\r\n".to_owned(); + let sf = SourceFile::new( + FileName::Anon(Hash64::ZERO), + source, + SourceFileHashAlgorithm::Sha256, + Some(SourceFileHashAlgorithm::Sha256), + ) + .unwrap(); + assert_eq!(sf.unnormalized_source_len, 19); + assert_eq!(sf.normalized_source_len.0, 14); +} diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 43a8d9ca119d..2db8a7ff3020 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -4,7 +4,7 @@ use rustc_span::{Symbol, sym}; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; -use crate::spec::{RelocModel, Target}; +use crate::spec::{Env, Os, RelocModel, Target}; def_reg_class! { AArch64 AArch64InlineAsmRegClass { @@ -75,9 +75,9 @@ pub(crate) fn target_reserves_x18(target: &Target, target_features: &FxIndexSet< // See isX18ReservedByDefault in LLVM for targets reserve x18 by default: // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/TargetParser/AArch64TargetParser.cpp#L102-L105 // Note that +reserve-x18 is currently not set for the above targets. - target.os == "android" - || target.os == "fuchsia" - || target.env == "ohos" + target.os == Os::Android + || target.os == Os::Fuchsia + || target.env == Env::Ohos || target.is_like_darwin || target.is_like_windows || target_features.contains(&sym::reserve_x18) diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index cd7d9125d7b5..57d9cdad454a 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -273,7 +273,7 @@ pub fn from_arch(arch: &Arch) -> Option { Arch::Msp430 => Some(Self::Msp430), Arch::M68k => Some(Self::M68k), Arch::CSky => Some(Self::CSKY), - Arch::AmdGpu | Arch::Xtensa | Arch::Unknown(_) => None, + Arch::AmdGpu | Arch::Xtensa | Arch::Other(_) => None, } } } diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index 09682ee9d4e6..e72984057a82 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -4,7 +4,7 @@ use rustc_span::Symbol; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; -use crate::spec::{RelocModel, Target}; +use crate::spec::{Abi, RelocModel, Target}; def_reg_class! { PowerPC PowerPCInlineAsmRegClass { @@ -104,10 +104,10 @@ fn reserved_v20to31( _is_clobber: bool, ) -> Result<(), &'static str> { if target.is_like_aix { - match &*target.options.abi { - "vec-default" => Err("v20-v31 (vs52-vs63) are reserved on vec-default ABI"), - "vec-extabi" => Ok(()), - _ => unreachable!("unrecognized AIX ABI"), + match &target.options.abi { + Abi::VecDefault => Err("v20-v31 (vs52-vs63) are reserved on vec-default ABI"), + Abi::VecExtAbi => Ok(()), + abi => unreachable!("unrecognized AIX ABI: {abi}"), } } else { Ok(()) diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index b86d0baeb942..cb2b7f4455c6 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -3,7 +3,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, Primitive, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::{HasTargetSpec, Target}; +use crate::spec::{Abi, HasTargetSpec, Target}; /// Indicates the variant of the AArch64 ABI we are compiling for. /// Used to accommodate Apple and Microsoft's deviations from the usual AAPCS ABI. @@ -33,7 +33,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Opti RegKind::Integer => false, // The softfloat ABI treats floats like integers, so they // do not get homogeneous aggregate treatment. - RegKind::Float => cx.target_spec().abi != "softfloat", + RegKind::Float => cx.target_spec().abi != Abi::SoftFloat, RegKind::Vector => size.bits() == 64 || size.bits() == 128, }; @@ -42,7 +42,7 @@ fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Opti } fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { - if target.abi != "softfloat" { + if target.abi != Abi::SoftFloat { return; } // Do *not* use the float registers for passing arguments, as that would make LLVM pick the ABI diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 147b17b24bb5..878340143582 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -702,7 +702,7 @@ pub fn adjust_for_foreign_abi(&mut self, cx: &C, abi: ExternAbi) Arch::RiscV32 | Arch::RiscV64 => riscv::compute_abi_info(cx, self), Arch::Wasm32 | Arch::Wasm64 => wasm::compute_abi_info(cx, self), Arch::Bpf => bpf::compute_abi_info(cx, self), - arch @ (Arch::PowerPC64LE | Arch::SpirV | Arch::Unknown(_)) => { + arch @ (Arch::PowerPC64LE | Arch::SpirV | Arch::Other(_)) => { panic!("no lowering implemented for {arch}") } } diff --git a/compiler/rustc_target/src/callconv/powerpc.rs b/compiler/rustc_target/src/callconv/powerpc.rs index 67066672eca3..2b6a104e1221 100644 --- a/compiler/rustc_target/src/callconv/powerpc.rs +++ b/compiler/rustc_target/src/callconv/powerpc.rs @@ -1,7 +1,7 @@ use rustc_abi::TyAbiInterface; use crate::callconv::{ArgAbi, FnAbi}; -use crate::spec::HasTargetSpec; +use crate::spec::{Env, HasTargetSpec, Os}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if ret.layout.is_aggregate() { @@ -17,8 +17,8 @@ fn classify_arg<'a, Ty, C: HasTargetSpec>(cx: &C, arg: &mut ArgAbi<'a, Ty>) { if arg.is_ignore() { // powerpc-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. - if cx.target_spec().os == "linux" - && matches!(&*cx.target_spec().env, "gnu" | "musl" | "uclibc") + if cx.target_spec().os == Os::Linux + && matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc) && arg.layout.is_zst() { arg.make_indirect_from_ignore(); diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 380b280fbc64..a77724a572dc 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -5,7 +5,7 @@ use rustc_abi::{Endian, HasDataLayout, TyAbiInterface}; use crate::callconv::{Align, ArgAbi, FnAbi, Reg, RegKind, Uniform}; -use crate::spec::HasTargetSpec; +use crate::spec::{Env, HasTargetSpec, Os}; #[derive(Debug, Clone, Copy, PartialEq)] enum ABI { @@ -106,9 +106,9 @@ pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) Ty: TyAbiInterface<'a, C> + Copy, C: HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().env == "musl" || cx.target_spec().os == "freebsd" { + let abi = if cx.target_spec().env == Env::Musl || cx.target_spec().os == Os::FreeBsd { ELFv2 - } else if cx.target_spec().os == "aix" { + } else if cx.target_spec().os == Os::Aix { AIX } else { match cx.data_layout().endian { diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index c2f2b47690ca..a2ff6f5a3a03 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -4,7 +4,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, TyAbiInterface}; use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind}; -use crate::spec::HasTargetSpec; +use crate::spec::{Env, HasTargetSpec, Os}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { let size = ret.layout.size; @@ -29,8 +29,8 @@ fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) } if arg.is_ignore() { // s390x-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. - if cx.target_spec().os == "linux" - && matches!(&*cx.target_spec().env, "gnu" | "musl" | "uclibc") + if cx.target_spec().os == Os::Linux + && matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc) && arg.layout.is_zst() { arg.make_indirect_from_ignore(); diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index 911eaaf08f82..73e9a46ed5b2 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -6,7 +6,7 @@ }; use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform}; -use crate::spec::HasTargetSpec; +use crate::spec::{Env, HasTargetSpec, Os}; #[derive(Clone, Debug)] struct Sdata { @@ -223,8 +223,8 @@ pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) for arg in fn_abi.args.iter_mut() { if arg.is_ignore() { // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs. - if cx.target_spec().os == "linux" - && matches!(&*cx.target_spec().env, "gnu" | "musl" | "uclibc") + if cx.target_spec().os == Os::Linux + && matches!(cx.target_spec().env, Env::Gnu | Env::Musl | Env::Uclibc) && arg.layout.is_zst() { arg.make_indirect_from_ignore(); diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 2d83caa07676..1210169b3a5c 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -9,7 +9,9 @@ // tidy-alphabetical-start #![cfg_attr(bootstrap, feature(debug_closure_helpers))] +#![expect(internal_features)] #![feature(iter_intersperse)] +#![feature(rustc_attrs)] // tidy-alphabetical-end use std::path::{Path, PathBuf}; @@ -135,6 +137,14 @@ pub enum $Name { $( #[$variant_attr:meta] )* $Variant, )* + /// The vast majority of the time, the compiler deals with a fixed + /// set of values, so it is convenient for them to be represented in + /// an enum. However, it is possible to have arbitrary values in a + /// target JSON file (which can be parsed when `--target` is + /// specified). This might occur, for example, for an out-of-tree + /// codegen backend that supports a value (e.g. architecture or OS) + /// that rustc currently doesn't know about. This variant exists as + /// an escape hatch for such cases. $( #[$other_variant_attr] )* $OtherVariant(crate::spec::StaticCow), } diff --git a/compiler/rustc_target/src/spec/base/aix.rs b/compiler/rustc_target/src/spec/base/aix.rs index aa42b4d1c502..76e8fb763e58 100644 --- a/compiler/rustc_target/src/spec/base/aix.rs +++ b/compiler/rustc_target/src/spec/base/aix.rs @@ -1,15 +1,16 @@ use rustc_abi::Endian; use crate::spec::{ - BinaryFormat, Cc, CodeModel, LinkOutputKind, LinkerFlavor, TargetOptions, crt_objects, cvs, + Abi, BinaryFormat, Cc, CodeModel, LinkOutputKind, LinkerFlavor, Os, TargetOptions, crt_objects, + cvs, }; pub(crate) fn opts() -> TargetOptions { TargetOptions { - abi: "vec-extabi".into(), + abi: Abi::VecExtAbi, code_model: Some(CodeModel::Large), cpu: "pwr7".into(), - os: "aix".into(), + os: Os::Aix, vendor: "ibm".into(), dynamic_linking: true, endian: Endian::Big, diff --git a/compiler/rustc_target/src/spec/base/android.rs b/compiler/rustc_target/src/spec/base/android.rs index df2757aaabf6..c7f5d863bf67 100644 --- a/compiler/rustc_target/src/spec/base/android.rs +++ b/compiler/rustc_target/src/spec/base/android.rs @@ -1,8 +1,8 @@ -use crate::spec::{SanitizerSet, TargetOptions, TlsModel, base}; +use crate::spec::{Os, SanitizerSet, TargetOptions, TlsModel, base}; pub(crate) fn opts() -> TargetOptions { let mut base = base::linux::opts(); - base.os = "android".into(); + base.os = Os::Android; base.is_like_android = true; base.default_dwarf_version = 2; base.tls_model = TlsModel::Emulated; diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs index 047bc9b8518b..542c5e278499 100644 --- a/compiler/rustc_target/src/spec/base/apple/mod.rs +++ b/compiler/rustc_target/src/spec/base/apple/mod.rs @@ -4,8 +4,8 @@ use std::str::FromStr; use crate::spec::{ - BinaryFormat, Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, - SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs, + Abi, BinaryFormat, Cc, DebuginfoKind, Env, FloatAbi, FramePointer, LinkerFlavor, Lld, Os, + RustcAbi, SplitDebuginfo, StackProbeType, StaticCow, Target, TargetOptions, cvs, }; #[cfg(test)] @@ -94,11 +94,25 @@ pub(crate) enum TargetEnv { } impl TargetEnv { - fn target_env(self) -> &'static str { + fn target_env(self) -> Env { match self { - Self::Normal => "", - Self::MacCatalyst => "macabi", - Self::Simulator => "sim", + Self::Normal => Env::Unspecified, + Self::MacCatalyst => Env::MacAbi, + Self::Simulator => Env::Sim, + } + } + + // NOTE: We originally set `cfg(target_abi = "macabi")` / `cfg(target_abi = "sim")`, + // before it was discovered that those are actually environments: + // https://github.com/rust-lang/rust/issues/133331 + // + // But let's continue setting them for backwards compatibility. + // FIXME(madsmtm): Warn about using these in the future. + fn target_abi(self) -> Abi { + match self { + Self::Normal => Abi::Unspecified, + Self::MacCatalyst => Abi::MacAbi, + Self::Simulator => Abi::Sim, } } } @@ -106,23 +120,19 @@ fn target_env(self) -> &'static str { /// Get the base target options, unversioned LLVM target and `target_arch` from the three /// things that uniquely identify Rust's Apple targets: The OS, the architecture, and the ABI. pub(crate) fn base( - os: &'static str, + os: Os, arch: Arch, env: TargetEnv, ) -> (TargetOptions, StaticCow, crate::spec::Arch) { + let link_env_remove = link_env_remove(&os); + let unversioned_llvm_target = unversioned_llvm_target(&os, arch, env); let mut opts = TargetOptions { llvm_floatabi: Some(FloatAbi::Hard), - os: os.into(), - env: env.target_env().into(), - // NOTE: We originally set `cfg(target_abi = "macabi")` / `cfg(target_abi = "sim")`, - // before it was discovered that those are actually environments: - // https://github.com/rust-lang/rust/issues/133331 - // - // But let's continue setting them for backwards compatibility. - // FIXME(madsmtm): Warn about using these in the future. - abi: env.target_env().into(), + os, + env: env.target_env(), + abi: env.target_abi(), cpu: arch.target_cpu(env).into(), - link_env_remove: link_env_remove(os), + link_env_remove, vendor: "apple".into(), linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No), // macOS has -dead_strip, which doesn't rely on function_sections @@ -189,23 +199,23 @@ pub(crate) fn base( // All Apple x86-32 targets have SSE2. opts.rustc_abi = Some(RustcAbi::X86Sse2); } - (opts, unversioned_llvm_target(os, arch, env), arch.target_arch()) + (opts, unversioned_llvm_target, arch.target_arch()) } /// Generate part of the LLVM target triple. /// /// See `rustc_codegen_ssa::back::versioned_llvm_target` for the full triple passed to LLVM and /// Clang. -fn unversioned_llvm_target(os: &str, arch: Arch, env: TargetEnv) -> StaticCow { +fn unversioned_llvm_target(os: &Os, arch: Arch, env: TargetEnv) -> StaticCow { let arch = arch.target_name(); // Convert to the "canonical" OS name used by LLVM: // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L236-L282 let os = match os { - "macos" => "macosx", - "ios" => "ios", - "watchos" => "watchos", - "tvos" => "tvos", - "visionos" => "xros", + Os::MacOs => "macosx", + Os::IOs => "ios", + Os::WatchOs => "watchos", + Os::TvOs => "tvos", + Os::VisionOs => "xros", _ => unreachable!("tried to get LLVM target OS for non-Apple platform"), }; let environment = match env { @@ -216,13 +226,13 @@ fn unversioned_llvm_target(os: &str, arch: Arch, env: TargetEnv) -> StaticCow StaticCow<[StaticCow]> { +fn link_env_remove(os: &Os) -> StaticCow<[StaticCow]> { // Apple platforms only officially support macOS as a host for any compilation. // // If building for macOS, we go ahead and remove any erroneous environment state // that's only applicable to cross-OS compilation. Always leave anything for the // host OS alone though. - if os == "macos" { + if *os == Os::MacOs { // `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", // although this is apparently ignored when using the linker at "/usr/bin/ld". @@ -284,7 +294,7 @@ pub fn fmt_full(self) -> impl Display { } /// Minimum operating system versions currently supported by `rustc`. - pub fn os_minimum_deployment_target(os: &str) -> Self { + pub fn os_minimum_deployment_target(os: &Os) -> Self { // When bumping a version in here, remember to update the platform-support docs too. // // NOTE: The defaults may change in future `rustc` versions, so if you are looking for the @@ -293,12 +303,14 @@ pub fn os_minimum_deployment_target(os: &str) -> Self { // $ rustc --print deployment-target // ``` let (major, minor, patch) = match os { - "macos" => (10, 12, 0), - "ios" => (10, 0, 0), - "tvos" => (10, 0, 0), - "watchos" => (5, 0, 0), - "visionos" => (1, 0, 0), - _ => unreachable!("tried to get deployment target for non-Apple platform"), + Os::MacOs => (10, 12, 0), + Os::IOs => (10, 0, 0), + Os::TvOs => (10, 0, 0), + Os::WatchOs => (5, 0, 0), + Os::VisionOs => (1, 0, 0), + other => { + unreachable!("tried to get deployment target for non-Apple platform: {:?}", other) + } }; Self { major, minor, patch } } @@ -311,36 +323,36 @@ pub fn os_minimum_deployment_target(os: &str) -> Self { /// This matches what LLVM does, see in part: /// pub fn minimum_deployment_target(target: &Target) -> Self { - let (major, minor, patch) = match (&*target.os, &target.arch, &*target.env) { - ("macos", crate::spec::Arch::AArch64, _) => (11, 0, 0), - ("ios", crate::spec::Arch::AArch64, "macabi") => (14, 0, 0), - ("ios", crate::spec::Arch::AArch64, "sim") => (14, 0, 0), - ("ios", _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), + let (major, minor, patch) = match (&target.os, &target.arch, &target.env) { + (Os::MacOs, crate::spec::Arch::AArch64, _) => (11, 0, 0), + (Os::IOs, crate::spec::Arch::AArch64, Env::MacAbi) => (14, 0, 0), + (Os::IOs, crate::spec::Arch::AArch64, Env::Sim) => (14, 0, 0), + (Os::IOs, _, _) if target.llvm_target.starts_with("arm64e") => (14, 0, 0), // Mac Catalyst defaults to 13.1 in Clang. - ("ios", _, "macabi") => (13, 1, 0), - ("tvos", crate::spec::Arch::AArch64, "sim") => (14, 0, 0), - ("watchos", crate::spec::Arch::AArch64, "sim") => (7, 0, 0), + (Os::IOs, _, Env::MacAbi) => (13, 1, 0), + (Os::TvOs, crate::spec::Arch::AArch64, Env::Sim) => (14, 0, 0), + (Os::WatchOs, crate::spec::Arch::AArch64, Env::Sim) => (7, 0, 0), // True Aarch64 on watchOS (instead of their Aarch64 Ilp32 called `arm64_32`) has been // available since Xcode 14, but it's only actually used more recently in watchOS 26. - ("watchos", crate::spec::Arch::AArch64, "") + (Os::WatchOs, crate::spec::Arch::AArch64, Env::Unspecified) if !target.llvm_target.starts_with("arm64_32") => { (26, 0, 0) } - (os, _, _) => return Self::os_minimum_deployment_target(os), + _ => return Self::os_minimum_deployment_target(&target.os), }; Self { major, minor, patch } } } /// Name of the environment variable used to fetch the deployment target on the given OS. -pub fn deployment_target_env_var(os: &str) -> &'static str { +pub fn deployment_target_env_var(os: &Os) -> &'static str { match os { - "macos" => "MACOSX_DEPLOYMENT_TARGET", - "ios" => "IPHONEOS_DEPLOYMENT_TARGET", - "watchos" => "WATCHOS_DEPLOYMENT_TARGET", - "tvos" => "TVOS_DEPLOYMENT_TARGET", - "visionos" => "XROS_DEPLOYMENT_TARGET", + Os::MacOs => "MACOSX_DEPLOYMENT_TARGET", + Os::IOs => "IPHONEOS_DEPLOYMENT_TARGET", + Os::WatchOs => "WATCHOS_DEPLOYMENT_TARGET", + Os::TvOs => "TVOS_DEPLOYMENT_TARGET", + Os::VisionOs => "XROS_DEPLOYMENT_TARGET", _ => unreachable!("tried to get deployment target env var for non-Apple platform"), } } diff --git a/compiler/rustc_target/src/spec/base/apple/tests.rs b/compiler/rustc_target/src/spec/base/apple/tests.rs index bca86ce33c3d..fd3fd6da9116 100644 --- a/compiler/rustc_target/src/spec/base/apple/tests.rs +++ b/compiler/rustc_target/src/spec/base/apple/tests.rs @@ -4,6 +4,7 @@ aarch64_apple_watchos_sim, i686_apple_darwin, x86_64_apple_darwin, x86_64_apple_ios, x86_64_apple_tvos, x86_64_apple_watchos_sim, }; +use crate::spec::{Abi, Env}; #[test] fn simulator_targets_set_env() { @@ -18,9 +19,9 @@ fn simulator_targets_set_env() { ]; for target in &all_sim_targets { - assert_eq!(target.env, "sim"); + assert_eq!(target.env, Env::Sim); // Ensure backwards compat - assert_eq!(target.abi, "sim"); + assert_eq!(target.abi, Abi::Sim); } } diff --git a/compiler/rustc_target/src/spec/base/cygwin.rs b/compiler/rustc_target/src/spec/base/cygwin.rs index d6ae0a905bf6..fcc2a732c18d 100644 --- a/compiler/rustc_target/src/spec/base/cygwin.rs +++ b/compiler/rustc_target/src/spec/base/cygwin.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use crate::spec::{ - BinaryFormat, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions, TlsModel, - cvs, + BinaryFormat, Cc, DebuginfoKind, LinkerFlavor, Lld, Os, SplitDebuginfo, TargetOptions, + TlsModel, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -24,7 +24,7 @@ pub(crate) fn opts() -> TargetOptions { cygwin_libs, ); TargetOptions { - os: "cygwin".into(), + os: Os::Cygwin, vendor: "pc".into(), // FIXME(#13846) this should be enabled for cygwin function_sections: false, diff --git a/compiler/rustc_target/src/spec/base/dragonfly.rs b/compiler/rustc_target/src/spec/base/dragonfly.rs index 36c9489fb705..af8fd9a03689 100644 --- a/compiler/rustc_target/src/spec/base/dragonfly.rs +++ b/compiler/rustc_target/src/spec/base/dragonfly.rs @@ -1,8 +1,8 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "dragonfly".into(), + os: Os::Dragonfly, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/freebsd.rs b/compiler/rustc_target/src/spec/base/freebsd.rs index 360232933027..6bbb84a653fb 100644 --- a/compiler/rustc_target/src/spec/base/freebsd.rs +++ b/compiler/rustc_target/src/spec/base/freebsd.rs @@ -1,8 +1,8 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "freebsd".into(), + os: Os::FreeBsd, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/fuchsia.rs b/compiler/rustc_target/src/spec/base/fuchsia.rs index 92cb0299ddb2..a43c3e2bab55 100644 --- a/compiler/rustc_target/src/spec/base/fuchsia.rs +++ b/compiler/rustc_target/src/spec/base/fuchsia.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Cc, FramePointer, LinkOutputKind, LinkerFlavor, Lld, TargetOptions, crt_objects, cvs, + Cc, FramePointer, LinkOutputKind, LinkerFlavor, Lld, Os, TargetOptions, crt_objects, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -30,7 +30,7 @@ pub(crate) fn opts() -> TargetOptions { ); TargetOptions { - os: "fuchsia".into(), + os: Os::Fuchsia, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), dynamic_linking: true, diff --git a/compiler/rustc_target/src/spec/base/haiku.rs b/compiler/rustc_target/src/spec/base/haiku.rs index 35e4cd2962ee..d4e6545a9f58 100644 --- a/compiler/rustc_target/src/spec/base/haiku.rs +++ b/compiler/rustc_target/src/spec/base/haiku.rs @@ -1,8 +1,8 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "haiku".into(), + os: Os::Haiku, dynamic_linking: true, families: cvs!["unix"], relro_level: RelroLevel::Full, diff --git a/compiler/rustc_target/src/spec/base/helenos.rs b/compiler/rustc_target/src/spec/base/helenos.rs index 8d6f406e41f7..e26633121b82 100644 --- a/compiler/rustc_target/src/spec/base/helenos.rs +++ b/compiler/rustc_target/src/spec/base/helenos.rs @@ -1,8 +1,8 @@ -use crate::spec::{PanicStrategy, RelroLevel, StackProbeType, TargetOptions}; +use crate::spec::{Os, PanicStrategy, RelroLevel, StackProbeType, TargetOptions}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "helenos".into(), + os: Os::HelenOs, dynamic_linking: true, // we need the linker to keep libgcc and friends diff --git a/compiler/rustc_target/src/spec/base/hermit.rs b/compiler/rustc_target/src/spec/base/hermit.rs index 971abef80d9b..1fa50c25439c 100644 --- a/compiler/rustc_target/src/spec/base/hermit.rs +++ b/compiler/rustc_target/src/spec/base/hermit.rs @@ -1,8 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, TargetOptions, TlsModel}; +use crate::spec::{Cc, LinkerFlavor, Lld, Os, PanicStrategy, TargetOptions, TlsModel}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "hermit".into(), + os: Os::Hermit, linker: Some("rust-lld".into()), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), tls_model: TlsModel::InitialExec, diff --git a/compiler/rustc_target/src/spec/base/hurd.rs b/compiler/rustc_target/src/spec/base/hurd.rs index dcb346ddecca..c814fe934291 100644 --- a/compiler/rustc_target/src/spec/base/hurd.rs +++ b/compiler/rustc_target/src/spec/base/hurd.rs @@ -1,8 +1,8 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "hurd".into(), + os: Os::Hurd, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/hurd_gnu.rs b/compiler/rustc_target/src/spec/base/hurd_gnu.rs index d33372b41b90..cc1696b3ab04 100644 --- a/compiler/rustc_target/src/spec/base/hurd_gnu.rs +++ b/compiler/rustc_target/src/spec/base/hurd_gnu.rs @@ -1,5 +1,5 @@ -use crate::spec::{TargetOptions, base}; +use crate::spec::{Env, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - TargetOptions { env: "gnu".into(), ..base::hurd::opts() } + TargetOptions { env: Env::Gnu, ..base::hurd::opts() } } diff --git a/compiler/rustc_target/src/spec/base/illumos.rs b/compiler/rustc_target/src/spec/base/illumos.rs index 2391b229e5ba..50ff14bbf0a2 100644 --- a/compiler/rustc_target/src/spec/base/illumos.rs +++ b/compiler/rustc_target/src/spec/base/illumos.rs @@ -1,4 +1,4 @@ -use crate::spec::{Cc, FramePointer, LinkerFlavor, TargetOptions, cvs}; +use crate::spec::{Cc, FramePointer, LinkerFlavor, Os, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { let late_link_args = TargetOptions::link_args( @@ -25,7 +25,7 @@ pub(crate) fn opts() -> TargetOptions { ); TargetOptions { - os: "illumos".into(), + os: Os::Illumos, dynamic_linking: true, has_rpath: true, families: cvs!["unix"], diff --git a/compiler/rustc_target/src/spec/base/l4re.rs b/compiler/rustc_target/src/spec/base/l4re.rs index 072a6a1001bd..8722c8a71e23 100644 --- a/compiler/rustc_target/src/spec/base/l4re.rs +++ b/compiler/rustc_target/src/spec/base/l4re.rs @@ -1,9 +1,9 @@ -use crate::spec::{Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, cvs}; +use crate::spec::{Cc, Env, LinkerFlavor, Os, PanicStrategy, RelocModel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "l4re".into(), - env: "uclibc".into(), + os: Os::L4Re, + env: Env::Uclibc, linker_flavor: LinkerFlavor::Unix(Cc::No), panic_strategy: PanicStrategy::Abort, linker: Some("l4-bender".into()), diff --git a/compiler/rustc_target/src/spec/base/linux.rs b/compiler/rustc_target/src/spec/base/linux.rs index 26e4590cf5e6..e7757fd4726c 100644 --- a/compiler/rustc_target/src/spec/base/linux.rs +++ b/compiler/rustc_target/src/spec/base/linux.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; -use crate::spec::{RelroLevel, SplitDebuginfo, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, SplitDebuginfo, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "linux".into(), + os: Os::Linux, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/linux_gnu.rs b/compiler/rustc_target/src/spec/base/linux_gnu.rs index 2fcd8c61a9c6..7a907c802df2 100644 --- a/compiler/rustc_target/src/spec/base/linux_gnu.rs +++ b/compiler/rustc_target/src/spec/base/linux_gnu.rs @@ -1,7 +1,7 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, TargetOptions, base}; +use crate::spec::{Cc, Env, LinkerFlavor, Lld, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - let mut base = TargetOptions { env: "gnu".into(), ..base::linux::opts() }; + let mut base = TargetOptions { env: Env::Gnu, ..base::linux::opts() }; // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using // linker flavor, and self-contained linker component. diff --git a/compiler/rustc_target/src/spec/base/linux_musl.rs b/compiler/rustc_target/src/spec/base/linux_musl.rs index 1bef602404e5..6d3124b559b1 100644 --- a/compiler/rustc_target/src/spec/base/linux_musl.rs +++ b/compiler/rustc_target/src/spec/base/linux_musl.rs @@ -1,8 +1,8 @@ -use crate::spec::{LinkSelfContainedDefault, TargetOptions, base, crt_objects}; +use crate::spec::{Env, LinkSelfContainedDefault, TargetOptions, base, crt_objects}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - env: "musl".into(), + env: Env::Musl, pre_link_objects_self_contained: crt_objects::pre_musl_self_contained(), post_link_objects_self_contained: crt_objects::post_musl_self_contained(), link_self_contained: LinkSelfContainedDefault::InferredForMusl, diff --git a/compiler/rustc_target/src/spec/base/linux_ohos.rs b/compiler/rustc_target/src/spec/base/linux_ohos.rs index 1b7f1e196664..dee78fb325c1 100644 --- a/compiler/rustc_target/src/spec/base/linux_ohos.rs +++ b/compiler/rustc_target/src/spec/base/linux_ohos.rs @@ -1,8 +1,8 @@ -use crate::spec::{TargetOptions, TlsModel, base}; +use crate::spec::{Env, TargetOptions, TlsModel, base}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - env: "ohos".into(), + env: Env::Ohos, crt_static_default: false, tls_model: TlsModel::Emulated, has_thread_local: false, diff --git a/compiler/rustc_target/src/spec/base/linux_uclibc.rs b/compiler/rustc_target/src/spec/base/linux_uclibc.rs index 40801b76dca7..c65a3b9139f1 100644 --- a/compiler/rustc_target/src/spec/base/linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/base/linux_uclibc.rs @@ -1,5 +1,5 @@ -use crate::spec::{TargetOptions, base}; +use crate::spec::{Env, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - TargetOptions { env: "uclibc".into(), ..base::linux::opts() } + TargetOptions { env: Env::Uclibc, ..base::linux::opts() } } diff --git a/compiler/rustc_target/src/spec/base/linux_wasm.rs b/compiler/rustc_target/src/spec/base/linux_wasm.rs index a8c137c22a97..994299e5c025 100644 --- a/compiler/rustc_target/src/spec/base/linux_wasm.rs +++ b/compiler/rustc_target/src/spec/base/linux_wasm.rs @@ -2,8 +2,8 @@ //! aspects from their respective base targets use crate::spec::{ - Cc, LinkSelfContainedDefault, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel, - add_link_args, crt_objects, cvs, + Cc, Env, LinkSelfContainedDefault, LinkerFlavor, Os, PanicStrategy, RelocModel, TargetOptions, + TlsModel, add_link_args, crt_objects, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -57,8 +57,8 @@ macro_rules! args { TargetOptions { is_like_wasm: true, families: cvs!["wasm", "unix"], - os: "linux".into(), - env: "musl".into(), + os: Os::Linux, + env: Env::Musl, // we allow dynamic linking, but only cdylibs. Basically we allow a // final library artifact that exports some symbols (a wasm module) but diff --git a/compiler/rustc_target/src/spec/base/lynxos178.rs b/compiler/rustc_target/src/spec/base/lynxos178.rs index b9434ff5faaf..3ce53d59949c 100644 --- a/compiler/rustc_target/src/spec/base/lynxos178.rs +++ b/compiler/rustc_target/src/spec/base/lynxos178.rs @@ -1,12 +1,12 @@ use std::borrow::Cow; use crate::spec::{ - PanicStrategy, RelocModel, RelroLevel, SplitDebuginfo, StackProbeType, TargetOptions, cvs, + Os, PanicStrategy, RelocModel, RelroLevel, SplitDebuginfo, StackProbeType, TargetOptions, cvs, }; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "lynxos178".into(), + os: Os::LynxOs178, dynamic_linking: false, families: cvs!["unix"], position_independent_executables: false, diff --git a/compiler/rustc_target/src/spec/base/managarm_mlibc.rs b/compiler/rustc_target/src/spec/base/managarm_mlibc.rs index da3856b212d9..2384fc37d84a 100644 --- a/compiler/rustc_target/src/spec/base/managarm_mlibc.rs +++ b/compiler/rustc_target/src/spec/base/managarm_mlibc.rs @@ -1,9 +1,9 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Env, Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "managarm".into(), - env: "mlibc".into(), + os: Os::Managarm, + env: Env::Mlibc, dynamic_linking: true, executables: true, families: cvs!["unix"], diff --git a/compiler/rustc_target/src/spec/base/motor.rs b/compiler/rustc_target/src/spec/base/motor.rs index 18485b2cef2f..7b75c9bf253a 100644 --- a/compiler/rustc_target/src/spec/base/motor.rs +++ b/compiler/rustc_target/src/spec/base/motor.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Cc, FramePointer, LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions, + Cc, FramePointer, LinkerFlavor, Lld, Os, PanicStrategy, StackProbeType, TargetOptions, }; pub(crate) fn opts() -> TargetOptions { @@ -16,7 +16,7 @@ pub(crate) fn opts() -> TargetOptions { ], ); TargetOptions { - os: "motor".into(), + os: Os::Motor, executables: true, // TLS is false below because if true, the compiler assumes // we handle TLS at the ELF loading level, which we don't. diff --git a/compiler/rustc_target/src/spec/base/netbsd.rs b/compiler/rustc_target/src/spec/base/netbsd.rs index b92d8933a27a..566cebdf744c 100644 --- a/compiler/rustc_target/src/spec/base/netbsd.rs +++ b/compiler/rustc_target/src/spec/base/netbsd.rs @@ -1,8 +1,8 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "netbsd".into(), + os: Os::NetBsd, dynamic_linking: true, families: cvs!["unix"], no_default_libraries: false, diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/nto_qnx.rs index 119c5ec98f95..417ac0bc0189 100644 --- a/compiler/rustc_target/src/spec/base/nto_qnx.rs +++ b/compiler/rustc_target/src/spec/base/nto_qnx.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Cc, LinkArgs, LinkerFlavor, Lld, RelroLevel, Target, TargetMetadata, TargetOptions, cvs, + Cc, LinkArgs, LinkerFlavor, Lld, Os, RelroLevel, Target, TargetMetadata, TargetOptions, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -11,7 +11,7 @@ pub(crate) fn opts() -> TargetOptions { has_rpath: true, has_thread_local: false, linker: Some("qcc".into()), - os: "nto".into(), + os: Os::Nto, // We want backtraces to work by default and they rely on unwind tables // (regardless of `-C panic` strategy). default_uwtable: true, diff --git a/compiler/rustc_target/src/spec/base/openbsd.rs b/compiler/rustc_target/src/spec/base/openbsd.rs index 481fbdf0b08e..84395a407895 100644 --- a/compiler/rustc_target/src/spec/base/openbsd.rs +++ b/compiler/rustc_target/src/spec/base/openbsd.rs @@ -1,8 +1,8 @@ -use crate::spec::{FramePointer, RelroLevel, TargetOptions, TlsModel, cvs}; +use crate::spec::{FramePointer, Os, RelroLevel, TargetOptions, TlsModel, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "openbsd".into(), + os: Os::OpenBsd, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/redox.rs b/compiler/rustc_target/src/spec/base/redox.rs index e9b47f3fa3fd..fc12cbb7b2b4 100644 --- a/compiler/rustc_target/src/spec/base/redox.rs +++ b/compiler/rustc_target/src/spec/base/redox.rs @@ -1,9 +1,9 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, RelroLevel, TargetOptions, cvs}; +use crate::spec::{Cc, Env, LinkerFlavor, Lld, Os, RelroLevel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "redox".into(), - env: "relibc".into(), + os: Os::Redox, + env: Env::Relibc, dynamic_linking: true, families: cvs!["unix"], has_rpath: true, diff --git a/compiler/rustc_target/src/spec/base/solaris.rs b/compiler/rustc_target/src/spec/base/solaris.rs index 22288b86e2ec..be43e44557b7 100644 --- a/compiler/rustc_target/src/spec/base/solaris.rs +++ b/compiler/rustc_target/src/spec/base/solaris.rs @@ -1,8 +1,8 @@ -use crate::spec::{Cc, LinkerFlavor, TargetOptions, cvs}; +use crate::spec::{Cc, LinkerFlavor, Os, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "solaris".into(), + os: Os::Solaris, dynamic_linking: true, has_rpath: true, families: cvs!["unix"], diff --git a/compiler/rustc_target/src/spec/base/solid.rs b/compiler/rustc_target/src/spec/base/solid.rs index fd55e1d15014..042942a5a196 100644 --- a/compiler/rustc_target/src/spec/base/solid.rs +++ b/compiler/rustc_target/src/spec/base/solid.rs @@ -1,8 +1,8 @@ -use crate::spec::{FramePointer, TargetOptions}; +use crate::spec::{FramePointer, Os, TargetOptions}; -pub(crate) fn opts(kernel: &str) -> TargetOptions { +pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: format!("solid_{kernel}").into(), + os: Os::SolidAsp3, vendor: "kmc".into(), executables: false, frame_pointer: FramePointer::NonLeaf, diff --git a/compiler/rustc_target/src/spec/base/teeos.rs b/compiler/rustc_target/src/spec/base/teeos.rs index ecd9752c213e..f78ea035b221 100644 --- a/compiler/rustc_target/src/spec/base/teeos.rs +++ b/compiler/rustc_target/src/spec/base/teeos.rs @@ -1,4 +1,6 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelroLevel, TargetOptions, add_link_args}; +use crate::spec::{ + Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelroLevel, TargetOptions, add_link_args, +}; pub(crate) fn opts() -> TargetOptions { let lld_args = &["-zmax-page-size=4096", "-znow", "-ztext", "--execute-only"]; @@ -8,8 +10,7 @@ pub(crate) fn opts() -> TargetOptions { add_link_args(&mut pre_link_args, LinkerFlavor::Gnu(Cc::Yes, Lld::No), cc_args); TargetOptions { - os: "teeos".into(), - vendor: "unknown".into(), + os: Os::TeeOs, dynamic_linking: true, linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), // rpath hardcodes -Wl, so it can't be used together with ld.lld. diff --git a/compiler/rustc_target/src/spec/base/uefi_msvc.rs b/compiler/rustc_target/src/spec/base/uefi_msvc.rs index 4766eab7fd7c..99005715018a 100644 --- a/compiler/rustc_target/src/spec/base/uefi_msvc.rs +++ b/compiler/rustc_target/src/spec/base/uefi_msvc.rs @@ -9,7 +9,7 @@ // the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all // code runs in the same environment, no process separation is supported. -use crate::spec::{LinkerFlavor, Lld, PanicStrategy, StackProbeType, TargetOptions, base}; +use crate::spec::{LinkerFlavor, Lld, Os, PanicStrategy, StackProbeType, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { let mut base = base::msvc::opts(); @@ -35,7 +35,7 @@ pub(crate) fn opts() -> TargetOptions { ); TargetOptions { - os: "uefi".into(), + os: Os::Uefi, linker_flavor: LinkerFlavor::Msvc(Lld::Yes), disable_redzone: true, exe_suffix: ".efi".into(), diff --git a/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs b/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs index 319a6182303b..4efcc4b03a95 100644 --- a/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs +++ b/compiler/rustc_target/src/spec/base/unikraft_linux_musl.rs @@ -1,9 +1,9 @@ -use crate::spec::{PanicStrategy, RelocModel, TargetOptions, cvs}; +use crate::spec::{Env, Os, PanicStrategy, RelocModel, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "linux".into(), - env: "musl".into(), + os: Os::Linux, + env: Env::Musl, vendor: "unikraft".into(), linker: Some("kraftld".into()), relocation_model: RelocModel::Static, diff --git a/compiler/rustc_target/src/spec/base/vxworks.rs b/compiler/rustc_target/src/spec/base/vxworks.rs index bb0d00f4a4c0..129554b5aae4 100644 --- a/compiler/rustc_target/src/spec/base/vxworks.rs +++ b/compiler/rustc_target/src/spec/base/vxworks.rs @@ -1,9 +1,9 @@ -use crate::spec::{TargetOptions, cvs}; +use crate::spec::{Env, Os, TargetOptions, cvs}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "vxworks".into(), - env: "gnu".into(), + os: Os::VxWorks, + env: Env::Gnu, vendor: "wrs".into(), linker: Some("wr-c++".into()), exe_suffix: ".vxe".into(), diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index 2867428e42f7..d2fe42b90306 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use crate::spec::{ - BinaryFormat, Cc, DebuginfoKind, LinkSelfContainedDefault, LinkerFlavor, Lld, SplitDebuginfo, - TargetOptions, add_link_args, crt_objects, cvs, + BinaryFormat, Cc, DebuginfoKind, Env, LinkSelfContainedDefault, LinkerFlavor, Lld, Os, + SplitDebuginfo, TargetOptions, add_link_args, crt_objects, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -77,8 +77,8 @@ pub(crate) fn opts() -> TargetOptions { ); TargetOptions { - os: "windows".into(), - env: "gnu".into(), + os: Os::Windows, + env: Env::Gnu, vendor: "pc".into(), // FIXME(#13846) this should be enabled for windows function_sections: false, diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index f24ad781e2b8..58aff198a660 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use crate::spec::{ - BinaryFormat, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions, cvs, + Abi, BinaryFormat, Cc, DebuginfoKind, Env, LinkerFlavor, Lld, Os, SplitDebuginfo, + TargetOptions, cvs, }; pub(crate) fn opts() -> TargetOptions { @@ -20,10 +21,10 @@ pub(crate) fn opts() -> TargetOptions { ); TargetOptions { - os: "windows".into(), - env: "gnu".into(), + os: Os::Windows, + env: Env::Gnu, vendor: "pc".into(), - abi: "llvm".into(), + abi: Abi::Llvm, linker: Some("clang".into()), dynamic_linking: true, dll_tls_export: false, diff --git a/compiler/rustc_target/src/spec/base/windows_msvc.rs b/compiler/rustc_target/src/spec/base/windows_msvc.rs index 066785a1ae41..963253c3162b 100644 --- a/compiler/rustc_target/src/spec/base/windows_msvc.rs +++ b/compiler/rustc_target/src/spec/base/windows_msvc.rs @@ -1,11 +1,11 @@ -use crate::spec::{TargetOptions, base, cvs}; +use crate::spec::{Env, Os, TargetOptions, base, cvs}; pub(crate) fn opts() -> TargetOptions { let base = base::msvc::opts(); TargetOptions { - os: "windows".into(), - env: "msvc".into(), + os: Os::Windows, + env: Env::Msvc, vendor: "pc".into(), dynamic_linking: true, dll_prefix: "".into(), diff --git a/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs b/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs index 09eb52491f74..0f009fffae8d 100644 --- a/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_uwp_gnu.rs @@ -1,4 +1,4 @@ -use crate::spec::{Cc, LinkArgs, LinkerFlavor, Lld, TargetOptions, add_link_args, base}; +use crate::spec::{Abi, Cc, LinkArgs, LinkerFlavor, Lld, TargetOptions, add_link_args, base}; pub(crate) fn opts() -> TargetOptions { let base = base::windows_gnu::opts(); @@ -23,7 +23,7 @@ pub(crate) fn opts() -> TargetOptions { let late_link_args_static = LinkArgs::new(); TargetOptions { - abi: "uwp".into(), + abi: Abi::Uwp, vendor: "uwp".into(), limit_rdylib_exports: false, late_link_args, diff --git a/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs b/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs index 374918d38a79..15832989f085 100644 --- a/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs +++ b/compiler/rustc_target/src/spec/base/windows_uwp_msvc.rs @@ -1,10 +1,9 @@ -use crate::spec::{LinkerFlavor, Lld, TargetOptions, base}; +use crate::spec::{Abi, LinkerFlavor, Lld, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - let mut opts = base::windows_msvc::opts(); + let mut opts = + TargetOptions { abi: Abi::Uwp, vendor: "uwp".into(), ..base::windows_msvc::opts() }; - opts.abi = "uwp".into(); - opts.vendor = "uwp".into(); opts.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/APPCONTAINER", "mincore.lib"]); opts diff --git a/compiler/rustc_target/src/spec/base/xtensa.rs b/compiler/rustc_target/src/spec/base/xtensa.rs index a7cc748973c4..d1b803d51a2b 100644 --- a/compiler/rustc_target/src/spec/base/xtensa.rs +++ b/compiler/rustc_target/src/spec/base/xtensa.rs @@ -1,10 +1,10 @@ use rustc_abi::Endian; -use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, TargetOptions}; +use crate::spec::{Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, TargetOptions}; pub(crate) fn opts() -> TargetOptions { TargetOptions { - os: "none".into(), + os: Os::None, endian: Endian::Little, c_int_width: 32, linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index ab2879f6c6ea..a972299deeac 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -5,9 +5,9 @@ use super::crt_objects::CrtObjects; use super::{ - Arch, BinaryFormat, CodeModel, DebuginfoKind, FloatAbi, FramePointer, LinkArgsCli, + Abi, Arch, BinaryFormat, CodeModel, DebuginfoKind, Env, FloatAbi, FramePointer, LinkArgsCli, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFlavorCli, LldFlavor, - MergeFunctions, PanicStrategy, RelocModel, RelroLevel, RustcAbi, SanitizerSet, + MergeFunctions, Os, PanicStrategy, RelocModel, RelroLevel, RustcAbi, SanitizerSet, SmallDataThresholdSupport, SplitDebuginfo, StackProbeType, StaticCow, SymbolVisibility, Target, TargetKind, TargetOptions, TargetWarnings, TlsModel, }; @@ -261,6 +261,7 @@ macro_rules! target_option_val { ($attr:ident) => {{ target_option_val!($attr, (stringify!($attr)).replace("_", "-")) }}; ($attr:ident, $json_name:expr) => {{ let name = $json_name; + #[allow(rustc::bad_opt_access)] if default.$attr != target.$attr { d.insert(name.into(), target.$attr.to_json()); } @@ -504,9 +505,9 @@ struct TargetSpecJson { #[serde(rename = "target-c-int-width")] c_int_width: Option, c_enum_min_bits: Option, - os: Option>, - env: Option>, - abi: Option>, + os: Option, + env: Option, + abi: Option, vendor: Option>, linker: Option>, #[serde(rename = "linker-flavor")] diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2dd0fbc4517a..62d3809c2c64 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1884,14 +1884,7 @@ pub enum Arch { X86_64 = "x86_64", Xtensa = "xtensa", } - /// The vast majority of the time, the compiler deals with a fixed set of - /// target architectures, so it is convenient for them to be represented in - /// an enum. However, it is possible to have arbitrary values for the "arch" - /// field in a target JSON file (which can be parsed when `--target` is - /// specified). This might occur, for example, for an out-of-tree codegen - /// backend that supports an architecture that rustc currently doesn't know - /// about. This variant exists as an escape hatch for such cases. - other_variant = Unknown; + other_variant = Other; } impl Arch { @@ -1928,11 +1921,135 @@ pub fn desc_symbol(&self) -> Symbol { Self::X86 => sym::x86, Self::X86_64 => sym::x86_64, Self::Xtensa => sym::xtensa, - Self::Unknown(name) => rustc_span::Symbol::intern(name), + Self::Other(name) => rustc_span::Symbol::intern(name), } } } +crate::target_spec_enum! { + pub enum Os { + Aix = "aix", + AmdHsa = "amdhsa", + Android = "android", + Cuda = "cuda", + Cygwin = "cygwin", + Dragonfly = "dragonfly", + Emscripten = "emscripten", + EspIdf = "espidf", + FreeBsd = "freebsd", + Fuchsia = "fuchsia", + Haiku = "haiku", + HelenOs = "helenos", + Hermit = "hermit", + Horizon = "horizon", + Hurd = "hurd", + Illumos = "illumos", + IOs = "ios", + L4Re = "l4re", + Linux = "linux", + LynxOs178 = "lynxos178", + MacOs = "macos", + Managarm = "managarm", + Motor = "motor", + NetBsd = "netbsd", + None = "none", + Nto = "nto", + NuttX = "nuttx", + OpenBsd = "openbsd", + Psp = "psp", + Psx = "psx", + Redox = "redox", + Rtems = "rtems", + Solaris = "solaris", + SolidAsp3 = "solid_asp3", + TeeOs = "teeos", + Trusty = "trusty", + TvOs = "tvos", + Uefi = "uefi", + VexOs = "vexos", + VisionOs = "visionos", + Vita = "vita", + VxWorks = "vxworks", + Wasi = "wasi", + WatchOs = "watchos", + Windows = "windows", + Xous = "xous", + Zkvm = "zkvm", + Unknown = "unknown", + } + other_variant = Other; +} + +impl Os { + pub fn desc_symbol(&self) -> Symbol { + Symbol::intern(self.desc()) + } +} + +crate::target_spec_enum! { + pub enum Env { + Gnu = "gnu", + MacAbi = "macabi", + Mlibc = "mlibc", + Msvc = "msvc", + Musl = "musl", + Newlib = "newlib", + Nto70 = "nto70", + Nto71 = "nto71", + Nto71IoSock = "nto71_iosock", + Nto80 = "nto80", + Ohos = "ohos", + Relibc = "relibc", + Sgx = "sgx", + Sim = "sim", + P1 = "p1", + P2 = "p2", + P3 = "p3", + Uclibc = "uclibc", + V5 = "v5", + Unspecified = "", + } + other_variant = Other; +} + +impl Env { + pub fn desc_symbol(&self) -> Symbol { + Symbol::intern(self.desc()) + } +} + +crate::target_spec_enum! { + pub enum Abi { + Abi64 = "abi64", + AbiV2 = "abiv2", + AbiV2Hf = "abiv2hf", + Eabi = "eabi", + EabiHf = "eabihf", + ElfV1 = "elfv1", + ElfV2 = "elfv2", + Fortanix = "fortanix", + Ilp32 = "ilp32", + Ilp32e = "ilp32e", + Llvm = "llvm", + MacAbi = "macabi", + Sim = "sim", + SoftFloat = "softfloat", + Spe = "spe", + Uwp = "uwp", + VecDefault = "vec-default", + VecExtAbi = "vec-extabi", + X32 = "x32", + Unspecified = "", + } + other_variant = Other; +} + +impl Abi { + pub fn desc_symbol(&self) -> Symbol { + Symbol::intern(self.desc()) + } +} + /// Everything `rustc` knows about how to compile for a specific target. /// /// Every field here must be specified, and has no default value. @@ -2045,25 +2162,29 @@ pub trait HasX86AbiOpt { /// construction, all its fields logically belong to `Target` and available from `Target` /// through `Deref` impls. #[derive(PartialEq, Clone, Debug)] +#[rustc_lint_opt_ty] pub struct TargetOptions { /// Used as the `target_endian` `cfg` variable. Defaults to little endian. pub endian: Endian, /// Width of c_int type. Defaults to "32". pub c_int_width: u16, - /// OS name to use for conditional compilation (`target_os`). Defaults to "none". - /// "none" implies a bare metal target without `std` library. - /// A couple of targets having `std` also use "unknown" as an `os` value, + /// OS name to use for conditional compilation (`target_os`). Defaults to [`Os::None`]. + /// [`Os::None`] implies a bare metal target without `std` library. + /// A couple of targets having `std` also use [`Os::Unknown`] as their `os` value, /// but they are exceptions. - pub os: StaticCow, - /// Environment name to use for conditional compilation (`target_env`). Defaults to "". - pub env: StaticCow, + pub os: Os, + /// Environment name to use for conditional compilation (`target_env`). Defaults to [`Env::Unspecified`]. + pub env: Env, /// ABI name to distinguish multiple ABIs on the same OS and architecture. For instance, `"eabi"` - /// or `"eabihf"`. Defaults to "". + /// or `"eabihf"`. Defaults to [`Abi::Unspecified`]. /// This field is *not* forwarded directly to LLVM; its primary purpose is `cfg(target_abi)`. /// However, parts of the backend do check this field for specific values to enable special behavior. - pub abi: StaticCow, + pub abi: Abi, /// Vendor name to use for conditional compilation (`target_vendor`). Defaults to "unknown". - pub vendor: StaticCow, + #[rustc_lint_opt_deny_field_access( + "use `Target::is_like_*` instead of this field; see https://github.com/rust-lang/rust/issues/100343 for rationale" + )] + vendor: StaticCow, /// Linker to invoke pub linker: Option>, @@ -2561,9 +2682,9 @@ fn default() -> TargetOptions { TargetOptions { endian: Endian::Little, c_int_width: 32, - os: "none".into(), - env: "".into(), - abi: "".into(), + os: Os::None, + env: Env::Unspecified, + abi: Abi::Unspecified, vendor: "unknown".into(), linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.into()), linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), @@ -2759,7 +2880,7 @@ macro_rules! check_matches { ); check_eq!( self.is_like_solaris, - self.os == "solaris" || self.os == "illumos", + matches!(self.os, Os::Solaris | Os::Illumos), "`is_like_solaris` must be set if and only if `os` is `solaris` or `illumos`" ); check_eq!( @@ -2769,7 +2890,7 @@ macro_rules! check_matches { ); check_eq!( self.is_like_windows, - self.os == "windows" || self.os == "uefi" || self.os == "cygwin", + matches!(self.os, Os::Windows | Os::Uefi | Os::Cygwin), "`is_like_windows` must be set if and only if `os` is `windows`, `uefi` or `cygwin`" ); check_eq!( @@ -2780,7 +2901,7 @@ macro_rules! check_matches { if self.is_like_msvc { check!(self.is_like_windows, "if `is_like_msvc` is set, `is_like_windows` must be set"); } - if self.os == "emscripten" { + if self.os == Os::Emscripten { check!(self.is_like_wasm, "the `emcscripten` os only makes sense on wasm-like targets"); } @@ -2796,12 +2917,12 @@ macro_rules! check_matches { "`linker_flavor` must be `msvc` if and only if `is_like_msvc` is set" ); check_eq!( - self.is_like_wasm && self.os != "emscripten", + self.is_like_wasm && self.os != Os::Emscripten, matches!(self.linker_flavor, LinkerFlavor::WasmLld(..)), "`linker_flavor` must be `wasm-lld` if and only if `is_like_wasm` is set and the `os` is not `emscripten`", ); check_eq!( - self.os == "emscripten", + self.os == Os::Emscripten, matches!(self.linker_flavor, LinkerFlavor::EmCc), "`linker_flavor` must be `em-cc` if and only if `os` is `emscripten`" ); @@ -2928,12 +3049,14 @@ macro_rules! check_matches { // except it and document the reasons. // Keep the default "unknown" vendor instead. check_ne!(self.vendor, "", "`vendor` cannot be empty"); - check_ne!(self.os, "", "`os` cannot be empty"); + if let Os::Other(s) = &self.os { + check!(!s.is_empty(), "`os` cannot be empty"); + } if !self.can_use_os_unknown() { // Keep the default "none" for bare metal targets instead. check_ne!( self.os, - "unknown", + Os::Unknown, "`unknown` os can only be used on particular targets; use `none` for bare-metal targets" ); } @@ -2947,7 +3070,7 @@ macro_rules! check_matches { // BPF: when targeting user space vms (like rbpf), those can load dynamic libraries. // hexagon: when targeting QuRT, that OS can load dynamic libraries. // wasm{32,64}: dynamic linking is inherent in the definition of the VM. - if self.os == "none" + if self.os == Os::None && !matches!(self.arch, Arch::Bpf | Arch::Hexagon | Arch::Wasm32 | Arch::Wasm64) { check!( @@ -2980,7 +3103,7 @@ macro_rules! check_matches { ); } // The UEFI targets do not support dynamic linking but still require PIC (#101377). - if self.relocation_model == RelocModel::Pic && (self.os != "uefi") { + if self.relocation_model == RelocModel::Pic && self.os != Os::Uefi { check!( self.dynamic_linking || self.position_independent_executables, "when the relocation model is `pic`, the target must support dynamic linking or use position-independent executables. \ @@ -3119,7 +3242,7 @@ fn test_target(mut self) { fn can_use_os_unknown(&self) -> bool { self.llvm_target == "wasm32-unknown-unknown" || self.llvm_target == "wasm64-unknown-unknown" - || (self.env == "sgx" && self.vendor == "fortanix") + || (self.env == Env::Sgx && self.vendor == "fortanix") } /// Load a built-in target @@ -3194,15 +3317,7 @@ fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> { return load_file(&p); } - // Leave in a specialized error message for the removed target. - // FIXME: If you see this and it's been a few months after this has been released, - // you can probably remove it. - if target_tuple == "i586-pc-windows-msvc" { - Err("the `i586-pc-windows-msvc` target has been removed. Use the `i686-pc-windows-msvc` target instead.\n\ - Windows 10 (the minimum required OS version) requires a CPU baseline of at least i686 so you can safely switch".into()) - } else { - Err(format!("could not find specification for target {target_tuple:?}")) - } + Err(format!("could not find specification for target {target_tuple:?}")) } TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents), } @@ -3301,7 +3416,7 @@ pub fn object_architecture( | Arch::SpirV | Arch::Wasm32 | Arch::Wasm64 - | Arch::Unknown(_) => return None, + | Arch::Other(_) => return None, }) } @@ -3323,6 +3438,10 @@ pub fn max_reliable_alignment(&self) -> Align { Align::MAX } } + + pub fn vendor_symbol(&self) -> Symbol { + Symbol::intern(&self.vendor) + } } /// Either a target tuple string or a path to a JSON file. diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs index ecfd6ebba863..da3d7c7d99db 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_darwin.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("macos", Arch::Arm64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::MacOs, Arch::Arm64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs index 85b4a0614a85..e6c64add9ecb 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::Arm64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::Arm64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs index 4d6a3103ee30..28d22e022c4e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::Arm64, TargetEnv::MacCatalyst); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::Arm64, TargetEnv::MacCatalyst); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs index 2924ba5ebb10..3b6a075588a8 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_sim.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::Arm64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::Arm64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs index 7aef6f96e1c5..b4e60f769b3b 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("tvos", Arch::Arm64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::TvOs, Arch::Arm64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs index f0d17db873bf..0f2f73c50d0b 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_tvos_sim.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("tvos", Arch::Arm64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::TvOs, Arch::Arm64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs index 22ce52e637f3..b7ddd58fbb32 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("visionos", Arch::Arm64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::VisionOs, Arch::Arm64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs index 21739ba9fdb7..9ad313f14922 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_visionos_sim.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("visionos", Arch::Arm64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::VisionOs, Arch::Arm64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs index 2e88f95f1dd8..f0a5a616a6d2 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("watchos", Arch::Arm64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::WatchOs, Arch::Arm64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs index 272dc682dc0e..db73687a715f 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_watchos_sim.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("watchos", Arch::Arm64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::WatchOs, Arch::Arm64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs index 63a13b8a7d11..4abd5553eb97 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_linux_gnu_ilp32.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - abi: "ilp32".into(), + abi: Abi::Ilp32, features: "+v8a,+outline-atomics".into(), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs index 8ba909a79601..3b899b13d6d0 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs @@ -8,13 +8,13 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, - TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, + Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "softfloat".into(), + abi: Abi::SoftFloat, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon,-fp-armv8".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs b/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs index d6b0e0919512..77ed9a6b82c1 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_kmc_solid_asp3.rs @@ -1,7 +1,7 @@ use crate::spec::{Arch, RelocModel, StackProbeType, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let base = base::solid::opts("asp3"); + let base = base::solid::opts(); Target { llvm_target: "aarch64-unknown-none".into(), metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs index eb8144dd44e2..32bdf1d97042 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_nintendo_switch_freestanding.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelroLevel, StackProbeType, Target, TargetMetadata, - TargetOptions, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelroLevel, StackProbeType, Target, + TargetMetadata, TargetOptions, }; const LINKER_SCRIPT: &str = include_str!("./aarch64_nintendo_switch_freestanding_linker_script.ld"); @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), link_script: Some(LINKER_SCRIPT.into()), - os: "horizon".into(), + os: Os::Horizon, vendor: "nintendo".into(), max_atomic_width: Some(128), stack_probes: StackProbeType::Inline, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs index 9b7db11e2f29..7b3a234cd9e8 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_pc_windows_gnullvm.rs @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { metadata: TargetMetadata { description: Some("ARM64 MinGW (Windows 10+), LLVM ABI".into()), tier: Some(2), - host_tools: Some(false), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs index 5020d893c86b..22f2717b56b4 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_gnu_ilp32.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, FramePointer, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), arch: Arch::AArch64, options: TargetOptions { - abi: "ilp32".into(), + abi: Abi::Ilp32, features: "+v8a,+outline-atomics".into(), // the AAPCS64 expects use of non-leaf frame pointers per // https://github.com/ARM-software/abi-aa/blob/4492d1570eb70c8fd146623e0db65b2d241f12e7/aapcs64/aapcs64.rst#the-frame-pointer diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index 387fef3074d4..c0fd021497b6 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,13 +7,13 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, - TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, + Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "softfloat".into(), + abi: Abi::SoftFloat, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), features: "+v8a,+strict-align,-neon,-fp-armv8".into(), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs index b26e6f19e1ab..e1f29f832f0b 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs @@ -1,11 +1,11 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::aarch64(); target.metadata.description = Some("ARM64 QNX Neutrino 7.0 RTOS".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); - target.options.env = "nto70".into(); + target.options.env = Env::Nto70; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs index 3a78952c36c4..1baa56630d3a 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs @@ -1,5 +1,5 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::aarch64(); @@ -7,6 +7,6 @@ pub(crate) fn target() -> Target { Some("ARM64 QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); - target.options.env = "nto71".into(); + target.options.env = Env::Nto71; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs index 4964f4078f5c..80ae93247a3f 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs @@ -1,5 +1,5 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::aarch64(); @@ -7,6 +7,6 @@ pub(crate) fn target() -> Target { Some("ARM64 QNX Neutrino 7.1 RTOS with io-sock network stack".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::Aarch64); - target.options.env = "nto71_iosock".into(); + target.options.env = Env::Nto71IoSock; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs index 5b820681efe9..5d265087c4a2 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs @@ -1,11 +1,11 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::aarch64(); target.metadata.description = Some("ARM64 QNX Neutrino 8.0 RTOS".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); - target.options.env = "nto80".into(); + target.options.env = Env::Nto80; target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs index 8bcc3e0ae16d..b8618050074d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs @@ -7,8 +7,8 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, - TargetMetadata, TargetOptions, cvs, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, + Target, TargetMetadata, TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -28,7 +28,7 @@ pub(crate) fn target() -> Target { stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, ..Default::default() }; Target { diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs index 3d0560eb7963..7dfa29787c86 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_trusty.rs @@ -1,7 +1,7 @@ // Trusty OS target for AArch64. use crate::spec::{ - Arch, LinkSelfContainedDefault, PanicStrategy, RelroLevel, Target, TargetMetadata, + Arch, LinkSelfContainedDefault, Os, PanicStrategy, RelroLevel, Target, TargetMetadata, TargetOptions, }; @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { executables: true, max_atomic_width: Some(128), panic_strategy: PanicStrategy::Abort, - os: "trusty".into(), + os: Os::Trusty, position_independent_executables: true, static_position_independent_executables: true, crt_static_default: true, diff --git a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs index d80a3ffd0c7f..828d853ac65e 100644 --- a/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs +++ b/compiler/rustc_target/src/spec/targets/amdgcn_amd_amdhsa.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, Target, TargetMetadata, TargetOptions, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { pointer_width: 64, options: TargetOptions { - os: "amdhsa".into(), + os: Os::AmdHsa, vendor: "amd".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs index 564ac2cd7081..dab8cd157d2b 100644 --- a/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64_32_apple_watchos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("watchos", Arch::Arm64_32, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::WatchOs, Arch::Arm64_32, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs index 86e178a95728..ab8f369f1bee 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_darwin.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("macos", Arch::Arm64e, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::MacOs, Arch::Arm64e, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs index dae3f77d7ae3..4ee4a16ab747 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_ios.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::Arm64e, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::Arm64e, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs index a99fc5dc68c9..c09591be95f9 100644 --- a/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/arm64e_apple_tvos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("tvos", Arch::Arm64e, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::TvOs, Arch::Arm64e, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/arm_linux_androideabi.rs b/compiler/rustc_target/src/spec/targets/arm_linux_androideabi.rs index dad8f5e1c552..7dc6509fc823 100644 --- a/compiler/rustc_target/src/spec/targets/arm_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_linux_androideabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, SanitizerSet, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, SanitizerSet, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // https://developer.android.com/ndk/guides/abis.html#armeabi features: "+strict-align,+v5te".into(), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabi.rs index 55136255babc..cc63ffa2e2a5 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+strict-align,+v6".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs index 087d4cfe800d..e003a65be393 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_gnueabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+strict-align,+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs index e5224e4cce48..25ef767c4b10 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // Most of these settings are copied from the arm_unknown_linux_gnueabi // target. diff --git a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs index 0e958455994c..76051403a559 100644 --- a/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/arm_unknown_linux_musleabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Most of these settings are copied from the arm_unknown_linux_gnueabihf // target. diff --git a/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs index a9794f80e687..73374bcdf0b1 100644 --- a/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armeb_unknown_linux_gnueabi.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+strict-align,+v8,+crc".into(), endian: Endian::Big, diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs index bf9519b8a105..849a0f4c9a68 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabi.rs @@ -3,7 +3,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), endian: Endian::Big, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), diff --git a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs index 75a4ab4d877a..0cf3880cadc9 100644 --- a/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armebv7r_none_eabihf.rs @@ -3,7 +3,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), endian: Endian::Big, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), diff --git a/compiler/rustc_target/src/spec/targets/armv4t_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armv4t_none_eabi.rs index 2bb35bd6ea20..129b639c5248 100644 --- a/compiler/rustc_target/src/spec/targets/armv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv4t_none_eabi.rs @@ -10,7 +10,7 @@ //! `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs, }; @@ -36,7 +36,7 @@ pub(crate) fn target() -> Target { */ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs index 1e13e02045a1..8c386011f89a 100644 --- a/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv4t_unknown_linux_gnueabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+soft-float,+strict-align".into(), // Atomic operations provided by compiler-builtins diff --git a/compiler/rustc_target/src/spec/targets/armv5te_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_none_eabi.rs index 28f2533d7a36..7cd571b91479 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_none_eabi.rs @@ -1,6 +1,8 @@ //! Targets the ARMv5TE, with code as `a32` code by default. -use crate::spec::{Arch, FloatAbi, FramePointer, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{ + Abi, Arch, FloatAbi, FramePointer, Target, TargetMetadata, TargetOptions, base, cvs, +}; pub(crate) fn target() -> Target { Target { @@ -25,7 +27,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // extra args passed to the external assembler (assuming `arm-none-eabi-as`): // * activate t32/a32 interworking diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs index 763d68f0495a..d1fded203671 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_gnueabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+soft-float,+strict-align".into(), // Atomic operations provided by compiler-builtins diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs index a08d1a1c63ef..2daf2ab4d8fe 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_musleabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+soft-float,+strict-align".into(), // Atomic operations provided by compiler-builtins diff --git a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs index 1719ab7314a4..b05df3a77a43 100644 --- a/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv5te_unknown_linux_uclibceabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+soft-float,+strict-align".into(), // Atomic operations provided by compiler-builtins diff --git a/compiler/rustc_target/src/spec/targets/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/armv6_unknown_freebsd.rs index 2da15219d6ce..20d91d6968a6 100644 --- a/compiler/rustc_target/src/spec/targets/armv6_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/armv6_unknown_freebsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv6_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv6_unknown_netbsd_eabihf.rs index fbd55a371487..0316bd92999b 100644 --- a/compiler/rustc_target/src/spec/targets/armv6_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv6_unknown_netbsd_eabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v6,+vfp2,-d32".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs index a79c64287012..ed2ab47c0e24 100644 --- a/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs +++ b/compiler/rustc_target/src/spec/targets/armv6k_nintendo_3ds.rs @@ -1,11 +1,11 @@ use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, RelocModel, Target, TargetMetadata, TargetOptions, cvs, + Abi, Arch, Cc, Env, FloatAbi, LinkerFlavor, Lld, Os, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; /// A base target for Nintendo 3DS devices using the devkitARM toolchain. /// /// Requires the devkitARM toolchain for 3DS targets on the host system. - pub(crate) fn target() -> Target { let pre_link_args = TargetOptions::link_args( LinkerFlavor::Gnu(Cc::Yes, Lld::No), @@ -25,11 +25,11 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - os: "horizon".into(), - env: "newlib".into(), + os: Os::Horizon, + env: Env::Newlib, vendor: "nintendo".into(), cpu: "mpcore".into(), - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), families: cvs!["unix"], linker: Some("arm-none-eabi-gcc".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs index ce6c3c8a6c14..ad8a14fdad53 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_linux_androideabi.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, SanitizerSet, Target, TargetMetadata, TargetOptions, - base, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, SanitizerSet, Target, TargetMetadata, + TargetOptions, base, }; // This target if is for the baseline of the Android v7a ABI @@ -26,7 +26,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb-mode,+thumb2,+vfp3,-d32,-neon".into(), supported_sanitizers: SanitizerSet::ADDRESS, diff --git a/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs index 0958f617663f..382658a87b54 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_rtems_eabihf.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, - TargetOptions, cvs, + Abi, Arch, Cc, Env, FloatAbi, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, + TargetMetadata, TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -17,9 +17,9 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - os: "rtems".into(), + os: Os::Rtems, families: cvs!["unix"], - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), linker: None, @@ -32,7 +32,7 @@ pub(crate) fn target() -> Target { c_enum_min_bits: Some(8), eh_frame_header: false, no_default_libraries: false, - env: "newlib".into(), + env: Env::Newlib, ..Default::default() }, } diff --git a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs index 6c02ec26fea4..20e5c0acca96 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_sony_vita_newlibeabihf.rs @@ -1,13 +1,13 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, RelocModel, Target, TargetMetadata, TargetOptions, cvs, + Abi, Arch, Cc, Env, FloatAbi, LinkerFlavor, Lld, Os, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; /// A base target for PlayStation Vita devices using the VITASDK toolchain (using newlib). /// /// Requires the VITASDK toolchain on the host system. - pub(crate) fn target() -> Target { let pre_link_args = TargetOptions::link_args( LinkerFlavor::Gnu(Cc::Yes, Lld::No), @@ -29,12 +29,12 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - os: "vita".into(), + os: Os::Vita, endian: Endian::Little, c_int_width: 32, - env: "newlib".into(), + env: Env::Newlib, vendor: "sony".into(), - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No), no_default_libraries: false, diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs index 63aee180299e..0a8432ea8fe9 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_freebsd.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabi.rs index 6288095fcf4c..8fad2ce19632 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for glibc Linux on ARMv7 without thumb-mode, NEON or // hardfloat. @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs index 57374b8af9d7..cbf6d5d08dd0 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_gnueabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for glibc Linux on ARMv7 without NEON or // thumb-mode. See the thumbv7neon variant for enabling both. @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs index 20545aba821f..9bf0b4fd7e4c 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for musl Linux on ARMv7 without thumb-mode, NEON or // hardfloat. @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs index 7028f430ff8f..9b5fcd87b120 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_musleabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for musl Linux on ARMv7 without thumb-mode or NEON. @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { // Most of these settings are copied from the armv7_unknown_linux_gnueabihf // target. options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs index f0946183fcad..318170fe0f8d 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for OpenHarmony on ARMv7 Linux with thumb-mode, but no NEON or // hardfloat. @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabi.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabi.rs index c2cc2526ec3f..37a70da09c51 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for uclibc Linux on ARMv7 without NEON, // thumb-mode or hardfloat. @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb2,+soft-float,-neon".into(), cpu: "generic".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs index 0649ba3c5ed5..927c910da596 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_uclibceabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for uclibc Linux on ARMv7 without NEON or // thumb-mode. See the thumbv7neon variant for enabling both. @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { cpu: "generic".into(), max_atomic_width: Some(64), mcount: "_mcount".into(), - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), ..base }, diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs index d6cb517759d9..788944e64c84 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_netbsd_eabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs index 765d1982a679..fb3954abb286 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_trusty.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, FloatAbi, LinkSelfContainedDefault, PanicStrategy, RelroLevel, Target, TargetMetadata, - TargetOptions, + Abi, Arch, FloatAbi, LinkSelfContainedDefault, Os, PanicStrategy, RelroLevel, Target, + TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { @@ -19,12 +19,12 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb2,+soft-float,-neon".into(), max_atomic_width: Some(64), mcount: "\u{1}mcount".into(), - os: "trusty".into(), + os: Os::Trusty, link_self_contained: LinkSelfContainedDefault::InferredForMusl, dynamic_linking: false, executables: true, diff --git a/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs index ef261150f220..e43e4fe85cad 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_wrs_vxworks_eabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs index 3ceeef4e0696..bd18d6d8ae90 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabi.rs @@ -1,7 +1,7 @@ -use crate::spec::{Arch, FloatAbi, RelocModel, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, RelocModel, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let base = base::solid::opts("asp3"); + let base = base::solid::opts(); Target { llvm_target: "armv7a-none-eabi".into(), metadata: TargetMetadata { @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+soft-float,+thumb2,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs index 65af0bda5dfe..9433cbd46627 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_kmc_solid_asp3_eabihf.rs @@ -1,7 +1,7 @@ -use crate::spec::{Arch, FloatAbi, RelocModel, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, RelocModel, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let base = base::solid::opts("asp3"); + let base = base::solid::opts(); Target { llvm_target: "armv7a-none-eabihf".into(), metadata: TargetMetadata { @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker: Some("arm-kmc-eabi-gcc".into()), features: "+v7,+vfp3,-d32,+thumb2,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_none_eabi.rs index 3d4328abe9b4..13a95a113b13 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_none_eabi.rs @@ -15,13 +15,13 @@ // linking. rationale: matches `thumb` targets use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs index e3c5dce88fc1..e03fe3e78b9d 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_none_eabihf.rs @@ -6,13 +6,13 @@ // `thumb` & `aarch64` targets. use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs index 93302540795d..5ab74e0533ba 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs @@ -5,13 +5,13 @@ // configuration without hardware floating point support. use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, - TargetOptions, cvs, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, + TargetMetadata, TargetOptions, cvs, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { emit_debug_gdb_scripts: false, c_enum_min_bits: Some(8), families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, ..Default::default() }; Target { diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs index 864517557c30..4ac07e24a48b 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs @@ -5,13 +5,13 @@ // configuration with hardware floating point support. use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, - TargetOptions, cvs, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, + TargetMetadata, TargetOptions, cvs, }; pub(crate) fn target() -> Target { let opts = TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), @@ -23,7 +23,7 @@ pub(crate) fn target() -> Target { emit_debug_gdb_scripts: false, c_enum_min_bits: Some(8), families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, ..Default::default() }; Target { diff --git a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs index 6da1a35474ac..b83d7b090689 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_vex_v5.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, - TargetOptions, + Abi, Arch, Cc, Env, FloatAbi, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, + TargetMetadata, TargetOptions, }; const LINKER_SCRIPT: &str = include_str!("./armv7a_vex_v5_linker_script.ld"); @@ -8,10 +8,10 @@ pub(crate) fn target() -> Target { let opts = TargetOptions { vendor: "vex".into(), - env: "v5".into(), - os: "vexos".into(), + env: Env::V5, + os: Os::VexOs, cpu: "cortex-a9".into(), - abi: "eabihf".into(), + abi: Abi::EabiHf, is_like_vexos: true, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), diff --git a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs index df58559848a0..2547cf19004b 100644 --- a/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs +++ b/compiler/rustc_target/src/spec/targets/armv7k_apple_watchos.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("watchos", Arch::Armv7k, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::WatchOs, Arch::Armv7k, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7r_none_eabi.rs index bd07e724901c..b490b716f4b0 100644 --- a/compiler/rustc_target/src/spec/targets/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7r_none_eabi.rs @@ -1,7 +1,7 @@ // Targets the Little-endian Cortex-R4/R5 processor (ARMv7-R) use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs index 2f89f8042c4a..7a3eb3b811a6 100644 --- a/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7r_none_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Little-endian Cortex-R4F/R5F processor (ARMv7-R) use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs index 63259043b73d..cd9da114acfb 100644 --- a/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/armv7s_apple_ios.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::Armv7s, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::Armv7s, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs index 58bd9fafde96..423bf260d89c 100644 --- a/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv8r_none_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Little-endian Cortex-R52 processor (ARMv8-R) use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -19,7 +19,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2.rs b/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2.rs index 1f7f7899f3a6..176b853493ef 100644 --- a/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2.rs +++ b/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base}; // This target is for glibc Linux on Csky @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32".into(), arch: Arch::CSky, options: TargetOptions { - abi: "abiv2".into(), + abi: Abi::AbiV2, features: "+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(), late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a"]), max_atomic_width: Some(32), diff --git a/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2hf.rs b/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2hf.rs index 68fdb69fe9d8..5af54493063d 100644 --- a/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2hf.rs +++ b/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2hf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base}; // This target is for glibc Linux on Csky @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32".into(), arch: Arch::CSky, options: TargetOptions { - abi: "abiv2hf".into(), + abi: Abi::AbiV2Hf, cpu: "ck860fv".into(), features: "+hard-float,+hard-float-abi,+2e3,+3e7,+7e10,+cache,+dsp1e2,+dspe60,+e1,+e2,+edsp,+elrw,+hard-tp,+high-registers,+hwdiv,+mp,+mp1e2,+nvic,+trust".into(), late_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-l:libatomic.a", "-mhard-float"]), diff --git a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs index a919be765a27..3992e7aa9dbc 100644 --- a/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/i386_apple_ios.rs @@ -1,10 +1,10 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { // i386-apple-ios is a simulator target, even though it isn't declared // that way in the target name like the other ones... - let (opts, llvm_target, arch) = base("ios", Arch::I386, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::I386, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs index f04e3c2c2a5b..508afd479352 100644 --- a/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i586_unknown_linux_gnu.rs @@ -5,5 +5,11 @@ pub(crate) fn target() -> Target { base.rustc_abi = None; // overwrite the SSE2 ABI set by the base target base.cpu = "pentium".into(); base.llvm_target = "i586-unknown-linux-gnu".into(); + base.metadata = crate::spec::TargetMetadata { + description: Some("32-bit Linux (kernel 3.2, glibc 2.17+)".into()), + tier: Some(2), + host_tools: Some(false), + std: Some(true), + }; base } diff --git a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs index 96c477d52367..1c3481928a85 100644 --- a/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/i686_apple_darwin.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("macos", Arch::I686, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::MacOs, Arch::I686, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs index af7f3b6b8563..7184a54961e2 100644 --- a/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i686_pc_nto_qnx700.rs @@ -1,5 +1,5 @@ use crate::spec::base::nto_qnx; -use crate::spec::{Arch, RustcAbi, StackProbeType, Target, TargetOptions, base}; +use crate::spec::{Arch, Env, RustcAbi, StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { let mut meta = nto_qnx::meta(); @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { nto_qnx::ApiVariant::Default, nto_qnx::Arch::I586, ), - env: "nto70".into(), + env: Env::Nto70, vendor: "pc".into(), stack_probes: StackProbeType::Inline, ..base::nto_qnx::opts() diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs index c026f915906c..43ebc54c2b40 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_gnu.rs @@ -1,15 +1,18 @@ use crate::spec::{ - Arch, Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, base, + Arch, Cc, FramePointer, LinkerFlavor, Lld, RustcAbi, Target, TargetMetadata, TargetOptions, + base, }; pub(crate) fn target() -> Target { - let mut base = base::windows_gnu::opts(); - base.vendor = "win7".into(); - base.rustc_abi = Some(RustcAbi::X86Sse2); - base.cpu = "pentium4".into(); - base.max_atomic_width = Some(64); - base.frame_pointer = FramePointer::Always; // Required for backtraces - base.linker = Some("i686-w64-mingw32-gcc".into()); + let mut base = TargetOptions { + vendor: "win7".into(), + rustc_abi: Some(RustcAbi::X86Sse2), + cpu: "pentium4".into(), + max_atomic_width: Some(64), + frame_pointer: FramePointer::Always, // Required for backtraces + linker: Some("i686-w64-mingw32-gcc".into()), + ..base::windows_gnu::opts() + }; // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index 68131bf65426..8994c245d7b9 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -1,18 +1,22 @@ -use crate::spec::{Arch, LinkerFlavor, Lld, RustcAbi, SanitizerSet, Target, TargetMetadata, base}; +use crate::spec::{ + Arch, LinkerFlavor, Lld, RustcAbi, SanitizerSet, Target, TargetMetadata, TargetOptions, base, +}; pub(crate) fn target() -> Target { - let mut base = base::windows_msvc::opts(); - base.vendor = "win7".into(); - base.rustc_abi = Some(RustcAbi::X86Sse2); - base.cpu = "pentium4".into(); - base.max_atomic_width = Some(64); - base.supported_sanitizers = SanitizerSet::ADDRESS; - // On Windows 7 32-bit, the alignment characteristic of the TLS Directory - // don't appear to be respected by the PE Loader, leading to crashes. As - // a result, let's disable has_thread_local to make sure TLS goes through - // the emulation layer. - // See https://github.com/rust-lang/rust/issues/138903 - base.has_thread_local = false; + let mut base = TargetOptions { + vendor: "win7".into(), + rustc_abi: Some(RustcAbi::X86Sse2), + cpu: "pentium4".into(), + max_atomic_width: Some(64), + supported_sanitizers: SanitizerSet::ADDRESS, + // On Windows 7 32-bit, the alignment characteristic of the TLS Directory + // don't appear to be respected by the PE Loader, leading to crashes. As + // a result, let's disable has_thread_local to make sure TLS goes through + // the emulation layer. + // See https://github.com/rust-lang/rust/issues/138903 + has_thread_local: false, + ..base::windows_msvc::opts() + }; base.add_pre_link_args( LinkerFlavor::Msvc(Lld::No), diff --git a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs index 4ac9241fb8c6..896cf0f59d58 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch32_unknown_none_softfloat.rs @@ -1,5 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -17,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cpu: "generic".into(), features: "-f,-d".into(), - abi: "softfloat".into(), + abi: Abi::SoftFloat, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), llvm_abiname: "ilp32s".into(), diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs index e33d8c954fd0..efeb864de1a7 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + Abi, Arch, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, }; @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { cpu: "generic".into(), features: "-f,-d".into(), - abi: "softfloat".into(), + abi: Abi::SoftFloat, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), llvm_abiname: "lp64s".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs index 0cd93429f6e2..f90e689912e1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_openwrt_linux_musl.rs @@ -2,7 +2,7 @@ use rustc_abi::Endian; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); @@ -24,7 +24,7 @@ pub(crate) fn target() -> Target { arch: Arch::Mips64, options: TargetOptions { vendor: "openwrt".into(), - abi: "abi64".into(), + abi: Abi::Abi64, endian: Endian::Big, mcount: "_mcount".into(), llvm_abiname: "n64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs index 4d06466e6553..ecb66b43712c 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_gnuabi64.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, endian: Endian::Big, // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs index a37508c34dbd..ccb13fee37a1 100644 --- a/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64_unknown_linux_muslabi64.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); @@ -20,7 +20,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, endian: Endian::Big, mcount: "_mcount".into(), llvm_abiname: "n64".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs index 9ece84ed5442..2457bd838c57 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_gnuabi64.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".into(), features: "+mips64r2,+xgot".into(), diff --git a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs index 759a04d24b7c..41353729df8f 100644 --- a/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mips64el_unknown_linux_muslabi64.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { let mut base = base::linux_musl::opts(); @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, mcount: "_mcount".into(), llvm_abiname: "n64".into(), ..base diff --git a/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs index 200735f4e0fb..370e585f65ad 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_sony_psp.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, RelocModel, Target, TargetMetadata, TargetOptions, cvs, + Arch, Cc, LinkerFlavor, Lld, Os, RelocModel, Target, TargetMetadata, TargetOptions, cvs, }; // The PSP has custom linker requirements. @@ -24,7 +24,7 @@ pub(crate) fn target() -> Target { arch: Arch::Mips, options: TargetOptions { - os: "psp".into(), + os: Os::Psp, vendor: "sony".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), cpu: "mips2".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsel_sony_psx.rs b/compiler/rustc_target/src/spec/targets/mipsel_sony_psx.rs index b6d30be589ea..06631fdefb4a 100644 --- a/compiler/rustc_target/src/spec/targets/mipsel_sony_psx.rs +++ b/compiler/rustc_target/src/spec/targets/mipsel_sony_psx.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - cvs, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { // of functionality post load, so we still declare it as `cfg!(target_os = "psx")`. // // See for details. - os: "psx".into(), + os: Os::Psx, vendor: "sony".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), cpu: "mips1".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs index 3a95f78482bb..fb97a2e65eab 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -1,6 +1,6 @@ use rustc_abi::Endian; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64r6, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, endian: Endian::Big, // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".into(), diff --git a/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs index 8e5bf3d36559..556962458fa5 100644 --- a/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/targets/mipsisa64r6el_unknown_linux_gnuabi64.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), arch: Arch::Mips64r6, options: TargetOptions { - abi: "abi64".into(), + abi: Abi::Abi64, // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".into(), features: "+mips64r6".into(), diff --git a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs index 5bbf40b5fadd..35003c4d4dd6 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, LinkSelfContainedDefault, LinkerFlavor, MergeFunctions, PanicStrategy, Target, + Arch, LinkSelfContainedDefault, LinkerFlavor, MergeFunctions, Os, PanicStrategy, Target, TargetMetadata, TargetOptions, }; @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { pointer_width: 64, options: TargetOptions { - os: "cuda".into(), + os: Os::Cuda, vendor: "nvidia".into(), linker_flavor: LinkerFlavor::Ptx, // The linker can be installed from `crates.io`. diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs index de94d2023452..f5d5698713c2 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_freebsd.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs index 18a3f059039d..e2dada235271 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnu.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv1".into(); + base.abi = Abi::ElfV1; base.llvm_abiname = "elfv1".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs index 64d4f58a4664..b663ddf962ea 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_musl.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs index 1034a1603026..082358e82be1 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_unknown_openbsd.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index fb40316d75dc..f576d2372bfa 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv1".into(); + base.abi = Abi::ElfV1; base.llvm_abiname = "elfv1".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs index 7ae74dc882fd..b2713af3bd56 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_freebsd.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs index 4d1e3dfae456..550ad563e6f2 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_gnu.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -8,7 +8,7 @@ pub(crate) fn target() -> Target { base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs index 3813e29229d9..38e3d09c2c35 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64le_unknown_linux_musl.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; // FIXME(compiler-team#422): musl targets should be dynamically linked by default. base.crt_static_default = true; - base.abi = "elfv2".into(); + base.abi = Abi::ElfV2; base.llvm_abiname = "elfv2".into(); Target { diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs index 8bbed94f2f7a..49620305e0e8 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), arch: Arch::PowerPC, options: TargetOptions { - abi: "spe".into(), + abi: Abi::Spe, endian: Endian::Big, features: "+secure-plt,+msync".into(), mcount: "_mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs index 0b6535e5debe..210a80fe422e 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_muslspe.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), arch: Arch::PowerPC, options: TargetOptions { - abi: "spe".into(), + abi: Abi::Spe, endian: Endian::Big, features: "+msync".into(), mcount: "_mcount".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs index 85de246605d7..041580f2bdaf 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks_spe.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, TargetOptions, base, }; pub(crate) fn target() -> Target { @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), arch: Arch::PowerPC, options: TargetOptions { - abi: "spe".into(), + abi: Abi::Spe, endian: Endian::Big, // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2 features: "+secure-plt,+msync".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs index 1f8b2cacd9b5..65dbd391db74 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -1,9 +1,9 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { - let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -19,12 +19,12 @@ pub(crate) fn target() -> Target { arch: Arch::RiscV32, options: TargetOptions { - abi: abi.into(), + abi: Abi::Ilp32e, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: abi.into(), + llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs index 1ae868e9b135..0826326a2b4b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -1,9 +1,9 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { - let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -19,12 +19,12 @@ pub(crate) fn target() -> Target { arch: Arch::RiscV32, options: TargetOptions { - abi: abi.into(), + abi: Abi::Ilp32e, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: abi.into(), + llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs index e0958e0f9c5e..e37a44e1edc4 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -1,9 +1,9 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Abi, Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { - let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -19,12 +19,12 @@ pub(crate) fn target() -> Target { arch: Arch::RiscV32, options: TargetOptions { - abi: abi.into(), + abi: Abi::Ilp32e, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: abi.into(), + llvm_abiname: "ilp32e".into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+c,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs index c0372035cd7e..af25334a2e53 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32im_risc0_zkvm_elf.rs @@ -1,5 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -16,7 +17,7 @@ pub(crate) fn target() -> Target { arch: Arch::RiscV32, options: TargetOptions { - os: "zkvm".into(), + os: Os::Zkvm, vendor: "risc0".into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs index 6b3e5a47ad87..11a5626c5641 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_esp_espidf.rs @@ -1,4 +1,6 @@ -use crate::spec::{Arch, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{ + Arch, Env, Os, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn target() -> Target { Target { @@ -15,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), linker: Some("riscv32-esp-elf-gcc".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs index 70eb7cb78d65..7205fb61de74 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - cvs, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs index e50de1cc9371..363e465e2ef4 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_xous_elf.rs @@ -1,5 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -16,7 +17,7 @@ pub(crate) fn target() -> Target { arch: Arch::RiscV32, options: TargetOptions { - os: "xous".into(), + os: Os::Xous, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs index 196a3de95cfb..b3a3ab9b7543 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs @@ -1,4 +1,6 @@ -use crate::spec::{Arch, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{ + Arch, Env, Os, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn target() -> Target { Target { @@ -15,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), linker: Some("riscv32-esp-elf-gcc".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs index f7b8bd49e527..0bba2e47dc85 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - cvs, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs index 66c64667d0dc..f903bbdb04bf 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_esp_espidf.rs @@ -1,4 +1,6 @@ -use crate::spec::{Arch, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{ + Arch, Env, Os, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn target() -> Target { Target { @@ -15,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), linker: Some("riscv32-esp-elf-gcc".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs index a10ddb2d0569..d1d73842703e 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - cvs, + Arch, Cc, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, cvs, }; pub(crate) fn target() -> Target { @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs index 93dd6dc7ed1c..7850cc8105d9 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, Target, + Arch, Cc, CodeModel, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, SanitizerSet, Target, TargetMetadata, TargetOptions, cvs, }; @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), llvm_abiname: "lp64d".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs index 25e3e72f9de9..b1a1817bbe1f 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, Target, + Arch, Cc, CodeModel, LinkerFlavor, Lld, Os, PanicStrategy, RelocModel, SanitizerSet, Target, TargetMetadata, TargetOptions, cvs, }; @@ -18,7 +18,7 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), + os: Os::NuttX, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv64".into(), diff --git a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs index 79035c791156..a6067f64c686 100644 --- a/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/sparcv9_sun_solaris.rs @@ -1,22 +1,24 @@ use rustc_abi::Endian; -use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let mut base = base::solaris::opts(); - base.endian = Endian::Big; + let mut base = TargetOptions { + endian: Endian::Big, + // llvm calls this "v9" + cpu: "v9".into(), + vendor: "sun".into(), + max_atomic_width: Some(64), + ..base::solaris::opts() + }; base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64"]); - // llvm calls this "v9" - base.cpu = "v9".into(); - base.vendor = "sun".into(); - base.max_atomic_width = Some(64); Target { llvm_target: "sparcv9-sun-solaris".into(), metadata: TargetMetadata { description: Some("SPARC Solaris 11.4".into()), tier: Some(2), - host_tools: Some(false), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv4t_none_eabi.rs index 7a183bf5414e..0498c55e9827 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv4t_none_eabi.rs @@ -10,8 +10,8 @@ //! `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. use crate::spec::{ - Arch, FloatAbi, FramePointer, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - base, cvs, + Abi, Arch, FloatAbi, FramePointer, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, base, cvs, }; pub(crate) fn target() -> Target { @@ -36,7 +36,7 @@ pub(crate) fn target() -> Target { */ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // extra args passed to the external assembler (assuming `arm-none-eabi-as`): diff --git a/compiler/rustc_target/src/spec/targets/thumbv5te_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv5te_none_eabi.rs index b20be27665e0..a07e9127a36e 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv5te_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv5te_none_eabi.rs @@ -1,6 +1,8 @@ //! Targets the ARMv5TE, with code as `t32` code by default. -use crate::spec::{Arch, FloatAbi, FramePointer, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{ + Abi, Arch, FloatAbi, FramePointer, Target, TargetMetadata, TargetOptions, base, cvs, +}; pub(crate) fn target() -> Target { Target { @@ -25,7 +27,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // extra args passed to the external assembler (assuming `arm-none-eabi-as`): // * activate t32/a32 interworking diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_none_eabi.rs index 684e465af4a6..836b2ff63a16 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv6m_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_none_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs index c0dd931e66a4..fa0154d65d68 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -17,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs index 335c4c1af510..7c1adc932626 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs @@ -4,7 +4,7 @@ // and will use software floating point operations. This matches the NuttX EABI // configuration without hardware floating point support. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -21,8 +21,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // Cortex-A7/A8/A9 with software floating point features: "+soft-float,-neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs index 0a0dbaf12554..0e6d5b1f2ea9 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs @@ -7,7 +7,7 @@ // This target uses the "hard" floating convention (ABI) where floating point values // are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.). -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -24,8 +24,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabihf".into(), + os: Os::NuttX, + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Cortex-A7/A8/A9 support VFPv3-D32/VFPv4-D32 with optional double-precision // and NEON SIMD instructions diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabi.rs index c67e955d0856..9e0f09b45191 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabi.rs @@ -9,7 +9,7 @@ // To opt-in to hardware accelerated floating point operations, you can use, for example, // `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -25,7 +25,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs index 9a05209b0eb6..acc31cc42d4a 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_none_eabihf.rs @@ -8,7 +8,7 @@ // // To opt into double precision hardware support, use the `-C target-feature=+fp64` flag. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -24,7 +24,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // vfp4 is the lowest common denominator between the Cortex-M4F (vfp4) and the // Cortex-M7 (vfp5). diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs index 7888587ce9e3..796206d4ffee 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs @@ -9,7 +9,7 @@ // To opt-in to hardware accelerated floating point operations, you can use, for example, // `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -26,8 +26,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs index 51e57f77963a..f85aef1ab5a6 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs @@ -8,7 +8,7 @@ // // To opt into double precision hardware support, use the `-C target-feature=+fp64` flag. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -25,8 +25,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabihf".into(), + os: Os::NuttX, + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // vfp4 is the lowest common denominator between the Cortex-M4F (vfp4) and the // Cortex-M7 (vfp5). diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_none_eabi.rs index fea5f15fe0a6..8c5807b1a907 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7m_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_none_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M3 processor (ARMv7-M) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs index 50ab8c5a4ee0..3d3d48748a9c 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M3 processor (ARMv7-M) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -17,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_linux_androideabi.rs index cc6f26d38dea..3ffa30129298 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_linux_androideabi.rs @@ -1,5 +1,5 @@ use crate::spec::{ - Arch, Cc, FloatAbi, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base, + Abi, Arch, Cc, FloatAbi, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base, }; // This target if is for the Android v7a ABI in thumb mode with @@ -25,7 +25,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_gnueabihf.rs index 9abbec824b0f..36b99516d377 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_gnueabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for glibc Linux on ARMv7 with thumb mode enabled // (for consistency with Android and Debian-based distributions) @@ -21,7 +21,7 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs index e8239172409b..41c4bc91f701 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7neon_unknown_linux_musleabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; // This target is for musl Linux on ARMv7 with thumb mode enabled // (for consistency with Android and Debian-based distributions) @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { // Most of these settings are copied from the thumbv7neon_unknown_linux_gnueabihf // target. options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), features: "+v7,+thumb-mode,+thumb2,+vfp3,+neon".into(), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_none_eabi.rs index aca7441d34ab..298bad565e48 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_base_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_none_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M23 processor (Baseline ARMv8-M) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -16,7 +16,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them // with +strict-align. diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs index 26bf14d176ba..18bafc72553d 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs @@ -1,6 +1,6 @@ // Targets the Cortex-M23 processor (Baseline ARMv8-M) -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -17,8 +17,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them // with +strict-align. diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabi.rs index e1cc16aeb5c6..90d7df75d44b 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabi.rs @@ -1,7 +1,7 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // without the Floating Point extension. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabi".into(), + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs index cd3d3327a5c4..debdb47d507b 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_none_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // with the Floating Point extension. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; +use crate::spec::{Abi, Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { Target { @@ -17,7 +17,7 @@ pub(crate) fn target() -> Target { arch: Arch::Arm, options: TargetOptions { - abi: "eabihf".into(), + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // If the Floating Point extension is implemented in the Cortex-M33 // processor, the Cortex-M33 Technical Reference Manual states that diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs index 1fae7a92d923..f5039f13bc09 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs @@ -1,7 +1,7 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // without the Floating Point extension. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -18,8 +18,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabi".into(), + os: Os::NuttX, + abi: Abi::Eabi, llvm_floatabi: Some(FloatAbi::Soft), max_atomic_width: Some(32), ..base::thumb::opts() diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs index b8de91edfb23..77d23a255278 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs @@ -1,7 +1,7 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // with the Floating Point extension. -use crate::spec::{Arch, FloatAbi, Target, TargetMetadata, TargetOptions, base, cvs}; +use crate::spec::{Abi, Arch, FloatAbi, Os, Target, TargetMetadata, TargetOptions, base, cvs}; pub(crate) fn target() -> Target { Target { @@ -18,8 +18,8 @@ pub(crate) fn target() -> Target { options: TargetOptions { families: cvs!["unix"], - os: "nuttx".into(), - abi: "eabihf".into(), + os: Os::NuttX, + abi: Abi::EabiHf, llvm_floatabi: Some(FloatAbi::Hard), // If the Floating Point extension is implemented in the Cortex-M33 // processor, the Cortex-M33 Technical Reference Manual states that diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index aa4a1be99291..47623c34dce3 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -1,6 +1,6 @@ use crate::spec::{ - Arch, LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetMetadata, TargetOptions, - base, cvs, + Arch, LinkArgs, LinkerFlavor, Os, PanicStrategy, RelocModel, Target, TargetMetadata, + TargetOptions, base, cvs, }; pub(crate) fn target() -> Target { @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { TargetOptions::link_args(LinkerFlavor::EmCc, &["-sABORTING_MALLOC=0", "-sWASM_BIGINT"]); let opts = TargetOptions { - os: "emscripten".into(), + os: Os::Emscripten, linker_flavor: LinkerFlavor::EmCc, // emcc emits two files - a .js file to instantiate the wasm and supply platform // functionality, and a .wasm file. diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs index 3050f17d5efb..cff336c04190 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_unknown.rs @@ -10,11 +10,11 @@ //! This target is more or less managed by the Rust and WebAssembly Working //! Group nowadays at . -use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Os, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "unknown".into(); + options.os = Os::Unknown; options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs index a7c196c4530f..2d3dd0862d64 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs @@ -11,14 +11,15 @@ //! introduced. use crate::spec::{ - Arch, Cc, LinkSelfContainedDefault, LinkerFlavor, Target, TargetMetadata, base, crt_objects, + Arch, Cc, Env, LinkSelfContainedDefault, LinkerFlavor, Os, Target, TargetMetadata, base, + crt_objects, }; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "wasi".into(); - options.env = "p1".into(); + options.os = Os::Wasi; + options.env = Env::P1; options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasip1"]); options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs index d4953cc9d498..645f7ad40bb6 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs @@ -8,14 +8,15 @@ //! Historically this target was known as `wasm32-wasi-preview1-threads`. use crate::spec::{ - Arch, Cc, LinkSelfContainedDefault, LinkerFlavor, Target, TargetMetadata, base, crt_objects, + Arch, Cc, Env, LinkSelfContainedDefault, LinkerFlavor, Os, Target, TargetMetadata, base, + crt_objects, }; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "wasi".into(); - options.env = "p1".into(); + options.os = Os::Wasi; + options.env = Env::P1; options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs index 717d49004a17..b53bf3b7bd52 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip2.rs @@ -17,14 +17,14 @@ //! . use crate::spec::{ - Arch, LinkSelfContainedDefault, RelocModel, Target, TargetMetadata, base, crt_objects, + Arch, Env, LinkSelfContainedDefault, Os, RelocModel, Target, TargetMetadata, base, crt_objects, }; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "wasi".into(); - options.env = "p2".into(); + options.os = Os::Wasi; + options.env = Env::P2; options.linker = Some("wasm-component-ld".into()); options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); @@ -63,7 +63,7 @@ pub(crate) fn target() -> Target { llvm_target: "wasm32-wasip2".into(), metadata: TargetMetadata { description: Some("WebAssembly".into()), - tier: Some(3), + tier: Some(2), host_tools: Some(false), std: Some(true), }, diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs index e3d5e6542c26..3fb1d11ef60d 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip3.rs @@ -8,13 +8,19 @@ //! all component-model-level imports anyway. Over time the imports of the //! standard library will change to WASIp3. -use crate::spec::Target; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { // As of now WASIp3 is a lightly edited wasip2 target, so start with that // and this may grow over time as more features are supported. let mut target = super::wasm32_wasip2::target(); target.llvm_target = "wasm32-wasip3".into(); - target.options.env = "p3".into(); + target.metadata = crate::spec::TargetMetadata { + description: Some("WebAssembly".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }; + target.options.env = Env::P3; target } diff --git a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs index 41267f0e5708..8c5a253b0e2f 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32v1_none.rs @@ -12,11 +12,11 @@ //! nightly Rust feature `-Zbuild-std`. This target is for people who want to //! use stable Rust, and target a stable set pf WebAssembly features. -use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Os, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "none".into(); + options.os = Os::None; // WebAssembly 1.0 shipped in 2019 and included exactly one proposal // after the initial "MVP" feature set: "mutable-globals". diff --git a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs index 850e85591661..7f9341b3ef91 100644 --- a/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/targets/wasm64_unknown_unknown.rs @@ -7,11 +7,11 @@ //! the standard library is available, most of it returns an error immediately //! (e.g. trying to create a TCP stream or something like that). -use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Os, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut options = base::wasm::options(); - options.os = "unknown".into(); + options.os = Os::Unknown; options.add_pre_link_args( LinkerFlavor::WasmLld(Cc::No), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs index cb517b2a357a..8d0febc362d4 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_darwin.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("macos", Arch::X86_64, TargetEnv::Normal); + let (opts, llvm_target, arch) = base(Os::MacOs, Arch::X86_64, TargetEnv::Normal); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs index d74a688fa0f7..349460331e56 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios.rs @@ -1,10 +1,10 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { // x86_64-apple-ios is a simulator target, even though it isn't declared // that way in the target name like the other ones... - let (opts, llvm_target, arch) = base("ios", Arch::X86_64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::X86_64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs index 193e26f94c98..2b8d552bf854 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("ios", Arch::X86_64, TargetEnv::MacCatalyst); + let (opts, llvm_target, arch) = base(Os::IOs, Arch::X86_64, TargetEnv::MacCatalyst); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs index e69bd17a0496..273cf31b7631 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_tvos.rs @@ -1,10 +1,10 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { // x86_64-apple-tvos is a simulator target, even though it isn't declared // that way in the target name like the other ones... - let (opts, llvm_target, arch) = base("tvos", Arch::X86_64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::TvOs, Arch::X86_64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs index 9490ca6aa36c..395f4fe5db4a 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_watchos_sim.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (opts, llvm_target, arch) = base("watchos", Arch::X86_64, TargetEnv::Simulator); + let (opts, llvm_target, arch) = base(Os::WatchOs, Arch::X86_64, TargetEnv::Simulator); Target { llvm_target, metadata: TargetMetadata { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs index 6232de48da70..290087d8cef6 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_fortanix_unknown_sgx.rs @@ -1,6 +1,8 @@ use std::borrow::Cow; -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{ + Abi, Arch, Cc, Env, LinkerFlavor, Lld, Os, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn target() -> Target { let pre_link_args = TargetOptions::link_args( @@ -55,10 +57,10 @@ pub(crate) fn target() -> Target { "TEXT_SIZE", ]; let opts = TargetOptions { - os: "unknown".into(), - env: "sgx".into(), + os: Os::Unknown, + env: Env::Sgx, vendor: "fortanix".into(), - abi: "fortanix".into(), + abi: Abi::Fortanix, linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), max_atomic_width: Some(64), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 248aa91862c9..916001548509 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -1,5 +1,5 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::x86_64(); @@ -7,6 +7,6 @@ pub(crate) fn target() -> Target { Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); - target.options.env = "nto71".into(); + target.options.env = Env::Nto71; target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs index 8f4c4924a295..1e97ae6b7a08 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs @@ -1,5 +1,5 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::x86_64(); @@ -7,6 +7,6 @@ pub(crate) fn target() -> Target { Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-sock network stack".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::X86_64); - target.options.env = "nto71_iosock".into(); + target.options.env = Env::Nto71IoSock; target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs index d91a94a2ba55..bd98df621db1 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs @@ -1,11 +1,11 @@ -use crate::spec::Target; use crate::spec::base::nto_qnx; +use crate::spec::{Env, Target}; pub(crate) fn target() -> Target { let mut target = nto_qnx::x86_64(); target.metadata.description = Some("x86 64-bit QNX Neutrino 8.0 RTOS".into()); target.options.pre_link_args = nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); - target.options.env = "nto80".into(); + target.options.env = Env::Nto80; target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs index 7b8d5def3a70..abcca352dfa3 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_solaris.rs @@ -1,23 +1,26 @@ use crate::spec::{ - Arch, Cc, LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetMetadata, base, + Arch, Cc, LinkerFlavor, SanitizerSet, StackProbeType, Target, TargetMetadata, TargetOptions, + base, }; pub(crate) fn target() -> Target { - let mut base = base::solaris::opts(); + let mut base = TargetOptions { + cpu: "x86-64".into(), + plt_by_default: false, + vendor: "pc".into(), + max_atomic_width: Some(64), + stack_probes: StackProbeType::Inline, + supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD, + ..base::solaris::opts() + }; base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64"]); - base.cpu = "x86-64".into(); - base.plt_by_default = false; - base.vendor = "pc".into(); - base.max_atomic_width = Some(64); - base.stack_probes = StackProbeType::Inline; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD; Target { llvm_target: "x86_64-pc-solaris".into(), metadata: TargetMetadata { description: Some("64-bit Solaris 11.4".into()), tier: Some(2), - host_tools: Some(false), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs index 28c9e6251255..0606d4508bad 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_gnullvm.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { metadata: TargetMetadata { description: Some("64-bit x86 MinGW (Windows 10+), LLVM ABI".into()), tier: Some(2), - host_tools: Some(false), + host_tools: Some(true), std: Some(true), }, pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs index 0b3e25bebf36..7e06a718e480 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnux32.rs @@ -1,9 +1,9 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, base}; +use crate::spec::{Abi, Arch, Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetMetadata, base}; pub(crate) fn target() -> Target { let mut base = base::linux_gnu::opts(); base.cpu = "x86-64".into(); - base.abi = "x32".into(); + base.abi = Abi::X32; base.max_atomic_width = Some(64); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-mx32"]); base.stack_probes = StackProbeType::Inline; diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs index 88c66ff6db9f..af9d653dbd2d 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_trusty.rs @@ -1,7 +1,7 @@ // Trusty OS target for X86_64. use crate::spec::{ - Arch, LinkSelfContainedDefault, PanicStrategy, RelroLevel, StackProbeType, Target, + Arch, LinkSelfContainedDefault, Os, PanicStrategy, RelroLevel, StackProbeType, Target, TargetMetadata, TargetOptions, }; @@ -22,7 +22,7 @@ pub(crate) fn target() -> Target { executables: true, max_atomic_width: Some(64), panic_strategy: PanicStrategy::Abort, - os: "trusty".into(), + os: Os::Trusty, link_self_contained: LinkSelfContainedDefault::InferredForMusl, position_independent_executables: true, static_position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs index 22a1a126b891..0bd421585526 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_gnu.rs @@ -1,18 +1,20 @@ -use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, base}; +use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let mut base = base::windows_gnu::opts(); - base.vendor = "win7".into(); - base.cpu = "x86-64".into(); - base.plt_by_default = false; + let mut base = TargetOptions { + vendor: "win7".into(), + cpu: "x86-64".into(), + plt_by_default: false, + max_atomic_width: Some(64), + linker: Some("x86_64-w64-mingw32-gcc".into()), + ..base::windows_gnu::opts() + }; // Use high-entropy 64 bit address space for ASLR base.add_pre_link_args( LinkerFlavor::Gnu(Cc::No, Lld::No), &["-m", "i386pep", "--high-entropy-va"], ); base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64", "-Wl,--high-entropy-va"]); - base.max_atomic_width = Some(64); - base.linker = Some("x86_64-w64-mingw32-gcc".into()); Target { llvm_target: "x86_64-pc-windows-gnu".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs index 99b59154811f..1cc2efaae5f5 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_win7_windows_msvc.rs @@ -1,12 +1,14 @@ -use crate::spec::{Arch, SanitizerSet, Target, TargetMetadata, base}; +use crate::spec::{Arch, SanitizerSet, Target, TargetMetadata, TargetOptions, base}; pub(crate) fn target() -> Target { - let mut base = base::windows_msvc::opts(); - base.vendor = "win7".into(); - base.cpu = "x86-64".into(); - base.plt_by_default = false; - base.max_atomic_width = Some(64); - base.supported_sanitizers = SanitizerSet::ADDRESS; + let base = TargetOptions { + vendor: "win7".into(), + cpu: "x86-64".into(), + plt_by_default: false, + max_atomic_width: Some(64), + supported_sanitizers: SanitizerSet::ADDRESS, + ..base::windows_msvc::opts() + }; Target { llvm_target: "x86_64-pc-windows-msvc".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs index 67b5a160a89a..adc87378fc7e 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64h_apple_darwin.rs @@ -1,8 +1,8 @@ use crate::spec::base::apple::{Arch, TargetEnv, base}; -use crate::spec::{SanitizerSet, Target, TargetMetadata, TargetOptions}; +use crate::spec::{Os, SanitizerSet, Target, TargetMetadata, TargetOptions}; pub(crate) fn target() -> Target { - let (mut opts, llvm_target, arch) = base("macos", Arch::X86_64h, TargetEnv::Normal); + let (mut opts, llvm_target, arch) = base(Os::MacOs, Arch::X86_64h, TargetEnv::Normal); opts.max_atomic_width = Some(128); opts.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD; diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs index d909271e218e..83835705f3da 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::base::xtensa; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{Arch, Env, Os, Target, TargetMetadata, TargetOptions, cvs}; pub(crate) fn target() -> Target { Target { @@ -15,8 +15,8 @@ pub(crate) fn target() -> Target { endian: Endian::Little, c_int_width: 32, families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), executables: true, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs index b1ed0ffe264e..42e4c7bf8bec 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::base::xtensa; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{Arch, Env, Os, Target, TargetMetadata, TargetOptions, cvs}; pub(crate) fn target() -> Target { Target { @@ -15,8 +15,8 @@ pub(crate) fn target() -> Target { endian: Endian::Little, c_int_width: 32, families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), executables: true, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs index 8e1fee4ad657..498daf4e6063 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -1,7 +1,7 @@ use rustc_abi::Endian; use crate::spec::base::xtensa; -use crate::spec::{Arch, Target, TargetMetadata, TargetOptions, cvs}; +use crate::spec::{Arch, Env, Os, Target, TargetMetadata, TargetOptions, cvs}; pub(crate) fn target() -> Target { Target { @@ -15,8 +15,8 @@ pub(crate) fn target() -> Target { endian: Endian::Little, c_int_width: 32, families: cvs!["unix"], - os: "espidf".into(), - env: "newlib".into(), + os: Os::EspIdf, + env: Env::Newlib, vendor: "espressif".into(), executables: true, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 1789e52ed26f..990928774731 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -5,7 +5,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_span::{Symbol, sym}; -use crate::spec::{Arch, FloatAbi, RustcAbi, Target}; +use crate::spec::{Abi, Arch, FloatAbi, RustcAbi, Target}; /// Features that control behaviour of rustc, rather than the codegen. /// These exist globally and are not in the target-specific lists below. @@ -977,7 +977,7 @@ pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, Implie | Arch::PowerPC64LE | Arch::SpirV | Arch::Xtensa - | Arch::Unknown(_) => &[], + | Arch::Other(_) => &[], } } @@ -1006,7 +1006,7 @@ pub fn features_for_correct_vector_abi(&self) -> &'static [(u64, &'static str)] | Arch::PowerPC64LE | Arch::SpirV | Arch::Xtensa - | Arch::Unknown(_) => &[], + | Arch::Other(_) => &[], } } @@ -1119,20 +1119,17 @@ pub fn abi_required_features(&self) -> FeatureConstraints { Arch::AArch64 | Arch::Arm64EC => { // Aarch64 has no sane ABI specifier, and LLVM doesn't even have a way to force // the use of soft-float, so all we can do here is some crude hacks. - match &*self.abi { - "softfloat" => { - // LLVM will use float registers when `fp-armv8` is available, e.g. for - // calls to built-ins. The only way to ensure a consistent softfloat ABI - // on aarch64 is to never enable `fp-armv8`, so we enforce that. - // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the - // feature we have to mark as incompatible. - FeatureConstraints { required: &[], incompatible: &["neon"] } - } - _ => { - // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. - // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. - FeatureConstraints { required: &["neon"], incompatible: &[] } - } + if matches!(self.abi, Abi::SoftFloat) { + // LLVM will use float registers when `fp-armv8` is available, e.g. for + // calls to built-ins. The only way to ensure a consistent softfloat ABI + // on aarch64 is to never enable `fp-armv8`, so we enforce that. + // In Rust we tie `neon` and `fp-armv8` together, therefore `neon` is the + // feature we have to mark as incompatible. + FeatureConstraints { required: &[], incompatible: &["neon"] } + } else { + // Everything else is assumed to use a hardfloat ABI. neon and fp-armv8 must be enabled. + // `FeatureConstraints` uses Rust feature names, hence only "neon" shows up. + FeatureConstraints { required: &["neon"], incompatible: &[] } } } Arch::RiscV32 | Arch::RiscV64 => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index e18e294635b5..b280a6ec55af 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -51,11 +51,8 @@ use std::{cmp, fmt, iter}; use rustc_abi::ExternAbi; -use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{ - Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize, -}; +use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -66,15 +63,12 @@ use rustc_middle::dep_graph::DepContext; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt}; -use rustc_middle::ty::print::{ - PrintError, PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths, -}; +use rustc_middle::ty::print::{PrintTraitRefExt as _, WrapBinderMode, with_forced_trimmed_paths}; use rustc_middle::ty::{ self, List, ParamEnv, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, Symbol, sym}; +use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Pos, Span, sym}; use tracing::{debug, instrument}; use crate::error_reporting::TypeErrCtxt; @@ -216,201 +210,6 @@ pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option> { /// Adds a note if the types come from similarly named crates fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool { - // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar, - // even if the logic needed to detect the case is very different. - use hir::def_id::CrateNum; - use rustc_hir::definitions::DisambiguatedDefPathData; - use ty::GenericArg; - use ty::print::Printer; - - struct ConflictingPathPrinter<'tcx> { - tcx: TyCtxt<'tcx>, - segments: Vec, - } - - impl<'tcx> Printer<'tcx> for ConflictingPathPrinter<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { - unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` - } - - fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` - } - - fn print_dyn_existential( - &mut self, - _predicates: &'tcx ty::List>, - ) -> Result<(), PrintError> { - unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` - } - - fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> { - unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs` - } - - fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> { - self.segments = vec![self.tcx.crate_name(cnum)]; - Ok(()) - } - - fn print_path_with_qualified( - &mut self, - _self_ty: Ty<'tcx>, - _trait_ref: Option>, - ) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn print_path_with_impl( - &mut self, - _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - _self_ty: Ty<'tcx>, - _trait_ref: Option>, - ) -> Result<(), PrintError> { - Err(fmt::Error) - } - - fn print_path_with_simple( - &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - disambiguated_data: &DisambiguatedDefPathData, - ) -> Result<(), PrintError> { - print_prefix(self)?; - self.segments.push(disambiguated_data.as_sym(true)); - Ok(()) - } - - fn print_path_with_generic_args( - &mut self, - print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, - _args: &[GenericArg<'tcx>], - ) -> Result<(), PrintError> { - print_prefix(self) - } - } - - let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool { - // Only report definitions from different crates. If both definitions - // are from a local module we could have false positives, e.g. - // let _ = [{struct Foo; Foo}, {struct Foo; Foo}]; - if did1.krate != did2.krate { - let abs_path = |def_id| { - let mut p = ConflictingPathPrinter { tcx: self.tcx, segments: vec![] }; - p.print_def_path(def_id, &[]).map(|_| p.segments) - }; - - // We compare strings because DefPath can be different for imported and - // non-imported crates. - let expected_str = self.tcx.def_path_str(did1); - let found_str = self.tcx.def_path_str(did2); - let Ok(expected_abs) = abs_path(did1) else { return false }; - let Ok(found_abs) = abs_path(did2) else { return false }; - let same_path = expected_str == found_str || expected_abs == found_abs; - if same_path { - // We want to use as unique a type path as possible. If both types are "locally - // known" by the same name, we use the "absolute path" which uses the original - // crate name instead. - let (expected, found) = if expected_str == found_str { - (join_path_syms(&expected_abs), join_path_syms(&found_abs)) - } else { - (expected_str, found_str) - }; - - // We've displayed "expected `a::b`, found `a::b`". We add context to - // differentiate the different cases where that might happen. - let expected_crate_name = self.tcx.crate_name(did1.krate); - let found_crate_name = self.tcx.crate_name(did2.krate); - let same_crate = expected_crate_name == found_crate_name; - let expected_sp = self.tcx.def_span(did1); - let found_sp = self.tcx.def_span(did2); - - let both_direct_dependencies = if !did1.is_local() - && !did2.is_local() - && let Some(data1) = self.tcx.extern_crate(did1.krate) - && let Some(data2) = self.tcx.extern_crate(did2.krate) - && data1.dependency_of == LOCAL_CRATE - && data2.dependency_of == LOCAL_CRATE - { - // If both crates are directly depended on, we don't want to mention that - // in the final message, as it is redundant wording. - // We skip the case of semver trick, where one version of the local crate - // depends on another version of itself by checking that both crates at play - // are not the current one. - true - } else { - false - }; - - let mut span: MultiSpan = vec![expected_sp, found_sp].into(); - span.push_span_label( - self.tcx.def_span(did1), - format!("this is the expected {ty} `{expected}`"), - ); - span.push_span_label( - self.tcx.def_span(did2), - format!("this is the found {ty} `{found}`"), - ); - for def_id in [did1, did2] { - let crate_name = self.tcx.crate_name(def_id.krate); - if !def_id.is_local() - && let Some(data) = self.tcx.extern_crate(def_id.krate) - { - let descr = if same_crate { - "one version of".to_string() - } else { - format!("one {ty} comes from") - }; - let dependency = if both_direct_dependencies { - if let rustc_session::cstore::ExternCrateSource::Extern(def_id) = - data.src - && let Some(name) = self.tcx.opt_item_name(def_id) - { - format!(", which is renamed locally to `{name}`") - } else { - String::new() - } - } else if data.dependency_of == LOCAL_CRATE { - ", as a direct dependency of the current crate".to_string() - } else { - let dep = self.tcx.crate_name(data.dependency_of); - format!(", as a dependency of crate `{dep}`") - }; - span.push_span_label( - data.span, - format!("{descr} crate `{crate_name}` used here{dependency}"), - ); - } - } - let msg = if (did1.is_local() || did2.is_local()) && same_crate { - format!( - "the crate `{expected_crate_name}` is compiled multiple times, \ - possibly with different configurations", - ) - } else if same_crate { - format!( - "two different versions of crate `{expected_crate_name}` are being \ - used; two types coming from two different versions of the same crate \ - are different types even if they look the same", - ) - } else { - format!( - "two types coming from two different crates are different types even \ - if they look the same", - ) - }; - err.span_note(span, msg); - if same_crate { - err.help("you can use `cargo tree` to explore your dependency tree"); - } - return true; - } - } - false - }; match terr { TypeError::Sorts(ref exp_found) => { // if they are both "path types", there's a chance of ambiguity @@ -418,11 +217,23 @@ fn print_path_with_generic_args( if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = (exp_found.expected.kind(), exp_found.found.kind()) { - return report_path_match(err, exp_adt.did(), found_adt.did(), "type"); + return self.check_same_definition_different_crate( + err, + exp_adt.did(), + [found_adt.did()].into_iter(), + |did| vec![self.tcx.def_span(did)], + "type", + ); } } TypeError::Traits(ref exp_found) => { - return report_path_match(err, exp_found.expected, exp_found.found, "trait"); + return self.check_same_definition_different_crate( + err, + exp_found.expected, + [exp_found.found].into_iter(), + |did| vec![self.tcx.def_span(did)], + "trait", + ); } _ => (), // FIXME(#22750) handle traits and stuff } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index e225656af852..da888acc4755 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -864,6 +864,42 @@ pub fn construct_generic_bound_failure( } } + if sub.kind() == ty::ReStatic + && let Some(node) = self.tcx.hir_get_if_local(generic_param_scope.into()) + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, body, has_body: true, .. }, + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body), .. + }) = node + && let hir::Node::Expr(expr) = self.tcx.hir_node(body.hir_id) + && let hir::ExprKind::Block(block, _) = expr.kind + && let Some(tail) = block.expr + && tail.span == span + && let hir::FnRetTy::Return(ty) = sig.decl.output + && let hir::TyKind::Path(path) = ty.kind + && let hir::QPath::Resolved(None, path) = path + && let hir::def::Res::Def(_, def_id) = path.res + && Some(def_id) == self.tcx.lang_items().owned_box() + && let [segment] = path.segments + && let Some(args) = segment.args + && let [hir::GenericArg::Type(ty)] = args.args + && let hir::TyKind::TraitObject(_, tagged_ref) = ty.kind + && let hir::LifetimeKind::ImplicitObjectLifetimeDefault = tagged_ref.pointer().kind + { + // Explicitly look for `-> Box` to point at it as the *likely* source of + // the `'static` lifetime requirement. + err.span_label( + ty.span, + format!("this `dyn Trait` has an implicit `'static` lifetime bound"), + ); + } + err } 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 c282f4204260..27eb614bae13 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 @@ -468,7 +468,7 @@ pub fn report_selection_error( span, leaf_trait_predicate, ); - self.note_trait_version_mismatch(&mut err, leaf_trait_predicate); + self.note_different_trait_with_same_name(&mut err, &obligation, leaf_trait_predicate); self.note_adt_version_mismatch(&mut err, leaf_trait_predicate); self.suggest_remove_await(&obligation, &mut err); self.suggest_derive(&obligation, &mut err, leaf_trait_predicate); @@ -1974,115 +1974,6 @@ pub(super) fn report_similar_impl_candidates( impl_candidates }; - // We'll check for the case where the reason for the mismatch is that the trait comes from - // one crate version and the type comes from another crate version, even though they both - // are from the same crate. - let trait_def_id = trait_pred.def_id(); - let trait_name = self.tcx.item_name(trait_def_id); - let crate_name = self.tcx.crate_name(trait_def_id.krate); - if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| { - trait_name == self.tcx.item_name(trait_def_id) - && trait_def_id.krate != def_id.krate - && crate_name == self.tcx.crate_name(def_id.krate) - }) { - // We've found two different traits with the same name, same crate name, but - // different crate `DefId`. We highlight the traits. - - let found_type = - if let ty::Adt(def, _) = trait_pred.self_ty().skip_binder().peel_refs().kind() { - Some(def.did()) - } else { - None - }; - let candidates = if impl_candidates.is_empty() { - alternative_candidates(trait_def_id) - } else { - impl_candidates.into_iter().map(|cand| (cand.trait_ref, cand.impl_def_id)).collect() - }; - let mut span: MultiSpan = self.tcx.def_span(trait_def_id).into(); - span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait"); - for (sp, label) in [trait_def_id, other_trait_def_id] - .iter() - // The current crate-version might depend on another version of the same crate - // (Think "semver-trick"). Do not call `extern_crate` in that case for the local - // crate as that doesn't make sense and ICEs (#133563). - .filter(|def_id| !def_id.is_local()) - .filter_map(|def_id| self.tcx.extern_crate(def_id.krate)) - .map(|data| { - let dependency = if data.dependency_of == LOCAL_CRATE { - "direct dependency of the current crate".to_string() - } else { - let dep = self.tcx.crate_name(data.dependency_of); - format!("dependency of crate `{dep}`") - }; - ( - data.span, - format!("one version of crate `{crate_name}` used here, as a {dependency}"), - ) - }) - { - span.push_span_label(sp, label); - } - let mut points_at_type = false; - if let Some(found_type) = found_type { - span.push_span_label( - self.tcx.def_span(found_type), - "this type doesn't implement the required trait", - ); - for (trait_ref, _) in candidates { - if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind() - && let candidate_def_id = def.did() - && let Some(name) = self.tcx.opt_item_name(candidate_def_id) - && let Some(found) = self.tcx.opt_item_name(found_type) - && name == found - && candidate_def_id.krate != found_type.krate - && self.tcx.crate_name(candidate_def_id.krate) - == self.tcx.crate_name(found_type.krate) - { - // A candidate was found of an item with the same name, from two separate - // versions of the same crate, let's clarify. - let candidate_span = self.tcx.def_span(candidate_def_id); - span.push_span_label( - candidate_span, - "this type implements the required trait", - ); - points_at_type = true; - } - } - } - span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait"); - err.highlighted_span_note( - span, - vec![ - StringPart::normal("there are ".to_string()), - StringPart::highlighted("multiple different versions".to_string()), - StringPart::normal(" of crate `".to_string()), - StringPart::highlighted(format!("{crate_name}")), - StringPart::normal("` in the dependency graph".to_string()), - ], - ); - if points_at_type { - // We only clarify that the same type from different crate versions are not the - // same when we *find* the same type coming from different crate versions, otherwise - // it could be that it was a type provided by a different crate than the one that - // provides the trait, and mentioning this adds verbosity without clarification. - err.highlighted_note(vec![ - StringPart::normal( - "two types coming from two different versions of the same crate are \ - different types " - .to_string(), - ), - StringPart::highlighted("even if they look the same".to_string()), - ]); - } - err.highlighted_help(vec![ - StringPart::normal("you can use `".to_string()), - StringPart::highlighted("cargo tree".to_string()), - StringPart::normal("` to explore your dependency tree".to_string()), - ]); - return true; - } - if let [single] = &impl_candidates { // If we have a single implementation, try to unify it with the trait ref // that failed. This should uncover a better hint for what *is* implemented. @@ -2478,10 +2369,7 @@ fn get_parent_trait_ref( } } - /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait - /// with the same path as `trait_ref`, a help message about - /// a probable version mismatch is added to `err` - fn note_trait_version_mismatch( + fn check_same_trait_different_version( &self, err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -2492,46 +2380,33 @@ fn note_trait_version_mismatch( trait_def_id, trait_pred.skip_binder().self_ty(), |impl_def_id| { - trait_impls.push(impl_def_id); + let impl_trait_header = self.tcx.impl_trait_header(impl_def_id); + trait_impls + .push(self.tcx.def_span(impl_trait_header.trait_ref.skip_binder().def_id)); }, ); trait_impls }; - - let required_trait_path = self.tcx.def_path_str(trait_pred.def_id()); - let traits_with_same_path: UnordSet<_> = self - .tcx - .visible_traits() - .filter(|trait_def_id| *trait_def_id != trait_pred.def_id()) - .map(|trait_def_id| (self.tcx.def_path_str(trait_def_id), trait_def_id)) - .filter(|(p, _)| *p == required_trait_path) - .collect(); - - let traits_with_same_path = - traits_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p); - let mut suggested = false; - for (_, trait_with_same_path) in traits_with_same_path { - let trait_impls = get_trait_impls(trait_with_same_path); - if trait_impls.is_empty() { - continue; - } - let impl_spans: Vec<_> = - trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect(); - err.span_help( - impl_spans, - format!("trait impl{} with same name found", pluralize!(trait_impls.len())), - ); - self.note_two_crate_versions(trait_with_same_path, err); - suggested = true; - } - suggested + self.check_same_definition_different_crate( + err, + trait_pred.def_id(), + self.tcx.visible_traits(), + get_trait_impls, + "trait", + ) } - fn note_two_crate_versions(&self, did: DefId, err: &mut Diag<'_>) { + pub fn note_two_crate_versions( + &self, + did: DefId, + sp: impl Into, + err: &mut Diag<'_>, + ) { let crate_name = self.tcx.crate_name(did.krate); - let crate_msg = - format!("perhaps two different versions of crate `{crate_name}` are being used?"); - err.note(crate_msg); + let crate_msg = format!( + "there are multiple different versions of crate `{crate_name}` in the dependency graph" + ); + err.span_note(sp, crate_msg); } fn note_adt_version_mismatch( @@ -2592,10 +2467,82 @@ fn note_adt_version_mismatch( for (similar_item, _) in similar_items { err.span_help(self.tcx.def_span(similar_item), "item with same name found"); - self.note_two_crate_versions(similar_item, err); + self.note_two_crate_versions(similar_item, MultiSpan::new(), err); } } + fn check_same_name_different_path( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let mut suggested = false; + let trait_def_id = trait_pred.def_id(); + let trait_has_same_params = |other_trait_def_id: DefId| -> bool { + let trait_generics = self.tcx.generics_of(trait_def_id); + let other_trait_generics = self.tcx.generics_of(other_trait_def_id); + + if trait_generics.count() != other_trait_generics.count() { + return false; + } + trait_generics.own_params.iter().zip(other_trait_generics.own_params.iter()).all( + |(a, b)| match (&a.kind, &b.kind) { + (ty::GenericParamDefKind::Lifetime, ty::GenericParamDefKind::Lifetime) + | ( + ty::GenericParamDefKind::Type { .. }, + ty::GenericParamDefKind::Type { .. }, + ) + | ( + ty::GenericParamDefKind::Const { .. }, + ty::GenericParamDefKind::Const { .. }, + ) => true, + _ => false, + }, + ) + }; + let trait_name = self.tcx.item_name(trait_def_id); + if let Some(other_trait_def_id) = self.tcx.all_traits_including_private().find(|def_id| { + trait_def_id != *def_id + && trait_name == self.tcx.item_name(def_id) + && trait_has_same_params(*def_id) + && self.predicate_must_hold_modulo_regions(&Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + trait_pred.map_bound(|tr| ty::TraitPredicate { + trait_ref: ty::TraitRef::new(self.tcx, *def_id, tr.trait_ref.args), + ..tr + }), + )) + }) { + err.note(format!( + "`{}` implements similarly named trait `{}`, but not `{}`", + trait_pred.self_ty(), + self.tcx.def_path_str(other_trait_def_id), + trait_pred.print_modifiers_and_trait_path() + )); + suggested = true; + } + suggested + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about a multiple different + /// versions of the same crate is added to `err`. Otherwise if it implements another + /// trait with the same name, a note message about a similarly named trait is added to `err`. + pub fn note_different_trait_with_same_name( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + if self.check_same_trait_different_version(err, trait_pred) { + return true; + } + self.check_same_name_different_path(err, obligation, trait_pred) + } + /// Add a `::` prefix when comparing paths so that paths with just one item /// like "Foo" does not equal the end of "OtherFoo". fn comparable_path(&self, did: DefId) -> String { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 1825719a3077..befc83a592c0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,6 +1,6 @@ pub mod ambiguity; pub mod call_kind; -mod fulfillment_errors; +pub mod fulfillment_errors; pub mod on_unimplemented; pub mod on_unimplemented_condition; pub mod on_unimplemented_format; @@ -10,6 +10,7 @@ use std::{fmt, iter}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::unord::UnordSet; use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; @@ -21,7 +22,7 @@ }; use rustc_middle::ty::print::{PrintTraitRefExt as _, with_no_trimmed_paths}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; -use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span}; +use rustc_span::{DesugaringKind, ErrorGuaranteed, ExpnKind, Span, Symbol}; use tracing::{info, instrument}; pub use self::overflow::*; @@ -351,6 +352,80 @@ fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuara FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle), } } + + fn get_extern_crate_renamed_symbol(&self, trait_def_id: DefId) -> Option { + if !trait_def_id.is_local() + && let Some(data) = self.tcx.extern_crate(trait_def_id.krate) + && let rustc_session::cstore::ExternCrateSource::Extern(def_id) = data.src + { + self.tcx.opt_item_name(def_id) + } else { + None + } + } + + pub fn check_same_definition_different_crate( + &self, + err: &mut Diag<'_>, + expected_did: DefId, + found_dids: impl Iterator, + get_impls: F, + ty: &str, + ) -> bool + where + F: Fn(DefId) -> Vec, + { + let krate = self.tcx.crate_name(expected_did.krate); + let name = self.tcx.item_name(expected_did); + let locally_renamed_krate = self + .get_extern_crate_renamed_symbol(expected_did) + .map_or(None, |s| if s != krate { Some(s) } else { None }); + let definitions_with_same_path: UnordSet<_> = found_dids + .filter(|def_id| { + def_id.krate != expected_did.krate + && (locally_renamed_krate == self.get_extern_crate_renamed_symbol(*def_id) + || self.tcx.crate_name(def_id.krate) == krate) + && self.tcx.item_name(def_id) == name + }) + .map(|def_id| (self.tcx.def_path_str(def_id), def_id)) + .collect(); + + let definitions_with_same_path = + definitions_with_same_path.into_items().into_sorted_stable_ord_by_key(|(p, _)| p); + let mut suggested = false; + let mut trait_is_impl = false; + + if !definitions_with_same_path.is_empty() { + let mut span: MultiSpan = self.tcx.def_span(expected_did).into(); + span.push_span_label( + self.tcx.def_span(expected_did), + format!("this is the expected {ty}"), + ); + suggested = true; + for (_, definition_with_same_path) in &definitions_with_same_path { + let definitions_impls = get_impls(*definition_with_same_path); + if definitions_impls.is_empty() { + continue; + } + + for candidate_span in definitions_impls { + span.push_span_label(candidate_span, format!("this is the found {ty}")); + trait_is_impl = true; + } + } + if !trait_is_impl { + for (_, def_id) in definitions_with_same_path { + span.push_span_label( + self.tcx.def_span(def_id), + format!("this is the {ty} that was imported"), + ); + } + } + self.note_two_crate_versions(expected_did, span, err); + err.help("you can use `cargo tree` to explore your dependency tree"); + } + suggested + } } /// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0906284e7019..e5c2adaa261d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2055,6 +2055,9 @@ fn confirm_impl_candidate<'cx, 'tcx>( // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. +// +// This is necessary for soundness until we properly handle implied bounds on binders. +// see tests/ui/generic-associated-types/must-prove-where-clauses-on-norm.rs. // FIXME(mgca): While this supports constants, it is only used for types by default right now fn assoc_term_own_obligations<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index cecb43e537a8..d0833f030835 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -67,7 +67,7 @@ pub(super) fn assemble_candidates<'o>( let lang_item = tcx.as_lang_item(def_id); match lang_item { - Some(LangItem::Copy | LangItem::Clone) => { + Some(LangItem::Copy | LangItem::Clone | LangItem::TrivialClone) => { debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); // User-defined copy impls are permitted, but only for diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 88f512708ff0..20a8842f2e8e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -249,7 +249,9 @@ fn confirm_builtin_candidate( Some(LangItem::PointeeSized) => { bug!("`PointeeSized` is removing during lowering"); } - Some(LangItem::Copy | LangItem::Clone) => self.copy_clone_conditions(self_ty), + Some(LangItem::Copy | LangItem::Clone | LangItem::TrivialClone) => { + self.copy_clone_conditions(self_ty) + } Some(LangItem::FusedIterator) => { if self.coroutine_is_gen(self_ty) { ty::Binder::dummy(vec![]) diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 5f503d8b912e..39b575ebab63 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -48,6 +48,7 @@ pub enum SolverTraitLangItem { PointeeTrait, Sized, TransmuteTrait, + TrivialClone, Tuple, Unpin, Unsize, diff --git a/library/Cargo.lock b/library/Cargo.lock index f06f57799c2f..b062b505cb24 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "addr2line" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", "rustc-std-workspace-alloc", @@ -49,9 +49,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" dependencies = [ "rustc-std-workspace-core", ] @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "dlmalloc" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3a2dbee57b69fbb5dbe852fa9c0925697fb0c7fbcb1593e90e5ffaedf13d51" +checksum = "06cdfe340b16dd990c54cce79743613fa09fbb16774f33a77c9fd196f8f3fa30" dependencies = [ "cfg-if", "libc", @@ -109,9 +109,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.32.0" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", @@ -393,9 +393,9 @@ dependencies = [ [[package]] name = "vex-sdk" -version = "0.27.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89f74fce61d7a7ba1589da9634c6305a72befb7cc9150c1f872d87d8060f32b9" +checksum = "79e5fe15afde1305478b35e2cb717fff59f485428534cf49cfdbfa4723379bf6" dependencies = [ "rustc-std-workspace-core", ] @@ -422,12 +422,18 @@ dependencies = [ ] [[package]] -name = "windows-sys" -version = "0.59.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.52.6", + "windows-targets 0.53.5", ] [[package]] @@ -436,10 +442,11 @@ version = "0.0.0" [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ + "windows-link", "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", @@ -452,57 +459,57 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index fde6b80a62fe..2b767ffe02be 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -725,9 +725,9 @@ pub fn into_inner(boxed: Self) -> T { #[unstable(feature = "box_take", issue = "147212")] pub fn take(boxed: Self) -> (T, Box, A>) { unsafe { - let (raw, alloc) = Box::into_raw_with_allocator(boxed); + let (raw, alloc) = Box::into_non_null_with_allocator(boxed); let value = raw.read(); - let uninit = Box::from_raw_in(raw.cast::>(), alloc); + let uninit = Box::from_non_null_in(raw.cast_uninit(), alloc); (value, uninit) } } diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index 45c46fb52636..73940db5d2f5 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -1,4 +1,6 @@ use core::any::Any; +#[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; use core::error::Error; use core::mem; use core::pin::Pin; @@ -75,11 +77,13 @@ impl BoxFromSlice for Box<[T]> { } #[cfg(not(no_global_oom_handling))] -impl BoxFromSlice for Box<[T]> { +impl BoxFromSlice for Box<[T]> { #[inline] fn from_slice(slice: &[T]) -> Self { let len = slice.len(); let buf = RawVec::with_capacity(len); + // SAFETY: since `T` implements `TrivialClone`, this is sound and + // equivalent to the above. unsafe { ptr::copy_nonoverlapping(slice.as_ptr(), buf.ptr(), len); buf.into_box(slice.len()).assume_init() diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index ca5b46c9b0fd..766f4589177a 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1434,7 +1434,8 @@ pub fn split_off(&mut self, key: &Q) -> Self /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain`] with a negated predicate if you do not need the returned iterator. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator, + /// or [`retain`] with a negated predicate if you also do not need to restrict the range. /// /// [`retain`]: BTreeMap::retain /// @@ -1945,7 +1946,8 @@ fn default() -> Self { /// An iterator produced by calling `extract_if` on BTreeMap. #[stable(feature = "btree_extract_if", since = "1.91.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain` or `extract_if().for_each(drop)` to remove and discard elements"] pub struct ExtractIf< 'a, K, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index cb3e14252f8a..28d26699d7d2 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1189,7 +1189,8 @@ pub fn split_off(&mut self, value: &Q) -> Self /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain`] with a negated predicate if you do not need the returned iterator. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator, + /// or [`retain`] with a negated predicate if you also do not need to restrict the range. /// /// [`retain`]: BTreeSet::retain /// # Examples @@ -1547,7 +1548,8 @@ fn into_iter(self) -> Iter<'a, T> { /// An iterator produced by calling `extract_if` on BTreeSet. #[stable(feature = "btree_extract_if", since = "1.91.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain` or `extract_if().for_each(drop)` to remove and discard elements"] pub struct ExtractIf< 'a, T, diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 31dfe73fc799..8bc0e08a4b26 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1943,7 +1943,8 @@ pub fn back_mut(&mut self) -> Option<&mut T> { /// An iterator produced by calling `extract_if` on LinkedList. #[stable(feature = "extract_if", since = "1.87.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `extract_if().for_each(drop)` to remove and discard elements"] pub struct ExtractIf< 'a, T: 'a, diff --git a/library/alloc/src/collections/vec_deque/extract_if.rs b/library/alloc/src/collections/vec_deque/extract_if.rs index bed7d46482cf..437f0d6dd5eb 100644 --- a/library/alloc/src/collections/vec_deque/extract_if.rs +++ b/library/alloc/src/collections/vec_deque/extract_if.rs @@ -21,7 +21,8 @@ /// let iter: ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); /// ``` #[unstable(feature = "vec_deque_extract_if", issue = "147750")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"] pub struct ExtractIf< 'a, T, diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 1d4b3b558c01..52e079d3ae8e 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -7,6 +7,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; use core::cmp::{self, Ordering}; use core::hash::{Hash, Hasher}; use core::iter::{ByRefSized, repeat_n, repeat_with}; @@ -674,7 +676,8 @@ unsafe fn handle_capacity_increase(&mut self, old_capacity: usize) { /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator, + /// or [`retain_mut`] with a negated predicate if you also do not need to restrict the range. /// /// [`retain_mut`]: VecDeque::retain_mut /// @@ -3419,7 +3422,7 @@ impl SpecExtendFromWithin for VecDeque { } #[cfg(not(no_global_oom_handling))] -impl SpecExtendFromWithin for VecDeque { +impl SpecExtendFromWithin for VecDeque { unsafe fn spec_extend_from_within(&mut self, src: Range) { let dst = self.len(); let count = src.end - src.start; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 73197d021f1a..979e873bb2a3 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -116,6 +116,7 @@ #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extend_one_unchecked)] +#![feature(fmt_arguments_from_str)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(formatting_options)] @@ -146,6 +147,8 @@ #![feature(std_internals)] #![feature(str_internals)] #![feature(temporary_niche_types)] +#![feature(transmutability)] +#![feature(trivial_clone)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 89520e68920e..0ab019a68ea0 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -243,9 +243,9 @@ use core::any::Any; use core::cell::{Cell, CloneFromCell}; -#[cfg(not(no_global_oom_handling))] -use core::clone::CloneToUninit; use core::clone::UseCloned; +#[cfg(not(no_global_oom_handling))] +use core::clone::{CloneToUninit, TrivialClone}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -2224,7 +2224,8 @@ unsafe fn allocate_for_slice(len: usize) -> *mut RcInner<[T]> { /// Copy elements from slice into newly allocated `Rc<[T]>` /// - /// Unsafe because the caller must either take ownership or bind `T: Copy` + /// Unsafe because the caller must either take ownership, bind `T: Copy` or + /// bind `T: TrivialClone`. #[cfg(not(no_global_oom_handling))] unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { unsafe { @@ -2314,9 +2315,11 @@ impl RcFromSlice for Rc<[T]> { } #[cfg(not(no_global_oom_handling))] -impl RcFromSlice for Rc<[T]> { +impl RcFromSlice for Rc<[T]> { #[inline] fn from_slice(v: &[T]) -> Self { + // SAFETY: `T` implements `TrivialClone`, so this is sound and equivalent + // to the above. unsafe { Rc::copy_from_slice(v) } } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index ce9f967cc387..a83b51ccb60c 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -11,6 +11,8 @@ use core::borrow::{Borrow, BorrowMut}; #[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; +#[cfg(not(no_global_oom_handling))] use core::cmp::Ordering::{self, Less}; #[cfg(not(no_global_oom_handling))] use core::mem::MaybeUninit; @@ -439,7 +441,7 @@ fn drop(&mut self) { } } - impl ConvertVec for T { + impl ConvertVec for T { #[inline] fn to_vec(s: &[Self], alloc: A) -> Vec { let mut v = Vec::with_capacity_in(s.len(), alloc); @@ -822,7 +824,7 @@ impl SpecCloneIntoVec for [T] { } #[cfg(not(no_global_oom_handling))] -impl SpecCloneIntoVec for [T] { +impl SpecCloneIntoVec for [T] { fn clone_into(&self, target: &mut Vec) { target.clear(); target.extend_from_slice(self); diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 31743b0e35b2..4a2689e01ff1 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -265,18 +265,11 @@ /// You can look at these with the [`as_ptr`], [`len`], and [`capacity`] /// methods: /// -// FIXME Update this when vec_into_raw_parts is stabilized /// ``` -/// use std::mem; -/// /// let story = String::from("Once upon a time..."); /// -/// // Prevent automatically dropping the String's data -/// let mut story = mem::ManuallyDrop::new(story); -/// -/// let ptr = story.as_mut_ptr(); -/// let len = story.len(); -/// let capacity = story.capacity(); +/// // Deconstruct the String into parts. +/// let (ptr, len, capacity) = story.into_raw_parts(); /// /// // story has nineteen bytes /// assert_eq!(19, len); @@ -932,7 +925,6 @@ pub fn from_utf16be_lossy(v: &[u8]) -> String { /// # Examples /// /// ``` - /// #![feature(vec_into_raw_parts)] /// let s = String::from("hello"); /// /// let (ptr, len, cap) = s.into_raw_parts(); @@ -941,7 +933,7 @@ pub fn from_utf16be_lossy(v: &[u8]) -> String { /// assert_eq!(rebuilt, "hello"); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { self.vec.into_raw_parts() } @@ -970,19 +962,12 @@ pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { /// /// # Examples /// - // FIXME Update this when vec_into_raw_parts is stabilized /// ``` - /// use std::mem; - /// /// unsafe { /// let s = String::from("hello"); /// - /// // Prevent automatically dropping the String's data - /// let mut s = mem::ManuallyDrop::new(s); - /// - /// let ptr = s.as_mut_ptr(); - /// let len = s.len(); - /// let capacity = s.capacity(); + /// // Deconstruct the String into parts. + /// let (ptr, len, capacity) = s.into_raw_parts(); /// /// let s = String::from_raw_parts(ptr, len, capacity); /// diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index ccf26ba4c2ac..c302f35e5ed6 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -12,6 +12,8 @@ use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::CloneToUninit; +#[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; use core::clone::UseCloned; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; @@ -2156,7 +2158,8 @@ unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { /// Copy elements from slice into newly allocated `Arc<[T]>` /// - /// Unsafe because the caller must either take ownership or bind `T: Copy`. + /// Unsafe because the caller must either take ownership, bind `T: Copy` or + /// bind `T: TrivialClone`. #[cfg(not(no_global_oom_handling))] unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { unsafe { @@ -2248,9 +2251,11 @@ impl ArcFromSlice for Arc<[T]> { } #[cfg(not(no_global_oom_handling))] -impl ArcFromSlice for Arc<[T]> { +impl ArcFromSlice for Arc<[T]> { #[inline] fn from_slice(v: &[T]) -> Self { + // SAFETY: `T` implements `TrivialClone`, so this is sound and equivalent + // to the above. unsafe { Arc::copy_from_slice(v) } } } diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index cb9e14f554d4..014219f8d461 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -16,7 +16,8 @@ /// let iter: std::vec::ExtractIf<'_, _, _> = v.extract_if(.., |x| *x % 2 == 0); /// ``` #[stable(feature = "extract_if", since = "1.87.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain_mut` or `extract_if().for_each(drop)` to remove and discard elements"] pub struct ExtractIf< 'a, T, diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index a3ddd6f6e230..04b50e576298 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -1,3 +1,4 @@ +use core::mem::SizedTypeProperties; use core::num::{NonZero, Saturating, Wrapping}; use crate::boxed::Box; @@ -20,6 +21,8 @@ fn is_zero(&self) -> bool { }; } +impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of (). + impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8. impl_is_zero!(i16, |x| x == 0); impl_is_zero!(i32, |x| x == 0); @@ -43,17 +46,38 @@ fn is_zero(&self) -> bool { // `IsZero` cannot be soundly implemented for pointers because of provenance // (see #135338). +unsafe impl IsZero for [T; N] { + #[inline] + default fn is_zero(&self) -> bool { + // If the array is of length zero, + // then it doesn't actually contain any `T`s, + // so `T::clone` doesn't need to be called, + // and we can "zero-initialize" all zero bytes of the array. + N == 0 + } +} + unsafe impl IsZero for [T; N] { #[inline] fn is_zero(&self) -> bool { - // Because this is generated as a runtime check, it's not obvious that - // it's worth doing if the array is really long. The threshold here - // is largely arbitrary, but was picked because as of 2022-07-01 LLVM - // fails to const-fold the check in `vec![[1; 32]; n]` - // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022 - // Feel free to tweak if you have better evidence. + if T::IS_ZST { + // If T is a ZST, then there is at most one possible value of `T`, + // so we only need to check one element for zeroness. + // We can't unconditionally return `true` here, since, e.g. + // `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero` + // due to the generic array impl, but `T::is_zero` returns `false` + // since the length is not 0. + self.get(0).is_none_or(IsZero::is_zero) + } else { + // Because this is generated as a runtime check, it's not obvious that + // it's worth doing if the array is really long. The threshold here + // is largely arbitrary, but was picked because as of 2022-07-01 LLVM + // fails to const-fold the check in `vec![[1; 32]; n]` + // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022 + // Feel free to tweak if you have better evidence. - N <= 16 && self.iter().all(IsZero::is_zero) + N <= 16 && self.iter().all(IsZero::is_zero) + } } } @@ -61,7 +85,7 @@ fn is_zero(&self) -> bool { macro_rules! impl_is_zero_tuples { // Stopper () => { - // No use for implementing for empty tuple because it is ZST. + // We already have an impl for () above. }; ($first_arg:ident $(,$rest:ident)*) => { unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){ diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index dc610d7b4674..13d38d3c9609 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -73,6 +73,8 @@ #![stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(no_global_oom_handling))] +use core::clone::TrivialClone; #[cfg(not(no_global_oom_handling))] use core::cmp; use core::cmp::Ordering; @@ -80,7 +82,7 @@ #[cfg(not(no_global_oom_handling))] use core::iter; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties}; +use core::mem::{self, Assume, ManuallyDrop, MaybeUninit, SizedTypeProperties, TransmuteFrom}; use core::ops::{self, Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice::{self, SliceIndex}; @@ -590,21 +592,13 @@ pub fn try_with_capacity(capacity: usize) -> Result { /// /// # Examples /// - // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// use std::ptr; - /// use std::mem; /// /// let v = vec![1, 2, 3]; /// - /// // Prevent running `v`'s destructor so we are in complete control - /// // of the allocation. - /// let mut v = mem::ManuallyDrop::new(v); - /// - /// // Pull out the various important pieces of information about `v` - /// let p = v.as_mut_ptr(); - /// let len = v.len(); - /// let cap = v.capacity(); + /// // Deconstruct the vector into parts. + /// let (p, len, cap) = v.into_raw_parts(); /// /// unsafe { /// // Overwrite memory with 4, 5, 6 @@ -698,23 +692,13 @@ pub unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Sel /// /// # Examples /// - // FIXME Update this when vec_into_raw_parts is stabilized /// ``` /// #![feature(box_vec_non_null)] /// - /// use std::ptr::NonNull; - /// use std::mem; - /// /// let v = vec![1, 2, 3]; /// - /// // Prevent running `v`'s destructor so we are in complete control - /// // of the allocation. - /// let mut v = mem::ManuallyDrop::new(v); - /// - /// // Pull out the various important pieces of information about `v` - /// let p = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) }; - /// let len = v.len(); - /// let cap = v.capacity(); + /// // Deconstruct the vector into parts. + /// let (p, len, cap) = v.into_parts(); /// /// unsafe { /// // Overwrite memory with 4, 5, 6 @@ -781,7 +765,6 @@ pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Sel /// # Examples /// /// ``` - /// #![feature(vec_into_raw_parts)] /// let v: Vec = vec![-1, 0, 1]; /// /// let (ptr, len, cap) = v.into_raw_parts(); @@ -796,7 +779,7 @@ pub unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Sel /// assert_eq!(rebuilt, [4294967295, 0, 1]); /// ``` #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + #[stable(feature = "vec_into_raw_parts", since = "CURRENT_RUSTC_VERSION")] pub fn into_raw_parts(self) -> (*mut T, usize, usize) { let mut me = ManuallyDrop::new(self); (me.as_mut_ptr(), me.len(), me.capacity()) @@ -821,7 +804,7 @@ pub fn into_raw_parts(self) -> (*mut T, usize, usize) { /// # Examples /// /// ``` - /// #![feature(vec_into_raw_parts, box_vec_non_null)] + /// #![feature(box_vec_non_null)] /// /// let v: Vec = vec![-1, 0, 1]; /// @@ -838,7 +821,6 @@ pub fn into_raw_parts(self) -> (*mut T, usize, usize) { /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_parts(self) -> (NonNull, usize, usize) { let (ptr, len, capacity) = self.into_raw_parts(); // SAFETY: A `Vec` always has a non-null pointer. @@ -994,29 +976,20 @@ pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result, length: usize, capacity: usize, all /// # Examples /// /// ``` - /// #![feature(allocator_api, vec_into_raw_parts)] + /// #![feature(allocator_api)] /// /// use std::alloc::System; /// @@ -1226,7 +1188,6 @@ pub unsafe fn from_parts_in(ptr: NonNull, length: usize, capacity: usize, all /// ``` #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { let mut me = ManuallyDrop::new(self); let len = me.len(); @@ -1254,7 +1215,7 @@ pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { /// # Examples /// /// ``` - /// #![feature(allocator_api, vec_into_raw_parts, box_vec_non_null)] + /// #![feature(allocator_api, box_vec_non_null)] /// /// use std::alloc::System; /// @@ -1277,7 +1238,6 @@ pub fn into_raw_parts_with_alloc(self) -> (*mut T, usize, usize, A) { #[must_use = "losing the pointer will leak memory"] #[unstable(feature = "allocator_api", issue = "32838")] // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] pub fn into_parts_with_alloc(self) -> (NonNull, usize, usize, A) { let (ptr, len, capacity, alloc) = self.into_raw_parts_with_alloc(); // SAFETY: A `Vec` always has a non-null pointer. @@ -3241,6 +3201,92 @@ unsafe fn split_at_spare_mut_with_len( // - `cap / N` fits the size of the allocated memory after shrinking unsafe { Vec::from_raw_parts_in(ptr.cast(), len / N, cap / N, alloc) } } + + /// This clears out this `Vec` and recycles the allocation into a new `Vec`. + /// The item type of the resulting `Vec` needs to have the same size and + /// alignment as the item type of the original `Vec`. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_recycle, transmutability)] + /// let a: Vec = vec![0; 100]; + /// let capacity = a.capacity(); + /// let addr = a.as_ptr().addr(); + /// let b: Vec = a.recycle(); + /// assert_eq!(b.len(), 0); + /// assert_eq!(b.capacity(), capacity); + /// assert_eq!(b.as_ptr().addr(), addr); + /// ``` + /// + /// The `Recyclable` bound prevents this method from being called when `T` and `U` have different sizes; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// let vec: Vec<[u8; 2]> = Vec::new(); + /// let _: Vec<[u8; 1]> = vec.recycle(); + /// ``` + /// ...or different alignments: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// let vec: Vec<[u16; 0]> = Vec::new(); + /// let _: Vec<[u8; 0]> = vec.recycle(); + /// ``` + /// + /// However, due to temporary implementation limitations of `Recyclable`, + /// this method is not yet callable when `T` or `U` are slices, trait objects, + /// or other exotic types; e.g.: + /// + /// ```compile_fail,E0277 + /// #![feature(vec_recycle, transmutability)] + /// # let inputs = ["a b c", "d e f"]; + /// # fn process(_: &[&str]) {} + /// let mut storage: Vec<&[&str]> = Vec::new(); + /// + /// for input in inputs { + /// let mut buffer: Vec<&str> = storage.recycle(); + /// buffer.extend(input.split(" ")); + /// process(&buffer); + /// storage = buffer.recycle(); + /// } + /// ``` + #[unstable(feature = "vec_recycle", issue = "148227")] + #[expect(private_bounds)] + pub fn recycle(mut self) -> Vec + where + U: Recyclable, + { + self.clear(); + const { + // FIXME(const-hack, 146097): compare `Layout`s + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + }; + let (ptr, length, capacity, alloc) = self.into_parts_with_alloc(); + debug_assert_eq!(length, 0); + // SAFETY: + // - `ptr` and `alloc` were just returned from `self.into_raw_parts_with_alloc()` + // - `T` & `U` have the same layout, so `capacity` does not need to be changed and we can safely use `alloc.dealloc` later + // - the original vector was cleared, so there is no problem with "transmuting" the stored values + unsafe { Vec::from_parts_in(ptr.cast::(), length, capacity, alloc) } + } +} + +/// Denotes that an allocation of `From` can be recycled into an allocation of `Self`. +/// +/// # Safety +/// +/// `Self` is `Recyclable` if `Layout::new::() == Layout::new::()`. +unsafe trait Recyclable: Sized {} + +#[unstable_feature_bound(transmutability)] +// SAFETY: enforced by `TransmuteFrom` +unsafe impl Recyclable for To +where + for<'a> &'a MaybeUninit: TransmuteFrom<&'a MaybeUninit, { Assume::SAFETY }>, + for<'a> &'a MaybeUninit: TransmuteFrom<&'a MaybeUninit, { Assume::SAFETY }>, +{ } impl Vec { @@ -3494,7 +3540,7 @@ impl ExtendFromWithinSpec for Vec { } #[cfg(not(no_global_oom_handling))] -impl ExtendFromWithinSpec for Vec { +impl ExtendFromWithinSpec for Vec { unsafe fn spec_extend_from_within(&mut self, src: Range) { let count = src.len(); { @@ -3507,8 +3553,8 @@ unsafe fn spec_extend_from_within(&mut self, src: Range) { // SAFETY: // - Both pointers are created from unique slice references (`&mut [_]`) // so they are valid and do not overlap. - // - Elements are :Copy so it's OK to copy them, without doing - // anything with the original values + // - Elements implement `TrivialClone` so this is equivalent to calling + // `clone` on every one of them. // - `count` is equal to the len of `source`, so source is valid for // `count` reads // - `.reserve(count)` guarantees that `spare.len() >= count` so spare @@ -3887,7 +3933,8 @@ pub fn splice(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoI /// /// If the returned `ExtractIf` is not exhausted, e.g. because it is dropped without iterating /// or the iteration short-circuits, then the remaining elements will be retained. - /// Use [`retain_mut`] with a negated predicate if you do not need the returned iterator. + /// Use `extract_if().for_each(drop)` if you do not need the returned iterator, + /// or [`retain_mut`] with a negated predicate if you also do not need to restrict the range. /// /// [`retain_mut`]: Vec::retain_mut /// diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index 7085bceef5ba..f5bcd3ec9d82 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -1,3 +1,4 @@ +use core::clone::TrivialClone; use core::iter::TrustedLen; use core::slice::{self}; @@ -48,7 +49,7 @@ impl<'a, T: 'a, I, A: Allocator> SpecExtend<&'a T, I> for Vec impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for Vec where - T: Copy, + T: TrivialClone, { fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { let slice = iterator.as_slice(); diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index efdcb893bfee..665bc2704794 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -40,6 +40,7 @@ #![feature(slice_range)] #![feature(std_internals)] #![feature(temporary_niche_types)] +#![feature(trivial_clone)] #![feature(trusted_fused)] #![feature(trusted_len)] #![feature(trusted_random_access)] diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index ea334ab0f143..03b82fa82440 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2299,20 +2299,6 @@ fn test_vec_swap() { assert_eq!(n, 0); } -#[test] -fn test_extend_from_within_spec() { - #[derive(Copy)] - struct CopyOnly; - - impl Clone for CopyOnly { - fn clone(&self) -> Self { - panic!("extend_from_within must use specialization on copy"); - } - } - - vec![CopyOnly, CopyOnly].extend_from_within(..); -} - #[test] fn test_extend_from_within_clone() { let mut v = vec![String::from("sssss"), String::from("12334567890"), String::from("c")]; diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 0dc10758a856..2dd639d68f0e 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -5,10 +5,10 @@ #![stable(feature = "core_array", since = "1.35.0")] use crate::borrow::{Borrow, BorrowMut}; +use crate::clone::TrivialClone; use crate::cmp::Ordering; use crate::convert::Infallible; use crate::error::Error; -use crate::fmt; use crate::hash::{self, Hash}; use crate::intrinsics::transmute_unchecked; use crate::iter::{UncheckedIterator, repeat_n}; @@ -18,6 +18,7 @@ }; use crate::ptr::{null, null_mut}; use crate::slice::{Iter, IterMut}; +use crate::{fmt, ptr}; mod ascii; mod drain; @@ -451,6 +452,10 @@ fn clone_from(&mut self, other: &Self) { } } +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for [T; N] {} + trait SpecArrayClone: Clone { fn clone(array: &[Self; N]) -> [Self; N]; } @@ -462,10 +467,12 @@ impl SpecArrayClone for T { } } -impl SpecArrayClone for T { +impl SpecArrayClone for T { #[inline] fn clone(array: &[T; N]) -> [T; N] { - *array + // SAFETY: `TrivialClone` implies that this is equivalent to calling + // `Clone` on every element. + unsafe { ptr::read(array) } } } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 06d2c93cc698..bf8875098edf 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -250,10 +250,42 @@ fn clone_from(&mut self, source: &Self) } } +/// Indicates that the `Clone` implementation is identical to copying the value. +/// +/// This is used for some optimizations in the standard library, which specializes +/// on this trait to select faster implementations of functions such as +/// [`clone_from_slice`](slice::clone_from_slice). It is automatically implemented +/// when using `#[derive(Clone, Copy)]`. +/// +/// Note that this trait does not imply that the type is `Copy`, because e.g. +/// `core::ops::Range` could soundly implement this trait. +/// +/// # Safety +/// `Clone::clone` must be equivalent to copying the value, otherwise calling functions +/// such as `slice::clone_from_slice` can have undefined behaviour. +#[unstable( + feature = "trivial_clone", + reason = "this isn't part of any API guarantee", + issue = "none" +)] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] +#[lang = "trivial_clone"] +// SAFETY: +// It is sound to specialize on this because the `clone` implementation cannot be +// lifetime-dependent. Therefore, if `TrivialClone` is implemented for any lifetime, +// its invariant holds whenever `Clone` is implemented, even if the actual +// `TrivialClone` bound would not be satisfied because of lifetime bounds. +#[rustc_unsafe_specialization_marker] +// If `#[derive(Clone, Clone, Copy)]` is written, there will be multiple +// implementations of `TrivialClone`. To keep it from appearing in error +// messages, make it a `#[marker]` trait. +#[marker] +pub const unsafe trait TrivialClone: [const] Clone {} + /// Derive macro generating an impl of the trait `Clone`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy)] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy, trivial_clone)] pub macro Clone($item:item) { /* compiler built-in */ } @@ -569,6 +601,7 @@ unsafe fn clone_to_uninit(&self, dst: *mut u8) { /// are implemented in `traits::SelectionContext::copy_clone_conditions()` /// in `rustc_trait_selection`. mod impls { + use super::TrivialClone; use crate::marker::PointeeSized; macro_rules! impl_clone { @@ -582,6 +615,11 @@ fn clone(&self) -> Self { *self } } + + #[doc(hidden)] + #[unstable(feature = "trivial_clone", issue = "none")] + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + unsafe impl const TrivialClone for $t {} )* } } @@ -602,6 +640,11 @@ fn clone(&self) -> Self { } } + #[doc(hidden)] + #[unstable(feature = "trivial_clone", issue = "none")] + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + unsafe impl const TrivialClone for ! {} + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] impl const Clone for *const T { @@ -611,6 +654,11 @@ fn clone(&self) -> Self { } } + #[doc(hidden)] + #[unstable(feature = "trivial_clone", issue = "none")] + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + unsafe impl const TrivialClone for *const T {} + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] impl const Clone for *mut T { @@ -620,6 +668,11 @@ fn clone(&self) -> Self { } } + #[doc(hidden)] + #[unstable(feature = "trivial_clone", issue = "none")] + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + unsafe impl const TrivialClone for *mut T {} + /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] @@ -631,6 +684,11 @@ fn clone(&self) -> Self { } } + #[doc(hidden)] + #[unstable(feature = "trivial_clone", issue = "none")] + #[rustc_const_unstable(feature = "const_clone", issue = "142757")] + unsafe impl const TrivialClone for &T {} + /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl !Clone for &mut T {} diff --git a/library/core/src/clone/uninit.rs b/library/core/src/clone/uninit.rs index 8b738bec796d..8d1185067eb8 100644 --- a/library/core/src/clone/uninit.rs +++ b/library/core/src/clone/uninit.rs @@ -1,3 +1,4 @@ +use super::TrivialClone; use crate::mem::{self, MaybeUninit}; use crate::ptr; @@ -49,9 +50,9 @@ unsafe impl CopySpec for T { } } -// Specialized implementation for types that are [`Copy`], not just [`Clone`], +// Specialized implementation for types that are [`TrivialClone`], not just [`Clone`], // and can therefore be copied bitwise. -unsafe impl CopySpec for T { +unsafe impl CopySpec for T { #[inline] unsafe fn clone_one(src: &Self, dst: *mut Self) { // SAFETY: The safety conditions of clone_to_uninit() are a superset of those of diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 09d9b160700c..9a35ed07b89a 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -15,18 +15,18 @@ // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the // links to `CString` and `String` for now until a solution is developed -/// Representation of a borrowed C string. +/// A dynamically-sized view of a C string. /// -/// This type represents a borrowed reference to a nul-terminated +/// The type `&CStr` represents a reference to a borrowed nul-terminated /// array of bytes. It can be constructed safely from a &[[u8]] /// slice, or unsafely from a raw `*const c_char`. It can be expressed as a /// literal in the form `c"Hello world"`. /// -/// The `CStr` can then be converted to a Rust &[str] by performing +/// The `&CStr` can then be converted to a Rust &[str] by performing /// UTF-8 validation, or into an owned `CString`. /// /// `&CStr` is to `CString` as &[str] is to `String`: the former -/// in each pair are borrowed references; the latter are owned +/// in each pair are borrowing references; the latter are owned /// strings. /// /// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)` diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 502ca4aefe10..8ff64109b5cf 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -4,10 +4,12 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell}; use crate::char::{EscapeDebugExtArgs, MAX_LEN_UTF8}; +use crate::hint::assert_unchecked; use crate::marker::{PhantomData, PointeeSized}; use crate::num::fmt as numfmt; use crate::ops::Deref; -use crate::{iter, result, str}; +use crate::ptr::NonNull; +use crate::{iter, mem, result, str}; mod builders; #[cfg(not(no_fp_fmt_parse))] @@ -288,7 +290,7 @@ pub struct FormattingOptions { /// ```text /// 31 30 29 28 27 26 25 24 23 22 21 20 0 /// ┌───┬───────┬───┬───┬───┬───┬───┬───┬───┬───┬──────────────────────────────────┐ - /// │ 1 │ align │ p │ w │ X?│ x?│'0'│ # │ - │ + │ fill │ + /// │ 0 │ align │ p │ w │ X?│ x?│'0'│ # │ - │ + │ fill │ /// └───┴───────┴───┴───┴───┴───┴───┴───┴───┴───┴──────────────────────────────────┘ /// │ │ │ │ └─┬───────────────────┘ └─┬──────────────────────────────┘ /// │ │ │ │ │ └─ The fill character (21 bits char). @@ -299,12 +301,9 @@ pub struct FormattingOptions { /// │ ├─ 1: Align right. (>) /// │ ├─ 2: Align center. (^) /// │ └─ 3: Alignment not set. (default) - /// └─ Always set. - /// This makes it possible to distinguish formatting flags from - /// a &str size when stored in (the upper bits of) the same field. - /// (fmt::Arguments will make use of this property in the future.) + /// └─ Always zero. /// ``` - // Note: This could use a special niche type with range 0x8000_0000..=0xfdd0ffff. + // Note: This could use a pattern type with range 0x0000_0000..=0x7dd0ffff. // It's unclear if that's useful, though. flags: u32, /// Width if width flag (bit 27) above is set. Otherwise, always 0. @@ -328,7 +327,6 @@ mod flags { pub(super) const ALIGN_RIGHT: u32 = 1 << 29; pub(super) const ALIGN_CENTER: u32 = 2 << 29; pub(super) const ALIGN_UNKNOWN: u32 = 3 << 29; - pub(super) const ALWAYS_SET: u32 = 1 << 31; } impl FormattingOptions { @@ -344,11 +342,7 @@ impl FormattingOptions { /// - no [`DebugAsHex`] output mode. #[unstable(feature = "formatting_options", issue = "118117")] pub const fn new() -> Self { - Self { - flags: ' ' as u32 | flags::ALIGN_UNKNOWN | flags::ALWAYS_SET, - width: 0, - precision: 0, - } + Self { flags: ' ' as u32 | flags::ALIGN_UNKNOWN, width: 0, precision: 0 } } /// Sets or removes the sign (the `+` or the `-` flag). @@ -612,19 +606,141 @@ pub const fn with_options<'b>(&'b mut self, options: FormattingOptions) -> Forma /// ``` /// /// [`format()`]: ../../std/fmt/fn.format.html +// +// Internal representation: +// +// fmt::Arguments is represented in one of two ways: +// +// 1) String literal representation (e.g. format_args!("hello")) +// ┌────────────────────────────────┐ +// template: │ *const u8 │ ─▷ "hello" +// ├──────────────────────────────┬─┤ +// args: │ len │1│ (lowest bit is 1; field contains `len << 1 | 1`) +// └──────────────────────────────┴─┘ +// In this representation, there are no placeholders and `fmt::Arguments::as_str()` returns Some. +// The pointer points to the start of a static `str`. The length is given by `args as usize >> 1`. +// (The length of a `&str` is isize::MAX at most, so it always fits in a usize minus one bit.) +// +// `fmt::Arguments::from_str()` constructs this representation from a `&'static str`. +// +// 2) Placeholders representation (e.g. format_args!("hello {name}\n")) +// ┌────────────────────────────────┐ +// template: │ *const u8 │ ─▷ b"\x06hello \x80\x01\n\x00" +// ├────────────────────────────────┤ +// args: │ &'a [Argument<'a>; _] 0│ (lower bit is 0 due to alignment of Argument type) +// └────────────────────────────────┘ +// In this representation, the template is a byte sequence encoding both the literal string pieces +// and the placeholders (including their options/flags). +// +// The `args` pointer points to an array of `fmt::Argument<'a>` values, of sufficient length to +// match the placeholders in the template. +// +// `fmt::Arguments::new()` constructs this representation from a template byte slice and a slice +// of arguments. This function is unsafe, as the template is assumed to be valid and the args +// slice is assumed to have elements matching the template. +// +// The template byte sequence is the concatenation of parts of the following types: +// +// - Literal string piece: +// Pieces that must be formatted verbatim (e.g. "hello " and "\n" in "hello {name}\n") +// appear literally in the template byte sequence, prefixed by their length. +// +// For pieces of up to 127 bytes, these are represented as a single byte containing the +// length followed directly by the bytes of the string: +// ┌───┬────────────────────────────┐ +// │len│ `len` bytes (utf-8) │ (e.g. b"\x06hello ") +// └───┴────────────────────────────┘ +// +// For larger pieces up to u16::MAX bytes, these are represented as a 0x80 followed by +// their length in 16-bit little endian, followed by the bytes of the string: +// ┌────┬─────────┬───────────────────────────┐ +// │0x80│ len │ `len` bytes (utf-8) │ (e.g. b"\x80\x00\x01hello … ") +// └────┴─────────┴───────────────────────────┘ +// +// Longer pieces are split into multiple pieces of max u16::MAX bytes (at utf-8 boundaries). +// +// - Placeholder: +// Placeholders (e.g. `{name}` in "hello {name}") are represented as a byte with the highest +// two bits set, followed by zero or more fields depending on the flags in the first byte: +// ┌──────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┬┄┄┄┄┄┄┄┄┄┄┄┐ +// │0b11______│ flags ┊ width ┊ precision ┊ arg_index ┊ (e.g. b"\xC2\x05\0") +// └────││││││┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┴┄┄┄┄┄┄┄┄┄┄┄┘ +// ││││││ 32 bit 16 bit 16 bit 16 bit +// │││││└─ flags present +// ││││└─ width present +// │││└─ precision present +// ││└─ arg_index present +// │└─ width indirect +// └─ precision indirect +// +// All fields other than the first byte are optional and only present when their +// corresponding flag is set in the first byte. +// +// So, a fully default placeholder without any options is just a single byte: +// ┌──────────┐ +// │0b11000000│ (b"\xC0") +// └──────────┘ +// +// The fields are stored as little endian. +// +// The `flags` fields corresponds to the `flags` field of `FormattingOptions`. +// See doc comment of `FormattingOptions::flags` for details. +// +// The `width` and `precision` fields correspond to their respective fields in +// `FormattingOptions`. However, if their "indirect" flag is set, the field contains the +// index in the `args` array where the dynamic width or precision is stored, rather than the +// value directly. +// +// The `arg_index` field is the index into the `args` array for the argument to be +// formatted. +// +// If omitted, the flags, width and precision of the default FormattingOptions::new() are +// used. +// +// If the `arg_index` is omitted, the next argument in the `args` array is used (starting +// at 0). +// +// - End: +// A single zero byte marks the end of the template: +// ┌───┐ +// │ 0 │ ("\0") +// └───┘ +// +// (Note that a zero byte may also occur naturally as part of the string pieces or flags, +// width, precision and arg_index fields above. That is, the template byte sequence ends +// with a 0 byte, but isn't terminated by the first 0 byte.) +// #[lang = "format_arguments"] #[stable(feature = "rust1", since = "1.0.0")] #[derive(Copy, Clone)] pub struct Arguments<'a> { - // Format string pieces to print. - pieces: &'a [&'static str], + template: NonNull, + args: NonNull>, +} - // Placeholder specs, or `None` if all specs are default (as in "{}{}"). - fmt: Option<&'a [rt::Placeholder]>, +/// Used by the format_args!() macro to create a fmt::Arguments object. +#[doc(hidden)] +#[rustc_diagnostic_item = "FmtArgumentsNew"] +#[unstable(feature = "fmt_internals", issue = "none")] +impl<'a> Arguments<'a> { + // SAFETY: The caller must ensure that the provided template and args encode a valid + // fmt::Arguments, as documented above. + #[inline] + pub unsafe fn new( + template: &'a [u8; N], + args: &'a [rt::Argument<'a>; M], + ) -> Arguments<'a> { + // SAFETY: Responsibility of the caller. + unsafe { Arguments { template: mem::transmute(template), args: mem::transmute(args) } } + } - // Dynamic arguments for interpolation, to be interleaved with string - // pieces. (Every argument is preceded by a string piece.) - args: &'a [rt::Argument<'a>], + // Same as `from_str`, but not const. + // Used by format_args!() expansion when arguments are inlined, + // e.g. format_args!("{}", 123), which is not allowed in const. + #[inline] + pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { + Arguments::from_str(s) + } } #[doc(hidden)] @@ -636,25 +752,76 @@ impl<'a> Arguments<'a> { /// when using `format!`. Note: this is neither the lower nor upper bound. #[inline] pub fn estimated_capacity(&self) -> usize { - let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); + if let Some(s) = self.as_str() { + return s.len(); + } + // Iterate over the template, counting the length of literal pieces. + let mut length = 0usize; + let mut starts_with_placeholder = false; + let mut template = self.template; + loop { + // SAFETY: We can assume the template is valid. + unsafe { + let n = template.read(); + template = template.add(1); + if n == 0 { + // End of template. + break; + } else if n < 128 { + // Short literal string piece. + length += n as usize; + template = template.add(n as usize); + } else if n == 128 { + // Long literal string piece. + let len = usize::from(u16::from_le_bytes(template.cast_array().read())); + length += len; + template = template.add(2 + len); + } else { + assert_unchecked(n >= 0xC0); + // Placeholder piece. + if length == 0 { + starts_with_placeholder = true; + } + // Skip remainder of placeholder: + let skip = (n & 1 != 0) as usize * 4 // flags (32 bit) + + (n & 2 != 0) as usize * 2 // width (16 bit) + + (n & 4 != 0) as usize * 2 // precision (16 bit) + + (n & 8 != 0) as usize * 2; // arg_index (16 bit) + template = template.add(skip as usize); + } + } + } - if self.args.is_empty() { - pieces_length - } else if !self.pieces.is_empty() && self.pieces[0].is_empty() && pieces_length < 16 { - // If the format string starts with an argument, + if starts_with_placeholder && length < 16 { + // If the format string starts with a placeholder, // don't preallocate anything, unless length - // of pieces is significant. + // of literal pieces is significant. 0 } else { - // There are some arguments, so any additional push + // There are some placeholders, so any additional push // will reallocate the string. To avoid that, // we're "pre-doubling" the capacity here. - pieces_length.checked_mul(2).unwrap_or(0) + length.wrapping_mul(2) } } } impl<'a> Arguments<'a> { + /// Create a `fmt::Arguments` object for a single static string. + /// + /// Formatting this `fmt::Arguments` will just produce the string as-is. + #[inline] + #[unstable(feature = "fmt_arguments_from_str", issue = "148905")] + pub const fn from_str(s: &'static str) -> Arguments<'a> { + // SAFETY: This is the "static str" representation of fmt::Arguments; see above. + unsafe { + Arguments { + template: mem::transmute(s.as_ptr()), + args: mem::transmute(s.len() << 1 | 1), + } + } + } + /// Gets the formatted string, if it has no arguments to be formatted at runtime. /// /// This can be used to avoid allocations in some cases. @@ -702,10 +869,22 @@ impl<'a> Arguments<'a> { #[must_use] #[inline] pub const fn as_str(&self) -> Option<&'static str> { - match (self.pieces, self.args) { - ([], []) => Some(""), - ([s], []) => Some(s), - _ => None, + // SAFETY: During const eval, `self.args` must have come from a usize, + // not a pointer, because that's the only way to create a fmt::Arguments in const. + // (I.e. only fmt::Arguments::from_str is const, fmt::Arguments::new is not.) + // + // Outside const eval, transmuting a pointer to a usize is fine. + let bits: usize = unsafe { mem::transmute(self.args) }; + if bits & 1 == 1 { + // SAFETY: This fmt::Arguments stores a &'static str. See encoding documentation above. + Some(unsafe { + str::from_utf8_unchecked(crate::slice::from_raw_parts( + self.template.as_ptr(), + bits >> 1, + )) + }) + } else { + None } } @@ -1448,86 +1627,113 @@ pub trait UpperExp: PointeeSized { /// /// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] -pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { - let mut formatter = Formatter::new(output, FormattingOptions::new()); - let mut idx = 0; +pub fn write(output: &mut dyn Write, fmt: Arguments<'_>) -> Result { + if let Some(s) = fmt.as_str() { + return output.write_str(s); + } - match args.fmt { - None => { - // We can use default formatting parameters for all arguments. - for (i, arg) in args.args.iter().enumerate() { - // SAFETY: args.args and args.pieces come from the same Arguments, - // which guarantees the indexes are always within bounds. - let piece = unsafe { args.pieces.get_unchecked(i) }; - if !piece.is_empty() { - formatter.buf.write_str(*piece)?; + let mut template = fmt.template; + let args = fmt.args; + + let mut arg_index = 0; + + // See comment on `fmt::Arguments` for the details of how the template is encoded. + + // This must match the encoding from `expand_format_args` in + // compiler/rustc_ast_lowering/src/format.rs. + loop { + // SAFETY: We can assume the template is valid. + let n = unsafe { + let n = template.read(); + template = template.add(1); + n + }; + + if n == 0 { + // End of template. + return Ok(()); + } else if n < 0x80 { + // Literal string piece of length `n`. + + // SAFETY: We can assume the strings in the template are valid. + let s = unsafe { + let s = crate::str::from_raw_parts(template.as_ptr(), n as usize); + template = template.add(n as usize); + s + }; + output.write_str(s)?; + } else if n == 0x80 { + // Literal string piece with a 16-bit length. + + // SAFETY: We can assume the strings in the template are valid. + let s = unsafe { + let len = usize::from(u16::from_le_bytes(template.cast_array().read())); + template = template.add(2); + let s = crate::str::from_raw_parts(template.as_ptr(), len); + template = template.add(len); + s + }; + output.write_str(s)?; + } else if n == 0xC0 { + // Placeholder for next argument with default options. + // + // Having this as a separate case improves performance for the common case. + + // SAFETY: We can assume the template only refers to arguments that exist. + unsafe { + args.add(arg_index) + .as_ref() + .fmt(&mut Formatter::new(output, FormattingOptions::new()))?; + } + arg_index += 1; + } else { + // SAFETY: We can assume the template is valid. + unsafe { assert_unchecked(n > 0xC0) }; + + // Placeholder with custom options. + + let mut opt = FormattingOptions::new(); + + // SAFETY: We can assume the template is valid. + unsafe { + if n & 1 != 0 { + opt.flags = u32::from_le_bytes(template.cast_array().read()); + template = template.add(4); } - - // SAFETY: There are no formatting parameters and hence no - // count arguments. + if n & 2 != 0 { + opt.width = u16::from_le_bytes(template.cast_array().read()); + template = template.add(2); + } + if n & 4 != 0 { + opt.precision = u16::from_le_bytes(template.cast_array().read()); + template = template.add(2); + } + if n & 8 != 0 { + arg_index = usize::from(u16::from_le_bytes(template.cast_array().read())); + template = template.add(2); + } + } + if n & 16 != 0 { + // Dynamic width from a usize argument. + // SAFETY: We can assume the template only refers to arguments that exist. unsafe { - arg.fmt(&mut formatter)?; + opt.width = args.add(opt.width as usize).as_ref().as_u16().unwrap_unchecked(); } - idx += 1; } - } - Some(fmt) => { - // Every spec has a corresponding argument that is preceded by - // a string piece. - for (i, arg) in fmt.iter().enumerate() { - // SAFETY: fmt and args.pieces come from the same Arguments, - // which guarantees the indexes are always within bounds. - let piece = unsafe { args.pieces.get_unchecked(i) }; - if !piece.is_empty() { - formatter.buf.write_str(*piece)?; + if n & 32 != 0 { + // Dynamic precision from a usize argument. + // SAFETY: We can assume the template only refers to arguments that exist. + unsafe { + opt.precision = + args.add(opt.precision as usize).as_ref().as_u16().unwrap_unchecked(); } - // SAFETY: arg and args.args come from the same Arguments, - // which guarantees the indexes are always within bounds. - unsafe { run(&mut formatter, arg, args.args) }?; - idx += 1; } - } - } - // There can be only one trailing string piece left. - if let Some(piece) = args.pieces.get(idx) { - formatter.buf.write_str(*piece)?; - } - - Ok(()) -} - -unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result { - let (width, precision) = - // SAFETY: arg and args come from the same Arguments, - // which guarantees the indexes are always within bounds. - unsafe { (getcount(args, &arg.width), getcount(args, &arg.precision)) }; - - let options = FormattingOptions { flags: arg.flags, width, precision }; - - // Extract the correct argument - debug_assert!(arg.position < args.len()); - // SAFETY: arg and args come from the same Arguments, - // which guarantees its index is always within bounds. - let value = unsafe { args.get_unchecked(arg.position) }; - - // Set all the formatting options. - fmt.options = options; - - // Then actually do some printing - // SAFETY: this is a placeholder argument. - unsafe { value.fmt(fmt) } -} - -unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> u16 { - match *cnt { - rt::Count::Is(n) => n, - rt::Count::Implied => 0, - rt::Count::Param(i) => { - debug_assert!(i < args.len()); - // SAFETY: cnt and args come from the same Arguments, - // which guarantees this index is always within bounds. - unsafe { args.get_unchecked(i).as_u16().unwrap_unchecked() } + // SAFETY: We can assume the template only refers to arguments that exist. + unsafe { + args.add(arg_index).as_ref().fmt(&mut Formatter::new(output, opt))?; + } + arg_index += 1; } } } diff --git a/library/core/src/fmt/rt.rs b/library/core/src/fmt/rt.rs index fb858a057261..5221783e7290 100644 --- a/library/core/src/fmt/rt.rs +++ b/library/core/src/fmt/rt.rs @@ -10,28 +10,6 @@ use crate::hint::unreachable_unchecked; use crate::ptr::NonNull; -#[lang = "format_placeholder"] -#[derive(Copy, Clone)] -pub struct Placeholder { - pub position: usize, - pub flags: u32, - pub precision: Count, - pub width: Count, -} - -/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) -/// and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers. -#[lang = "format_count"] -#[derive(Copy, Clone)] -pub enum Count { - /// Specified with a literal number, stores the value - Is(u16), - /// Specified using `$` and `*` syntaxes, stores the index into `args` - Param(usize), - /// Not specified - Implied, -} - #[derive(Copy, Clone)] enum ArgumentType<'a> { Placeholder { @@ -56,6 +34,7 @@ enum ArgumentType<'a> { /// precision and width. #[lang = "format_argument"] #[derive(Copy, Clone)] +#[repr(align(2))] // To ensure pointers to this struct always have their lowest bit cleared. pub struct Argument<'a> { ty: ArgumentType<'a>, } @@ -184,55 +163,3 @@ pub(super) const fn as_u16(&self) -> Option { } } } - -/// Used by the format_args!() macro to create a fmt::Arguments object. -#[doc(hidden)] -#[unstable(feature = "fmt_internals", issue = "none")] -#[rustc_diagnostic_item = "FmtArgumentsNew"] -impl<'a> Arguments<'a> { - #[inline] - pub const fn new_const(pieces: &'a [&'static str; N]) -> Self { - const { assert!(N <= 1) }; - Arguments { pieces, fmt: None, args: &[] } - } - - /// When using the format_args!() macro, this function is used to generate the - /// Arguments structure. - /// - /// This function should _not_ be const, to make sure we don't accept - /// format_args!() and panic!() with arguments in const, even when not evaluated: - /// - /// ```compile_fail,E0015 - /// const _: () = if false { panic!("a {}", "a") }; - /// ``` - #[inline] - pub fn new_v1( - pieces: &'a [&'static str; P], - args: &'a [rt::Argument<'a>; A], - ) -> Arguments<'a> { - const { assert!(P >= A && P <= A + 1, "invalid args") } - Arguments { pieces, fmt: None, args } - } - - /// Specifies nonstandard formatting parameters. - /// - /// SAFETY: the following invariants must be held: - /// 1. The `pieces` slice must be at least as long as `fmt`. - /// 2. Every `rt::Placeholder::position` value within `fmt` must be a valid index of `args`. - /// 3. Every `rt::Count::Param` within `fmt` must contain a valid index of `args`. - /// - /// This function should _not_ be const, to make sure we don't accept - /// format_args!() and panic!() with arguments in const, even when not evaluated: - /// - /// ```compile_fail,E0015 - /// const _: () = if false { panic!("a {:1}", "a") }; - /// ``` - #[inline] - pub unsafe fn new_v1_formatted( - pieces: &'a [&'static str], - args: &'a [rt::Argument<'a>], - fmt: &'a [rt::Placeholder], - ) -> Arguments<'a> { - Arguments { pieces, fmt: Some(fmt), args } - } -} diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 4f6a425a0983..82cb489ccf40 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -622,7 +622,7 @@ pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T /// // Crucially, we `as`-cast to a raw pointer before `transmute`ing to a function pointer. /// // This avoids an integer-to-pointer `transmute`, which can be problematic. /// // Transmuting between raw pointers and function pointers (i.e., two pointer types) is fine. -/// let pointer = foo as *const (); +/// let pointer = foo as fn() -> i32 as *const (); /// let function = unsafe { /// std::mem::transmute::<*const (), fn() -> i32>(pointer) /// }; @@ -769,13 +769,9 @@ pub const fn select_unpredictable(b: bool, true_val: T, false_val: T) -> T /// // in terms of converting the original inner type (`&i32`) to the new one (`Option<&i32>`), /// // this has all the same caveats. Besides the information provided above, also consult the /// // [`from_raw_parts`] documentation. +/// let (ptr, len, capacity) = v_clone.into_raw_parts(); /// let v_from_raw = unsafe { -// FIXME Update this when vec_into_raw_parts is stabilized -/// // Ensure the original vector is not dropped. -/// let mut v_clone = std::mem::ManuallyDrop::new(v_clone); -/// Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut Option<&i32>, -/// v_clone.len(), -/// v_clone.capacity()) +/// Vec::from_raw_parts(ptr.cast::<*mut Option<&i32>>(), len, capacity) /// }; /// ``` /// diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index df24dd43b82e..6156525b2f59 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -991,7 +991,7 @@ macro_rules! compile_error { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "format_args_macro"] #[allow_internal_unsafe] - #[allow_internal_unstable(fmt_internals)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[macro_export] macro_rules! format_args { @@ -1005,7 +1005,7 @@ macro_rules! format_args { /// /// This macro will be removed once `format_args` is allowed in const contexts. #[unstable(feature = "const_format_args", issue = "none")] - #[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[macro_export] macro_rules! const_format_args { @@ -1020,7 +1020,7 @@ macro_rules! const_format_args { reason = "`format_args_nl` is only for internal \ language use and is subject to change" )] - #[allow_internal_unstable(fmt_internals)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[doc(hidden)] #[macro_export] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5fd0611a1843..e945cd77a75f 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -14,6 +14,7 @@ PhantomInvariant, PhantomInvariantLifetime, Variance, variance, }; use crate::cell::UnsafeCell; +use crate::clone::TrivialClone; use crate::cmp; use crate::fmt::Debug; use crate::hash::{Hash, Hasher}; @@ -454,12 +455,8 @@ pub trait StructuralPartialEq { /// [impls]: #implementors #[stable(feature = "rust1", since = "1.0.0")] #[lang = "copy"] -// FIXME(matthewjasper) This allows copying a type that doesn't implement -// `Copy` because of unsatisfied lifetime bounds (copying `A<'_>` when only -// `A<'static>: Copy` and `A<'_>: Clone`). -// We have this attribute here for now only because there are quite a few -// existing specializations on `Copy` that already exist in the standard -// library, and there's no way to safely have this behavior right now. +// This is unsound, but required by `hashbrown` +// FIXME(joboet): change `hashbrown` to use `TrivialClone` #[rustc_unsafe_specialization_marker] #[rustc_diagnostic_item = "Copy"] pub trait Copy: Clone { @@ -861,6 +858,10 @@ fn clone(&self) -> Self { } } +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for PhantomData {} + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] impl const Default for PhantomData { diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs index 55fdacb014e6..5fc62a5ad7ac 100644 --- a/library/core/src/marker/variance.rs +++ b/library/core/src/marker/variance.rs @@ -2,6 +2,7 @@ use super::PhantomData; use crate::any::type_name; +use crate::clone::TrivialClone; use crate::cmp::Ordering; use crate::fmt; use crate::hash::{Hash, Hasher}; @@ -60,6 +61,9 @@ fn clone(&self) -> Self { impl Copy for $name where T: ?Sized {} + #[doc(hidden)] + unsafe impl TrivialClone for $name where T: ?Sized {} + impl PartialEq for $name where T: ?Sized { diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index 4ed914386eb8..3507d1a0a9a8 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -1,4 +1,5 @@ use crate::any::type_name; +use crate::clone::TrivialClone; use crate::marker::Destruct; use crate::mem::ManuallyDrop; use crate::{fmt, intrinsics, ptr, slice}; @@ -356,6 +357,11 @@ fn clone(&self) -> Self { } } +// SAFETY: the clone implementation is a copy, see above. +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for MaybeUninit where MaybeUninit: Clone {} + #[stable(feature = "maybe_uninit_debug", since = "1.41.0")] impl fmt::Debug for MaybeUninit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1599,8 +1605,12 @@ impl SpecFill for [MaybeUninit] { } } -impl SpecFill for [MaybeUninit] { +impl SpecFill for [MaybeUninit] { fn spec_fill(&mut self, value: T) { - self.fill(MaybeUninit::new(value)); + // SAFETY: because `T` is `TrivialClone`, this is equivalent to calling + // `T::clone` for every element. Notably, `TrivialClone` also implies + // that the `clone` implementation will not panic, so we can avoid + // initialization guards and such. + self.fill_with(|| MaybeUninit::new(unsafe { ptr::read(&value) })); } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 619e8a263db4..b180e88fd49b 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -6,6 +6,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use crate::alloc::Layout; +use crate::clone::TrivialClone; use crate::marker::{Destruct, DiscriminantKind}; use crate::panic::const_assert; use crate::{clone, cmp, fmt, hash, intrinsics, ptr}; @@ -1070,6 +1071,10 @@ fn clone(&self) -> Self { } } +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for Discriminant {} + #[stable(feature = "discriminant_value", since = "1.21.0")] impl cmp::PartialEq for Discriminant { fn eq(&self, rhs: &Self) -> bool { diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index e7101537b298..2cf06b6d6a35 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -1770,6 +1770,7 @@ pub fn rem_euclid(self, rhs: f128) -> f128 { /// assert!(abs_difference <= f128::EPSILON); /// /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// assert_eq!(f128::powi(0.0, 0), 1.0); /// # } /// ``` #[inline] diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index aa8342a22ad5..51f803672e5c 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -1745,6 +1745,7 @@ pub fn rem_euclid(self, rhs: f16) -> f16 { /// assert!(abs_difference <= f16::EPSILON); /// /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// assert_eq!(f16::powi(0.0, 0), 1.0); /// # } /// ``` #[inline] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 7d395eb78034..6a6853670665 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -992,10 +992,10 @@ pub const fn strict_div_euclid(self, rhs: Self) -> Self { /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_exact_div(-1), Some(", stringify!($Max), "));")] - #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_exact_div(2), None);")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_exact_div(-1), None);")] - #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_exact_div(0), None);")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).checked_div_exact(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").checked_div_exact(2), None);")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.checked_div_exact(-1), None);")] + #[doc = concat!("assert_eq!((1", stringify!($SelfT), ").checked_div_exact(0), None);")] /// ``` #[unstable( feature = "exact_div", @@ -1004,7 +1004,7 @@ pub const fn strict_div_euclid(self, rhs: Self) -> Self { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_exact_div(self, rhs: Self) -> Option { + pub const fn checked_div_exact(self, rhs: Self) -> Option { if intrinsics::unlikely(rhs == 0 || ((self == Self::MIN) && (rhs == -1))) { None } else { @@ -1034,18 +1034,18 @@ pub const fn checked_exact_div(self, rhs: Self) -> Option { /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), Some(32));")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), Some(2));")] - #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).exact_div(-1), Some(", stringify!($Max), "));")] - #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".exact_div(2), None);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".div_exact(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".div_exact(32), Some(2));")] + #[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).div_exact(-1), Some(", stringify!($Max), "));")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".div_exact(2), None);")] /// ``` /// ```should_panic /// #![feature(exact_div)] - #[doc = concat!("let _ = 64", stringify!($SelfT),".exact_div(0);")] + #[doc = concat!("let _ = 64", stringify!($SelfT),".div_exact(0);")] /// ``` /// ```should_panic /// #![feature(exact_div)] - #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.exact_div(-1);")] + #[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.div_exact(-1);")] /// ``` #[unstable( feature = "exact_div", @@ -1055,7 +1055,7 @@ pub const fn checked_exact_div(self, rhs: Self) -> Option { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] - pub const fn exact_div(self, rhs: Self) -> Option { + pub const fn div_exact(self, rhs: Self) -> Option { if self % rhs != 0 { None } else { @@ -1069,7 +1069,7 @@ pub const fn exact_div(self, rhs: Self) -> Option { /// /// This results in undefined behavior when `rhs == 0`, `self % rhs != 0`, or #[doc = concat!("`self == ", stringify!($SelfT), "::MIN && rhs == -1`,")] - /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + /// i.e. when [`checked_div_exact`](Self::checked_div_exact) would return `None`. #[unstable( feature = "exact_div", issue = "139911", @@ -1077,10 +1077,10 @@ pub const fn exact_div(self, rhs: Self) -> Option { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_div_exact(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, - concat!(stringify!($SelfT), "::unchecked_exact_div cannot overflow, divide by zero, or leave a remainder"), + concat!(stringify!($SelfT), "::unchecked_div_exact cannot overflow, divide by zero, or leave a remainder"), ( lhs: $SelfT = self, rhs: $SelfT = rhs, @@ -1431,17 +1431,17 @@ pub const fn unbounded_shl(self, rhs: u32) -> $SelfT{ /// ``` /// #![feature(exact_bitshifts)] /// - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")] - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 2), Some(1 << ", stringify!($SelfT), "::BITS - 2));")] - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 1), None);")] - #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 2), Some(-0x2 << ", stringify!($SelfT), "::BITS - 2));")] - #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 1), None);")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".shl_exact(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".shl_exact(", stringify!($SelfT), "::BITS - 2), Some(1 << ", stringify!($SelfT), "::BITS - 2));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".shl_exact(", stringify!($SelfT), "::BITS - 1), None);")] + #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").shl_exact(", stringify!($SelfT), "::BITS - 2), Some(-0x2 << ", stringify!($SelfT), "::BITS - 2));")] + #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").shl_exact(", stringify!($SelfT), "::BITS - 1), None);")] /// ``` #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { + pub const fn shl_exact(self, rhs: u32) -> Option<$SelfT> { if rhs < self.leading_zeros() || rhs < self.leading_ones() { // SAFETY: rhs is checked above Some(unsafe { self.unchecked_shl(rhs) }) @@ -1458,16 +1458,16 @@ pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { /// /// This results in undefined behavior when `rhs >= self.leading_zeros() && rhs >= /// self.leading_ones()` i.e. when - #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")] + #[doc = concat!("[`", stringify!($SelfT), "::shl_exact`]")] /// would return `None`. #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { + pub const unsafe fn unchecked_shl_exact(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( check_library_ub, - concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out bits that would change the value of the first bit"), + concat!(stringify!($SelfT), "::unchecked_shl_exact cannot shift out bits that would change the value of the first bit"), ( zeros: u32 = self.leading_zeros(), ones: u32 = self.leading_ones(), @@ -1503,7 +1503,7 @@ pub const fn checked_shr(self, rhs: u32) -> Option { } } - /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is + /// Strict shift right. Computes `self >> rhs`, panicking if `rhs` is /// larger than or equal to the number of bits in `self`. /// /// # Panics @@ -1611,14 +1611,14 @@ pub const fn unbounded_shr(self, rhs: u32) -> $SelfT{ /// ``` /// #![feature(exact_bitshifts)] /// - #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")] - #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".shr_exact(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".shr_exact(5), None);")] /// ``` #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { + pub const fn shr_exact(self, rhs: u32) -> Option<$SelfT> { if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS { // SAFETY: rhs is checked above Some(unsafe { self.unchecked_shr(rhs) }) @@ -1636,16 +1636,16 @@ pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >= #[doc = concat!(stringify!($SelfT), "::BITS`")] /// i.e. when - #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")] + #[doc = concat!("[`", stringify!($SelfT), "::shr_exact`]")] /// would return `None`. #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { + pub const unsafe fn unchecked_shr_exact(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( check_library_ub, - concat!(stringify!($SelfT), "::unchecked_exact_shr cannot shift out non-zero bits"), + concat!(stringify!($SelfT), "::unchecked_shr_exact cannot shift out non-zero bits"), ( zeros: u32 = self.trailing_zeros(), bits: u32 = <$SelfT>::BITS, @@ -1720,6 +1720,7 @@ pub const fn strict_abs(self) -> Self { /// /// ``` #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".checked_pow(2), Some(64));")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".checked_pow(0), Some(1));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] /// ``` @@ -1761,6 +1762,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option { /// /// ``` #[doc = concat!("assert_eq!(8", stringify!($SelfT), ".strict_pow(2), 64);")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".strict_pow(0), 1);")] /// ``` /// /// The following panics because of overflow: @@ -2033,6 +2035,7 @@ pub const fn saturating_div(self, rhs: Self) -> Self { /// /// ``` #[doc = concat!("assert_eq!((-4", stringify!($SelfT), ").saturating_pow(3), -64);")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".saturating_pow(0), 1);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.saturating_pow(3), ", stringify!($SelfT), "::MIN);")] /// ``` @@ -2377,6 +2380,7 @@ pub const fn unsigned_abs(self) -> $UnsignedT { #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(4), 81);")] /// assert_eq!(3i8.wrapping_pow(5), -13); /// assert_eq!(3i8.wrapping_pow(6), -39); + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".wrapping_pow(0), 1);")] /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] @@ -2967,6 +2971,7 @@ pub const fn overflowing_abs(self) -> (Self, bool) { /// /// ``` #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(4), (81, false));")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".overflowing_pow(0), (1, false));")] /// assert_eq!(3i8.overflowing_pow(5), (-13, true)); /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] @@ -3010,6 +3015,7 @@ pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { #[doc = concat!("let x: ", stringify!($SelfT), " = 2; // or any other integer type")] /// /// assert_eq!(x.pow(5), 32); + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".pow(0), 1);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 8cd8b0850e94..983e00e46a5f 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -1,7 +1,7 @@ //! Definitions of integer that is known not to equal zero. use super::{IntErrorKind, ParseIntError}; -use crate::clone::UseCloned; +use crate::clone::{TrivialClone, UseCloned}; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; use crate::marker::{Destruct, Freeze, StructuralPartialEq}; @@ -199,6 +199,10 @@ impl UseCloned for NonZero where T: ZeroablePrimitive {} #[stable(feature = "nonzero", since = "1.28.0")] impl Copy for NonZero where T: ZeroablePrimitive {} +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for NonZero where T: ZeroablePrimitive {} + #[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] impl const PartialEq for NonZero diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 2996e7b00da4..d638f5551fea 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1222,10 +1222,10 @@ pub const fn strict_div_euclid(self, rhs: Self) -> Self { /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(2), Some(32));")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(32), Some(2));")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_exact_div(0), None);")] - #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".checked_exact_div(2), None);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_div_exact(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_div_exact(32), Some(2));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_div_exact(0), None);")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".checked_div_exact(2), None);")] /// ``` #[unstable( feature = "exact_div", @@ -1234,7 +1234,7 @@ pub const fn strict_div_euclid(self, rhs: Self) -> Self { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn checked_exact_div(self, rhs: Self) -> Option { + pub const fn checked_div_exact(self, rhs: Self) -> Option { if intrinsics::unlikely(rhs == 0) { None } else { @@ -1259,9 +1259,9 @@ pub const fn checked_exact_div(self, rhs: Self) -> Option { /// /// ``` /// #![feature(exact_div)] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(2), Some(32));")] - #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".exact_div(32), Some(2));")] - #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".exact_div(2), None);")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".div_exact(2), Some(32));")] + #[doc = concat!("assert_eq!(64", stringify!($SelfT), ".div_exact(32), Some(2));")] + #[doc = concat!("assert_eq!(65", stringify!($SelfT), ".div_exact(2), None);")] /// ``` #[unstable( feature = "exact_div", @@ -1271,7 +1271,7 @@ pub const fn checked_exact_div(self, rhs: Self) -> Option { without modifying the original"] #[inline] #[rustc_inherit_overflow_checks] - pub const fn exact_div(self, rhs: Self) -> Option { + pub const fn div_exact(self, rhs: Self) -> Option { if self % rhs != 0 { None } else { @@ -1284,7 +1284,7 @@ pub const fn exact_div(self, rhs: Self) -> Option { /// # Safety /// /// This results in undefined behavior when `rhs == 0` or `self % rhs != 0`, - /// i.e. when [`checked_exact_div`](Self::checked_exact_div) would return `None`. + /// i.e. when [`checked_div_exact`](Self::checked_div_exact) would return `None`. #[unstable( feature = "exact_div", issue = "139911", @@ -1292,10 +1292,10 @@ pub const fn exact_div(self, rhs: Self) -> Option { #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_div(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_div_exact(self, rhs: Self) -> Self { assert_unsafe_precondition!( check_language_ub, - concat!(stringify!($SelfT), "::unchecked_exact_div divide by zero or leave a remainder"), + concat!(stringify!($SelfT), "::unchecked_div_exact divide by zero or leave a remainder"), ( lhs: $SelfT = self, rhs: $SelfT = rhs, @@ -1830,14 +1830,14 @@ pub const fn unbounded_shl(self, rhs: u32) -> $SelfT{ /// ``` /// #![feature(exact_bitshifts)] /// - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")] - #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(129), None);")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".shl_exact(4), Some(0x10));")] + #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".shl_exact(129), None);")] /// ``` #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { + pub const fn shl_exact(self, rhs: u32) -> Option<$SelfT> { if rhs <= self.leading_zeros() && rhs < <$SelfT>::BITS { // SAFETY: rhs is checked above Some(unsafe { self.unchecked_shl(rhs) }) @@ -1855,16 +1855,16 @@ pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> { /// This results in undefined behavior when `rhs > self.leading_zeros() || rhs >= #[doc = concat!(stringify!($SelfT), "::BITS`")] /// i.e. when - #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")] + #[doc = concat!("[`", stringify!($SelfT), "::shl_exact`]")] /// would return `None`. #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT { + pub const unsafe fn unchecked_shl_exact(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( check_library_ub, - concat!(stringify!($SelfT), "::exact_shl_unchecked cannot shift out non-zero bits"), + concat!(stringify!($SelfT), "::unchecked_shl_exact cannot shift out non-zero bits"), ( zeros: u32 = self.leading_zeros(), bits: u32 = <$SelfT>::BITS, @@ -1900,7 +1900,7 @@ pub const fn checked_shr(self, rhs: u32) -> Option { } } - /// Strict shift right. Computes `self >> rhs`, panicking `rhs` is + /// Strict shift right. Computes `self >> rhs`, panicking if `rhs` is /// larger than or equal to the number of bits in `self`. /// /// # Panics @@ -2002,14 +2002,14 @@ pub const fn unbounded_shr(self, rhs: u32) -> $SelfT{ /// ``` /// #![feature(exact_bitshifts)] /// - #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")] - #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".shr_exact(4), Some(0x1));")] + #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".shr_exact(5), None);")] /// ``` #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { + pub const fn shr_exact(self, rhs: u32) -> Option<$SelfT> { if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS { // SAFETY: rhs is checked above Some(unsafe { self.unchecked_shr(rhs) }) @@ -2027,16 +2027,16 @@ pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >= #[doc = concat!(stringify!($SelfT), "::BITS`")] /// i.e. when - #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")] + #[doc = concat!("[`", stringify!($SelfT), "::shr_exact`]")] /// would return `None`. #[unstable(feature = "exact_bitshifts", issue = "144336")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] - pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT { + pub const unsafe fn unchecked_shr_exact(self, rhs: u32) -> $SelfT { assert_unsafe_precondition!( check_library_ub, - concat!(stringify!($SelfT), "::exact_shr_unchecked cannot shift out non-zero bits"), + concat!(stringify!($SelfT), "::unchecked_shr_exact cannot shift out non-zero bits"), ( zeros: u32 = self.trailing_zeros(), bits: u32 = <$SelfT>::BITS, @@ -2055,6 +2055,7 @@ pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> { /// /// ``` #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".checked_pow(5), Some(32));")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".checked_pow(0), Some(1));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.checked_pow(2), None);")] /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] @@ -2095,6 +2096,7 @@ pub const fn checked_pow(self, mut exp: u32) -> Option { /// /// ``` #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".strict_pow(5), 32);")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".strict_pow(0), 1);")] /// ``` /// /// The following panics because of overflow: @@ -2269,6 +2271,7 @@ pub const fn saturating_div(self, rhs: Self) -> Self { /// /// ``` #[doc = concat!("assert_eq!(4", stringify!($SelfT), ".saturating_pow(3), 64);")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".saturating_pow(0), 1);")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.saturating_pow(2), ", stringify!($SelfT), "::MAX);")] /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] @@ -2578,6 +2581,7 @@ pub const fn wrapping_shr(self, rhs: u32) -> Self { /// ``` #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".wrapping_pow(5), 243);")] /// assert_eq!(3u8.wrapping_pow(6), 217); + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".wrapping_pow(0), 1);")] /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] @@ -3252,6 +3256,7 @@ pub const fn overflowing_shr(self, rhs: u32) -> (Self, bool) { /// /// ``` #[doc = concat!("assert_eq!(3", stringify!($SelfT), ".overflowing_pow(5), (243, false));")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".overflowing_pow(0), (1, false));")] /// assert_eq!(3u8.overflowing_pow(6), (217, true)); /// ``` #[stable(feature = "no_panic_pow", since = "1.34.0")] @@ -3293,6 +3298,7 @@ pub const fn overflowing_pow(self, mut exp: u32) -> (Self, bool) { /// /// ``` #[doc = concat!("assert_eq!(2", stringify!($SelfT), ".pow(5), 32);")] + #[doc = concat!("assert_eq!(0_", stringify!($SelfT), ".pow(0), 1);")] /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_int_pow", since = "1.50.0")] diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index e1f2ebcf4c28..f68782c804cd 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -359,11 +359,24 @@ pub fn from_yeet(yeeted: Y) -> T /// and in the other direction, /// ` as Residual>::TryType = Result`. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub const trait Residual { +#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")] +pub const trait Residual: Sized { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] - type TryType: Try; + type TryType: [const] Try; +} + +/// Used in `try {}` blocks so the type produced in the `?` desugaring +/// depends on the residual type `R` and the output type of the block `O`, +/// but importantly not on the contextual type the way it would be if +/// we called `<_ as FromResidual>::from_residual(r)` directly. +#[unstable(feature = "try_trait_v2_residual", issue = "91285")] +// needs to be `pub` to avoid `private type` errors +#[expect(unreachable_pub)] +#[inline] // FIXME: force would be nice, but fails -- see #148915 +#[lang = "into_try_type"] +pub fn residual_into_try_type, O>(r: R) -> >::TryType { + FromResidual::from_residual(r) } #[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index e3c4758bc6af..57098b95f641 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -581,6 +581,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::clone::TrivialClone; use crate::iter::{self, FusedIterator, TrustedLen}; use crate::marker::Destruct; use crate::ops::{self, ControlFlow, Deref, DerefMut}; @@ -2215,6 +2216,11 @@ fn clone_from(&mut self, source: &Self) { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl crate::clone::UseCloned for Option where T: crate::clone::UseCloned {} +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +#[rustc_const_unstable(feature = "const_clone", issue = "142757")] +unsafe impl const TrivialClone for Option where T: [const] TrivialClone + [const] Destruct {} + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_default", issue = "143894")] impl const Default for Option { diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 448f4ffc3dae..3609dd1fe2e0 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -136,18 +136,18 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = "panic"] // used by lints and miri for panics pub const fn panic(expr: &'static str) -> ! { - // Use Arguments::new_const instead of format_args!("{expr}") to potentially + // Use Arguments::from_str instead of format_args!("{expr}") to potentially // reduce size overhead. The format_args! macro uses str's Display trait to // write expr, which calls Formatter::pad, which must accommodate string // truncation and padding (even though none is used here). Using - // Arguments::new_const may allow the compiler to omit Formatter::pad from the + // Arguments::from_str may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. - // However, this optimization only works for `'static` strings: `new_const` also makes this + // However, this optimization only works for `'static` strings: `from_str` also makes this // message return `Some` from `Arguments::as_str`, which means it can become part of the panic // payload without any allocation or copying. Shorter-lived strings would become invalid as // stack frames get popped during unwinding, and couldn't be directly referenced from the // payload. - panic_fmt(fmt::Arguments::new_const(&[expr])); + panic_fmt(fmt::Arguments::from_str(expr)); } // We generate functions for usage by compiler-generated assertions. @@ -171,13 +171,8 @@ macro_rules! panic_const { #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable #[lang = stringify!($lang)] pub const fn $lang() -> ! { - // Use Arguments::new_const instead of format_args!("{expr}") to potentially - // reduce size overhead. The format_args! macro uses str's Display trait to - // write expr, which calls Formatter::pad, which must accommodate string - // truncation and padding (even though none is used here). Using - // Arguments::new_const may allow the compiler to omit Formatter::pad from the - // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_const(&[$message])); + // See the comment in `panic(&'static str)` for why we use `Arguments::from_str` here. + panic_fmt(fmt::Arguments::from_str($message)); } )+ } @@ -227,7 +222,7 @@ pub mod panic_const { #[rustc_nounwind] #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable pub const fn panic_nounwind(expr: &'static str) -> ! { - panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ false); + panic_nounwind_fmt(fmt::Arguments::from_str(expr), /* force_no_backtrace */ false); } /// Like `panic_nounwind`, but also inhibits showing a backtrace. @@ -235,7 +230,7 @@ pub const fn panic_nounwind(expr: &'static str) -> ! { #[cfg_attr(panic = "immediate-abort", inline)] #[rustc_nounwind] pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { - panic_nounwind_fmt(fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ true); + panic_nounwind_fmt(fmt::Arguments::from_str(expr), /* force_no_backtrace */ true); } #[inline] diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs index 1c824e336bed..15ba72bccaa9 100644 --- a/library/core/src/primitive_docs.rs +++ b/library/core/src/primitive_docs.rs @@ -1531,9 +1531,8 @@ mod prim_usize {} /// `&mut T` references can be freely coerced into `&T` references with the same referent type, and /// references with longer lifetimes can be freely coerced into references with shorter ones. /// -/// Reference equality by address, instead of comparing the values pointed to, is accomplished via -/// implicit reference-pointer coercion and raw pointer equality via [`ptr::eq`], while -/// [`PartialEq`] compares values. +/// [`PartialEq`] will compare referenced values. It is possible to compare the reference address +/// using reference-pointer coercion and raw pointer equality via [`ptr::eq`]. /// /// ``` /// use std::ptr; @@ -1648,7 +1647,7 @@ mod prim_usize {} /// For the other direction, things are more complicated: when unsafe code passes arguments /// to safe functions or returns values from safe functions, they generally must *at least* /// not violate these invariants. The full requirements are stronger, as the reference generally -/// must point to data that is safe to use at type `T`. +/// must point to data that is safe to use as type `T`. /// /// It is not decided yet whether unsafe code may violate these invariants temporarily on internal /// data. As a consequence, unsafe code which violates these invariants temporarily on internal data diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index dc3ec3fd1994..998a5b031c28 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -1,5 +1,6 @@ #![unstable(feature = "ptr_metadata", issue = "81513")] +use crate::clone::TrivialClone; use crate::fmt; use crate::hash::{Hash, Hasher}; use crate::intrinsics::{aggregate_raw_ptr, ptr_metadata}; @@ -231,6 +232,9 @@ fn clone(&self) -> Self { } } +#[doc(hidden)] +unsafe impl TrivialClone for DynMetadata {} + impl Eq for DynMetadata {} impl PartialEq for DynMetadata { diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index fd067d19fcd9..ea0514f405f1 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1352,40 +1352,6 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` -/// -/// # Const evaluation limitations -/// -/// If this function is invoked during const-evaluation, the current implementation has a small (and -/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` -/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may -/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the -/// future. -/// -/// The limitation is illustrated by the following example: -/// -/// ``` -/// use std::mem::size_of; -/// use std::ptr; -/// -/// const { unsafe { -/// const PTR_SIZE: usize = size_of::<*const i32>(); -/// let mut data1 = [0u8; PTR_SIZE]; -/// let mut data2 = [0u8; PTR_SIZE]; -/// // Store a pointer in `data1`. -/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); -/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. -/// // This call will fail, because the pointer in `data1` crosses the boundary -/// // between several of the 1-byte chunks that are being swapped here. -/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); -/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size -/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between -/// // two chunks. -/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); -/// // Read the pointer from `data2` and dereference it. -/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); -/// assert!(*ptr == 42); -/// } } -/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1414,9 +1380,7 @@ pub const fn slice_from_raw_parts_mut(data: *mut T, len: usize) -> *mut [T] { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we want to always copy this in chunks of `T`, to ensure that if there - // are pointers inside `T` we will copy them in one go rather than trying to copy a part - // of a pointer (which would not work). + // At compile-time we don't need all the special code below. // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index a762e969b52d..aa3af2f18528 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -1,3 +1,4 @@ +use crate::clone::TrivialClone; use crate::cmp::Ordering; use crate::marker::{Destruct, PointeeSized, Unsize}; use crate::mem::{MaybeUninit, SizedTypeProperties}; @@ -1653,6 +1654,10 @@ fn clone(&self) -> Self { #[stable(feature = "nonnull", since = "1.25.0")] impl Copy for NonNull {} +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for NonNull {} + #[unstable(feature = "coerce_unsized", issue = "18598")] impl CoerceUnsized> for NonNull where T: Unsize {} diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index cdc8b6cc936d..5e7b1f703802 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -1,3 +1,4 @@ +use crate::clone::TrivialClone; use crate::fmt; use crate::marker::{PhantomData, PointeeSized, Unsize}; use crate::ops::{CoerceUnsized, DispatchFromDyn}; @@ -165,6 +166,10 @@ fn clone(&self) -> Self { #[unstable(feature = "ptr_internals", issue = "none")] impl Copy for Unique {} +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for Unique {} + #[unstable(feature = "ptr_internals", issue = "none")] impl CoerceUnsized> for Unique where T: Unsize {} diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 24efd4a204a5..9a8824baefe4 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -3,6 +3,7 @@ }; use crate::num::NonZero; use crate::range::{Range, RangeFrom, RangeInclusive, legacy}; +use crate::{intrinsics, mem}; /// By-value [`Range`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] @@ -168,7 +169,7 @@ pub fn remainder(self) -> Option> { } } -#[unstable(feature = "trusted_random_access", issue = "none")] +#[unstable(feature = "new_range_api", issue = "125687")] impl Iterator for IterRangeInclusive { type Item = A; @@ -293,32 +294,74 @@ impl ExactSizeIterator for IterRangeInclusive<$t> { } /// By-value [`RangeFrom`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeFrom(legacy::RangeFrom); +pub struct IterRangeFrom { + start: A, + /// Whether the first element of the iterator has yielded. + /// Only used when overflow checks are enabled. + first: bool, +} -impl IterRangeFrom { +impl IterRangeFrom { /// Returns the remainder of the range being iterated over. + #[inline] + #[rustc_inherit_overflow_checks] pub fn remainder(self) -> RangeFrom { - RangeFrom { start: self.0.start } + if intrinsics::overflow_checks() { + if !self.first { + return RangeFrom { start: Step::forward(self.start, 1) }; + } + } + + RangeFrom { start: self.start } } } -#[unstable(feature = "trusted_random_access", issue = "none")] +#[unstable(feature = "new_range_api", issue = "125687")] impl Iterator for IterRangeFrom { type Item = A; #[inline] + #[rustc_inherit_overflow_checks] fn next(&mut self) -> Option { - self.0.next() + if intrinsics::overflow_checks() { + if self.first { + self.first = false; + return Some(self.start.clone()); + } + + self.start = Step::forward(self.start.clone(), 1); + return Some(self.start.clone()); + } + + let n = Step::forward(self.start.clone(), 1); + Some(mem::replace(&mut self.start, n)) } #[inline] fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() + (usize::MAX, None) } #[inline] + #[rustc_inherit_overflow_checks] fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) + if intrinsics::overflow_checks() { + if self.first { + self.first = false; + + let plus_n = Step::forward(self.start.clone(), n); + self.start = plus_n.clone(); + return Some(plus_n); + } + + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); + return Some(self.start.clone()); + } + + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); + Some(plus_n) } } @@ -334,6 +377,6 @@ impl IntoIterator for RangeFrom { type IntoIter = IterRangeFrom; fn into_iter(self) -> Self::IntoIter { - IterRangeFrom(self.into()) + IterRangeFrom { start: self.start, first: true } } } diff --git a/library/core/src/slice/iter/macros.rs b/library/core/src/slice/iter/macros.rs index 7c1ed3fe8a24..c46b7c797aab 100644 --- a/library/core/src/slice/iter/macros.rs +++ b/library/core/src/slice/iter/macros.rs @@ -350,7 +350,6 @@ fn find_map(&mut self, mut f: F) -> Option // because this simple implementation generates less LLVM IR and is // faster to compile. Also, the `assume` avoids a bounds check. #[inline] - #[rustc_inherit_overflow_checks] fn position

(&mut self, mut predicate: P) -> Option where Self: Sized, P: FnMut(Self::Item) -> bool, diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1d88eb33dce1..f03f2045444d 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -6,6 +6,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::clone::TrivialClone; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{exact_div, unchecked_sub}; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; @@ -3890,30 +3891,8 @@ pub const fn copy_from_slice(&mut self, src: &[T]) where T: Copy, { - // The panic code path was put into a cold function to not bloat the - // call site. - #[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] - #[cfg_attr(panic = "immediate-abort", inline)] - #[track_caller] - const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { - const_panic!( - "copy_from_slice: source slice length does not match destination slice length", - "copy_from_slice: source slice length ({src_len}) does not match destination slice length ({dst_len})", - src_len: usize, - dst_len: usize, - ) - } - - if self.len() != src.len() { - len_mismatch_fail(self.len(), src.len()); - } - - // SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was - // checked to have the same length. The slices cannot overlap because - // mutable references are exclusive. - unsafe { - ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len()); - } + // SAFETY: `T` implements `Copy`. + unsafe { copy_from_slice_impl(self, src) } } /// Copies elements from one part of the slice to another part of itself, @@ -5123,6 +5102,38 @@ pub fn sort_floats(&mut self) { } } +/// Copies `src` to `dest`. +/// +/// # Safety +/// `T` must implement one of `Copy` or `TrivialClone`. +#[track_caller] +const unsafe fn copy_from_slice_impl(dest: &mut [T], src: &[T]) { + // The panic code path was put into a cold function to not bloat the + // call site. + #[cfg_attr(not(panic = "immediate-abort"), inline(never), cold)] + #[cfg_attr(panic = "immediate-abort", inline)] + #[track_caller] + const fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! { + const_panic!( + "copy_from_slice: source slice length does not match destination slice length", + "copy_from_slice: source slice length ({src_len}) does not match destination slice length ({dst_len})", + src_len: usize, + dst_len: usize, + ) + } + + if dest.len() != src.len() { + len_mismatch_fail(dest.len(), src.len()); + } + + // SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was + // checked to have the same length. The slices cannot overlap because + // mutable references are exclusive. + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), dest.as_mut_ptr(), dest.len()); + } +} + trait CloneFromSpec { fn spec_clone_from(&mut self, src: &[T]); } @@ -5147,11 +5158,14 @@ impl CloneFromSpec for [T] impl CloneFromSpec for [T] where - T: Copy, + T: TrivialClone, { #[track_caller] fn spec_clone_from(&mut self, src: &[T]) { - self.copy_from_slice(src); + // SAFETY: `T` implements `TrivialClone`. + unsafe { + copy_from_slice_impl(self, src); + } } } diff --git a/library/core/src/slice/specialize.rs b/library/core/src/slice/specialize.rs index 17436395fee6..c44225b75364 100644 --- a/library/core/src/slice/specialize.rs +++ b/library/core/src/slice/specialize.rs @@ -1,3 +1,6 @@ +use crate::clone::TrivialClone; +use crate::ptr; + pub(super) trait SpecFill { fn spec_fill(&mut self, value: T); } @@ -14,10 +17,12 @@ impl SpecFill for [T] { } } -impl SpecFill for [T] { +impl SpecFill for [T] { default fn spec_fill(&mut self, value: T) { for item in self.iter_mut() { - *item = value; + // SAFETY: `TrivialClone` indicates that this is equivalent to + // calling `Clone::clone` + *item = unsafe { ptr::read(&value) }; } } } diff --git a/library/core/src/sync/exclusive.rs b/library/core/src/sync/exclusive.rs index f181c5514f25..35b812099518 100644 --- a/library/core/src/sync/exclusive.rs +++ b/library/core/src/sync/exclusive.rs @@ -1,5 +1,6 @@ //! Defines [`Exclusive`]. +use core::clone::TrivialClone; use core::cmp::Ordering; use core::fmt; use core::future::Future; @@ -261,6 +262,10 @@ fn clone(&self) -> Self { } } +#[doc(hidden)] +#[unstable(feature = "trivial_clone", issue = "none")] +unsafe impl TrivialClone for Exclusive where T: Sync + TrivialClone {} + #[unstable(feature = "exclusive_wrapper", issue = "98407")] impl Copy for Exclusive where T: Sync + Copy {} diff --git a/library/core/src/time.rs b/library/core/src/time.rs index f721fcd6156c..51a01545f5cf 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -317,7 +317,6 @@ pub const fn from_nanos(nanos: u64) -> Duration { /// # Examples /// /// ``` - /// #![feature(duration_from_nanos_u128)] /// use std::time::Duration; /// /// let nanos = 10_u128.pow(24) + 321; @@ -326,12 +325,12 @@ pub const fn from_nanos(nanos: u64) -> Duration { /// assert_eq!(10_u64.pow(15), duration.as_secs()); /// assert_eq!(321, duration.subsec_nanos()); /// ``` - #[unstable(feature = "duration_from_nanos_u128", issue = "139201")] - // This is necessary because of const `try_from`, but can be removed if a trait-free impl is used instead - #[rustc_const_unstable(feature = "duration_from_nanos_u128", issue = "139201")] + #[stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "duration_from_nanos_u128", since = "CURRENT_RUSTC_VERSION")] #[must_use] #[inline] #[track_caller] + #[rustc_allow_const_fn_unstable(const_trait_impl, const_convert)] // for `u64::try_from` pub const fn from_nanos_u128(nanos: u128) -> Duration { const NANOS_PER_SEC: u128 = self::NANOS_PER_SEC as u128; let Ok(secs) = u64::try_from(nanos / NANOS_PER_SEC) else { diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs index 514ff93c9820..50e02320748b 100644 --- a/library/core/src/ub_checks.rs +++ b/library/core/src/ub_checks.rs @@ -70,7 +70,7 @@ const fn precondition_check($($name:$ty),*) { let msg = concat!("unsafe precondition(s) violated: ", $message, "\n\nThis indicates a bug in the program. \ This Undefined Behavior check is optional, and cannot be relied on for safety."); - ::core::panicking::panic_nounwind_fmt(::core::fmt::Arguments::new_const(&[msg]), false); + ::core::panicking::panic_nounwind_fmt(::core::fmt::Arguments::from_str(msg), false); } } diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 80b62038c40e..e190536abcf9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -42,7 +42,6 @@ #![feature(drop_guard)] #![feature(duration_constants)] #![feature(duration_constructors)] -#![feature(duration_from_nanos_u128)] #![feature(error_generic_member_access)] #![feature(exact_div)] #![feature(exact_size_is_empty)] diff --git a/library/coretests/tests/num/int_macros.rs b/library/coretests/tests/num/int_macros.rs index e640b7853bd9..37336f49ef1b 100644 --- a/library/coretests/tests/num/int_macros.rs +++ b/library/coretests/tests/num/int_macros.rs @@ -724,42 +724,42 @@ fn test_unbounded_shr() { } } - const EXACT_DIV_SUCCESS_DIVIDEND1: $T = 42; - const EXACT_DIV_SUCCESS_DIVISOR1: $T = 6; - const EXACT_DIV_SUCCESS_QUOTIENT1: $T = 7; - const EXACT_DIV_SUCCESS_DIVIDEND2: $T = 18; - const EXACT_DIV_SUCCESS_DIVISOR2: $T = 3; - const EXACT_DIV_SUCCESS_QUOTIENT2: $T = 6; - const EXACT_DIV_SUCCESS_DIVIDEND3: $T = -91; - const EXACT_DIV_SUCCESS_DIVISOR3: $T = 13; - const EXACT_DIV_SUCCESS_QUOTIENT3: $T = -7; - const EXACT_DIV_SUCCESS_DIVIDEND4: $T = -57; - const EXACT_DIV_SUCCESS_DIVISOR4: $T = -3; - const EXACT_DIV_SUCCESS_QUOTIENT4: $T = 19; + const DIV_EXACT_SUCCESS_DIVIDEND1: $T = 42; + const DIV_EXACT_SUCCESS_DIVISOR1: $T = 6; + const DIV_EXACT_SUCCESS_QUOTIENT1: $T = 7; + const DIV_EXACT_SUCCESS_DIVIDEND2: $T = 18; + const DIV_EXACT_SUCCESS_DIVISOR2: $T = 3; + const DIV_EXACT_SUCCESS_QUOTIENT2: $T = 6; + const DIV_EXACT_SUCCESS_DIVIDEND3: $T = -91; + const DIV_EXACT_SUCCESS_DIVISOR3: $T = 13; + const DIV_EXACT_SUCCESS_QUOTIENT3: $T = -7; + const DIV_EXACT_SUCCESS_DIVIDEND4: $T = -57; + const DIV_EXACT_SUCCESS_DIVISOR4: $T = -3; + const DIV_EXACT_SUCCESS_QUOTIENT4: $T = 19; test_runtime_and_compiletime! { - fn test_exact_div() { + fn test_div_exact() { // 42 / 6 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND1, DIV_EXACT_SUCCESS_DIVISOR1), Some(DIV_EXACT_SUCCESS_QUOTIENT1)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND1, DIV_EXACT_SUCCESS_DIVISOR1), Some(DIV_EXACT_SUCCESS_QUOTIENT1)); // 18 / 3 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND2, DIV_EXACT_SUCCESS_DIVISOR2), Some(DIV_EXACT_SUCCESS_QUOTIENT2)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND2, DIV_EXACT_SUCCESS_DIVISOR2), Some(DIV_EXACT_SUCCESS_QUOTIENT2)); // -91 / 13 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND3, EXACT_DIV_SUCCESS_DIVISOR3), Some(EXACT_DIV_SUCCESS_QUOTIENT3)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND3, EXACT_DIV_SUCCESS_DIVISOR3), Some(EXACT_DIV_SUCCESS_QUOTIENT3)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND3, DIV_EXACT_SUCCESS_DIVISOR3), Some(DIV_EXACT_SUCCESS_QUOTIENT3)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND3, DIV_EXACT_SUCCESS_DIVISOR3), Some(DIV_EXACT_SUCCESS_QUOTIENT3)); // -57 / -3 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND4, EXACT_DIV_SUCCESS_DIVISOR4), Some(EXACT_DIV_SUCCESS_QUOTIENT4)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND4, EXACT_DIV_SUCCESS_DIVISOR4), Some(EXACT_DIV_SUCCESS_QUOTIENT4)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND4, DIV_EXACT_SUCCESS_DIVISOR4), Some(DIV_EXACT_SUCCESS_QUOTIENT4)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND4, DIV_EXACT_SUCCESS_DIVISOR4), Some(DIV_EXACT_SUCCESS_QUOTIENT4)); // failures - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(1, 2), None); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(1, 2), None); - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(<$T>::MIN, -1), None); - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(0, 0), None); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(1, 2), None); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(1, 2), None); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(<$T>::MIN, -1), None); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(0, 0), None); } } }; diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index c1cfc448f14f..b89a371efcc2 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -595,27 +595,27 @@ fn test_unbounded_shr() { } } - const EXACT_DIV_SUCCESS_DIVIDEND1: $T = 42; - const EXACT_DIV_SUCCESS_DIVISOR1: $T = 6; - const EXACT_DIV_SUCCESS_QUOTIENT1: $T = 7; - const EXACT_DIV_SUCCESS_DIVIDEND2: $T = 18; - const EXACT_DIV_SUCCESS_DIVISOR2: $T = 3; - const EXACT_DIV_SUCCESS_QUOTIENT2: $T = 6; + const DIV_EXACT_SUCCESS_DIVIDEND1: $T = 42; + const DIV_EXACT_SUCCESS_DIVISOR1: $T = 6; + const DIV_EXACT_SUCCESS_QUOTIENT1: $T = 7; + const DIV_EXACT_SUCCESS_DIVIDEND2: $T = 18; + const DIV_EXACT_SUCCESS_DIVISOR2: $T = 3; + const DIV_EXACT_SUCCESS_QUOTIENT2: $T = 6; test_runtime_and_compiletime! { - fn test_exact_div() { + fn test_div_exact() { // 42 / 6 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND1, EXACT_DIV_SUCCESS_DIVISOR1), Some(EXACT_DIV_SUCCESS_QUOTIENT1)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND1, DIV_EXACT_SUCCESS_DIVISOR1), Some(DIV_EXACT_SUCCESS_QUOTIENT1)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND1, DIV_EXACT_SUCCESS_DIVISOR1), Some(DIV_EXACT_SUCCESS_QUOTIENT1)); // 18 / 3 - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2)); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(EXACT_DIV_SUCCESS_DIVIDEND2, EXACT_DIV_SUCCESS_DIVISOR2), Some(EXACT_DIV_SUCCESS_QUOTIENT2)); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(DIV_EXACT_SUCCESS_DIVIDEND2, DIV_EXACT_SUCCESS_DIVISOR2), Some(DIV_EXACT_SUCCESS_QUOTIENT2)); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(DIV_EXACT_SUCCESS_DIVIDEND2, DIV_EXACT_SUCCESS_DIVISOR2), Some(DIV_EXACT_SUCCESS_QUOTIENT2)); // failures - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(1, 2), None); - assert_eq_const_safe!(Option<$T>: <$T>::exact_div(1, 2), None); - assert_eq_const_safe!(Option<$T>: <$T>::checked_exact_div(0, 0), None); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(1, 2), None); + assert_eq_const_safe!(Option<$T>: <$T>::div_exact(1, 2), None); + assert_eq_const_safe!(Option<$T>: <$T>::checked_div_exact(0, 0), None); } } }; diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index 4d5138d539b9..93f9454d7137 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -565,6 +565,7 @@ fn ptr_metadata() { #[test] fn ptr_metadata_bounds() { + #[allow(unknown_lints, function_casts_as_integer)] fn metadata_eq_method_address() -> usize { // The `Metadata` associated type has an `Ord` bound, so this is valid: <::Metadata as PartialEq>::eq as usize @@ -944,13 +945,12 @@ struct S { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, again as an array. - // FIXME(#146291): we should be swapping back at type `u8` but that currently does not work. + // Swap them back, byte-for-byte unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - 1, + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + size_of::(), ); } diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index a5d67dbb6a9f..257916c4d5cd 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -336,6 +336,7 @@ unsafe fn throw_exception(data: Option>) -> ! { // In any case, we basically need to do something like this until we can // express more operations in statics (and we may never be able to). unsafe { + #[allow(function_casts_as_integer)] atomic_store::<_, { AtomicOrdering::SeqCst }>( (&raw mut THROW_INFO.pmfnUnwind).cast(), ptr_t::new(exception_cleanup as *mut u8).raw(), @@ -352,6 +353,7 @@ unsafe fn throw_exception(data: Option>) -> ! { (&raw mut CATCHABLE_TYPE.pType).cast(), ptr_t::new((&raw mut TYPE_DESCRIPTOR).cast()).raw(), ); + #[allow(function_casts_as_integer)] atomic_store::<_, { AtomicOrdering::SeqCst }>( (&raw mut CATCHABLE_TYPE.copyFunction).cast(), ptr_t::new(exception_copy as *mut u8).raw(), diff --git a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs index 8221d8f17e90..32d37b553392 100644 --- a/library/portable-simd/crates/core_simd/src/masks/bitmask.rs +++ b/library/portable-simd/crates/core_simd/src/masks/bitmask.rs @@ -170,7 +170,6 @@ impl core::ops::BitAnd for Mask { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(mut self, rhs: Self) -> Self { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l &= r; @@ -187,7 +186,6 @@ impl core::ops::BitOr for Mask { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(mut self, rhs: Self) -> Self { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l |= r; @@ -203,7 +201,6 @@ impl core::ops::BitXor for Mask { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(mut self, rhs: Self) -> Self::Output { for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) { *l ^= r; @@ -219,7 +216,6 @@ impl core::ops::Not for Mask { type Output = Self; #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] fn not(mut self) -> Self::Output { for x in self.0.as_mut() { *x = !*x; diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index c3fcb0e2e42b..99724e29e02b 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -293,7 +293,7 @@ pub fn capture() -> Backtrace { if !Backtrace::enabled() { return Backtrace { inner: Inner::Disabled }; } - Backtrace::create(Backtrace::capture as usize) + Backtrace::create(Backtrace::capture as fn() -> Backtrace as usize) } /// Forcibly captures a full backtrace, regardless of environment variable @@ -309,7 +309,7 @@ pub fn capture() -> Backtrace { #[stable(feature = "backtrace", since = "1.65.0")] #[inline(never)] // want to make sure there's a frame here to remove pub fn force_capture() -> Backtrace { - Backtrace::create(Backtrace::force_capture as usize) + Backtrace::create(Backtrace::force_capture as fn() -> Backtrace as usize) } /// Forcibly captures a disabled backtrace, regardless of environment diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index fc0fef620e3b..ab21e3b927e2 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1685,7 +1685,8 @@ pub(super) fn iter(&self) -> Iter<'_, K, V> { /// let iter = map.extract_if(|_k, v| *v % 2 == 0); /// ``` #[stable(feature = "hash_extract_if", since = "1.88.0")] -#[must_use = "iterators are lazy and do nothing unless consumed"] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain` to remove and discard elements"] pub struct ExtractIf<'a, K, V, F> { base: base::ExtractIf<'a, K, V, F>, } diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 482d57b47f67..6795da80aacb 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -1391,6 +1391,8 @@ pub struct Drain<'a, K: 'a> { /// let mut extract_ifed = a.extract_if(|v| v % 2 == 0); /// ``` #[stable(feature = "hash_extract_if", since = "1.88.0")] +#[must_use = "iterators are lazy and do nothing unless consumed; \ + use `retain` to remove and discard elements"] pub struct ExtractIf<'a, K, F> { base: base::ExtractIf<'a, K, F>, } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 25a4661a0bc9..b7756befa11e 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -330,7 +330,7 @@ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, util::{Empty, Repeat, Sink, empty, repeat, sink}, }; -use crate::mem::take; +use crate::mem::{MaybeUninit, take}; use crate::ops::{Deref, DerefMut}; use crate::{cmp, fmt, slice, str, sys}; @@ -1242,6 +1242,46 @@ fn take(self, limit: u64) -> Take { Take { inner: self, len: limit, limit } } + + /// Read and return a fixed array of bytes from this source. + /// + /// This function uses an array sized based on a const generic size known at compile time. You + /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference + /// determine the number of bytes needed based on how the return value gets used. For instance, + /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of + /// bytes into an integer of the same size. + /// + /// Like `read_exact`, if this function encounters an "end of file" before reading the desired + /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// ``` + /// #![feature(read_array)] + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); + /// let x = u64::from_le_bytes(buf.read_array()?); + /// let y = u32::from_be_bytes(buf.read_array()?); + /// let z = u16::from_be_bytes(buf.read_array()?); + /// assert_eq!(x, 0x807060504030201); + /// assert_eq!(y, 0x9080706); + /// assert_eq!(z, 0x504); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_array", issue = "148848")] + fn read_array(&mut self) -> Result<[u8; N]> + where + Self: Sized, + { + let mut buf = [MaybeUninit::uninit(); N]; + let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); + self.read_buf_exact(borrowed_buf.unfilled())?; + // Guard against incorrect `read_buf_exact` implementations. + assert_eq!(borrowed_buf.len(), N); + Ok(unsafe { MaybeUninit::array_assume_init(buf) }) + } } /// Reads all bytes from a [reader][Read] into a new [`String`]. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7b6cfbfe0f25..79deb3221ce5 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -348,6 +348,7 @@ #![feature(int_from_ascii)] #![feature(ip)] #![feature(lazy_get)] +#![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] @@ -381,7 +382,6 @@ #![feature(try_reserve_kind)] #![feature(try_with_capacity)] #![feature(unique_rc_arc)] -#![feature(vec_into_raw_parts)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/std/src/num/f128.rs b/library/std/src/num/f128.rs index 40061d089284..3b787713afa2 100644 --- a/library/std/src/num/f128.rs +++ b/library/std/src/num/f128.rs @@ -37,6 +37,7 @@ impl f128 { /// /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); + /// assert_eq!(f128::powf(0.0, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/num/f16.rs b/library/std/src/num/f16.rs index 0d43b60a62fe..4af21c95c9ba 100644 --- a/library/std/src/num/f16.rs +++ b/library/std/src/num/f16.rs @@ -37,6 +37,7 @@ impl f16 { /// /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); + /// assert_eq!(f16::powf(0.0, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/num/f32.rs b/library/std/src/num/f32.rs index c9e192201aff..09ced388a339 100644 --- a/library/std/src/num/f32.rs +++ b/library/std/src/num/f32.rs @@ -308,6 +308,7 @@ pub fn rem_euclid(self, rhs: f32) -> f32 { /// assert!(abs_difference <= 1e-5); /// /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); + /// assert_eq!(f32::powi(0.0, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -333,6 +334,7 @@ pub fn powi(self, n: i32) -> f32 { /// /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); + /// assert_eq!(f32::powf(0.0, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/num/f64.rs b/library/std/src/num/f64.rs index 11874f9280f0..79adf076e4b1 100644 --- a/library/std/src/num/f64.rs +++ b/library/std/src/num/f64.rs @@ -308,6 +308,7 @@ pub fn rem_euclid(self, rhs: f64) -> f64 { /// assert!(abs_difference <= 1e-14); /// /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); + /// assert_eq!(f64::powi(0.0, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -333,6 +334,7 @@ pub fn powi(self, n: i32) -> f64 { /// /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); + /// assert_eq!(f64::powf(0.0, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 5b7b5a8ea803..ee0c460f7dfa 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -80,6 +80,9 @@ pub trait CommandExt: Sealed { /// or acquiring a mutex are not guaranteed to work (due to /// other threads perhaps still running when the `fork` was run). /// + /// Note that the list of allocating functions includes [`Error::new`] and + /// [`Error::other`]. To signal a non-trivial error, prefer [`panic!`]. + /// /// For further details refer to the [POSIX fork() specification] /// and the equivalent documentation for any targeted /// platform, especially the requirements around *async-signal-safety*. @@ -102,6 +105,8 @@ pub trait CommandExt: Sealed { /// [POSIX fork() specification]: /// https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html /// [`std::env`]: mod@crate::env + /// [`Error::new`]: crate::io::Error::new + /// [`Error::other`]: crate::io::Error::other #[stable(feature = "process_pre_exec", since = "1.34.0")] unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command where diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index e4e7274ae8cb..fc5f159ec188 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -18,6 +18,9 @@ pub struct FileAttr { attr: u64, size: u64, + accessed: SystemTime, + modified: SystemTime, + created: SystemTime, } pub struct ReadDir(!); @@ -33,7 +36,10 @@ pub struct OpenOptions { } #[derive(Copy, Clone, Debug, Default)] -pub struct FileTimes {} +pub struct FileTimes { + accessed: Option, + modified: Option, +} #[derive(Clone, PartialEq, Eq, Debug)] // Bool indicates if file is readonly @@ -60,15 +66,15 @@ pub fn file_type(&self) -> FileType { } pub fn modified(&self) -> io::Result { - unsupported() + Ok(self.modified) } pub fn accessed(&self) -> io::Result { - unsupported() + Ok(self.accessed) } pub fn created(&self) -> io::Result { - unsupported() + Ok(self.created) } } @@ -92,8 +98,13 @@ const fn to_attr(&self) -> u64 { } impl FileTimes { - pub fn set_accessed(&mut self, _t: SystemTime) {} - pub fn set_modified(&mut self, _t: SystemTime) {} + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t); + } } impl FileType { @@ -394,6 +405,7 @@ mod uefi_fs { use crate::path::Path; use crate::ptr::NonNull; use crate::sys::helpers; + use crate::sys::time::{self, SystemTime}; pub(crate) struct File(NonNull); @@ -541,4 +553,23 @@ pub(crate) fn mkdir(path: &Path) -> io::Result<()> { Ok(()) } + + /// EDK2 FAT driver uses EFI_UNSPECIFIED_TIMEZONE to represent localtime. So for proper + /// conversion to SystemTime, we use the current time to get the timezone in such cases. + #[expect(dead_code)] + fn uefi_to_systemtime(mut time: r_efi::efi::Time) -> SystemTime { + time.timezone = if time.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + time::system_time_internal::now().unwrap().timezone + } else { + time.timezone + }; + SystemTime::from_uefi(time) + } + + /// Convert to UEFI Time with the current timezone. + #[expect(dead_code)] + fn systemtime_to_uefi(time: SystemTime) -> r_efi::efi::Time { + let now = time::system_time_internal::now().unwrap(); + time.to_uefi_loose(now.timezone, now.daylight) + } } diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs index 56ca999cc7e9..df3344e2df34 100644 --- a/library/std/src/sys/pal/uefi/tests.rs +++ b/library/std/src/sys/pal/uefi/tests.rs @@ -8,6 +8,20 @@ const SECS_IN_MINUTE: u64 = 60; +const MAX_UEFI_TIME: Duration = from_uefi(r_efi::efi::Time { + year: 9999, + month: 12, + day: 31, + hour: 23, + minute: 59, + second: 59, + nanosecond: 999_999_999, + timezone: 1440, + daylight: 0, + pad1: 0, + pad2: 0, +}); + #[test] fn align() { // UEFI ABI specifies that allocation alignment minimum is always 8. So this can be @@ -28,6 +42,19 @@ fn align() { } } +// UEFI Time cannot implement Eq due to uninitilaized pad1 and pad2 +fn uefi_time_cmp(t1: r_efi::efi::Time, t2: r_efi::efi::Time) -> bool { + t1.year == t2.year + && t1.month == t2.month + && t1.day == t2.day + && t1.hour == t2.hour + && t1.minute == t2.minute + && t1.second == t2.second + && t1.nanosecond == t2.nanosecond + && t1.timezone == t2.timezone + && t1.daylight == t2.daylight +} + #[test] fn systemtime_start() { let t = r_efi::efi::Time { @@ -37,14 +64,15 @@ fn systemtime_start() { hour: 0, minute: 0, second: 0, + pad1: 0, nanosecond: 0, timezone: -1440, daylight: 0, pad2: 0, }; assert_eq!(from_uefi(&t), Duration::new(0, 0)); - assert_eq!(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap()); - assert!(to_uefi(&from_uefi(&t), 0, 0).is_none()); + assert!(uefi_time_cmp(t, to_uefi(&from_uefi(&t), -1440, 0).unwrap())); + assert!(to_uefi(&from_uefi(&t), 0, 0).is_err()); } #[test] @@ -63,8 +91,8 @@ fn systemtime_utc_start() { pad2: 0, }; assert_eq!(from_uefi(&t), Duration::new(1440 * SECS_IN_MINUTE, 0)); - assert_eq!(t, to_uefi(&from_uefi(&t), 0, 0).unwrap()); - assert!(to_uefi(&from_uefi(&t), -1440, 0).is_some()); + assert!(uefi_time_cmp(t, to_uefi(&from_uefi(&t), 0, 0).unwrap())); + assert!(to_uefi(&from_uefi(&t), -1440, 0).is_ok()); } #[test] @@ -82,8 +110,49 @@ fn systemtime_end() { daylight: 0, pad2: 0, }; - assert!(to_uefi(&from_uefi(&t), 1440, 0).is_some()); - assert!(to_uefi(&from_uefi(&t), 1439, 0).is_none()); + assert!(to_uefi(&from_uefi(&t), 1440, 0).is_ok()); + assert!(to_uefi(&from_uefi(&t), 1439, 0).is_err()); +} + +#[test] +fn min_time() { + let inp = Duration::from_secs(1440 * SECS_IN_MINUTE); + let new_tz = to_uefi(&inp, 1440, 0).err().unwrap(); + assert_eq!(new_tz, 0); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); + + let inp = Duration::from_secs(1450 * SECS_IN_MINUTE); + let new_tz = to_uefi(&inp, 1440, 0).err().unwrap(); + assert_eq!(new_tz, 10); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); + + let inp = Duration::from_secs(1450 * SECS_IN_MINUTE + 10); + let new_tz = to_uefi(&inp, 1440, 0).err().unwrap(); + assert_eq!(new_tz, 10); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); + + let inp = Duration::from_secs(1430 * SECS_IN_MINUTE); + let new_tz = to_uefi(&inp, 1440, 0).err().unwrap(); + assert_eq!(new_tz, -10); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); +} + +#[test] +fn max_time() { + let inp = MAX_UEFI_TIME.0; + let new_tz = to_uefi(&inp, -1440, 0).err().unwrap(); + assert_eq!(new_tz, 1440); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); + + let inp = MAX_UEFI_TIME.0 - Duration::from_secs(1440 * SECS_IN_MINUTE); + let new_tz = to_uefi(&inp, -1440, 0).err().unwrap(); + assert_eq!(new_tz, 0); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); + + let inp = MAX_UEFI_TIME.0 - Duration::from_secs(1440 * SECS_IN_MINUTE + 10); + let new_tz = to_uefi(&inp, -1440, 0).err().unwrap(); + assert_eq!(new_tz, 0); + assert!(to_uefi(&inp, new_tz, 0).is_ok()); } // UEFI IoSlice and IoSliceMut Tests diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index 861b98da18da..f9f90a454976 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -1,5 +1,7 @@ use crate::time::Duration; +const SECS_IN_MINUTE: u64 = 60; + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant(Duration); @@ -70,13 +72,32 @@ pub(crate) const fn from_uefi(t: r_efi::efi::Time) -> Self { Self(system_time_internal::from_uefi(&t)) } - #[expect(dead_code)] - pub(crate) const fn to_uefi(self, timezone: i16, daylight: u8) -> Option { - system_time_internal::to_uefi(&self.0, timezone, daylight) + pub(crate) const fn to_uefi( + self, + timezone: i16, + daylight: u8, + ) -> Result { + // system_time_internal::to_uefi requires a valid timezone. In case of unspecified timezone, + // we just pass 0 since it is assumed that no timezone related adjustments are required. + if timezone == r_efi::efi::UNSPECIFIED_TIMEZONE { + system_time_internal::to_uefi(&self.0, 0, daylight) + } else { + system_time_internal::to_uefi(&self.0, timezone, daylight) + } + } + + /// Create UEFI Time with the closest timezone (minute offset) that still allows the time to be + /// represented. + pub(crate) fn to_uefi_loose(self, timezone: i16, daylight: u8) -> r_efi::efi::Time { + match self.to_uefi(timezone, daylight) { + Ok(x) => x, + Err(tz) => self.to_uefi(tz, daylight).unwrap(), + } } pub fn now() -> SystemTime { system_time_internal::now() + .map(Self::from_uefi) .unwrap_or_else(|| panic!("time not implemented on this platform")) } @@ -104,12 +125,11 @@ pub(crate) mod system_time_internal { use crate::mem::MaybeUninit; use crate::ptr::NonNull; - const SECS_IN_MINUTE: u64 = 60; const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60; const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24; - const TIMEZONE_DELTA: u64 = 1440 * SECS_IN_MINUTE; + const SYSTEMTIME_TIMEZONE: i64 = -1440 * SECS_IN_MINUTE as i64; - pub fn now() -> Option { + pub(crate) fn now() -> Option{text}"#, - path = join_path_syms(fqp), + r#"{text}"#, + path = join_path_syms(rust_path), text = EscapeBodyText(text.as_str()), ) } else { @@ -1056,14 +1082,14 @@ fn print_qpath_data(qpath_data: &clean::QPathData, cx: &Context<'_>) -> impl Dis None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()), }; - if let Some((url, _, path)) = parent_href { + if let Some(HrefInfo { url, rust_path, .. }) = parent_href { write!( f, "{name}", shortty = ItemType::AssocType, name = assoc.name, - path = join_path_syms(path), + path = join_path_syms(rust_path), ) } else { write!(f, "{}", assoc.name) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 798fbd284ca8..6f6345cd8666 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -21,6 +21,7 @@ use crate::clean::PrimitiveType; use crate::display::Joined as _; use crate::html::escape::EscapeBodyText; +use crate::html::format::HrefInfo; use crate::html::macro_expansion::ExpandedCode; use crate::html::render::span_map::{DUMMY_SP, Span}; use crate::html::render::{Context, LinkFromSrc}; @@ -1357,7 +1358,7 @@ fn generate_link_to_def( LinkFromSrc::External(def_id) => { format::href_with_root_path(*def_id, context, Some(href_context.root_path)) .ok() - .map(|(url, _, _)| url) + .map(|HrefInfo { url, .. }| url) } LinkFromSrc::Primitive(prim) => format::href_with_root_path( PrimitiveType::primitive_locations(context.tcx())[prim], @@ -1365,11 +1366,11 @@ fn generate_link_to_def( Some(href_context.root_path), ) .ok() - .map(|(url, _, _)| url), + .map(|HrefInfo { url, .. }| url), LinkFromSrc::Doc(def_id) => { format::href_with_root_path(*def_id, context, Some(href_context.root_path)) .ok() - .map(|(doc_link, _, _)| doc_link) + .map(|HrefInfo { url, .. }| url) } } }) diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 4c06d0da4701..e42997d5b4a1 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -203,52 +203,54 @@ fn render_item(&mut self, it: &clean::Item, is_module: bool) -> String { // `record_extern_fqn` correctly points to external items. render_redirect_pages = true; } - let mut title = String::new(); - if !is_module { - title.push_str(it.name.unwrap().as_str()); - } - let short_title; - let short_title = if is_module { - let module_name = self.current.last().unwrap(); - short_title = if it.is_crate() { - format!("Crate {module_name}") - } else { - format!("Module {module_name}") - }; - &short_title[..] - } else { - it.name.as_ref().unwrap().as_str() - }; - if !it.is_fake_item() { - if !is_module { - title.push_str(" in "); - } - // No need to include the namespace for primitive types and keywords - title.push_str(&join_path_syms(&self.current)); - }; - title.push_str(" - Rust"); - let tyname = it.type_(); - let desc = plain_text_summary(&it.doc_value(), &it.link_names(self.cache())); - let desc = if !desc.is_empty() { - desc - } else if it.is_crate() { - format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) - } else { - format!( - "API documentation for the Rust `{name}` {tyname} in crate `{krate}`.", - name = it.name.as_ref().unwrap(), - krate = self.shared.layout.krate, - ) - }; - let name; - let tyname_s = if it.is_crate() { - name = format!("{tyname} crate"); - name.as_str() - } else { - tyname.as_str() - }; if !render_redirect_pages { + let mut title = String::new(); + if !is_module { + title.push_str(it.name.unwrap().as_str()); + } + let short_title; + let short_title = if is_module { + let module_name = self.current.last().unwrap(); + short_title = if it.is_crate() { + format!("Crate {module_name}") + } else { + format!("Module {module_name}") + }; + &short_title[..] + } else { + it.name.as_ref().unwrap().as_str() + }; + if !it.is_fake_item() { + if !is_module { + title.push_str(" in "); + } + // No need to include the namespace for primitive types and keywords + title.push_str(&join_path_syms(&self.current)); + }; + title.push_str(" - Rust"); + let tyname = it.type_(); + let desc = plain_text_summary(&it.doc_value(), &it.link_names(self.cache())); + let desc = if !desc.is_empty() { + desc + } else if it.is_crate() { + format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) + } else { + format!( + "API documentation for the Rust `{name}` {tyname} in crate `{krate}`.", + name = it.name.as_ref().unwrap(), + krate = self.shared.layout.krate, + ) + }; + + let name; + let tyname_s = if it.is_crate() { + name = format!("{tyname} crate"); + name.as_str() + } else { + tyname.as_str() + }; + let content = print_item(self, it); let page = layout::Page { css_class: tyname_s, diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 36990332b2fc..871ed53bd338 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -74,7 +74,7 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::format::{ - Ending, HrefError, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, + Ending, HrefError, HrefInfo, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space, print_constness_with_space, print_default_space, print_generic_bounds, print_generics, print_impl, print_path, print_type, print_where_clause, visibility_print_with_space, }; @@ -982,7 +982,7 @@ enum Href<'a> { }; match href(did.expect_def_id(), cx) { - Ok((url, ..)) => Href::Url(url, item_type), + Ok(HrefInfo { url, .. }) => Href::Url(url, item_type), // The link is broken since it points to an external crate that wasn't documented. // Do not create any link in such case. This is better than falling back to a // dummy anchor like `#{item_type}.{name}` representing the `id` of *this* impl item diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 9c072eed51ae..3514c517d913 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -3,7 +3,9 @@ use std::collections::BTreeSet; use std::collections::hash_map::Entry; +use std::io; use std::path::Path; +use std::string::FromUtf8Error; use ::serde::de::{self, Deserializer, Error as _}; use ::serde::ser::{SerializeSeq, Serializer}; @@ -95,21 +97,22 @@ fn perform_read_strings( ) -> Result<(), Error> { let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); let column_path = doc_root.join(format!("search.index/{column_name}/")); + + let mut consume = |_, cell: &[u8]| { + column.push(String::from_utf8(cell.to_vec())?); + Ok::<_, FromUtf8Error>(()) + }; + stringdex_internals::read_data_from_disk_column( root_path, column_name.as_bytes(), column_path.clone(), - &mut |_id, item| { - column.push(String::from_utf8(item.to_vec())?); - Ok(()) - }, - ) - .map_err( - |error: stringdex_internals::ReadDataError>| Error { - file: column_path, - error: format!("failed to read column from disk: {error}"), - }, + &mut consume, ) + .map_err(|error| Error { + file: column_path, + error: format!("failed to read column from disk: {error}"), + }) } fn perform_read_serde( resource_suffix: &str, @@ -119,25 +122,26 @@ fn perform_read_serde( ) -> Result<(), Error> { let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); let column_path = doc_root.join(format!("search.index/{column_name}/")); + + let mut consume = |_, cell: &[u8]| { + if cell.is_empty() { + column.push(None); + } else { + column.push(Some(serde_json::from_slice(cell)?)); + } + Ok::<_, serde_json::Error>(()) + }; + stringdex_internals::read_data_from_disk_column( root_path, column_name.as_bytes(), column_path.clone(), - &mut |_id, item| { - if item.is_empty() { - column.push(None); - } else { - column.push(Some(serde_json::from_slice(item)?)); - } - Ok(()) - }, - ) - .map_err( - |error: stringdex_internals::ReadDataError>| Error { - file: column_path, - error: format!("failed to read column from disk: {error}"), - }, + &mut consume, ) + .map_err(|error| Error { + file: column_path, + error: format!("failed to read column from disk: {error}"), + }) } fn perform_read_postings( resource_suffix: &str, @@ -147,23 +151,28 @@ fn perform_read_postings( ) -> Result<(), Error> { let root_path = doc_root.join(format!("search.index/root{resource_suffix}.js")); let column_path = doc_root.join(format!("search.index/{column_name}/")); + + fn consumer( + column: &mut Vec>>, + ) -> impl FnMut(u32, &[u8]) -> io::Result<()> { + |_, cell| { + let mut postings = Vec::new(); + encode::read_postings_from_string(&mut postings, cell); + column.push(postings); + Ok(()) + } + } + stringdex_internals::read_data_from_disk_column( root_path, column_name.as_bytes(), column_path.clone(), - &mut |_id, buf| { - let mut postings = Vec::new(); - encode::read_postings_from_string(&mut postings, buf); - column.push(postings); - Ok(()) - }, - ) - .map_err( - |error: stringdex_internals::ReadDataError>| Error { - file: column_path, - error: format!("failed to read column from disk: {error}"), - }, + &mut consumer(column), ) + .map_err(|error| Error { + file: column_path, + error: format!("failed to read column from disk: {error}"), + }) } assert_eq!(names.len(), path_data.len()); @@ -1055,12 +1064,12 @@ fn serialize(&self, serializer: S) -> Result let mut buf = Vec::new(); encode::write_postings_to_string(&self.inverted_function_inputs_index, &mut buf); let mut serialized_result = Vec::new(); - stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result); + stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result).unwrap(); seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?; buf.clear(); serialized_result.clear(); encode::write_postings_to_string(&self.inverted_function_output_index, &mut buf); - stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result); + stringdex_internals::encode::write_base64_to_bytes(&buf, &mut serialized_result).unwrap(); seq.serialize_element(&str::from_utf8(&serialized_result).unwrap())?; if self.search_unbox { seq.serialize_element(&1)?; diff --git a/src/librustdoc/html/render/search_index/encode.rs b/src/librustdoc/html/render/search_index/encode.rs index d15e13a2d374..a05c14374d1d 100644 --- a/src/librustdoc/html/render/search_index/encode.rs +++ b/src/librustdoc/html/render/search_index/encode.rs @@ -78,16 +78,9 @@ pub fn read_postings_from_string(postings: &mut Vec>, mut buf: &[u8]) { while let Some(&c) = buf.get(0) { if c < 0x3a { buf = &buf[1..]; - let mut slot = Vec::new(); - for _ in 0..c { - slot.push( - (buf[0] as u32) - | ((buf[1] as u32) << 8) - | ((buf[2] as u32) << 16) - | ((buf[3] as u32) << 24), - ); - buf = &buf[4..]; - } + let buf = buf.split_off(..usize::from(c) * size_of::()).unwrap(); + let (chunks, _) = buf.as_chunks(); + let slot = chunks.iter().copied().map(u32::from_le_bytes).collect(); postings.push(slot); } else { let (bitmap, consumed_bytes_len) = diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 3ea9de381eca..dff40485e9a9 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -683,6 +683,7 @@ function preLoadCss(cssUrl) { break; case "+": + case "=": ev.preventDefault(); expandAllDocs(); break; @@ -1620,7 +1621,7 @@ function preLoadCss(cssUrl) { ["↓", "Move down in search results"], ["← / →", "Switch result tab (when results focused)"], ["⏎", "Go to active search result"], - ["+", "Expand all sections"], + ["+ / =", "Expand all sections"], ["-", "Collapse all sections"], // for the sake of brevity, we don't say "inherit impl blocks", // although that would be more correct, diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index e3829603072b..2b381d64547b 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3926,16 +3926,25 @@ class DocSearch { * @returns {Promise} */ const handleAlias = async(name, alias, dist, index) => { + const item = nonnull(await this.getRow(alias, false)); + // space both is an alias for ::, + // and is also allowed to appear in doc alias names + const path_dist = name.includes(" ") || parsedQuery.elems.length === 0 ? + 0 : checkRowPath(parsedQuery.elems[0].pathWithoutLast, item); + // path distance exceeds max, omit alias from results + if (path_dist === null) { + return null; + } return { id: alias, dist, - path_dist: 0, + path_dist, index, alias: name, is_alias: true, elems: [], // only used in type-based queries returned: [], // only used in type-based queries - item: nonnull(await this.getRow(alias, false)), + item, }; }; /** diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 909262d563e9..edafc9e7a089 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -4,6 +4,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_hir::attrs::{self, DeprecatedSince}; @@ -988,3 +989,55 @@ fn format_integer_type(it: rustc_abi::IntegerType) -> String { } .to_owned() } + +pub(super) fn target(sess: &rustc_session::Session) -> Target { + // Build a set of which features are enabled on this target + let globally_enabled_features: FxHashSet<&str> = + sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); + + // Build a map of target feature stability by feature name + use rustc_target::target_features::Stability; + let feature_stability: FxHashMap<&str, Stability> = sess + .target + .rust_target_features() + .iter() + .copied() + .map(|(name, stability, _)| (name, stability)) + .collect(); + + Target { + triple: sess.opts.target_triple.tuple().into(), + target_features: sess + .target + .rust_target_features() + .iter() + .copied() + .filter(|(_, stability, _)| { + // Describe only target features which the user can toggle + stability.toggle_allowed().is_ok() + }) + .map(|(name, stability, implied_features)| { + TargetFeature { + name: name.into(), + unstable_feature_gate: match stability { + Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()), + _ => None, + }, + implies_features: implied_features + .iter() + .copied() + .filter(|name| { + // Imply only target features which the user can toggle + feature_stability + .get(name) + .map(|stability| stability.toggle_allowed().is_ok()) + .unwrap_or(false) + }) + .map(String::from) + .collect(), + globally_enabled: globally_enabled_features.contains(name), + } + }) + .collect(), + } +} diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index b724d7e866a0..b020e3d924a4 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -14,7 +14,6 @@ use std::path::PathBuf; use std::rc::Rc; -use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -123,58 +122,6 @@ fn serialize_and_write( } } -fn target(sess: &rustc_session::Session) -> types::Target { - // Build a set of which features are enabled on this target - let globally_enabled_features: FxHashSet<&str> = - sess.unstable_target_features.iter().map(|name| name.as_str()).collect(); - - // Build a map of target feature stability by feature name - use rustc_target::target_features::Stability; - let feature_stability: FxHashMap<&str, Stability> = sess - .target - .rust_target_features() - .iter() - .copied() - .map(|(name, stability, _)| (name, stability)) - .collect(); - - types::Target { - triple: sess.opts.target_triple.tuple().into(), - target_features: sess - .target - .rust_target_features() - .iter() - .copied() - .filter(|(_, stability, _)| { - // Describe only target features which the user can toggle - stability.toggle_allowed().is_ok() - }) - .map(|(name, stability, implied_features)| { - types::TargetFeature { - name: name.into(), - unstable_feature_gate: match stability { - Stability::Unstable(feature_gate) => Some(feature_gate.as_str().into()), - _ => None, - }, - implies_features: implied_features - .iter() - .copied() - .filter(|name| { - // Imply only target features which the user can toggle - feature_stability - .get(name) - .map(|stability| stability.toggle_allowed().is_ok()) - .unwrap_or(false) - }) - .map(String::from) - .collect(), - globally_enabled: globally_enabled_features.contains(name), - } - }) - .collect(), - } -} - impl<'tcx> JsonRenderer<'tcx> { pub(crate) fn init( krate: clean::Crate, @@ -317,7 +264,7 @@ fn after_krate(mut self) -> Result<(), Error> { // multiple targets: https://github.com/rust-lang/rust/pull/137632 // // We want to describe a single target, so pass tcx.sess rather than tcx. - let target = target(self.tcx.sess); + let target = conversions::target(self.tcx.sess); debug!("Constructing Output"); let output_crate = types::Crate { diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index e0ea760cf3ba..39116061f485 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -102,7 +102,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - return false; } - if (!cx.render_options.document_hidden + if (!cx.document_hidden() && (cx.tcx.is_doc_hidden(def_id.to_def_id()) || inherits_doc_hidden(cx.tcx, def_id, None))) || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion() { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index d09949e6868d..e29256bb5d1a 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1070,7 +1070,7 @@ fn preprocessed_markdown_links(s: &str) -> Vec { impl LinkCollector<'_, '_> { #[instrument(level = "debug", skip_all)] fn resolve_links(&mut self, item: &Item) { - if !self.cx.render_options.document_private + if !self.cx.document_private() && let Some(def_id) = item.item_id.as_def_id() && let Some(def_id) = def_id.as_local() && !self.cx.tcx.effective_visibilities(()).is_exported(def_id) @@ -1215,7 +1215,6 @@ fn validate_link(&self, original_did: DefId) -> bool { || !did.is_local() } - #[allow(rustc::potential_query_instability)] pub(crate) fn resolve_ambiguities(&mut self) { let mut ambiguous_links = mem::take(&mut self.ambiguous_links); for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() { @@ -2400,7 +2399,7 @@ fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: diag.span_label(sp, "this item is private"); } - let note_msg = if cx.render_options.document_private { + let note_msg = if cx.document_private() { "this link resolves only because you passed `--document-private-items`, but will break without" } else { "this link will resolve properly if you pass `--document-private-items`" diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs index 855b1884cea9..e49f54f6df94 100644 --- a/src/librustdoc/passes/lint/redundant_explicit_links.rs +++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs @@ -46,13 +46,13 @@ fn check_redundant_explicit_link_for_did( return; }; - let is_hidden = !cx.render_options.document_hidden + let is_hidden = !cx.document_hidden() && (item.is_doc_hidden() || inherits_doc_hidden(cx.tcx, local_item_id, None)); if is_hidden { return; } - let is_private = !cx.render_options.document_private - && !cx.cache.effective_visibilities.is_directly_public(cx.tcx, did); + let is_private = + !cx.document_private() && !cx.cache.effective_visibilities.is_directly_public(cx.tcx, did); if is_private { return; } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 525d05b6a986..27065d7675bb 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -43,8 +43,8 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea retained: &retained, cache: &cx.cache, is_json_output, - document_private: cx.render_options.document_private, - document_hidden: cx.render_options.document_hidden, + document_private: cx.document_private(), + document_hidden: cx.document_hidden(), }; stripper.fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_priv_imports.rs b/src/librustdoc/passes/strip_priv_imports.rs index b9b2431f06f2..a169797e00b2 100644 --- a/src/librustdoc/passes/strip_priv_imports.rs +++ b/src/librustdoc/passes/strip_priv_imports.rs @@ -14,10 +14,6 @@ pub(crate) fn strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate { let is_json_output = cx.is_json_output(); - ImportStripper { - tcx: cx.tcx, - is_json_output, - document_hidden: cx.render_options.document_hidden, - } - .fold_crate(krate) + ImportStripper { tcx: cx.tcx, is_json_output, document_hidden: cx.document_hidden() } + .fold_crate(krate) } diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs index 1bd8a7838ec0..045bf0c0be02 100644 --- a/src/librustdoc/passes/strip_private.rs +++ b/src/librustdoc/passes/strip_private.rs @@ -29,12 +29,9 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> is_json_output, tcx: cx.tcx, }; - krate = ImportStripper { - tcx: cx.tcx, - is_json_output, - document_hidden: cx.render_options.document_hidden, - } - .fold_crate(stripper.fold_crate(krate)); + krate = + ImportStripper { tcx: cx.tcx, is_json_output, document_hidden: cx.document_hidden() } + .fold_crate(stripper.fold_crate(krate)); } // strip all impls referencing private items @@ -43,8 +40,8 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> retained: &retained, cache: &cx.cache, is_json_output, - document_private: cx.render_options.document_private, - document_hidden: cx.render_options.document_hidden, + document_private: cx.document_private(), + document_hidden: cx.document_hidden(), }; stripper.fold_crate(krate) } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index dc9889cec219..51b2821eb1e5 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -243,7 +243,7 @@ fn maybe_inline_local( return false; }; - let document_hidden = self.cx.render_options.document_hidden; + let document_hidden = self.cx.document_hidden(); let use_attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id)); // Don't inline `doc(hidden)` imports so they can be stripped at a later stage. let is_no_inline = hir_attr_lists(use_attrs, sym::doc).has_word(sym::no_inline) @@ -351,7 +351,7 @@ fn reexport_public_and_not_hidden( import_def_id: LocalDefId, target_def_id: LocalDefId, ) -> bool { - if self.cx.render_options.document_hidden { + if self.cx.document_hidden() { return true; } let tcx = self.cx.tcx; diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 369fc52860ee..297485659fa1 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -30,11 +30,12 @@ impl RustdocEffectiveVisibilities { pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) { assert!(!def_id.is_local()); + let document_hidden = cx.document_hidden(); LibEmbargoVisitor { tcx: cx.tcx, extern_public: &mut cx.cache.effective_visibilities.extern_public, visited_mods: Default::default(), - document_hidden: cx.render_options.document_hidden, + document_hidden, } .visit_item(def_id) } diff --git a/src/llvm-project b/src/llvm-project index 4f74b76fb696..16b5ac8b0a54 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 4f74b76fb69688474e073fb26b316d9ea571388f +Subproject commit 16b5ac8b0a5456eb23c72e1bff3fc9bd6e824f84 diff --git a/src/stage0 b/src/stage0 index 43e1a635a42c..0e4e9eb1c4b9 100644 --- a/src/stage0 +++ b/src/stage0 @@ -2,7 +2,7 @@ dist_server=https://static.rust-lang.org artifacts_server=https://ci-artifacts.rust-lang.org/rustc-builds artifacts_with_llvm_assertions_server=https://ci-artifacts.rust-lang.org/rustc-builds-alt git_merge_commit_email=bors@rust-lang.org -nightly_branch=master +nightly_branch=main # The configuration above this comment is editable, and can be changed # by forks of the repository if they have alternate values. diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index efa99f181b3a..c0271bbc89b5 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -14,3 +14,7 @@ tar = "0.4.29" sha2 = "0.10.1" rayon = "1.5.1" hex = "0.4.2" + +[build-dependencies] +serde = "1" +serde_json = "1" diff --git a/src/tools/build-manifest/README.md b/src/tools/build-manifest/README.md index 2ea1bffb35f4..bc1992ef80cc 100644 --- a/src/tools/build-manifest/README.md +++ b/src/tools/build-manifest/README.md @@ -2,7 +2,13 @@ This tool generates the manifests uploaded to static.rust-lang.org and used by rustup. You can see a full list of all manifests at . -This listing is updated by every 7 days. + +We auto-generate the host targets (those with full compiler toolchains) and +target targets (a superset of hosts, some of which only support std) through +`build.rs`, which internally uses a stage 1 rustc to produce the target list +and uses the `TargetMetadata` to determine whether host tools are expected and +whether artifacts are expected. This list is not currently verified against the +actually produced artifacts by CI, though that may change in the future. This gets called by `promote-release` . `promote-release` downloads a pre-built binary of `build-manifest` which is generated in the dist-x86_64-linux builder and uploaded to s3. diff --git a/src/tools/build-manifest/build.rs b/src/tools/build-manifest/build.rs new file mode 100644 index 000000000000..c804921408a9 --- /dev/null +++ b/src/tools/build-manifest/build.rs @@ -0,0 +1,76 @@ +use std::fmt::Write; +use std::path::PathBuf; +use std::process::{Command, Stdio}; + +#[derive(Default, Debug)] +pub(crate) struct RustcTargets { + /// Targets with host tool artifacts. + pub(crate) hosts: Vec, + + /// All targets we distribute some artifacts for (superset of `hosts`). + pub(crate) targets: Vec, +} + +fn collect_rustc_targets() -> RustcTargets { + let rustc_path = std::env::var("RUSTC").expect("RUSTC set"); + let output = Command::new(&rustc_path) + .arg("--print=all-target-specs-json") + .env("RUSTC_BOOTSTRAP", "1") + .arg("-Zunstable-options") + .stderr(Stdio::inherit()) + .output() + .unwrap(); + assert!(output.status.success()); + let json: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap(); + + let mut rustc_targets = RustcTargets::default(); + for (target, json) in json.as_object().unwrap().iter() { + let Some(tier) = json["metadata"]["tier"].as_u64() else { + eprintln!("skipping {target}: no tier in metadata"); + continue; + }; + let host_tools: Option = + serde_json::from_value(json["metadata"]["host_tools"].clone()).unwrap(); + + if !(tier == 1 || tier == 2) { + eprintln!("ignoring {target}: tier {tier} insufficient for target to be in manifest"); + continue; + } + + if host_tools == Some(true) { + rustc_targets.hosts.push(target.to_owned()); + rustc_targets.targets.push(target.to_owned()); + } else { + rustc_targets.targets.push(target.to_owned()); + } + } + + rustc_targets +} + +fn main() { + let targets = collect_rustc_targets(); + + // Verify we ended up with a reasonable target list. + assert!(targets.hosts.len() >= 10); + assert!(targets.targets.len() >= 30); + assert!(targets.hosts.iter().any(|e| e == "x86_64-unknown-linux-gnu")); + assert!(targets.targets.iter().any(|e| e == "x86_64-unknown-linux-gnu")); + + let mut output = String::new(); + + writeln!(output, "static HOSTS: &[&str] = &[").unwrap(); + for host in targets.hosts { + writeln!(output, " {:?},", host).unwrap(); + } + writeln!(output, "];").unwrap(); + + writeln!(output, "static TARGETS: &[&str] = &[").unwrap(); + for target in targets.targets { + writeln!(output, " {:?},", target).unwrap(); + } + writeln!(output, "];").unwrap(); + + std::fs::write(PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("targets.rs"), output) + .unwrap(); +} diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 9bae8b241a94..5d9d63093860 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -12,193 +12,7 @@ use crate::manifest::{Component, Manifest, Package, Rename, Target}; use crate::versions::{PkgType, Versions}; -static HOSTS: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-pc-windows-gnullvm", - "aarch64-pc-windows-msvc", - "aarch64-unknown-linux-gnu", - "aarch64-unknown-linux-musl", - "arm-unknown-linux-gnueabi", - "arm-unknown-linux-gnueabihf", - "armv7-unknown-linux-gnueabihf", - "i686-apple-darwin", - "i686-pc-windows-gnu", - "i686-pc-windows-msvc", - "i686-unknown-linux-gnu", - "loongarch64-unknown-linux-gnu", - "loongarch64-unknown-linux-musl", - "mips-unknown-linux-gnu", - "mips64-unknown-linux-gnuabi64", - "mips64el-unknown-linux-gnuabi64", - "mipsel-unknown-linux-gnu", - "mipsisa32r6-unknown-linux-gnu", - "mipsisa32r6el-unknown-linux-gnu", - "mipsisa64r6-unknown-linux-gnuabi64", - "mipsisa64r6el-unknown-linux-gnuabi64", - "powerpc-unknown-linux-gnu", - "powerpc64-unknown-linux-gnu", - "powerpc64le-unknown-linux-gnu", - "powerpc64le-unknown-linux-musl", - "riscv64gc-unknown-linux-gnu", - "s390x-unknown-linux-gnu", - "sparcv9-sun-solaris", - "x86_64-apple-darwin", - "x86_64-pc-solaris", - "x86_64-pc-windows-gnu", - "x86_64-pc-windows-gnullvm", - "x86_64-pc-windows-msvc", - "x86_64-unknown-freebsd", - "x86_64-unknown-illumos", - "x86_64-unknown-linux-gnu", - "x86_64-unknown-linux-musl", - "x86_64-unknown-netbsd", -]; - -static TARGETS: &[&str] = &[ - "aarch64-apple-darwin", - "aarch64-apple-ios", - "aarch64-apple-ios-macabi", - "aarch64-apple-ios-sim", - "aarch64-unknown-fuchsia", - "aarch64-linux-android", - "aarch64-pc-windows-gnullvm", - "aarch64-pc-windows-msvc", - "aarch64-unknown-hermit", - "aarch64-unknown-linux-gnu", - "aarch64-unknown-linux-musl", - "aarch64-unknown-linux-ohos", - "aarch64-unknown-none", - "aarch64-unknown-none-softfloat", - "aarch64-unknown-redox", - "aarch64-unknown-uefi", - "aarch64-unknown-managarm-mlibc", - "amdgcn-amd-amdhsa", - "arm64e-apple-darwin", - "arm64e-apple-ios", - "arm64e-apple-tvos", - "arm-linux-androideabi", - "arm-unknown-linux-gnueabi", - "arm-unknown-linux-gnueabihf", - "arm-unknown-linux-musleabi", - "arm-unknown-linux-musleabihf", - "arm64ec-pc-windows-msvc", - "armv5te-unknown-linux-gnueabi", - "armv5te-unknown-linux-musleabi", - "armv7-linux-androideabi", - "thumbv7neon-linux-androideabi", - "armv7-unknown-linux-gnueabi", - "armv7-unknown-linux-gnueabihf", - "armv7a-none-eabi", - "armv7a-none-eabihf", - "thumbv7neon-unknown-linux-gnueabihf", - "armv7-unknown-linux-musleabi", - "armv7-unknown-linux-musleabihf", - "armv7-unknown-linux-ohos", - "armebv7r-none-eabi", - "armebv7r-none-eabihf", - "armv7r-none-eabi", - "armv7r-none-eabihf", - "armv8r-none-eabihf", - "armv7s-apple-ios", - "bpfeb-unknown-none", - "bpfel-unknown-none", - "i386-apple-ios", - "i586-unknown-linux-gnu", - "i586-unknown-linux-musl", - "i586-unknown-redox", - "i686-apple-darwin", - "i686-linux-android", - "i686-pc-windows-gnu", - "i686-pc-windows-gnullvm", - "i686-pc-windows-msvc", - "i686-unknown-freebsd", - "i686-unknown-linux-gnu", - "i686-unknown-linux-musl", - "i686-unknown-uefi", - "loongarch64-unknown-linux-gnu", - "loongarch64-unknown-linux-musl", - "loongarch32-unknown-none", - "loongarch32-unknown-none-softfloat", - "loongarch64-unknown-none", - "loongarch64-unknown-none-softfloat", - "m68k-unknown-linux-gnu", - "m68k-unknown-none-elf", - "csky-unknown-linux-gnuabiv2", - "csky-unknown-linux-gnuabiv2hf", - "mips-unknown-linux-gnu", - "mips-unknown-linux-musl", - "mips64-unknown-linux-gnuabi64", - "mips64-unknown-linux-muslabi64", - "mips64el-unknown-linux-gnuabi64", - "mips64el-unknown-linux-muslabi64", - "mipsisa32r6-unknown-linux-gnu", - "mipsisa32r6el-unknown-linux-gnu", - "mipsisa64r6-unknown-linux-gnuabi64", - "mipsisa64r6el-unknown-linux-gnuabi64", - "mipsel-unknown-linux-gnu", - "mipsel-unknown-linux-musl", - "mips-mti-none-elf", - "mipsel-mti-none-elf", - "nvptx64-nvidia-cuda", - "powerpc-unknown-linux-gnu", - "powerpc64-unknown-linux-gnu", - "powerpc64le-unknown-linux-gnu", - "powerpc64le-unknown-linux-musl", - "riscv32i-unknown-none-elf", - "riscv32im-risc0-zkvm-elf", - "riscv32im-unknown-none-elf", - "riscv32ima-unknown-none-elf", - "riscv32imc-unknown-none-elf", - "riscv32imac-unknown-none-elf", - "riscv32imafc-unknown-none-elf", - "riscv32gc-unknown-linux-gnu", - "riscv64imac-unknown-none-elf", - "riscv64gc-unknown-hermit", - "riscv64gc-unknown-none-elf", - "riscv64gc-unknown-linux-gnu", - "riscv64gc-unknown-linux-musl", - "riscv64gc-unknown-managarm-mlibc", - "s390x-unknown-linux-gnu", - "sparc64-unknown-linux-gnu", - "sparcv9-sun-solaris", - "sparc-unknown-none-elf", - "thumbv6m-none-eabi", - "thumbv7em-none-eabi", - "thumbv7em-none-eabihf", - "thumbv7m-none-eabi", - "thumbv8m.base-none-eabi", - "thumbv8m.main-none-eabi", - "thumbv8m.main-none-eabihf", - "wasm32-unknown-emscripten", - "wasm32-unknown-unknown", - "wasm32-wasip1", - "wasm32-wasip1-threads", - "wasm32-wasip2", - "wasm32v1-none", - "x86_64-apple-darwin", - "x86_64-apple-ios", - "x86_64-apple-ios-macabi", - "x86_64-fortanix-unknown-sgx", - "x86_64-unknown-fuchsia", - "x86_64-linux-android", - "x86_64-pc-windows-gnu", - "x86_64-pc-windows-gnullvm", - "x86_64-pc-windows-msvc", - "x86_64-pc-solaris", - "x86_64-unikraft-linux-musl", - "x86_64-unknown-freebsd", - "x86_64-unknown-illumos", - "x86_64-unknown-linux-gnu", - "x86_64-unknown-linux-gnux32", - "x86_64-unknown-linux-musl", - "x86_64-unknown-linux-ohos", - "x86_64-unknown-netbsd", - "x86_64-unknown-none", - "x86_64-unknown-redox", - "x86_64-unknown-hermit", - "x86_64-unknown-uefi", - "x86_64-unknown-managarm-mlibc", -]; +include!(concat!(env!("OUT_DIR"), "/targets.rs")); /// This allows the manifest to contain rust-docs for hosts that don't build /// docs. diff --git a/src/tools/cargo b/src/tools/cargo index 445fe4a68f46..2d4fa139552e 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 445fe4a68f469bf936b2fd81de2c503b233a7f4f +Subproject commit 2d4fa139552ebdd5f091a1401ed03f7dc62cb43f diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml index b6f70a7f1830..91aaf1f3644e 100644 --- a/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/bug_report.yml @@ -20,15 +20,15 @@ body: label: Reproducer description: Please provide the code and steps to reproduce the bug value: | - I tried this code: + Code: ```rust ``` - I expected to see this happen: + Current output: - Instead, this happened: + Desired output: - type: textarea id: version attributes: diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index 9d099137449e..ce15a861bb07 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -25,8 +25,6 @@ jobs: host: i686-unknown-linux-gnu - os: windows-latest host: x86_64-pc-windows-msvc - - os: macos-13 - host: x86_64-apple-darwin - os: macos-latest host: aarch64-apple-darwin diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 2a042e6c3d85..8cdd99ac44a8 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -108,7 +108,7 @@ struct ConfError { impl ConfError { fn from_toml(file: &SourceFile, error: &toml::de::Error) -> Self { - let span = error.span().unwrap_or(0..file.source_len.0 as usize); + let span = error.span().unwrap_or(0..file.normalized_source_len.0 as usize); Self::spanned(file, error.message(), None, span) } diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index a180db6ad062..0b6d702d7721 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -157,18 +157,19 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let path = "clippy_lints/src/lib.rs"; let mut lib_rs = fs::read_to_string(path).context("reading")?; - let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment"); - let ctor_arg = if lint.pass == Pass::Late { "_" } else { "" }; - let lint_pass = lint.pass; + let (comment, ctor_arg) = if lint.pass == Pass::Late { + ("// add late passes here", "_") + } else { + ("// add early passes here", "") + }; + let comment_start = lib_rs.find(comment).expect("Couldn't find comment"); let module_name = lint.name; let camel_name = to_camel_case(lint.name); let new_lint = if enable_msrv { - format!( - "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf)));\n ", - ) + format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",) } else { - format!("store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n ",) + format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",) }; lib_rs.insert_str(comment_start, &new_lint); diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index f3985603c4d2..902ba70577b9 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; @@ -415,19 +415,20 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio BinOpKind::Ge => Some(" < "), _ => None, } - .and_then(|op| { - let lhs_snippet = lhs.span.get_source_text(cx)?; - let rhs_snippet = rhs.span.get_source_text(cx)?; + .map(|op| { + let mut app = Applicability::MachineApplicable; + let (lhs_snippet, _) = snippet_with_context(cx, lhs.span, SyntaxContext::root(), "", &mut app); + let (rhs_snippet, _) = snippet_with_context(cx, rhs.span, SyntaxContext::root(), "", &mut app); if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) && let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { // e.g. `(a as u64) < b`. Without the parens the `<` is // interpreted as a start of generic arguments for `u64` - return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); + return format!("({lhs_snippet}){op}{rhs_snippet}"); } - Some(format!("{lhs_snippet}{op}{rhs_snippet}")) + format!("{lhs_snippet}{op}{rhs_snippet}") }) }, ExprKind::MethodCall(path, receiver, args, _) => { diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index c3fc09343dbf..9b5cd7e1731f 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -55,7 +55,7 @@ fn array_rec(pats: &[Pat<'_>]) -> bool { | PatKind::Err(_) => false, PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), - PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), + PatKind::Ref(x, _, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index dfc8411baa00..7f3ef58c93d1 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -2,14 +2,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, peel_blocks, sym, + as_some_expr, contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, + is_none_expr, peel_blocks, sym, }; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -70,11 +69,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { }) = higher::If::hir(expr) && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr - && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && let Some(then_arg) = as_some_expr(cx, then_expr) && !expr.span.from_expansion() && !then_expr.span.from_expansion() - && then_call.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && peel_blocks(els).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) + && is_none_expr(cx, peel_blocks(els)) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) && self.msrv.meets(cx, msrvs::BOOL_THEN) diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 716334656926..c3bc9048c23a 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -1,14 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_in_const_context, is_in_test}; +use clippy_utils::{is_in_const_context, is_in_test, sym}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::{CrateNum, DefId}; -use rustc_span::{ExpnKind, Span, sym}; +use rustc_span::{ExpnKind, Span}; declare_clippy_lint! { /// ### What it does @@ -77,11 +77,36 @@ enum Availability { Since(RustcVersion), } +/// All known std crates containing a stability attribute. +struct StdCrates([Option; 6]); +impl StdCrates { + fn new(tcx: TyCtxt<'_>) -> Self { + let mut res = Self([None; _]); + for &krate in tcx.crates(()) { + // FIXME(@Jarcho): We should have an internal lint to detect when this list is out of date. + match tcx.crate_name(krate) { + sym::alloc => res.0[0] = Some(krate), + sym::core => res.0[1] = Some(krate), + sym::core_arch => res.0[2] = Some(krate), + sym::proc_macro => res.0[3] = Some(krate), + sym::std => res.0[4] = Some(krate), + sym::std_detect => res.0[5] = Some(krate), + _ => {}, + } + } + res + } + + fn contains(&self, krate: CrateNum) -> bool { + self.0.contains(&Some(krate)) + } +} + pub struct IncompatibleMsrv { msrv: Msrv, availability_cache: FxHashMap<(DefId, bool), Availability>, check_in_tests: bool, - core_crate: Option, + std_crates: StdCrates, // The most recently called path. Used to skip checking the path after it's // been checked when visiting the call expression. @@ -96,11 +121,7 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { msrv: conf.msrv, availability_cache: FxHashMap::default(), check_in_tests: conf.check_incompatible_msrv_in_tests, - core_crate: tcx - .crates(()) - .iter() - .find(|krate| tcx.crate_name(**krate) == sym::core) - .copied(), + std_crates: StdCrates::new(tcx), called_path: None, } } @@ -152,21 +173,24 @@ fn emit_lint_if_under_msrv( node: HirId, span: Span, ) { - if def_id.is_local() { - // We don't check local items since their MSRV is supposed to always be valid. + if !self.std_crates.contains(def_id.krate) { + // No stability attributes to lookup for these items. return; } - let expn_data = span.ctxt().outer_expn_data(); - if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = expn_data.kind { - // Desugared expressions get to cheat and stability is ignored. - // Intentionally not using `.from_expansion()`, since we do still care about macro expansions - return; - } - // Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the - // macros may have existed prior to the checked MSRV, but their expansion with a recent compiler - // might use recent functions or methods. Compiling with an older compiler would not use those. - if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate { - return; + // Use `from_expansion` to fast-path the common case. + if span.from_expansion() { + let expn = span.ctxt().outer_expn_data(); + match expn.kind { + // FIXME(@Jarcho): Check that the actual desugaring or std macro is supported by the + // current MSRV. Note that nested expansions need to be handled as well. + ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return, + ExpnKind::Macro(..) if expn.macro_def_id.is_some_and(|did| self.std_crates.contains(did.krate)) => { + return; + }, + // All other expansions share the target's MSRV. + // FIXME(@Jarcho): What should we do about version dependant macros from external crates? + _ => {}, + } } if (self.check_in_tests || !is_in_test(cx.tcx, node)) diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index a08efbc52d45..f59c7615d745 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::InherentImplLintScope; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::fulfill_or_allowed; +use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_in_cfg_test}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{Item, ItemKind, Node}; @@ -100,7 +100,8 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { }, InherentImplLintScope::Crate => Criterion::Crate, }; - match type_map.entry((impl_ty, criterion)) { + let is_test = is_cfg_test(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id); + match type_map.entry((impl_ty, criterion, is_test)) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. e.insert(IdOrSpan::Id(impl_id)); diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs index 2dbf55a8540b..dc7a916614be 100644 --- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs +++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; use clippy_utils::visitors::is_local_used; use rustc_errors::Applicability; use rustc_hir as hir; @@ -59,80 +59,88 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { for [stmt, next] in block.stmts.array_windows::<2>() { - if let hir::StmtKind::Let(local) = stmt.kind - && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind - && let hir::StmtKind::Expr(if_) = next.kind - && let hir::ExprKind::If(cond, then, else_) = if_.kind - && !is_local_used(cx, cond, canonical_id) - && let hir::ExprKind::Block(then, _) = then.kind - && let Some(value) = check_assign(cx, canonical_id, then) - && !is_local_used(cx, value, canonical_id) - { - let span = stmt.span.to(if_.span); - - let has_interior_mutability = !cx - .typeck_results() - .node_type(canonical_id) - .is_freeze(cx.tcx, cx.typing_env()); - if has_interior_mutability { - return; - } - - let (default_multi_stmts, default) = if let Some(else_) = else_ { - if let hir::ExprKind::Block(else_, _) = else_.kind { - if let Some(default) = check_assign(cx, canonical_id, else_) { - (else_.stmts.len() > 1, default) - } else if let Some(default) = local.init { - (true, default) - } else { - continue; - } - } else { - continue; - } - } else if let Some(default) = local.init { - (false, default) - } else { - continue; - }; - - let mutability = match mode { - BindingMode(_, Mutability::Mut) => " ", - _ => "", - }; - - // FIXME: this should not suggest `mut` if we can detect that the variable is not - // use mutably after the `if` - - let sug = format!( - "let {mutability}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};", - name=ident.name, - cond=snippet(cx, cond.span, "_"), - then=if then.stmts.len() > 1 { " ..;" } else { "" }, - else=if default_multi_stmts { " ..;" } else { "" }, - value=snippet(cx, value.span, ""), - default=snippet(cx, default.span, ""), - ); - span_lint_hir_and_then( - cx, - USELESS_LET_IF_SEQ, - local.hir_id, - span, - "`if _ { .. } else { .. }` is an expression", - |diag| { - diag.span_suggestion( - span, - "it is more idiomatic to write", - sug, - Applicability::HasPlaceholders, - ); - if !mutability.is_empty() { - diag.note("you might not need `mut` at all"); - } - }, - ); + if let hir::StmtKind::Expr(if_) = next.kind { + check_block_inner(cx, stmt, if_); } } + + if let Some(expr) = block.expr + && let Some(stmt) = block.stmts.last() + { + check_block_inner(cx, stmt, expr); + } + } +} + +fn check_block_inner<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'tcx>, if_: &'tcx hir::Expr<'tcx>) { + if let hir::StmtKind::Let(local) = stmt.kind + && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind + && let hir::ExprKind::If(cond, then, else_) = if_.kind + && !is_local_used(cx, cond, canonical_id) + && let hir::ExprKind::Block(then, _) = then.kind + && let Some(value) = check_assign(cx, canonical_id, then) + && !is_local_used(cx, value, canonical_id) + { + let span = stmt.span.to(if_.span); + + let has_interior_mutability = !cx + .typeck_results() + .node_type(canonical_id) + .is_freeze(cx.tcx, cx.typing_env()); + if has_interior_mutability { + return; + } + + let (default_multi_stmts, default) = if let Some(else_) = else_ { + if let hir::ExprKind::Block(else_, _) = else_.kind { + if let Some(default) = check_assign(cx, canonical_id, else_) { + (else_.stmts.len() > 1, default) + } else if let Some(default) = local.init { + (true, default) + } else { + return; + } + } else { + return; + } + } else if let Some(default) = local.init { + (false, default) + } else { + return; + }; + + let mutability = match mode { + BindingMode(_, Mutability::Mut) => " ", + _ => "", + }; + + // FIXME: this should not suggest `mut` if we can detect that the variable is not + // use mutably after the `if` + + let mut applicability = Applicability::HasPlaceholders; + let (cond_snip, _) = snippet_with_context(cx, cond.span, if_.span.ctxt(), "_", &mut applicability); + let (value_snip, _) = snippet_with_context(cx, value.span, if_.span.ctxt(), "", &mut applicability); + let (default_snip, _) = + snippet_with_context(cx, default.span, if_.span.ctxt(), "", &mut applicability); + let sug = format!( + "let {mutability}{name} = if {cond_snip} {{{then} {value_snip} }} else {{{else} {default_snip} }};", + name=ident.name, + then=if then.stmts.len() > 1 { " ..;" } else { "" }, + else=if default_multi_stmts { " ..;" } else { "" }, + ); + span_lint_hir_and_then( + cx, + USELESS_LET_IF_SEQ, + local.hir_id, + span, + "`if _ { .. } else { .. }` is an expression", + |diag| { + diag.span_suggestion(span, "it is more idiomatic to write", sug, applicability); + if !mutability.is_empty() { + diag.note("you might not need `mut` at all"); + } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 99cafc7fc6d8..4542105d3277 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -401,7 +401,9 @@ use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::Lint; +use rustc_data_structures::sync; +use rustc_lint::{EarlyLintPass, LateLintPass, Lint}; +use rustc_middle::ty::TyCtxt; use utils::attr_collector::{AttrCollector, AttrStorage}; pub fn explain(name: &str) -> i32 { @@ -443,381 +445,410 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); - store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))); - let format_args_storage = FormatArgsStorage::default(); - let format_args = format_args_storage.clone(); - store.register_early_pass(move || { - Box::new(utils::format_args_collector::FormatArgsCollector::new( - format_args.clone(), - )) - }); - let attr_storage = AttrStorage::default(); - let attrs = attr_storage.clone(); - store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone()))); - store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); - store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); - store.register_late_pass(|_| Box::new(utils::author::Author)); - store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))); - store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); - store.register_late_pass(move |_| Box::new(types::Types::new(conf))); - store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); - store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); - store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf))); - store.register_late_pass(|_| Box::new(ptr::Ptr)); - store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison)); - store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|_| Box::new(misc::LintPass)); - store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); - store.register_late_pass(|_| Box::new(mut_mut::MutMut::default())); - store.register_late_pass(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)); - store.register_late_pass(|_| Box::>::default()); - store.register_late_pass(move |_| Box::new(len_zero::LenZero::new(conf))); - store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); - store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions)); - store.register_late_pass(|_| Box::new(unicode::Unicode)); - store.register_late_pass(|_| Box::new(uninit_vec::UninitVec)); - store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); - store.register_late_pass(|_| Box::new(strings::StringAdd)); - store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); - store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); - store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(move |_| { - Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( - conf, - )) - }); - store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); - store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); - store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(conf))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(conf))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(conf))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(conf))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))); - store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(conf))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))); - store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); - store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); - store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))); - store.register_late_pass(|_| Box::::default()); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))); - store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); - store.register_late_pass(|_| Box::new(entry::HashMapPass)); - store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); - store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv)); - store.register_late_pass(|_| Box::new(mutex_atomic::Mutex)); - store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate)); - store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)); - store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(conf))); - store.register_late_pass(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))); - store.register_late_pass(move |_| Box::new(escape::BoxedLocal::new(conf))); - store.register_late_pass(move |_| Box::new(vec::UselessVec::new(conf))); - store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))); - store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); - store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))); - store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); - store.register_late_pass(|_| Box::new(empty_enums::EmptyEnums)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))); - store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); - store.register_late_pass(|_| Box::new(swap::Swap)); - store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))); - store.register_early_pass(|| Box::new(functions::EarlyFunctions)); - store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf))); - store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf))); - store.register_early_pass(move || Box::new(doc::Documentation::new(conf))); - store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); - store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); - store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); - store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); - store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); - store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); - store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk)); - store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); - store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); - store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); - store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); - store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); - store.register_late_pass(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))); - store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); - store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); - store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); - store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); - store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(conf))); - store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed)); - store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); - store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); - store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit)); - store.register_late_pass(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))); - store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); - store.register_late_pass(move |_| Box::new(unwrap::Unwrap::new(conf))); - store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); - store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); - store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); - store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); - store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))); - store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); - store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); - store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); - store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))); - store.register_late_pass(|_| Box::new(reference::DerefAddrOf)); - store.register_early_pass(|| Box::new(double_parens::DoubleParens)); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))); - store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)); - store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); - store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne)); - store.register_early_pass(|| Box::new(formatting::Formatting)); - store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints)); - store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall)); - store.register_early_pass(|| Box::new(unused_unit::UnusedUnit)); - store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit)); - store.register_late_pass(|_| Box::new(returns::Return)); - store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))); - store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements)); - store.register_early_pass(|| Box::new(precedence::Precedence)); - store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)); - store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue)); - store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); - store.register_late_pass(|_| Box::new(create_dir::CreateDir)); - store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); - store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))); - store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))); - store.register_late_pass(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))); - store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); - store.register_late_pass(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); - store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); - store.register_late_pass(|_| Box::new(exit::Exit)); - store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))); - store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); - store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); - store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); - store.register_late_pass(|_| Box::new(as_conversions::AsConversions)); - store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::::default()); - store.register_late_pass(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))); - store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); - store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::>::default()); - store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); - store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); - store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(conf))); - store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); - store.register_late_pass(|_| Box::new(if_not_else::IfNotElse)); - store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality)); - store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn)); - store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn)); - store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))); - store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); - store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - let attrs = attr_storage.clone(); - store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))); - store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))); - store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); - store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); - store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop)); - store.register_late_pass(|_| Box::new(strings::StrToString)); - store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); - store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); - store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle::default())); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); - store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); - store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))); - store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); - store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); - store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); - store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); - store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))); - store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone()))); - store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); - store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); - store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); - store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); - store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); - store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(conf))); - store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(conf))); - let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); - store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); - store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); - store.register_late_pass(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())); - store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); - store.register_early_pass(|| Box::new(pub_use::PubUse)); - store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); - store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))); - store.register_early_pass(move || Box::new(large_include_file::LargeIncludeFile::new(conf))); - store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); - store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); - store.register_early_pass(|| Box::::default()); - store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))); - store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); - store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); - store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); - store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); - store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); - store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); - store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))); - store.register_late_pass(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))); - store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); - store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); - store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); - store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); - store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)); - store.register_late_pass(|_| Box::new(box_default::BoxDefault)); - store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); - store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); - store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); - store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); - store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))); - store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))); - store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); - store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); - store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); - store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))); - store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); - store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); - store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); - store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); - store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); - store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped)); - store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); - store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); - store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); - store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); - store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))); - store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); - store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); - store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); - store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); - store.register_early_pass(|| Box::new(needless_else::NeedlessElse)); - store.register_late_pass(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)); - store.register_late_pass(|_| Box::new(endian_bytes::EndianBytes)); - store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); - store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); - store.register_late_pass(|_| Box::new(needless_ifs::NeedlessIfs)); - store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))); - store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); - store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); - store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); - store.register_late_pass(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))); - store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); - store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); - store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); - store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); - store.register_early_pass(|| Box::new(visibility::Visibility)); - store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))); - store.register_late_pass(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))); - store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); - store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); - store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))); - store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals)); - store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)); - store.register_late_pass(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)); - store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); - store.register_late_pass(move |_| { - Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( - conf, - )) - }); - store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))); - store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); - store.register_late_pass(|_| Box::>::default()); - store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); - store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); - store.register_late_pass(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))); - store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); - store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))); - store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))); - store.register_late_pass(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))); - store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); - store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); - store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); - store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); - store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); - store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); - store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); - store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); - store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); - store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); - store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); - store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); - store.register_late_pass(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))); - store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); - store.register_late_pass(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)); - store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); - store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); - store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); - store.register_early_pass(|| Box::new(empty_line_after::EmptyLineAfter::new())); - store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); - store.register_late_pass(|_| Box::new(useless_concat::UselessConcat)); - store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); - store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))); - store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap)); - store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)); - store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); - store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); - store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); - store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); - store.register_late_pass(|_| Box::new(volatile_composites::VolatileComposites)); - store.register_late_pass(|_| Box::new(replace_box::ReplaceBox)); - // add lints here, do not remove this comment, it's used in `new_lint` + let early_lints: [Box Box + sync::DynSend + sync::DynSync>; _] = [ + { + let format_args = format_args_storage.clone(); + Box::new(move || { + Box::new(utils::format_args_collector::FormatArgsCollector::new( + format_args.clone(), + )) + }) + }, + { + let attrs = attr_storage.clone(); + Box::new(move || Box::new(AttrCollector::new(attrs.clone()))) + }, + Box::new(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf))), + Box::new(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)), + Box::new(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))), + Box::new(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))), + Box::new(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))), + Box::new(|| Box::new(functions::EarlyFunctions)), + Box::new(move || Box::new(doc::Documentation::new(conf))), + Box::new(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)), + Box::new(|| Box::new(double_parens::DoubleParens)), + Box::new(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval)), + Box::new(|| Box::new(else_if_without_else::ElseIfWithoutElse)), + Box::new(|| Box::new(int_plus_one::IntPlusOne)), + Box::new(|| Box::new(formatting::Formatting)), + Box::new(|| Box::new(misc_early::MiscEarlyLints)), + Box::new(|| Box::new(unused_unit::UnusedUnit)), + Box::new(|| Box::new(precedence::Precedence)), + Box::new(|| Box::new(redundant_else::RedundantElse)), + Box::new(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)), + Box::new(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))), + Box::new(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))), + Box::new(|| Box::new(tabs_in_doc_comments::TabsInDocComments)), + Box::new(|| Box::::default()), + Box::new(|| Box::new(option_env_unwrap::OptionEnvUnwrap)), + Box::new(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))), + Box::new(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))), + Box::new(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)), + Box::new(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)), + Box::new(move || Box::new(module_style::ModStyle::default())), + Box::new(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))), + Box::new(|| Box::new(octal_escapes::OctalEscapes)), + Box::new(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)), + Box::new(|| Box::new(crate_in_macro_def::CrateInMacroDef)), + Box::new(|| Box::new(pub_use::PubUse)), + Box::new(move || Box::new(large_include_file::LargeIncludeFile::new(conf))), + Box::new(|| Box::::default()), + Box::new(|| Box::new(unused_rounding::UnusedRounding)), + Box::new(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))), + Box::new(|| Box::new(multi_assignments::MultiAssignments)), + Box::new(|| Box::new(partial_pub_fields::PartialPubFields)), + Box::new(|| Box::new(let_with_type_underscore::UnderscoreTyped)), + Box::new(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))), + Box::new(|| Box::new(ref_patterns::RefPatterns)), + Box::new(|| Box::new(needless_else::NeedlessElse)), + Box::new(move || Box::new(raw_strings::RawStrings::new(conf))), + Box::new(|| Box::new(visibility::Visibility)), + Box::new(|| Box::new(multiple_bound_locations::MultipleBoundLocations)), + Box::new(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)), + Box::new(|| Box::new(byte_char_slices::ByteCharSlice)), + Box::new(|| Box::new(cfg_not_test::CfgNotTest)), + Box::new(|| Box::new(empty_line_after::EmptyLineAfter::new())), + // add early passes here, used by `cargo dev new_lint` + ]; + store.early_passes.extend(early_lints); + + #[expect(clippy::type_complexity)] + let late_lints: [Box< + dyn for<'tcx> Fn(TyCtxt<'tcx>) -> Box + 'tcx> + sync::DynSend + sync::DynSync, + >; _] = [ + Box::new(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))), + Box::new(|_| Box::new(utils::dump_hir::DumpHir)), + Box::new(|_| Box::new(utils::author::Author)), + Box::new(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))), + Box::new(|_| Box::new(serde_api::SerdeApi)), + Box::new(move |_| Box::new(types::Types::new(conf))), + Box::new(move |_| Box::new(booleans::NonminimalBool::new(conf))), + Box::new(|_| Box::new(enum_clike::UnportableVariant)), + Box::new(move |_| Box::new(float_literal::FloatLiteral::new(conf))), + Box::new(|_| Box::new(ptr::Ptr)), + Box::new(|_| Box::new(needless_bool::NeedlessBool)), + Box::new(|_| Box::new(bool_comparison::BoolComparison)), + Box::new(|_| Box::new(needless_for_each::NeedlessForEach)), + Box::new(|_| Box::new(misc::LintPass)), + Box::new(|_| Box::new(eta_reduction::EtaReduction)), + Box::new(|_| Box::new(mut_mut::MutMut::default())), + Box::new(|_| Box::new(unnecessary_mut_passed::UnnecessaryMutPassed)), + Box::new(|_| Box::>::default()), + Box::new(move |_| Box::new(len_zero::LenZero::new(conf))), + Box::new(move |_| Box::new(attrs::Attributes::new(conf))), + Box::new(|_| Box::new(blocks_in_conditions::BlocksInConditions)), + Box::new(|_| Box::new(unicode::Unicode)), + Box::new(|_| Box::new(uninit_vec::UninitVec)), + Box::new(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)), + Box::new(|_| Box::new(strings::StringAdd)), + Box::new(|_| Box::new(implicit_return::ImplicitReturn)), + Box::new(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))), + Box::new(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)), + Box::new(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)), + Box::new(move |_| Box::new(approx_const::ApproxConstant::new(conf))), + Box::new(move |_| Box::new(matches::Matches::new(conf))), + Box::new(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))), + Box::new(move |_| Box::new(manual_strip::ManualStrip::new(conf))), + Box::new(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))), + Box::new(move |_| Box::new(mem_replace::MemReplace::new(conf))), + Box::new(move |_| Box::new(ranges::Ranges::new(conf))), + Box::new(move |_| Box::new(from_over_into::FromOverInto::new(conf))), + Box::new(move |_| Box::new(use_self::UseSelf::new(conf))), + Box::new(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))), + Box::new(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)), + Box::new(move |_| Box::new(casts::Casts::new(conf))), + Box::new(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)), + Box::new(|_| Box::new(same_name_method::SameNameMethod)), + Box::new(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))), + Box::new(|_| Box::::default()), + Box::new(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }), + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))) + }, + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(unit_types::UnitTypes::new(format_args.clone()))) + }, + Box::new(move |_| Box::new(loops::Loops::new(conf))), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(lifetimes::Lifetimes::new(conf))), + Box::new(|_| Box::new(entry::HashMapPass)), + Box::new(|_| Box::new(minmax::MinMaxPass)), + Box::new(|_| Box::new(zero_div_zero::ZeroDiv)), + Box::new(|_| Box::new(mutex_atomic::Mutex)), + Box::new(|_| Box::new(needless_update::NeedlessUpdate)), + Box::new(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef)), + Box::new(|_| Box::new(borrow_deref_ref::BorrowDerefRef)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(temporary_assignment::TemporaryAssignment)), + Box::new(move |_| Box::new(transmute::Transmute::new(conf))), + Box::new(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))), + Box::new(move |_| Box::new(escape::BoxedLocal::new(conf))), + Box::new(move |_| Box::new(vec::UselessVec::new(conf))), + Box::new(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))), + Box::new(|_| Box::new(strings::StringLitAsBytes)), + Box::new(|_| Box::new(derive::Derive)), + Box::new(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))), + Box::new(|_| Box::new(drop_forget_ref::DropForgetRef)), + Box::new(|_| Box::new(empty_enums::EmptyEnums)), + Box::new(|_| Box::::default()), + Box::new(move |tcx| Box::new(ifs::CopyAndPaste::new(tcx, conf))), + Box::new(|_| Box::new(copy_iterator::CopyIterator)), + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(format::UselessFormat::new(format_args.clone()))) + }, + Box::new(|_| Box::new(swap::Swap)), + Box::new(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))), + Box::new(move |tcx| Box::new(functions::Functions::new(tcx, conf))), + Box::new(move |_| Box::new(doc::Documentation::new(conf))), + Box::new(|_| Box::new(neg_multiply::NegMultiply)), + Box::new(|_| Box::new(let_if_seq::LetIfSeq)), + Box::new(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)), + Box::new(move |_| Box::new(missing_doc::MissingDoc::new(conf))), + Box::new(|_| Box::new(missing_inline::MissingInline)), + Box::new(move |_| Box::new(exhaustive_items::ExhaustiveItems)), + Box::new(|_| Box::new(unused_result_ok::UnusedResultOk)), + Box::new(|_| Box::new(match_result_ok::MatchResultOk)), + Box::new(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)), + Box::new(|_| Box::new(unused_io_amount::UnusedIoAmount)), + Box::new(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))), + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))) + }, + Box::new(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)), + Box::new(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))), + Box::new(|_| Box::new(ref_option_ref::RefOptionRef)), + Box::new(|_| Box::new(infinite_iter::InfiniteIter)), + Box::new(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(implicit_hasher::ImplicitHasher)), + Box::new(|_| Box::new(fallible_impl_from::FallibleImplFrom)), + Box::new(move |_| Box::new(question_mark::QuestionMark::new(conf))), + Box::new(|_| Box::new(question_mark_used::QuestionMarkUsed)), + Box::new(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)), + Box::new(|_| Box::new(map_unit_fn::MapUnit)), + Box::new(move |_| Box::new(inherent_impl::MultipleInherentImpl::new(conf))), + Box::new(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)), + Box::new(move |_| Box::new(unwrap::Unwrap::new(conf))), + Box::new(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))), + Box::new(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))), + Box::new(|_| Box::new(redundant_clone::RedundantClone)), + Box::new(|_| Box::new(slow_vector_initialization::SlowVectorInit)), + Box::new(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))), + Box::new(|_| Box::new(assertions_on_constants::AssertionsOnConstants::new(conf))), + Box::new(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)), + Box::new(|_| Box::new(inherent_to_string::InherentToString)), + Box::new(move |_| Box::new(trait_bounds::TraitBounds::new(conf))), + Box::new(|_| Box::new(comparison_chain::ComparisonChain)), + Box::new(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))), + Box::new(|_| Box::new(reference::DerefAddrOf)), + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone()))) + }, + Box::new(|_| Box::new(redundant_closure_call::RedundantClosureCall)), + Box::new(|_| Box::new(unused_unit::UnusedUnit)), + Box::new(|_| Box::new(returns::Return)), + Box::new(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf))), + Box::new(|_| Box::new(items_after_statements::ItemsAfterStatements)), + Box::new(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals)), + Box::new(|_| Box::new(needless_continue::NeedlessContinue)), + Box::new(|_| Box::new(create_dir::CreateDir)), + Box::new(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))), + Box::new(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(unused_self::UnusedSelf::new(conf))), + Box::new(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)), + Box::new(|_| Box::new(exit::Exit)), + Box::new(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf))), + Box::new(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))), + Box::new(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))), + Box::new(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)), + Box::new(|_| Box::new(as_conversions::AsConversions)), + Box::new(|_| Box::new(let_underscore::LetUnderscore)), + Box::new(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))), + Box::new(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))), + Box::new(|_| Box::::default()), + Box::new(|_| Box::>::default()), + Box::new(|_| Box::new(option_if_let_else::OptionIfLetElse)), + Box::new(|_| Box::new(future_not_send::FutureNotSend)), + Box::new(move |_| Box::new(large_futures::LargeFuture::new(conf))), + Box::new(|_| Box::new(if_let_mutex::IfLetMutex)), + Box::new(|_| Box::new(if_not_else::IfNotElse)), + Box::new(|_| Box::new(equatable_if_let::PatternEquality)), + Box::new(|_| Box::new(manual_async_fn::ManualAsyncFn)), + Box::new(|_| Box::new(panic_in_result_fn::PanicInResultFn)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)), + Box::new(|_| Box::new(async_yields_async::AsyncYieldsAsync)), + { + let attrs = attr_storage.clone(); + Box::new(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone()))) + }, + Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))), + Box::new(|_| Box::new(empty_drop::EmptyDrop)), + Box::new(|_| Box::new(strings::StrToString)), + Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(redundant_slicing::RedundantSlicing)), + Box::new(|_| Box::new(from_str_radix_10::FromStrRadix10)), + Box::new(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))), + Box::new(|_| Box::new(bool_assert_comparison::BoolAssertComparison)), + Box::new(|_| Box::::default()), + Box::new(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))), + Box::new(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))), + Box::new(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)), + Box::new(move |_| Box::new(self_named_constructors::SelfNamedConstructors)), + Box::new(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)), + Box::new(move |_| Box::new(manual_assert::ManualAssert)), + Box::new(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))), + Box::new(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))), + { + let format_args = format_args_storage.clone(); + Box::new(move |tcx| Box::new(format_args::FormatArgs::new(tcx, conf, format_args.clone()))) + }, + Box::new(|_| Box::new(trailing_empty_array::TrailingEmptyArray)), + Box::new(|_| Box::new(needless_late_init::NeedlessLateInit)), + Box::new(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)), + Box::new(|_| Box::new(init_numbered_fields::NumberedFields)), + Box::new(move |_| Box::new(manual_bits::ManualBits::new(conf))), + Box::new(|_| Box::new(default_union_representation::DefaultUnionRepresentation)), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(dbg_macro::DbgMacro::new(conf))), + { + let format_args = format_args_storage.clone(); + Box::new(move |_| Box::new(write::Write::new(conf, format_args.clone()))) + }, + Box::new(move |_| Box::new(cargo::Cargo::new(conf))), + Box::new(|_| Box::new(empty_with_brackets::EmptyWithBrackets::default())), + Box::new(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)), + Box::new(|_| Box::new(format_push_string::FormatPushString)), + Box::new(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))), + Box::new(|_| Box::new(strings::TrimSplitWhitespace)), + Box::new(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)), + Box::new(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)), + Box::new(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)), + Box::new(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)), + Box::new(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)), + Box::new(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))), + Box::new(move |_| Box::new(manual_retain::ManualRetain::new(conf))), + Box::new(move |_| Box::new(manual_rotate::ManualRotate)), + Box::new(move |_| Box::new(operators::Operators::new(conf))), + Box::new(move |_| Box::new(std_instead_of_core::StdReexports::new(conf))), + Box::new(move |_| Box::new(time_subtraction::UncheckedTimeSubtraction::new(conf))), + Box::new(|_| Box::new(partialeq_to_none::PartialeqToNone)), + Box::new(move |_| Box::new(manual_abs_diff::ManualAbsDiff::new(conf))), + Box::new(move |_| Box::new(manual_clamp::ManualClamp::new(conf))), + Box::new(|_| Box::new(manual_string_new::ManualStringNew)), + Box::new(|_| Box::new(unused_peekable::UnusedPeekable)), + Box::new(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf)), + Box::new(|_| Box::new(box_default::BoxDefault)), + Box::new(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)), + Box::new(|_| Box::new(missing_trait_methods::MissingTraitMethods)), + Box::new(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)), + Box::new(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)), + Box::new(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))), + Box::new(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))), + Box::new(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)), + Box::new(|_| Box::new(size_of_ref::SizeOfRef)), + Box::new(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)), + Box::new(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))), + Box::new(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)), + Box::new(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)), + Box::new(|_| Box::new(missing_assert_message::MissingAssertMessage)), + Box::new(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)), + Box::new(|_| Box::new(redundant_async_block::RedundantAsyncBlock)), + Box::new(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))), + Box::new(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)), + Box::new(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))), + Box::new(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)), + Box::new(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation::new(conf))), + Box::new(|_| Box::new(items_after_test_module::ItemsAfterTestModule)), + Box::new(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)), + Box::new(|_| Box::new(missing_fields_in_debug::MissingFieldsInDebug)), + Box::new(|_| Box::new(endian_bytes::EndianBytes)), + Box::new(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)), + Box::new(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)), + Box::new(|_| Box::new(needless_ifs::NeedlessIfs)), + Box::new(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))), + Box::new(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))), + Box::new(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)), + Box::new(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))), + Box::new(|tcx| Box::new(non_canonical_impls::NonCanonicalImpls::new(tcx))), + Box::new(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))), + Box::new(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))), + Box::new(|_| Box::new(manual_range_patterns::ManualRangePatterns)), + Box::new(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))), + Box::new(move |_| Box::new(manual_float_methods::ManualFloatMethods::new(conf))), + Box::new(|_| Box::new(four_forward_slashes::FourForwardSlashes)), + Box::new(|_| Box::new(error_impl_error::ErrorImplError)), + Box::new(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))), + Box::new(|_| Box::new(redundant_locals::RedundantLocals)), + Box::new(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)), + Box::new(|_| Box::::default()), + Box::new(|_| Box::new(implied_bounds_in_impls::ImpliedBoundsInImpls)), + Box::new(|_| Box::new(missing_asserts_for_indexing::MissingAssertsForIndexing)), + Box::new(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)), + Box::new(move |_| { + Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( + conf, + )) + }), + Box::new(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))), + Box::new(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)), + Box::new(|_| Box::>::default()), + Box::new(|_| Box::new(iter_over_hash_type::IterOverHashType)), + Box::new(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)), + Box::new(move |_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity::new(conf))), + Box::new(|_| Box::new(uninhabited_references::UninhabitedReferences)), + Box::new(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))), + Box::new(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))), + Box::new(move |tcx| Box::new(incompatible_msrv::IncompatibleMsrv::new(tcx, conf))), + Box::new(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)), + Box::new(move |_| Box::new(assigning_clones::AssigningClones::new(conf))), + Box::new(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)), + Box::new(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))), + Box::new(move |_| Box::new(string_patterns::StringPatterns::new(conf))), + Box::new(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)), + Box::new(|_| Box::new(zombie_processes::ZombieProcesses)), + Box::new(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)), + Box::new(move |_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo::new(conf))), + Box::new(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)), + Box::new(|_| Box::new(literal_string_with_formatting_args::LiteralStringWithFormattingArg)), + Box::new(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))), + Box::new(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)), + Box::new(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)), + Box::new(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))), + Box::new(|_| Box::new(useless_concat::UselessConcat)), + Box::new(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)), + Box::new(|_| Box::::default()), + Box::new(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))), + Box::new(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf))), + Box::new(|_| Box::new(single_option_map::SingleOptionMap)), + Box::new(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix)), + Box::new(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))), + Box::new(|_| Box::new(infallible_try_from::InfallibleTryFrom)), + Box::new(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)), + Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)), + Box::new(|_| Box::new(volatile_composites::VolatileComposites)), + Box::new(|_| Box::::default()), + // add late passes here, used by `cargo dev new_lint` + ]; + store.late_passes.extend(late_lints); } diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index c38cf83f4410..d94dcfab23c7 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{higher, peel_blocks_with_stmt}; +use clippy_utils::{as_some_expr, higher, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind}; @@ -33,8 +33,7 @@ pub(super) fn check<'tcx>( && let [stmt] = block.stmts && let StmtKind::Semi(semi) = stmt.kind && let ExprKind::Ret(Some(ret_value)) = semi.kind - && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind - && ctor.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some(inner_ret) = as_some_expr(cx, ret_value) && inner_ret.res_local_id() == Some(binding_id) && !contains_return_break_continue_macro(cond) && let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr) @@ -43,7 +42,7 @@ pub(super) fn check<'tcx>( let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); // Checks if `pat` is a single reference to a binding (`&x`) let is_ref_to_binding = - matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..))); + matches!(pat.kind, PatKind::Ref(inner, _, _) if matches!(inner.kind, PatKind::Binding(..))); // If `pat` is not a binding or a reference to a binding (`x` or `&x`) // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`) if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) { diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index a064a5910ef9..21198c3c8bc2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -880,6 +880,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { missing_spin_loop::check(cx, condition, body); manual_while_let_some::check(cx, condition, body, span); } + + if let ExprKind::MethodCall(path, recv, [arg], _) = expr.kind + && matches!( + path.ident.name, + sym::all | sym::any | sym::filter_map | sym::find_map | sym::flat_map | sym::for_each | sym::map + ) + { + unused_enumerate_index::check_method(cx, expr, recv, arg); + } } } @@ -908,7 +917,7 @@ fn check_for_loop<'tcx>( same_item_push::check(cx, pat, arg, body, expr, self.msrv); manual_flatten::check(cx, pat, arg, body, span, self.msrv); manual_find::check(cx, pat, arg, body, span, expr); - unused_enumerate_index::check(cx, pat, arg, body); + unused_enumerate_index::check(cx, arg, pat, None, body); char_indices_as_byte_indices::check(cx, pat, arg, body); } diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index b893b0baad49..82ded453616d 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,42 +1,87 @@ use super::UNUSED_ENUMERATE_INDEX; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; -use clippy_utils::{pat_is_wild, sugg}; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::{expr_or_init, pat_is_wild}; use rustc_errors::Applicability; -use rustc_hir::def::DefKind; -use rustc_hir::{Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{Expr, ExprKind, Pat, PatKind, TyKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Span, SyntaxContext, sym}; -/// Checks for the `UNUSED_ENUMERATE_INDEX` lint. -/// -/// The lint is also partially implemented in `clippy_lints/src/methods/unused_enumerate_index.rs`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_>, body: &'tcx Expr<'tcx>) { - if let PatKind::Tuple([index, elem], _) = pat.kind - && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind - && let ty = cx.typeck_results().expr_ty(arg) - && pat_is_wild(cx, &index.kind, body) - && ty.is_diag_item(cx, sym::Enumerate) - && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) - && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + iter_expr: &'tcx Expr<'tcx>, + pat: &Pat<'tcx>, + ty_spans: Option<(Span, Span)>, + body: &'tcx Expr<'tcx>, +) { + if let PatKind::Tuple([idx_pat, inner_pat], _) = pat.kind + && cx.typeck_results().expr_ty(iter_expr).is_diag_item(cx, sym::Enumerate) + && pat_is_wild(cx, &idx_pat.kind, body) + && let enumerate_call = expr_or_init(cx, iter_expr) + && let ExprKind::MethodCall(_, _, [], enumerate_span) = enumerate_call.kind + && let Some(enumerate_id) = cx.typeck_results().type_dependent_def_id(enumerate_call.hir_id) + && cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_id) + && !enumerate_call.span.from_expansion() + && !pat.span.from_expansion() + && !idx_pat.span.from_expansion() + && !inner_pat.span.from_expansion() + && let Some(enumerate_range) = enumerate_span.map_range(cx, |_, text, range| { + text.get(..range.start)? + .ends_with('.') + .then_some(range.start - 1..range.end) + }) { - span_lint_and_then( + let enumerate_span = Span::new(enumerate_range.start, enumerate_range.end, SyntaxContext::root(), None); + span_lint_hir_and_then( cx, UNUSED_ENUMERATE_INDEX, - arg.span, + enumerate_call.hir_id, + enumerate_span, "you seem to use `.enumerate()` and immediately discard the index", |diag| { - let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter"); + let mut spans = Vec::with_capacity(5); + spans.push((enumerate_span, String::new())); + spans.push((pat.span.with_hi(inner_pat.span.lo()), String::new())); + spans.push((pat.span.with_lo(inner_pat.span.hi()), String::new())); + if let Some((outer, inner)) = ty_spans { + spans.push((outer.with_hi(inner.lo()), String::new())); + spans.push((outer.with_lo(inner.hi()), String::new())); + } diag.multipart_suggestion( "remove the `.enumerate()` call", - vec![ - (pat.span, snippet(cx, elem.span, "..").into_owned()), - (arg.span, base_iter.to_string()), - ], + spans, Applicability::MachineApplicable, ); }, ); } } + +pub(super) fn check_method<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, +) { + if let ExprKind::Closure(closure) = arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let [param] = body.params + && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator) + && let [input] = closure.fn_decl.inputs + && !arg.span.from_expansion() + && !input.span.from_expansion() + && !recv.span.from_expansion() + && !param.span.from_expansion() + { + let ty_spans = if let TyKind::Tup([_, inner]) = input.kind { + let Some(inner) = walk_span_to_context(inner.span, SyntaxContext::root()) else { + return; + }; + Some((input.span, inner)) + } else { + None + }; + check(cx, recv, param.pat, ty_spans, body.value); + } +} diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 3ea6ba341bed..2545f81f1afa 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -5,11 +5,11 @@ use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::is_res_used; -use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; +use clippy_utils::{as_some_pattern, get_enclosing_loop_or_multi_call_closure, higher, is_refutable}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -19,8 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) // check for `Some(..)` pattern - && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind - && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some(some_pat) = as_some_pattern(cx, let_pat) // check for call to `Iterator::next` && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind && method_name.ident.name == sym::next diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs index 4439a28763a2..25db719c8214 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -70,12 +70,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if !expr.span.from_expansion() && let Some((lhs, rhs)) = unexpanded_binop_operands(expr, BinOpKind::Eq) { - if let Some(a) = count_ones_receiver(cx, lhs) - && is_integer_literal(rhs, 1) + if is_integer_literal(rhs, 1) + && let Some(a) = count_ones_receiver(cx, lhs) { self.build_sugg(cx, expr, a); - } else if let Some(a) = count_ones_receiver(cx, rhs) - && is_integer_literal(lhs, 1) + } else if is_integer_literal(lhs, 1) + && let Some(a) = count_ones_receiver(cx, rhs) { self.build_sugg(cx, expr, a); } else if is_integer_literal(rhs, 0) diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs index dce0d105f4c5..5cf90eecaa97 100644 --- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs +++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs @@ -3,10 +3,9 @@ use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{as_some_pattern, is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, QPath, is_range_literal}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; @@ -154,10 +153,8 @@ fn check_as_ref(cx: &LateContext<'_>, expr: &Expr<'_>, span: Span, msrv: Msrv) { } fn extract_ident_from_some_pat(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option { - if let PatKind::TupleStruct(QPath::Resolved(None, path), [binding], _) = pat.kind - && let Res::Def(DefKind::Ctor(..), def_id) = path.res + if let Some([binding]) = as_some_pattern(cx, pat) && let PatKind::Binding(_mode, _hir_id, ident, _inner_pat) = binding.kind - && clippy_utils::is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) { Some(ident.name) } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index 674f0da818f5..6870c9819fc0 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -157,7 +157,7 @@ fn check_iter( ), ); }, - hir::PatKind::Ref(pat, _) => make_span_lint_and_sugg( + hir::PatKind::Ref(pat, _, _) => make_span_lint_and_sugg( cx, parent_expr_span, format!( @@ -196,7 +196,7 @@ fn check_to_owned( && let filter_body = cx.tcx.hir_body(closure.body) && let [filter_params] = filter_body.params && msrv.meets(cx, msrvs::STRING_RETAIN) - && let hir::PatKind::Ref(pat, _) = filter_params.pat.kind + && let hir::PatKind::Ref(pat, _, _) = filter_params.pat.kind { make_span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/match_result_ok.rs b/src/tools/clippy/clippy_lints/src/match_result_ok.rs index fb83f7cf65dd..1ebbd209ae52 100644 --- a/src/tools/clippy/clippy_lints/src/match_result_ok.rs +++ b/src/tools/clippy/clippy_lints/src/match_result_ok.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet_with_context; -use clippy_utils::{higher, sym}; +use clippy_utils::{as_some_pattern, higher, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -55,10 +55,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }; if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result.ok(, _) - && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation && ok_path.ident.name == sym::ok && cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - && cx.qpath_res(pat_path, let_pat.hir_id).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some([ok_pat]) = as_some_pattern(cx, let_pat) //get operation && let ctxt = expr.span.ctxt() && let_expr.span.ctxt() == ctxt && let_pat.span.ctxt() == ctxt diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index d7224052ebc5..da68f8421c16 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -1,8 +1,9 @@ +use clippy_utils::as_some_expr; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; use clippy_utils::visitors::contains_unsafe_block; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -52,21 +53,19 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr) } -// function called for each expression: +/// Checks whether resolves to `Some(target)` +// NOTE: called for each expression: // Some(x) => if { // // } else { // // } -// Returns true if resolves to `Some(x)`, `false` otherwise fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) // there can be not statements in the block as they would be removed when switching to `.filter` - && let ExprKind::Call(callee, [arg]) = inner_expr.kind + && let Some(arg) = as_some_expr(cx, inner_expr) { - return ctxt == expr.span.ctxt() - && callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) - && arg.res_local_id() == Some(target); + return ctxt == expr.span.ctxt() && arg.res_local_id() == Some(target); } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index c9293412fba8..c35c3d1f62e6 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; -use clippy_utils::{get_parent_expr, peel_blocks, span_contains_comment}; +use clippy_utils::{as_some_expr, get_parent_expr, is_none_expr, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; +use rustc_hir::LangItem::ResultErr; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::{LateContext, LintContext}; @@ -78,7 +78,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool .is_lang_item(cx, ResultErr) == must_match_err }, - PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _, _) => { is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) }, _ => false, @@ -106,8 +106,7 @@ fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &' /// Check if `expr` contains `Some(ident)`, possibly as a block fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { - if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind - && body_callee.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + if let Some(body_arg) = as_some_expr(cx, peel_blocks(expr)) && cx.typeck_results().expr_ty(body_arg) == ty && let ExprKind::Path(QPath::Resolved( _, @@ -124,7 +123,7 @@ fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, t /// Check if `expr` is `None`, possibly as a block fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) + is_none_expr(cx, peel_blocks(expr)) } /// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 235cb9e4ecce..6a755fac45fe 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -1,18 +1,17 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; -use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ - CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, peel_blocks, - peel_hir_expr_refs, peel_hir_expr_while, + CaptureKind, as_some_pattern, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, + is_none_expr, is_none_pattern, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def::Res; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -44,16 +43,16 @@ pub(super) fn check_with<'tcx, F>( try_parse_pattern(cx, then_pat, expr_ctxt), else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => { (else_body, pattern, ref_count, true) }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_arm_body(cx, then_body) => { (else_body, pattern, ref_count, false) }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_arm_body(cx, else_body) => { (then_body, pattern, ref_count, true) }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_arm_body(cx, else_body) => { (then_body, pattern, ref_count, false) }, _ => return None, @@ -254,24 +253,10 @@ fn f<'tcx>( ) -> Option> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - hir_id, - .. - }) if cx - .qpath_res(qpath, *hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone) => - { - Some(OptionPat::None) - }, - PatKind::TupleStruct(ref qpath, [pattern], _) - if cx - .qpath_res(qpath, pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionSome) - && pat.span.ctxt() == ctxt => + PatKind::Ref(pat, _, _) => f(cx, pat, ref_count + 1, ctxt), + _ if is_none_pattern(cx, pat) => Some(OptionPat::None), + _ if let Some([pattern]) = as_some_pattern(cx, pat) + && pat.span.ctxt() == ctxt => { Some(OptionPat::Some { pattern, ref_count }) }, @@ -281,7 +266,7 @@ fn f<'tcx>( f(cx, pat, 0, ctxt) } -// Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - peel_blocks(expr).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) +/// Checks for the `None` value, possibly in a block. +fn is_none_arm_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_none_expr(cx, peel_blocks(expr)) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 2ca656edc66e..795355f25f9e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::option_arg_ty; -use clippy_utils::{is_none_arm, peel_blocks}; +use clippy_utils::{as_some_expr, as_some_pattern, is_none_arm, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, Mutability, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -82,14 +81,9 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`) fn as_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option { - if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind - && cx - .qpath_res(qpath, arm.pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, LangItem::OptionSome) + if let Some([first_pat, ..]) = as_some_pattern(cx, arm.pat) && let PatKind::Binding(BindingMode(ByRef::Yes(_, mutabl), _), .., ident, _) = first_pat.kind - && let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind - && e.res(cx).ctor_parent(cx).is_lang_item(cx, LangItem::OptionSome) + && let Some(arg) = as_some_expr(cx, peel_blocks(arm.body)) && let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind && path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name diff --git a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs index 5934ec409935..042817f5cdb8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs @@ -50,7 +50,7 @@ pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, scrutinee: &Expr<'_>, pats: } let remaining_suggs = pats.filter_map(|pat| { - if let PatKind::Ref(refp, _) = pat.kind { + if let PatKind::Ref(refp, _, _) = pat.kind { Some((pat.span, snippet(cx, refp.span, "..").to_string())) } else { None diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index be914429edb4..c20217563d62 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -264,7 +264,7 @@ fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Deref(pat) - | PatKind::Ref(pat, _) + | PatKind::Ref(pat, _, _) | PatKind::Guard(pat, _) => Self::from_pat(cx, arena, pat), PatKind::Never => Self::Never, PatKind::Struct(ref path, fields, _) => { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 82d5310663ee..e40e21c490f3 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_path, walk_stmt}; -use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Node, PatKind, Path, Stmt, StmtKind}; +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, PatKind, Path, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::{Span, Symbol}; @@ -307,26 +307,6 @@ fn expr_in_nested_block(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { false } -fn expr_must_have_curlies(cx: &LateContext<'_>, match_expr: &Expr<'_>) -> bool { - let parent = cx.tcx.parent_hir_node(match_expr.hir_id); - if let Node::Expr(Expr { - kind: ExprKind::Closure(..) | ExprKind::Binary(..), - .. - }) - | Node::AnonConst(..) = parent - { - return true; - } - - if let Node::Arm(arm) = &cx.tcx.parent_hir_node(match_expr.hir_id) - && let ExprKind::Match(..) = arm.body.kind - { - return true; - } - - false -} - fn indent_of_nth_line(snippet: &str, nth: usize) -> Option { snippet .lines() @@ -379,14 +359,47 @@ fn sugg_with_curlies<'a>( let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); - if !expr_in_nested_block(cx, match_expr) - && ((needs_var_binding && is_var_binding_used_later) || expr_must_have_curlies(cx, match_expr)) - { + let mut add_curlies = || { cbrace_end = format!("\n{indent}}}"); // Fix body indent due to the closure indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); cbrace_start = format!("{{\n{indent}"); snippet_body = reindent_snippet_if_in_block(&snippet_body, !assignment_str.is_empty()); + }; + + if !expr_in_nested_block(cx, match_expr) { + let mut parent = cx.tcx.parent_hir_node(match_expr.hir_id); + if let Node::Expr(Expr { + kind: ExprKind::Assign(..), + hir_id, + .. + }) = parent + { + parent = cx.tcx.parent_hir_node(*hir_id); + } + if let Node::Stmt(stmt) = parent { + parent = cx.tcx.parent_hir_node(stmt.hir_id); + } + + match parent { + Node::Block(..) + | Node::Expr(Expr { + kind: ExprKind::Block(..) | ExprKind::ConstBlock(..), + .. + }) => { + if needs_var_binding && is_var_binding_used_later { + add_curlies(); + } + }, + Node::Expr(..) + | Node::AnonConst(..) + | Node::Item(Item { + kind: ItemKind::Const(..), + .. + }) => add_curlies(), + Node::Arm(arm) if let ExprKind::Match(..) = arm.body.kind => add_curlies(), + _ => {}, + } } format!("{cbrace_start}{scrutinee};\n{indent}{assignment_str}{snippet_body}{cbrace_end}") diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 7a1dd94567b1..757ecf75ed45 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: && !pat_contains_disallowed_or(cx, arm.pat, msrv) { let pat_span = match (arm.pat.kind, binding.byref_ident) { - (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(pat, _, _), Some(_)) => pat.span, (PatKind::Ref(..), None) | (_, Some(_)) => continue, _ => arm.pat.span, }; @@ -49,7 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: && !pat_contains_disallowed_or(cx, let_expr.pat, msrv) { let pat_span = match (let_expr.pat.kind, binding.byref_ident) { - (PatKind::Ref(pat, _), Some(_)) => pat.span, + (PatKind::Ref(pat, _, _), Some(_)) => pat.span, (PatKind::Ref(..), None) | (_, Some(_)) => continue, _ => let_expr.pat.span, }; diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index a0f88cf911d8..bc3783750e5c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -186,7 +186,7 @@ fn find_method_sugg_for_if_let<'tcx>( // also look inside refs // if we have &None for example, peel it so we can detect "if let None = x" let check_pat = match let_pat.kind { - PatKind::Ref(inner, _mutability) => inner, + PatKind::Ref(inner, _pinnedness, _mutability) => inner, _ => let_pat, }; let op_ty = cx.typeck_results().expr_ty(let_expr); diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 81fecc87256c..bac35e7f8c70 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; -use clippy_utils::{get_attr, is_lint_allowed, sym}; +use clippy_utils::{get_builtin_attr, is_lint_allowed, sym}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; @@ -183,7 +183,7 @@ fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool { fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() - && get_attr( + && get_builtin_attr( self.cx.sess(), self.cx.tcx.get_all_attrs(adt.did()), sym::has_significant_drop, diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 44c4d7a31ff3..8642c7e349b1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -373,7 +373,10 @@ fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { }, // Patterns for things which can only contain a single sub-pattern. - PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => { + PatKind::Binding(_, _, _, Some(pat)) + | PatKind::Ref(pat, _, _) + | PatKind::Box(pat) + | PatKind::Deref(pat) => { self.add_pat(cx, pat) }, PatKind::Tuple([sub_pat], pos) diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index ac3cbaec55f3..0f32f89666a0 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,13 +1,14 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::{MaybeDef, MaybeQPath}; +use clippy_utils::res::MaybeDef; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_expr_used_or_unified, peel_ref_operators, std_or_core}; +use clippy_utils::{ + as_some_expr, is_default_equivalent, is_expr_used_or_unified, is_none_expr, peel_ref_operators, std_or_core, +}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -128,7 +129,7 @@ [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_OPTION_WITH_SOME, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]); fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) -> bool { - if src.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { + if is_none_expr(cx, src) { // Since this is a late pass (already type-checked), // and we already know that the second argument is an // `Option`, we do not need to check the first @@ -161,8 +162,7 @@ fn check_replace_option_with_some( expr_span: Span, msrv: Msrv, ) -> bool { - if let ExprKind::Call(src_func, [src_arg]) = src.kind - && src_func.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + if let Some(src_arg) = as_some_expr(cx, src) && msrv.meets(cx, msrvs::OPTION_REPLACE) { // We do not have to check for a `const` context here, because `core::mem::replace()` and diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index 6e9aebcf18ae..4353f6302c4b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -1,7 +1,6 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::res::MaybeDef; use clippy_utils::ty::has_debug_impl; use rustc_errors::Applicability; use rustc_lint::LateContext; @@ -17,12 +16,10 @@ pub(super) fn check( err_span: Span, msrv: Msrv, ) { - if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - // Grabs the `Result` type - && let result_type = cx.typeck_results().expr_ty(recv) - // Tests if the T type in a `Result` is not None - && let Some(data_type) = get_data_type(cx, result_type) - // Tests if the T type in a `Result` implements debug + let result_ty = cx.typeck_results().expr_ty(recv); + // Grabs the `Result` type + if let Some(data_type) = get_data_type(cx, result_ty) + // Tests if the T type in a `Result` implements Debug && has_debug_impl(cx, data_type) && msrv.meets(cx, msrvs::EXPECT_ERR) { @@ -41,7 +38,7 @@ pub(super) fn check( /// Given a `Result` type, return its data (`T`). fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().next(), + ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().next(), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 26b19848fe1b..7b10c37de42d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -404,7 +404,7 @@ fn is_find_or_filter<'a>( && let filter_body = cx.tcx.hir_body(filter_body_id) && let [filter_param] = filter_body.params // optional ref pattern: `filter(|&x| ..)` - && let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind { + && let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _, _) = filter_param.pat.kind { (ref_pat, true) } else { (filter_param.pat, false) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index 8d95b70c6a4b..aaface3aa971 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -50,7 +50,7 @@ fn is_method( fn pat_is_recv(ident: Ident, param: &hir::Pat<'_>) -> bool { match param.kind { hir::PatKind::Binding(_, _, other, _) => ident == other, - hir::PatKind::Ref(pat, _) => pat_is_recv(ident, pat), + hir::PatKind::Ref(pat, _, _) => pat_is_recv(ident, pat), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 8183c30f8c56..cdef98be14af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -1,13 +1,11 @@ use std::iter::once; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use clippy_utils::ty::{ExprFnSig, expr_sig, ty_sig}; -use clippy_utils::{get_expr_use_or_unification_node, std_or_core, sym}; +use clippy_utils::{as_some_expr, get_expr_use_or_unification_node, is_none_expr, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::hir_id::HirId; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; @@ -68,15 +66,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), - ExprKind::Path(ref p) - if cx - .qpath_res(p, recv.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone) => - { - None - }, - ExprKind::Call(f, [arg]) if f.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) => Some(arg), + _ if is_none_expr(cx, recv) => None, + _ if let Some(arg) = as_some_expr(cx, recv) => Some(arg), _ => return, }; let iter_type = match method_name { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index d43dc23a86b2..e3bcca64e923 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -81,7 +81,8 @@ pub(super) fn check<'tcx>( } match it.kind { - PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => { + PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) + | PatKind::Ref(_, _, Mutability::Mut) => { to_be_discarded = true; false }, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 1bc29c9c1dd1..a1aac96ccf86 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; -use rustc_middle::mir::Mutability; +use rustc_middle::mir::{Mutability, Pinnedness}; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::symbol::Ident; @@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ let closure_body = cx.tcx.hir_body(body); let closure_expr = peel_blocks(closure_body.value); match closure_body.params[0].pat.kind { - hir::PatKind::Ref(inner, Mutability::Not) => { + hir::PatKind::Ref(inner, Pinnedness::Not, Mutability::Not) => { if let hir::PatKind::Binding(hir::BindingMode::NONE, .., name, None) = inner.kind && ident_eq(name, closure_expr) { diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 20dfce914838..c22b0a548e3d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -138,7 +138,6 @@ mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; -mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; mod useless_nonzero_new_unchecked; @@ -1084,7 +1083,7 @@ /// /// ### Why is this bad? /// In versions of the compiler before Rust 1.82.0, this bypasses the specialized - /// implementation of`ToString` and instead goes through the more expensive string + /// implementation of `ToString` and instead goes through the more expensive string /// formatting facilities. /// /// ### Example @@ -5026,7 +5025,6 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { zst_offset::check(cx, expr, recv); }, (sym::all, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, true); match method_call(recv) { Some((sym::cloned, recv2, [], _, _)) => { @@ -5056,7 +5054,6 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } }, (sym::any, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); needless_character_iteration::check(cx, expr, recv, arg, false); match method_call(recv) { Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( @@ -5170,7 +5167,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, (sym::expect, [_]) => { match method_call(recv) { - Some((sym::ok, recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some((sym::ok, recv_inner, [], _, _)) => ok_expect::check(cx, expr, recv, recv_inner), Some((sym::err, recv, [], err_span, _)) => { err_expect::check(cx, expr, recv, span, err_span, self.msrv); }, @@ -5216,7 +5213,6 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } }, (sym::filter_map, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FilterMap); filter_map_bool_then::check(cx, expr, arg, call_span); filter_map_identity::check(cx, expr, arg, span); @@ -5231,11 +5227,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ); }, (sym::find_map, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap); }, (sym::flat_map, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); flat_map_identity::check(cx, expr, arg, span); flat_map_option::check(cx, expr, arg, span); lines_filter_map_ok::check_filter_or_flat_map( @@ -5263,20 +5257,17 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { manual_try_fold::check(cx, expr, init, acc, call_span, self.msrv); unnecessary_fold::check(cx, expr, init, acc, span); }, - (sym::for_each, [arg]) => { - unused_enumerate_index::check(cx, expr, recv, arg); - match method_call(recv) { - Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), - Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( - cx, - expr, - recv, - recv2, - iter_overeager_cloned::Op::NeedlessMove(arg), - false, - ), - _ => {}, - } + (sym::for_each, [arg]) => match method_call(recv) { + Some((sym::inspect, _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2), + Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( + cx, + expr, + recv, + recv2, + iter_overeager_cloned::Op::NeedlessMove(arg), + false, + ), + _ => {}, }, (sym::get, [arg]) => { get_first::check(cx, expr, recv, arg); @@ -5337,7 +5328,6 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, (name @ (sym::map | sym::map_err), [m_arg]) => { if name == sym::map { - unused_enumerate_index::check(cx, expr, recv, m_arg); map_clone::check(cx, expr, recv, m_arg, self.msrv); map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, self.msrv, span); manual_is_variant_and::check_map(cx, expr); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 4f005103d23f..055fdcabdd21 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -38,11 +38,14 @@ pub(super) fn check<'tcx>( Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); + let sugg: String; + let mut app; + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { - let mut app = Applicability::MachineApplicable; + app = Applicability::MachineApplicable; let collect_ty = cx.typeck_results().expr_ty(collect_expr); - let sugg: String = match name.ident.name { + sugg = match name.ident.name { sym::len => { if let Some(adt) = collect_ty.ty_adt_def() && matches!( @@ -78,17 +81,23 @@ pub(super) fn check<'tcx>( }, _ => return, }; - - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - call_span.with_hi(parent.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - app, - ); + } else if let ExprKind::Index(_, index, _) = parent.kind { + app = Applicability::MaybeIncorrect; + let snip = snippet_with_applicability(cx, index.span, "_", &mut app); + sugg = format!("nth({snip}).unwrap()"); + } else { + return; } + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); }, Node::LetStmt(l) => { if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs index c9c1f4865b81..5f1cae130dae 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ok_expect.rs @@ -1,28 +1,35 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::res::MaybeDef; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::has_debug_impl; +use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; use super::OK_EXPECT; /// lint use of `ok().expect()` for `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { - if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - // lint if the caller of `ok()` is a `Result` - && let result_type = cx.typeck_results().expr_ty(recv) - && let Some(error_type) = get_error_type(cx, result_type) +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, recv_inner: &hir::Expr<'_>) { + let result_ty = cx.typeck_results().expr_ty(recv_inner); + // lint if the caller of `ok()` is a `Result` + if let Some(error_type) = get_error_type(cx, result_ty) && has_debug_impl(cx, error_type) + && let Some(span) = recv.span.trim_start(recv_inner.span) { - span_lint_and_help( + span_lint_and_then( cx, OK_EXPECT, expr.span, "called `ok().expect()` on a `Result` value", - None, - "you can call `expect()` directly on the `Result`", + |diag| { + let span = cx.sess().source_map().span_extend_while_whitespace(span); + diag.span_suggestion_verbose( + span, + "call `expect()` directly on the `Result`", + String::new(), + Applicability::MachineApplicable, + ); + }, ); } } @@ -30,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr /// Given a `Result` type, return its error type (`E`). fn get_error_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option> { match ty.kind() { - ty::Adt(_, args) if ty.is_diag_item(cx, sym::Result) => args.types().nth(1), + ty::Adt(adt, args) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => args.types().nth(1), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs index 342ffea51d65..817388915f18 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_none_expr; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::OptionSome; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -48,7 +49,7 @@ pub(super) fn check<'tcx>( return; } - if !def_arg.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) { + if !is_none_expr(cx, def_arg) { // nothing to lint! return; } diff --git a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs index e2946c22a46b..d5477b9be4c1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/methods/result_map_or_else_none.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::source::snippet; +use clippy_utils::{is_none_expr, peel_blocks}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::LangItem::OptionSome; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -25,7 +25,7 @@ pub(super) fn check<'tcx>( && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = def_arg.kind && let body = cx.tcx.hir_body(body) // And finally we check that we return a `None` in the "else case". - && peel_blocks(body.value).res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) + && is_none_expr(cx, peel_blocks(body.value)) { let msg = "called `map_or_else(|_| None, Some)` on a `Result` value"; let self_snippet = snippet(cx, recv.span, ".."); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index 7f729ac7ca94..72f1c42da2ee 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -1,10 +1,10 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::sym; use clippy_utils::ty::{is_copy, option_arg_ty}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{as_some_expr, sym}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::LangItem::{OptionNone, OptionSome}; @@ -47,8 +47,7 @@ pub(super) fn check<'tcx>( let sugg = if !found_filtering { // Check if the closure is .filter_map(|x| Some(x)) if kind.is_filter_map() - && let hir::ExprKind::Call(expr, [arg]) = body.value.kind - && expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + && let Some(arg) = as_some_expr(cx, body.value) && let hir::ExprKind::Path(_) = arg.kind { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 410e973f855b..da6f03931e24 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::{last_path_segment, sym}; +use clippy_utils::{is_none_expr, last_path_segment, sym}; use rustc_errors::Applicability; use rustc_hir::{self as hir, AmbigArg}; use rustc_lint::LateContext; @@ -23,6 +23,7 @@ fn get_ty_from_args<'a>(args: Option<&'a [hir::GenericArg<'a>]>, index: usize) - } } +#[expect(clippy::too_many_lines)] pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -38,23 +39,24 @@ pub(super) fn check( } let (constructor, call_args, ty) = if let hir::ExprKind::Call(call, call_args) = init.kind { - let Some((qpath, hir_id)) = call.opt_qpath() else { - return; - }; - - let args = last_path_segment(qpath).args.map(|args| args.args); - let res = cx.qpath_res(qpath, hir_id); - - if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionSome) { - (sym::Some, call_args, get_ty_from_args(args, 0)) - } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultOk) { - (sym::Ok, call_args, get_ty_from_args(args, 0)) - } else if res.ctor_parent(cx).is_lang_item(cx, hir::LangItem::ResultErr) { - (sym::Err, call_args, get_ty_from_args(args, 1)) + if let Some((qpath, hir_id)) = call.opt_qpath() + && let args = last_path_segment(qpath).args.map(|args| args.args) + && let Some(did) = cx.qpath_res(qpath, hir_id).ctor_parent(cx).opt_def_id() + { + let lang_items = cx.tcx.lang_items(); + if Some(did) == lang_items.option_some_variant() { + (sym::Some, call_args, get_ty_from_args(args, 0)) + } else if Some(did) == lang_items.result_ok_variant() { + (sym::Ok, call_args, get_ty_from_args(args, 0)) + } else if Some(did) == lang_items.result_err_variant() { + (sym::Err, call_args, get_ty_from_args(args, 1)) + } else { + return; + } } else { return; } - } else if init.res(cx).ctor_parent(cx).is_lang_item(cx, hir::LangItem::OptionNone) { + } else if is_none_expr(cx, init) { let call_args: &[hir::Expr<'_>] = &[]; (sym::None, call_args, None) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs deleted file mode 100644 index a7d9b2e0fab0..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs +++ /dev/null @@ -1,138 +0,0 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::{expr_or_init, pat_is_wild}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; -use rustc_lint::LateContext; -use rustc_span::{Span, sym}; - -use crate::loops::UNUSED_ENUMERATE_INDEX; - -/// Check for the `UNUSED_ENUMERATE_INDEX` lint outside of loops. -/// -/// The lint is declared in `clippy_lints/src/loops/mod.rs`. There, the following pattern is -/// checked: -/// ```ignore -/// for (_, x) in some_iter.enumerate() { -/// // Index is ignored -/// } -/// ``` -/// -/// This `check` function checks for chained method calls constructs where we can detect that the -/// index is unused. Currently, this checks only for the following patterns: -/// ```ignore -/// some_iter.enumerate().map_function(|(_, x)| ..) -/// let x = some_iter.enumerate(); -/// x.map_function(|(_, x)| ..) -/// ``` -/// where `map_function` is one of `all`, `any`, `filter_map`, `find_map`, `flat_map`, `for_each` or -/// `map`. -/// -/// # Preconditions -/// This function must be called not on the `enumerate` call expression itself, but on any of the -/// map functions listed above. It will ensure that `recv` is a `std::iter::Enumerate` instance and -/// that the method call is one of the `std::iter::Iterator` trait. -/// -/// * `call_expr`: The map function call expression -/// * `recv`: The receiver of the call -/// * `closure_arg`: The argument to the map function call containing the closure/function to apply -pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { - let recv_ty = cx.typeck_results().expr_ty(recv); - // If we call a method on a `std::iter::Enumerate` instance - if recv_ty.is_diag_item(cx, sym::Enumerate) - // If we are calling a method of the `Iterator` trait - && cx.ty_based_def(call_expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) - // And the map argument is a closure - && let ExprKind::Closure(closure) = closure_arg.kind - && let closure_body = cx.tcx.hir_body(closure.body) - // And that closure has one argument ... - && let [closure_param] = closure_body.params - // .. which is a tuple of 2 elements - && let PatKind::Tuple([index, elem], ..) = closure_param.pat.kind - // And that the first element (the index) is either `_` or unused in the body - && pat_is_wild(cx, &index.kind, closure_body) - // Try to find the initializer for `recv`. This is needed in case `recv` is a local_binding. In the - // first example below, `expr_or_init` would return `recv`. - // ``` - // iter.enumerate().map(|(_, x)| x) - // ^^^^^^^^^^^^^^^^ `recv`, a call to `std::iter::Iterator::enumerate` - // - // let binding = iter.enumerate(); - // ^^^^^^^^^^^^^^^^ `recv_init_expr` - // binding.map(|(_, x)| x) - // ^^^^^^^ `recv`, not a call to `std::iter::Iterator::enumerate` - // ``` - && let recv_init_expr = expr_or_init(cx, recv) - // Make sure the initializer is a method call. It may be that the `Enumerate` comes from something - // that we cannot control. - // This would for instance happen with: - // ``` - // external_lib::some_function_returning_enumerate().map(|(_, x)| x) - // ``` - && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind - && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id) - // Make sure the method call is `std::iter::Iterator::enumerate`. - && cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid) - { - // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element - // that would be explicitly in the closure. - let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) { - // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`. - // Fallback to `..` if we fail getting either snippet. - Some(ty_span) => elem - .span - .get_source_text(cx) - .and_then(|binding_name| { - ty_span - .get_source_text(cx) - .map(|ty_name| format!("{binding_name}: {ty_name}")) - }) - .unwrap_or_else(|| "..".to_string()), - // Otherwise, we have no explicit type. We can replace with the binding name of the element. - None => snippet(cx, elem.span, "..").into_owned(), - }; - - // Suggest removing the tuple from the closure and the preceding call to `enumerate`, whose span we - // can get from the `MethodCall`. - span_lint_hir_and_then( - cx, - UNUSED_ENUMERATE_INDEX, - recv_init_expr.hir_id, - enumerate_span, - "you seem to use `.enumerate()` and immediately discard the index", - |diag| { - diag.multipart_suggestion( - "remove the `.enumerate()` call", - vec![ - (closure_param.span, new_closure_param), - ( - enumerate_span.with_lo(enumerate_recv.span.source_callsite().hi()), - String::new(), - ), - ], - Applicability::MachineApplicable, - ); - }, - ); - } -} - -/// Find the span of the explicit type of the element. -/// -/// # Returns -/// If the tuple argument: -/// * Has no explicit type, returns `None` -/// * Has an explicit tuple type with an implicit element type (`(usize, _)`), returns `None` -/// * Has an explicit tuple type with an explicit element type (`(_, i32)`), returns the span for -/// the element type. -fn find_elem_explicit_type_span(fn_decl: &FnDecl<'_>) -> Option { - if let [tuple_ty] = fn_decl.inputs - && let TyKind::Tup([_idx_ty, elem_ty]) = tuple_ty.kind - && !matches!(elem_ty.kind, TyKind::Err(..) | TyKind::Infer(())) - { - Some(elem_ty.span) - } else { - None - } -} diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 35d06780bcb8..808adb7e71ce 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -3,10 +3,11 @@ use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; +use clippy_utils::higher::{If, Range}; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace, root_macro_call}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{eq_expr_value, hash_expr, higher}; +use clippy_utils::{eq_expr_value, hash_expr}; use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; @@ -15,7 +16,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -134,15 +135,15 @@ macro_rules! int_lit_pat { fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, -) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) +) -> Option<(LengthComparison, usize, &'hir Expr<'hir>, Symbol)> { + let ((cmp, asserted_len, slice_len), macro_call) = if let Some(If { cond, then, .. }) = If::hir(expr) && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind && let ExprKind::Binary(bin_op, left, right) = &condition.kind // check if `then` block has a never type expression && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind && cx.typeck_results().expr_ty(then_expr).is_never() { - len_comparison(bin_op.node, left, right)? + (len_comparison(bin_op.node, left, right)?, sym::assert_macro) } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { match cx.tcx.get_diagnostic_name(macro_call.def_id) { Some(sym::assert_eq_macro) => Some((macro_call, BinOpKind::Eq)), @@ -151,7 +152,12 @@ fn assert_len_expr<'hir>( } }) && let Some((left, right, _)) = find_assert_eq_args(cx, expr, macro_call.expn) { - len_comparison(bin_op, left, right)? + ( + len_comparison(bin_op, left, right)?, + root_macro_call(expr.span) + .and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id)) + .unwrap_or(sym::assert_macro), + ) } else { return None; }; @@ -160,7 +166,7 @@ fn assert_len_expr<'hir>( && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() && method.ident.name == sym::len { - Some((cmp, asserted_len, recv)) + Some((cmp, asserted_len, recv, macro_call)) } else { None } @@ -174,6 +180,7 @@ enum IndexEntry<'hir> { comparison: LengthComparison, assert_span: Span, slice: &'hir Expr<'hir>, + macro_call: Symbol, }, /// `assert!` with indexing /// @@ -187,6 +194,7 @@ enum IndexEntry<'hir> { slice: &'hir Expr<'hir>, indexes: Vec, comparison: LengthComparison, + macro_call: Symbol, }, /// Indexing without an `assert!` IndexWithoutAssert { @@ -225,9 +233,9 @@ fn upper_index_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { && let LitKind::Int(Pu128(index), _) = lit.node { Some(index as usize) - } else if let Some(higher::Range { + } else if let Some(Range { end: Some(end), limits, .. - }) = higher::Range::hir(cx, expr) + }) = Range::hir(cx, expr) && let ExprKind::Lit(lit) = &end.kind && let LitKind::Int(Pu128(index @ 1..), _) = lit.node { @@ -258,6 +266,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni comparison, assert_span, slice, + macro_call, } => { if slice.span.lo() > assert_span.lo() { *entry = IndexEntry::AssertWithIndex { @@ -268,6 +277,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni slice, indexes: vec![expr.span], comparison: *comparison, + macro_call: *macro_call, }; } }, @@ -303,7 +313,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Uni /// Checks if the expression is an `assert!` expression and adds it to `asserts` fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap>>) { - if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) { + if let Some((comparison, asserted_len, slice, macro_call)) = assert_len_expr(cx, expr) { let hash = hash_expr(cx, slice); let indexes = map.entry(hash).or_default(); @@ -326,6 +336,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un assert_span: expr.span.source_callsite(), comparison, asserted_len, + macro_call, }; } } else { @@ -334,6 +345,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un comparison, assert_span: expr.span.source_callsite(), slice, + macro_call, }); } } @@ -362,6 +374,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap comparison, assert_span, slice, + macro_call, } if indexes.len() > 1 && !is_first_highest => { // if we have found an `assert!`, let's also check that it's actually right // and if it covers the highest index and if not, suggest the correct length @@ -382,11 +395,23 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap snippet(cx, slice.span, "..") )), // `highest_index` here is rather a length, so we need to add 1 to it - LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => Some(format!( - "assert!({}.len() == {})", - snippet(cx, slice.span, ".."), - highest_index + 1 - )), + LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call { + sym::assert_eq_macro => Some(format!( + "assert_eq!({}.len(), {})", + snippet(cx, slice.span, ".."), + highest_index + 1 + )), + sym::debug_assert_eq_macro => Some(format!( + "debug_assert_eq!({}.len(), {})", + snippet(cx, slice.span, ".."), + highest_index + 1 + )), + _ => Some(format!( + "assert!({}.len() == {})", + snippet(cx, slice.span, ".."), + highest_index + 1 + )), + }, _ => None, }; diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index bccc72c2a516..c308f2a34585 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -4,6 +4,7 @@ use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocContainer; +use rustc_session::config::CrateType; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -81,20 +82,20 @@ fn check_missing_inline_attrs( } } -fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool { - use rustc_session::config::CrateType; - - cx.tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro)) -} - declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingInline { fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if it.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) { + if it.span.in_external_macro(cx.sess().source_map()) { + return; + } + + if cx + .tcx + .crate_types() + .iter() + .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + { return; } @@ -149,7 +150,13 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if impl_item.span.in_external_macro(cx.sess().source_map()) || is_executable_or_proc_macro(cx) { + if impl_item.span.in_external_macro(cx.sess().source_map()) + || cx + .tcx + .crate_types() + .iter() + .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + { return; } diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index f132b90ac4f2..9096d6f1c7b3 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -4,7 +4,7 @@ use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{FileName, SourceFile, Span, SyntaxContext, sym}; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use std::sync::Arc; declare_clippy_lint! { @@ -150,7 +150,13 @@ fn check_self_named_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile /// Using `mod.rs` in integration tests is a [common pattern](https://doc.rust-lang.org/book/ch11-03-test-organization.html#submodules-in-integration-test) /// for code-sharing between tests. fn check_mod_module(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { - if path.ends_with("mod.rs") && !path.starts_with("tests") { + if path.ends_with("mod.rs") + && !path + .components() + .filter_map(|c| if let Component::Normal(d) = c { Some(d) } else { None }) + .take_while(|&c| c != "src") + .any(|c| c == "tests") + { span_lint_and_then( cx, MOD_MODULE_FILES, diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 5f7fde30f03f..691d9035d02c 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -1,10 +1,9 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, Path, TyKind}; +use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Param, PatKind, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; use rustc_span::symbol::kw; declare_clippy_lint! { @@ -65,52 +64,6 @@ enum Mode { Value, } -fn check_param_inner(cx: &EarlyContext<'_>, path: &Path, span: Span, binding_mode: &Mode, mutbl: Mutability) { - if let [segment] = &path.segments[..] - && segment.ident.name == kw::SelfUpper - { - // In case we have a named lifetime, we check if the name comes from expansion. - // If it does, at this point we know the rest of the parameter was written by the user, - // so let them decide what the name of the lifetime should be. - // See #6089 for more details. - let mut applicability = Applicability::MachineApplicable; - let self_param = match (binding_mode, mutbl) { - (Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Mut) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ mut self".to_string() - } else { - let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); - format!("&{lt_name} mut self") - } - }, - (Mode::Ref(None), Mutability::Not) => "&self".to_string(), - (Mode::Ref(Some(lifetime)), Mutability::Not) => { - if lifetime.ident.span.from_expansion() { - applicability = Applicability::HasPlaceholders; - "&'_ self".to_string() - } else { - let lt_name = snippet_with_applicability(cx, lifetime.ident.span, "..", &mut applicability); - format!("&{lt_name} self") - } - }, - (Mode::Value, Mutability::Mut) => "mut self".to_string(), - (Mode::Value, Mutability::Not) => "self".to_string(), - }; - - span_lint_and_sugg( - cx, - NEEDLESS_ARBITRARY_SELF_TYPE, - span, - "the type of the `self` parameter does not need to be arbitrary", - "consider to change this parameter to", - self_param, - applicability, - ); - } -} - impl EarlyLintPass for NeedlessArbitrarySelfType { fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { // Bail out if the parameter it's not a receiver or was not written by the user @@ -118,20 +71,55 @@ fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) { return; } - match &p.ty.kind { - TyKind::Path(None, path) => { - if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Value, mutbl); - } + let (path, binding_mode, mutbl) = match &p.ty.kind { + TyKind::Path(None, path) if let PatKind::Ident(BindingMode(ByRef::No, mutbl), _, _) = p.pat.kind => { + (path, Mode::Value, mutbl) }, - TyKind::Ref(lifetime, mut_ty) => { + TyKind::Ref(lifetime, mut_ty) if let TyKind::Path(None, path) = &mut_ty.ty.kind - && let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind - { - check_param_inner(cx, path, p.span.to(p.ty.span), &Mode::Ref(*lifetime), mut_ty.mutbl); - } + && let PatKind::Ident(BindingMode::NONE, _, _) = p.pat.kind => + { + (path, Mode::Ref(*lifetime), mut_ty.mutbl) }, - _ => {}, + _ => return, + }; + + let span = p.span.to(p.ty.span); + if let [segment] = &path.segments[..] + && segment.ident.name == kw::SelfUpper + { + span_lint_and_then( + cx, + NEEDLESS_ARBITRARY_SELF_TYPE, + span, + "the type of the `self` parameter does not need to be arbitrary", + |diag| { + let mut applicability = Applicability::MachineApplicable; + let add = match binding_mode { + Mode::Value => String::new(), + Mode::Ref(None) => mutbl.ref_prefix_str().to_string(), + Mode::Ref(Some(lifetime)) => { + // In case we have a named lifetime, we check if the name comes from expansion. + // If it does, at this point we know the rest of the parameter was written by the user, + // so let them decide what the name of the lifetime should be. + // See #6089 for more details. + let lt_name = if lifetime.ident.span.from_expansion() { + applicability = Applicability::HasPlaceholders; + "'_".into() + } else { + snippet_with_applicability(cx, lifetime.ident.span, "'_", &mut applicability) + }; + format!("&{lt_name} {mut_}", mut_ = mutbl.prefix_str()) + }, + }; + + let mut sugg = vec![(p.ty.span.with_lo(p.span.hi()), String::new())]; + if !add.is_empty() { + sugg.push((p.span.shrink_to_lo(), add)); + } + diag.multipart_suggestion_verbose("remove the type", sugg, applicability); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs index 1b6896827fed..29b4da93b7fb 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; -use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind}; +use rustc_hir::{BindingMode, Mutability, Node, Pat, PatKind, Pinnedness}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -37,7 +37,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { - if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind + if let PatKind::Ref(pat, Pinnedness::Not, Mutability::Not) = ref_pat.kind && !ref_pat.span.from_expansion() && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs index e11f775018ed..e66c088617cb 100644 --- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs +++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs @@ -354,10 +354,7 @@ fn self_cmp_call<'tcx>( needs_fully_qualified: &mut bool, ) -> bool { match cmp_expr.kind { - ExprKind::Call(path, [_, _]) => path - .res(typeck) - .opt_def_id() - .is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::ord_cmp_method, def_id)), + ExprKind::Call(path, [_, _]) => path.res(typeck).is_diag_item(cx, sym::ord_cmp_method), ExprKind::MethodCall(_, recv, [_], ..) => { let ExprKind::Path(path) = recv.kind else { return false; diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 3c3e5fea4970..9b0008a29c6b 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -706,7 +706,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, // FIXME: we just assume mgca rhs's are freeze - _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| !self.is_init_expr_freeze( + _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| !self.is_init_expr_freeze( cx.tcx, cx.typing_env(), cx.tcx.typeck(item.owner_id), @@ -749,7 +749,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_> !is_freeze }, // FIXME: we just assume mgca rhs's are freeze - _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| { + _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| { !self.is_init_expr_freeze( cx.tcx, cx.typing_env(), @@ -806,7 +806,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) { Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze, // FIXME: we just assume mgca rhs's are freeze - _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).map_or(false, |e| { + _ => const_item_rhs_to_expr(cx.tcx, ct_rhs).is_some_and(|e| { !self.is_init_expr_freeze( cx.tcx, cx.typing_env(), diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 0a6499e09583..91a069559f7b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,4 +1,5 @@ use super::ARITHMETIC_SIDE_EFFECTS; +use crate::clippy_utils::res::MaybeQPath as _; use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; @@ -6,7 +7,7 @@ use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, UintTy}; use rustc_session::impl_lint_pass; use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; @@ -88,74 +89,16 @@ fn has_allowed_unary(&self, ty: Ty<'_>) -> bool { self.allowed_unary.contains(ty_string_elem) } - fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - if let ty::Adt(adt, substs) = ty.kind() - && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) - && let int_type = substs.type_at(0) - && matches!(int_type.kind(), ty::Uint(_)) - { - true - } else { - false - } - } - - /// Verifies built-in types that have specific allowed operations - fn has_specific_allowed_type_and_operation<'tcx>( - cx: &LateContext<'tcx>, - lhs_ty: Ty<'tcx>, - op: hir::BinOpKind, - rhs_ty: Ty<'tcx>, - ) -> bool { - let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); - let is_sat_or_wrap = |ty: Ty<'_>| ty.is_diag_item(cx, sym::Saturating) || ty.is_diag_item(cx, sym::Wrapping); - - // If the RHS is `NonZero`, then division or module by zero will never occur. - if Self::is_non_zero_u(cx, rhs_ty) && is_div_or_rem { - return true; - } - - // `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module. - if is_sat_or_wrap(lhs_ty) { - return !is_div_or_rem; - } - - false - } - - // For example, 8i32 or &i64::MAX. - fn is_integral(ty: Ty<'_>) -> bool { - ty.peel_refs().is_integral() - } - // Common entry-point to avoid code duplication. fn issue_lint<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if is_from_proc_macro(cx, expr) { return; } - let msg = "arithmetic operation that can potentially result in unexpected side-effects"; span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); self.expr_span = Some(expr.span); } - /// Returns the numeric value of a literal integer originated from `expr`, if any. - /// - /// Literal integers can be originated from adhoc declarations like `1`, associated constants - /// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`, - fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { - let actual = peel_hir_expr_unary(expr).0; - if let hir::ExprKind::Lit(lit) = actual.kind - && let ast::LitKind::Int(n, _) = lit.node - { - return Some(n.get()); - } - if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) { - return Some(n); - } - None - } - /// Methods like `add_assign` are send to their `BinOps` references. fn manage_sugar_methods<'tcx>( &mut self, @@ -213,59 +156,53 @@ fn manage_bin_ops<'tcx>( && let hir::ExprKind::MethodCall(method, receiver, [], _) = actual_lhs.kind && method.ident.name == sym::get && let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs() - && Self::is_non_zero_u(cx, receiver_ty) - && let Some(1) = Self::literal_integer(cx, actual_rhs) + && is_non_zero_u(cx, receiver_ty) + && literal_integer(cx, actual_rhs) == Some(1) { return; } let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs(); - if self.has_allowed_binary(lhs_ty, rhs_ty) { + if self.has_allowed_binary(lhs_ty, rhs_ty) + | has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) + | is_safe_due_to_smaller_source_type(cx, op, (actual_lhs, lhs_ty), actual_rhs) + | is_safe_due_to_smaller_source_type(cx, op, (actual_rhs, rhs_ty), actual_lhs) + { return; } - if Self::has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) { - return; - } - - let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + if is_integer(lhs_ty) && is_integer(rhs_ty) { if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op { // At least for integers, shifts are already handled by the CTFE return; } - match ( - Self::literal_integer(cx, actual_lhs), - Self::literal_integer(cx, actual_rhs), - ) { - (None, None) => false, + match (literal_integer(cx, actual_lhs), literal_integer(cx, actual_rhs)) { (None, Some(n)) => match (&op, n) { // Division and module are always valid if applied to non-zero integers - (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true, + (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => return, // Adding or subtracting zeros is always a no-op (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) // Multiplication by 1 or 0 will never overflow | (hir::BinOpKind::Mul, 0 | 1) - => true, - _ => false, + => return, + _ => {}, }, - (Some(n), None) => match (&op, n) { - // Adding or subtracting zeros is always a no-op - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - // Multiplication by 1 or 0 will never overflow - | (hir::BinOpKind::Mul, 0 | 1) - => true, - _ => false, - }, - (Some(_), Some(_)) => { - matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + (Some(n), None) + if matches!( + (&op, n), + // Adding or subtracting zeros is always a no-op + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + // Multiplication by 1 or 0 will never overflow + | (hir::BinOpKind::Mul, 0 | 1) + ) => + { + return; }, + (Some(_), Some(_)) if matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) => return, + _ => {}, } - } else { - false - }; - if !has_valid_op { - self.issue_lint(cx, expr); } + self.issue_lint(cx, expr); } /// There are some integer methods like `wrapping_div` that will panic depending on the @@ -285,7 +222,7 @@ fn manage_method_call<'tcx>( return; } let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); - if !Self::is_integral(instance_ty) { + if !is_integer(instance_ty) { return; } self.manage_sugar_methods(cx, expr, receiver, ps, arg); @@ -293,7 +230,7 @@ fn manage_method_call<'tcx>( return; } let (actual_arg, _) = peel_hir_expr_refs(arg); - match Self::literal_integer(cx, actual_arg) { + match literal_integer(cx, actual_arg) { None | Some(0) => self.issue_lint(cx, arg), Some(_) => {}, } @@ -317,7 +254,7 @@ fn manage_unary_ops<'tcx>( return; } let actual_un_expr = peel_hir_expr_refs(un_expr).0; - if Self::literal_integer(cx, actual_un_expr).is_some() { + if literal_integer(cx, actual_un_expr).is_some() { return; } self.issue_lint(cx, expr); @@ -385,3 +322,120 @@ fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) } } } + +/// Detects a type-casting conversion and returns the type of the original expression. For +/// example, `let foo = u64::from(bar)`. +fn find_original_primitive_ty<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option> { + if let hir::ExprKind::Call(path, [arg]) = &expr.kind + && path.res(cx).opt_def_id().is_diag_item(&cx.tcx, sym::from_fn) + { + Some(cx.typeck_results().expr_ty(arg)) + } else { + None + } +} + +/// Verifies built-in types that have specific allowed operations +fn has_specific_allowed_type_and_operation<'tcx>( + cx: &LateContext<'tcx>, + lhs_ty: Ty<'tcx>, + op: hir::BinOpKind, + rhs_ty: Ty<'tcx>, +) -> bool { + let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem); + let is_sat_or_wrap = |ty: Ty<'_>| matches!(ty.opt_diag_name(cx), Some(sym::Saturating | sym::Wrapping)); + + // If the RHS is `NonZero`, then division or module by zero will never occur. + if is_non_zero_u(cx, rhs_ty) && is_div_or_rem { + return true; + } + + // `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module. + if is_sat_or_wrap(lhs_ty) { + return !is_div_or_rem; + } + + false +} + +// For example, `i8` or `u128` and possible associated references like `&&u16`. +fn is_integer(ty: Ty<'_>) -> bool { + ty.peel_refs().is_integral() +} + +fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { + if let ty::Adt(adt, substs) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && let int_type = substs.type_at(0) + && matches!(int_type.kind(), ty::Uint(_)) + { + true + } else { + false + } +} + +/// If one side is a literal it is possible to evaluate overflows as long as the other side has a +/// smaller type. `0` and `1` suffixes indicate different sides. +/// +/// For example, `1000u64 + u64::from(some_runtime_variable_of_type_u8)`. +fn is_safe_due_to_smaller_source_type( + cx: &LateContext<'_>, + op: hir::BinOpKind, + (expr0, ty0): (&hir::Expr<'_>, Ty<'_>), + expr1: &hir::Expr<'_>, +) -> bool { + let Some(num0) = literal_integer(cx, expr0) else { + return false; + }; + let Some(orig_ty1) = find_original_primitive_ty(cx, expr1) else { + return false; + }; + let Some(num1) = max_int_num(orig_ty1) else { + return false; + }; + let Some(rslt) = (match op { + hir::BinOpKind::Add => num0.checked_add(num1), + hir::BinOpKind::Mul => num0.checked_mul(num1), + _ => None, + }) else { + return false; + }; + match ty0.peel_refs().kind() { + ty::Uint(UintTy::U16) => u16::try_from(rslt).is_ok(), + ty::Uint(UintTy::U32) => u32::try_from(rslt).is_ok(), + ty::Uint(UintTy::U64) => u64::try_from(rslt).is_ok(), + ty::Uint(UintTy::U128) => true, + ty::Uint(UintTy::Usize) => usize::try_from(rslt).is_ok(), + _ => false, + } +} + +/// Returns the numeric value of a literal integer originated from `expr`, if any. +/// +/// Literal integers can be originated from adhoc declarations like `1`, associated constants +/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`, +fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { + let actual = peel_hir_expr_unary(expr).0; + if let hir::ExprKind::Lit(lit) = actual.kind + && let ast::LitKind::Int(n, _) = lit.node + { + return Some(n.get()); + } + if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) { + return Some(n); + } + None +} + +fn max_int_num(ty: Ty<'_>) -> Option { + match ty.peel_refs().kind() { + ty::Uint(UintTy::U8) => Some(u8::MAX.into()), + ty::Uint(UintTy::U16) => Some(u16::MAX.into()), + ty::Uint(UintTy::U32) => Some(u32::MAX.into()), + ty::Uint(UintTy::U64) => Some(u64::MAX.into()), + ty::Uint(UintTy::U128) => Some(u128::MAX), + ty::Uint(UintTy::Usize) => usize::MAX.try_into().ok(), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index c32c74a8fe60..85cf483fce90 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -6,16 +6,15 @@ use clippy_utils::ty::is_copy; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, eager_or_lazy, expr_requires_coercion, higher, is_else_clause, - is_in_const_context, peel_blocks, peel_hir_expr_while, + is_in_const_context, is_none_pattern, peel_blocks, peel_hir_expr_while, }; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::LangItem::ResultErr; use rustc_hir::def::Res; use rustc_hir::intravisit::{Visitor, walk_expr, walk_path}; use rustc_hir::{ - Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatExpr, PatExprKind, PatKind, Path, - QPath, UnOp, + Arm, BindingMode, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -313,11 +312,14 @@ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { } fn try_get_inner_pat_and_is_result<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<(&'tcx Pat<'tcx>, bool)> { - if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind { - let res = cx.qpath_res(qpath, pat.hir_id); - if res.ctor_parent(cx).is_lang_item(cx, OptionSome) { + if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind + && let res = cx.qpath_res(qpath, pat.hir_id) + && let Some(did) = res.ctor_parent(cx).opt_def_id() + { + let lang_items = cx.tcx.lang_items(); + if Some(did) == lang_items.option_some_variant() { return Some((inner_pat, false)); - } else if res.ctor_parent(cx).is_lang_item(cx, ResultOk) { + } else if Some(did) == lang_items.result_ok_variant() { return Some((inner_pat, true)); } } @@ -376,14 +378,7 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Expr(PatExpr { - kind: PatExprKind::Path(qpath), - hir_id, - .. - }) => cx - .qpath_res(qpath, *hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionNone), + _ if is_none_pattern(cx, arm.pat) => true, PatKind::TupleStruct(ref qpath, [first_pat], _) => { cx.qpath_res(qpath, arm.pat.hir_id) .ctor_parent(cx) diff --git a/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs b/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs new file mode 100644 index 000000000000..905b48e6d1d4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr/cmp_null.rs @@ -0,0 +1,49 @@ +use super::CMP_NULL; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_lint_allowed, sym}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: BinOpKind, + l: &Expr<'_>, + r: &Expr<'_>, +) -> bool { + let non_null_path_snippet = match ( + is_lint_allowed(cx, CMP_NULL, expr.hir_id), + is_null_path(cx, l), + is_null_path(cx, r), + ) { + (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(), + (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(), + _ => return false, + }; + let invert = if op == BinOpKind::Eq { "" } else { "!" }; + + span_lint_and_sugg( + cx, + CMP_NULL, + expr.span, + "comparing with null is better expressed by the `.is_null()` method", + "try", + format!("{invert}{non_null_path_snippet}.is_null()",), + Applicability::MachineApplicable, + ); + true +} + +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let ExprKind::Call(pathexp, []) = expr.kind { + matches!( + pathexp.basic_res().opt_diag_name(cx), + Some(sym::ptr_null | sym::ptr_null_mut) + ) + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr/mod.rs b/src/tools/clippy/clippy_lints/src/ptr/mod.rs new file mode 100644 index 000000000000..6b2647e7b0a2 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr/mod.rs @@ -0,0 +1,202 @@ +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, ImplItemKind, ItemKind, Node, TraitFn, TraitItem, TraitItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +mod cmp_null; +mod mut_from_ref; +mod ptr_arg; +mod ptr_eq; + +declare_clippy_lint! { + /// ### What it does + /// This lint checks for function arguments of type `&String`, `&Vec`, + /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls + /// with the appropriate `.to_owned()`/`to_string()` calls. + /// + /// ### Why is this bad? + /// Requiring the argument to be of the specific type + /// makes the function less useful for no benefit; slices in the form of `&[T]` + /// or `&str` usually suffice and can be obtained from other types, too. + /// + /// ### Known problems + /// There may be `fn(&Vec)`-typed references pointing to your function. + /// If you have them, you will get a compiler error after applying this lint's + /// suggestions. You then have the choice to undo your changes or change the + /// type of the reference. + /// + /// Note that if the function is part of your public interface, there may be + /// other crates referencing it, of which you may not be aware. Carefully + /// deprecate the function before applying the lint suggestions in this case. + /// + /// ### Example + /// ```ignore + /// fn foo(&Vec) { .. } + /// ``` + /// + /// Use instead: + /// ```ignore + /// fn foo(&[u32]) { .. } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PTR_ARG, + style, + "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint checks for equality comparisons with `ptr::null` + /// + /// ### Why is this bad? + /// It's easier and more readable to use the inherent + /// `.is_null()` + /// method instead + /// + /// ### Example + /// ```rust,ignore + /// use std::ptr; + /// + /// if x == ptr::null { + /// // .. + /// } + /// ``` + /// + /// Use instead: + /// ```rust,ignore + /// if x.is_null() { + /// // .. + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub CMP_NULL, + style, + "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint checks for functions that take immutable references and return + /// mutable ones. This will not trigger if no unsafe code exists as there + /// are multiple safe functions which will do this transformation + /// + /// To be on the conservative side, if there's at least one mutable + /// reference with the output lifetime, this lint will not trigger. + /// + /// ### Why is this bad? + /// Creating a mutable reference which can be repeatably derived from an + /// immutable reference is unsound as it allows creating multiple live + /// mutable references to the same object. + /// + /// This [error](https://github.com/rust-lang/rust/issues/39465) actually + /// lead to an interim Rust release 1.15.1. + /// + /// ### Known problems + /// This pattern is used by memory allocators to allow allocating multiple + /// objects while returning mutable references to each one. So long as + /// different mutable references are returned each time such a function may + /// be safe. + /// + /// ### Example + /// ```ignore + /// fn foo(&Foo) -> &mut Bar { .. } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub MUT_FROM_REF, + correctness, + "fns that create mutable refs from immutable ref args" +} + +declare_clippy_lint! { + /// ### What it does + /// Use `std::ptr::eq` when applicable + /// + /// ### Why is this bad? + /// `ptr::eq` can be used to compare `&T` references + /// (which coerce to `*const T` implicitly) by their address rather than + /// comparing the values they point to. + /// + /// ### Example + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(a as *const _ as usize == b as *const _ as usize); + /// ``` + /// Use instead: + /// ```no_run + /// let a = &[1, 2, 3]; + /// let b = &[1, 2, 3]; + /// + /// assert!(std::ptr::eq(a, b)); + /// ``` + #[clippy::version = "1.49.0"] + pub PTR_EQ, + style, + "use `std::ptr::eq` when comparing raw pointers" +} + +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); + +impl<'tcx> LateLintPass<'tcx> for Ptr { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(sig, trait_method) = &item.kind { + if matches!(trait_method, TraitFn::Provided(_)) { + // Handled by `check_body`. + return; + } + + mut_from_ref::check(cx, sig, None); + ptr_arg::check_trait_item(cx, item.owner_id, sig); + } + } + + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id); + let (item_id, sig, is_trait_item) = match parents.next() { + Some((_, Node::Item(i))) => { + if let ItemKind::Fn { sig, .. } = &i.kind { + (i.owner_id, sig, false) + } else { + return; + } + }, + Some((_, Node::ImplItem(i))) => { + if !matches!(parents.next(), + Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none()) + ) { + return; + } + if let ImplItemKind::Fn(sig, _) = &i.kind { + (i.owner_id, sig, false) + } else { + return; + } + }, + Some((_, Node::TraitItem(i))) => { + if let TraitItemKind::Fn(sig, _) = &i.kind { + (i.owner_id, sig, true) + } else { + return; + } + }, + _ => return, + }; + + mut_from_ref::check(cx, sig, Some(body)); + ptr_arg::check_body(cx, body, item_id, sig, is_trait_item); + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(op, l, r) = expr.kind + && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) + { + #[expect( + clippy::collapsible_if, + reason = "the outer `if`s check the HIR, the inner ones run lints" + )] + if !cmp_null::check(cx, expr, op.node, l, r) { + ptr_eq::check(cx, op.node, l, r, expr.span); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/ptr/mut_from_ref.rs b/src/tools/clippy/clippy_lints/src/ptr/mut_from_ref.rs new file mode 100644 index 000000000000..30d708f436b4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr/mut_from_ref.rs @@ -0,0 +1,75 @@ +use super::MUT_FROM_REF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::visitors::contains_unsafe_block; +use rustc_errors::MultiSpan; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, Body, FnRetTy, FnSig, GenericArg, Lifetime, Mutability, TyKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { + let FnRetTy::Return(ty) = sig.decl.output else { return }; + for (out, mutability, out_span) in get_lifetimes(ty) { + if mutability != Some(Mutability::Mut) { + continue; + } + let out_region = cx.tcx.named_bound_var(out.hir_id); + // `None` if one of the types contains `&'a mut T` or `T<'a>`. + // Else, contains all the locations of `&'a T` types. + let args_immut_refs: Option> = sig + .decl + .inputs + .iter() + .flat_map(get_lifetimes) + .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region) + .map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span)) + .collect(); + if let Some(args_immut_refs) = args_immut_refs + && !args_immut_refs.is_empty() + && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) + { + span_lint_and_then( + cx, + MUT_FROM_REF, + out_span, + "mutable borrow from immutable input(s)", + |diag| { + let ms = MultiSpan::from_spans(args_immut_refs); + diag.span_note(ms, "immutable borrow here"); + }, + ); + } + } +} + +struct LifetimeVisitor<'tcx> { + result: Vec<(&'tcx Lifetime, Option, Span)>, +} + +impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { + if let TyKind::Ref(lt, ref m) = ty.kind { + self.result.push((lt, Some(m.mutbl), ty.span)); + } + hir::intravisit::walk_ty(self, ty); + } + + fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { + if let GenericArg::Lifetime(lt) = generic_arg { + self.result.push((lt, None, generic_arg.span())); + } + hir::intravisit::walk_generic_arg(self, generic_arg); + } +} + +/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not. +/// +/// The second field of the vector's elements indicate if the lifetime is attached to a +/// shared reference, a mutable reference, or neither. +fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, Span)> { + use hir::intravisit::VisitorExt as _; + + let mut visitor = LifetimeVisitor { result: Vec::new() }; + visitor.visit_ty_unambig(ty); + visitor.result +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs similarity index 50% rename from src/tools/clippy/clippy_lints/src/ptr.rs rename to src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs index 8446b6fbbea5..fd9230f00a8b 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs @@ -1,24 +1,22 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use super::PTR_ARG; +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::SpanRangeExt; -use clippy_utils::sugg::Sugg; -use clippy_utils::visitors::contains_unsafe_block; -use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, std_or_core, sym}; +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; -use rustc_errors::{Applicability, MultiSpan}; +use rustc_errors::Applicability; use rustc_hir::hir_id::{HirId, HirIdMap}; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, - ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, + self as hir, AnonConst, BindingMode, Body, Expr, ExprKind, FnSig, GenericArg, Lifetime, Mutability, Node, OwnerId, + Param, PatKind, QPath, TyKind, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{Obligation, ObligationCause}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty}; -use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_span::symbol::Symbol; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -27,260 +25,65 @@ use crate::vec::is_allowed_vec_method; -declare_clippy_lint! { - /// ### What it does - /// This lint checks for function arguments of type `&String`, `&Vec`, - /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls - /// with the appropriate `.to_owned()`/`to_string()` calls. - /// - /// ### Why is this bad? - /// Requiring the argument to be of the specific type - /// makes the function less useful for no benefit; slices in the form of `&[T]` - /// or `&str` usually suffice and can be obtained from other types, too. - /// - /// ### Known problems - /// There may be `fn(&Vec)`-typed references pointing to your function. - /// If you have them, you will get a compiler error after applying this lint's - /// suggestions. You then have the choice to undo your changes or change the - /// type of the reference. - /// - /// Note that if the function is part of your public interface, there may be - /// other crates referencing it, of which you may not be aware. Carefully - /// deprecate the function before applying the lint suggestions in this case. - /// - /// ### Example - /// ```ignore - /// fn foo(&Vec) { .. } - /// ``` - /// - /// Use instead: - /// ```ignore - /// fn foo(&[u32]) { .. } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub PTR_ARG, - style, - "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint checks for equality comparisons with `ptr::null` - /// - /// ### Why is this bad? - /// It's easier and more readable to use the inherent - /// `.is_null()` - /// method instead - /// - /// ### Example - /// ```rust,ignore - /// use std::ptr; - /// - /// if x == ptr::null { - /// // .. - /// } - /// ``` - /// - /// Use instead: - /// ```rust,ignore - /// if x.is_null() { - /// // .. - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub CMP_NULL, - style, - "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint checks for functions that take immutable references and return - /// mutable ones. This will not trigger if no unsafe code exists as there - /// are multiple safe functions which will do this transformation - /// - /// To be on the conservative side, if there's at least one mutable - /// reference with the output lifetime, this lint will not trigger. - /// - /// ### Why is this bad? - /// Creating a mutable reference which can be repeatably derived from an - /// immutable reference is unsound as it allows creating multiple live - /// mutable references to the same object. - /// - /// This [error](https://github.com/rust-lang/rust/issues/39465) actually - /// lead to an interim Rust release 1.15.1. - /// - /// ### Known problems - /// This pattern is used by memory allocators to allow allocating multiple - /// objects while returning mutable references to each one. So long as - /// different mutable references are returned each time such a function may - /// be safe. - /// - /// ### Example - /// ```ignore - /// fn foo(&Foo) -> &mut Bar { .. } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub MUT_FROM_REF, - correctness, - "fns that create mutable refs from immutable ref args" -} - -declare_clippy_lint! { - /// ### What it does - /// Use `std::ptr::eq` when applicable - /// - /// ### Why is this bad? - /// `ptr::eq` can be used to compare `&T` references - /// (which coerce to `*const T` implicitly) by their address rather than - /// comparing the values they point to. - /// - /// ### Example - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(a as *const _ as usize == b as *const _ as usize); - /// ``` - /// Use instead: - /// ```no_run - /// let a = &[1, 2, 3]; - /// let b = &[1, 2, 3]; - /// - /// assert!(std::ptr::eq(a, b)); - /// ``` - #[clippy::version = "1.49.0"] - pub PTR_EQ, - style, - "use `std::ptr::eq` when comparing raw pointers" -} - -declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, PTR_EQ]); - -impl<'tcx> LateLintPass<'tcx> for Ptr { - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { - if let TraitItemKind::Fn(sig, trait_method) = &item.kind { - if matches!(trait_method, TraitFn::Provided(_)) { - // Handled by check body. - return; - } - - check_mut_from_ref(cx, sig, None); - - if !matches!(sig.header.abi, ExternAbi::Rust) { - // Ignore `extern` functions with non-Rust calling conventions - return; - } - - for arg in check_fn_args( - cx, - cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder(), - sig.decl.inputs, - &[], - ) - .filter(|arg| arg.mutability() == Mutability::Not) - { - span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| { - diag.span_suggestion( - arg.span, - "change this to", - format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)), - Applicability::Unspecified, - ); - }); - } - } +pub(super) fn check_body<'tcx>( + cx: &LateContext<'tcx>, + body: &Body<'tcx>, + item_id: OwnerId, + sig: &FnSig<'tcx>, + is_trait_item: bool, +) { + if !matches!(sig.header.abi, ExternAbi::Rust) { + // Ignore `extern` functions with non-Rust calling conventions + return; } - fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { - let mut parents = cx.tcx.hir_parent_iter(body.value.hir_id); - let (item_id, sig, is_trait_item) = match parents.next() { - Some((_, Node::Item(i))) => { - if let ItemKind::Fn { sig, .. } = &i.kind { - (i.owner_id, sig, false) - } else { - return; - } - }, - Some((_, Node::ImplItem(i))) => { - if !matches!(parents.next(), - Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none()) - ) { - return; - } - if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.owner_id, sig, false) - } else { - return; - } - }, - Some((_, Node::TraitItem(i))) => { - if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.owner_id, sig, true) - } else { - return; - } - }, - _ => return, - }; + let decl = sig.decl; + let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); + let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) + .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) + .collect(); + let results = check_ptr_arg_usage(cx, body, &lint_args); - check_mut_from_ref(cx, sig, Some(body)); - - if !matches!(sig.header.abi, ExternAbi::Rust) { - // Ignore `extern` functions with non-Rust calling conventions - return; - } - - let decl = sig.decl; - let sig = cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(); - let lint_args: Vec<_> = check_fn_args(cx, sig, decl.inputs, body.params) - .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) - .collect(); - let results = check_ptr_arg_usage(cx, body, &lint_args); - - for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) { - span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| { - diag.multipart_suggestion( - "change this to", - iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx)))) - .chain(result.replacements.iter().map(|r| { - ( - r.expr_span, - format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), - ) - })) - .collect(), - Applicability::Unspecified, - ); - }); - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Binary(op, l, r) = expr.kind - && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) - { - let non_null_path_snippet = match ( - is_lint_allowed(cx, CMP_NULL, expr.hir_id), - is_null_path(cx, l), - is_null_path(cx, r), - ) { - (false, true, false) if let Some(sugg) = Sugg::hir_opt(cx, r) => sugg.maybe_paren(), - (false, false, true) if let Some(sugg) = Sugg::hir_opt(cx, l) => sugg.maybe_paren(), - _ => return check_ptr_eq(cx, expr, op.node, l, r), - }; - let invert = if op.node == BinOpKind::Eq { "" } else { "!" }; - - span_lint_and_sugg( - cx, - CMP_NULL, - expr.span, - "comparing with null is better expressed by the `.is_null()` method", - "try", - format!("{invert}{non_null_path_snippet}.is_null()",), - Applicability::MachineApplicable, + for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) { + span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| { + diag.multipart_suggestion( + "change this to", + iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx)))) + .chain(result.replacements.iter().map(|r| { + ( + r.expr_span, + format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), + ) + })) + .collect(), + Applicability::Unspecified, ); - } + }); + } +} + +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item_id: OwnerId, sig: &FnSig<'tcx>) { + if !matches!(sig.header.abi, ExternAbi::Rust) { + // Ignore `extern` functions with non-Rust calling conventions + return; + } + + for arg in check_fn_args( + cx, + cx.tcx.fn_sig(item_id).instantiate_identity().skip_binder(), + sig.decl.inputs, + &[], + ) + .filter(|arg| arg.mutability() == Mutability::Not) + { + span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, arg.build_msg(), |diag| { + diag.span_suggestion( + arg.span, + "change this to", + format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)), + Applicability::Unspecified, + ); + }); } } @@ -393,10 +196,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( hir_tys: &'tcx [hir::Ty<'tcx>], params: &'tcx [Param<'tcx>], ) -> impl Iterator> + 'cx { - fn_sig - .inputs() - .iter() - .zip(hir_tys.iter()) + iter::zip(fn_sig.inputs(), hir_tys) .enumerate() .filter_map(move |(i, (ty, hir_ty))| { if let ty::Ref(_, ty, mutability) = *ty.kind() @@ -499,41 +299,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&Body<'tcx>>) { - let FnRetTy::Return(ty) = sig.decl.output else { return }; - for (out, mutability, out_span) in get_lifetimes(ty) { - if mutability != Some(Mutability::Mut) { - continue; - } - let out_region = cx.tcx.named_bound_var(out.hir_id); - // `None` if one of the types contains `&'a mut T` or `T<'a>`. - // Else, contains all the locations of `&'a T` types. - let args_immut_refs: Option> = sig - .decl - .inputs - .iter() - .flat_map(get_lifetimes) - .filter(|&(lt, _, _)| cx.tcx.named_bound_var(lt.hir_id) == out_region) - .map(|(_, mutability, span)| (mutability == Some(Mutability::Not)).then_some(span)) - .collect(); - if let Some(args_immut_refs) = args_immut_refs - && !args_immut_refs.is_empty() - && body.is_none_or(|body| sig.header.is_unsafe() || contains_unsafe_block(cx, body.value)) - { - span_lint_and_then( - cx, - MUT_FROM_REF, - out_span, - "mutable borrow from immutable input(s)", - |diag| { - let ms = MultiSpan::from_spans(args_immut_refs); - diag.span_note(ms, "immutable borrow here"); - }, - ); - } - } -} - #[expect(clippy::too_many_lines)] fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[PtrArg<'tcx>]) -> Vec { struct V<'cx, 'tcx> { @@ -658,11 +423,11 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) { match param.pat.kind { PatKind::Binding(BindingMode::NONE, id, ident, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) - // Let's not lint for the current parameter. The user may still intend to mutate - // (or, if not mutate, then perhaps call a method that's not otherwise available - // for) the referenced value behind the parameter with the underscore being only - // temporary. - && !ident.name.as_str().starts_with('_') => + // Let's not lint for the current parameter. The user may still intend to mutate + // (or, if not mutate, then perhaps call a method that's not otherwise available + // for) the referenced value behind the parameter with the underscore being only + // temporary. + && !ident.name.as_str().starts_with('_') => { Some((id, i)) }, @@ -708,123 +473,3 @@ fn matches_preds<'tcx>( .must_apply_modulo_regions(), }) } - -struct LifetimeVisitor<'tcx> { - result: Vec<(&'tcx Lifetime, Option, Span)>, -} - -impl<'tcx> Visitor<'tcx> for LifetimeVisitor<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, hir::AmbigArg>) { - if let TyKind::Ref(lt, ref m) = ty.kind { - self.result.push((lt, Some(m.mutbl), ty.span)); - } - hir::intravisit::walk_ty(self, ty); - } - - fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) { - if let GenericArg::Lifetime(lt) = generic_arg { - self.result.push((lt, None, generic_arg.span())); - } - hir::intravisit::walk_generic_arg(self, generic_arg); - } -} - -/// Visit `ty` and collect the all the lifetimes appearing in it, implicit or not. -/// -/// The second field of the vector's elements indicate if the lifetime is attached to a -/// shared reference, a mutable reference, or neither. -fn get_lifetimes<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Vec<(&'tcx Lifetime, Option, Span)> { - use hir::intravisit::VisitorExt as _; - - let mut visitor = LifetimeVisitor { result: Vec::new() }; - visitor.visit_ty_unambig(ty); - visitor.result -} - -fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let ExprKind::Call(pathexp, []) = expr.kind { - matches!( - pathexp.basic_res().opt_diag_name(cx), - Some(sym::ptr_null | sym::ptr_null_mut) - ) - } else { - false - } -} - -fn check_ptr_eq<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - op: BinOpKind, - left: &'tcx Expr<'_>, - right: &'tcx Expr<'_>, -) { - if expr.span.from_expansion() { - return; - } - - // Remove one level of usize conversion if any - let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { - (Some(lhs), Some(rhs)) => (lhs, rhs, true), - _ => (left, right, false), - }; - - // This lint concerns raw pointers - let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); - if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() { - return; - } - - let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = - (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); - - if !(usize_peeled || left_casts_peeled || right_casts_peeled) { - return; - } - - let mut app = Applicability::MachineApplicable; - let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app); - let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app); - { - let Some(top_crate) = std_or_core(cx) else { return }; - let invert = if op == BinOpKind::Eq { "" } else { "!" }; - span_lint_and_sugg( - cx, - PTR_EQ, - expr.span, - format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), - "try", - format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), - app, - ); - } -} - -// If the given expression is a cast to a usize, return the lhs of the cast -// E.g., `foo as *const _ as usize` returns `foo as *const _`. -fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - if !cast_expr.span.from_expansion() - && cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize - && let ExprKind::Cast(expr, _) = cast_expr.kind - { - Some(expr) - } else { - None - } -} - -// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been -// peeled or not. -fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { - if !expr.span.from_expansion() - && let ExprKind::Cast(inner, _) = expr.kind - && let ty::RawPtr(target_ty, _) = expr_ty.kind() - && let inner_ty = cx.typeck_results().expr_ty(inner) - && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() - && target_ty == inner_target_ty - { - (peel_raw_casts(cx, inner, inner_ty).0, true) - } else { - (expr, false) - } -} diff --git a/src/tools/clippy/clippy_lints/src/ptr/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_eq.rs new file mode 100644 index 000000000000..c982bb1ffbc5 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_eq.rs @@ -0,0 +1,87 @@ +use super::PTR_EQ; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::std_or_core; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + op: BinOpKind, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, + span: Span, +) { + if span.from_expansion() { + return; + } + + // Remove one level of usize conversion if any + let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) { + (Some(lhs), Some(rhs)) => (lhs, rhs, true), + _ => (left, right, false), + }; + + // This lint concerns raw pointers + let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right)); + if !left_ty.is_raw_ptr() || !right_ty.is_raw_ptr() { + return; + } + + let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) = + (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty)); + + if !(usize_peeled || left_casts_peeled || right_casts_peeled) { + return; + } + + let mut app = Applicability::MachineApplicable; + let ctxt = span.ctxt(); + let left_snip = Sugg::hir_with_context(cx, left_var, ctxt, "_", &mut app); + let right_snip = Sugg::hir_with_context(cx, right_var, ctxt, "_", &mut app); + { + let Some(top_crate) = std_or_core(cx) else { return }; + let invert = if op == BinOpKind::Eq { "" } else { "!" }; + span_lint_and_sugg( + cx, + PTR_EQ, + span, + format!("use `{top_crate}::ptr::eq` when comparing raw pointers"), + "try", + format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"), + app, + ); + } +} + +// If the given expression is a cast to a usize, return the lhs of the cast +// E.g., `foo as *const _ as usize` returns `foo as *const _`. +fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { + if !cast_expr.span.from_expansion() + && cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize + && let ExprKind::Cast(expr, _) = cast_expr.kind + { + Some(expr) + } else { + None + } +} + +// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been +// peeled or not. +fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) { + if !expr.span.from_expansion() + && let ExprKind::Cast(inner, _) = expr.kind + && let ty::RawPtr(target_ty, _) = expr_ty.kind() + && let inner_ty = cx.typeck_results().expr_ty(inner) + && let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind() + && target_ty == inner_target_ty + { + (peel_raw_casts(cx, inner, inner_ty).0, true) + } else { + (expr, false) + } +} diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 14675015c35e..59d31f782bc3 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -150,7 +150,7 @@ fn init_expr_can_use_question_mark(cx: &LateContext<'_>, init_expr: &Expr<'_>) - let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( - BindingMode(ByRef::Yes(_,ref_mutability), binding_mutability), + BindingMode(ByRef::Yes(_, ref_mutability), binding_mutability), _hir_id, ident, subpattern, diff --git a/src/tools/clippy/clippy_lints/src/replace_box.rs b/src/tools/clippy/clippy_lints/src/replace_box.rs index 4bbd1803a78d..638f6dc1532b 100644 --- a/src/tools/clippy/clippy_lints/src/replace_box.rs +++ b/src/tools/clippy/clippy_lints/src/replace_box.rs @@ -3,11 +3,17 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{is_default_equivalent_call, local_is_initialized}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::smallvec::SmallVec; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, LangItem, QPath}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::sym; +use rustc_middle::hir::place::ProjectionKind; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does @@ -33,17 +39,57 @@ perf, "assigning a newly created box to `Box` is inefficient" } -declare_lint_pass!(ReplaceBox => [REPLACE_BOX]); + +#[derive(Default)] +pub struct ReplaceBox { + consumed_locals: FxHashSet, + loaded_bodies: SmallVec<[BodyId; 2]>, +} + +impl ReplaceBox { + fn get_consumed_locals(&mut self, cx: &LateContext<'_>) -> &FxHashSet { + if let Some(body_id) = cx.enclosing_body + && !self.loaded_bodies.contains(&body_id) + { + self.loaded_bodies.push(body_id); + ExprUseVisitor::for_clippy( + cx, + cx.tcx.hir_body_owner_def_id(body_id), + MovedVariablesCtxt { + consumed_locals: &mut self.consumed_locals, + }, + ) + .consume_body(cx.tcx.hir_body(body_id)) + .into_ok(); + } + + &self.consumed_locals + } +} + +impl_lint_pass!(ReplaceBox => [REPLACE_BOX]); impl LateLintPass<'_> for ReplaceBox { + fn check_body_post(&mut self, _: &LateContext<'_>, body: &Body<'_>) { + if self.loaded_bodies.first().is_some_and(|&x| x == body.id()) { + self.consumed_locals.clear(); + self.loaded_bodies.clear(); + } + } + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Assign(lhs, rhs, _) = &expr.kind && !lhs.span.from_expansion() && !rhs.span.from_expansion() && let lhs_ty = cx.typeck_results().expr_ty(lhs) + && let Some(inner_ty) = lhs_ty.boxed_ty() // No diagnostic for late-initialized locals && lhs.res_local_id().is_none_or(|local| local_is_initialized(cx, local)) - && let Some(inner_ty) = lhs_ty.boxed_ty() + // No diagnostic if this is a local that has been moved, or the field + // of a local that has been moved, or several chained field accesses of a local + && local_base(lhs).is_none_or(|(base_id, _)| { + !self.get_consumed_locals(cx).contains(&base_id) + }) { if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) && implements_trait(cx, inner_ty, default_trait_id, &[]) @@ -109,3 +155,46 @@ fn get_box_new_payload<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option< None } } + +struct MovedVariablesCtxt<'a> { + consumed_locals: &'a mut FxHashSet, +} + +impl<'tcx> Delegate<'tcx> for MovedVariablesCtxt<'_> { + fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) { + if let PlaceBase::Local(id) = cmt.place.base + && let mut projections = cmt + .place + .projections + .iter() + .filter(|x| matches!(x.kind, ProjectionKind::Deref)) + // Either no deref or multiple derefs + && (projections.next().is_none() || projections.next().is_some()) + { + self.consumed_locals.insert(id); + } + } + + fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + + fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {} + + fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} + + fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} +} + +/// A local place followed by optional fields +type IdFields = (HirId, Vec); + +/// If `expr` is a local variable with optional field accesses, return it. +fn local_base(expr: &Expr<'_>) -> Option { + match expr.kind { + ExprKind::Path(qpath) => qpath.res_local_id().map(|id| (id, Vec::new())), + ExprKind::Field(expr, field) => local_base(expr).map(|(id, mut fields)| { + fields.push(field.name); + (id, fields) + }), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index f54a26a77620..0a00981e15be 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_cfg}; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, span_contains_non_whitespace}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, PatKind, StmtKind}; @@ -27,7 +27,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) && !local.span.from_expansion() - && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) + && !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), true) { span_lint_hir_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index c4604fb1558d..fabb21f78b9e 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; use clippy_utils::source::{indent_of, snippet}; -use clippy_utils::{expr_or_init, get_attr, peel_hir_expr_unary, sym}; +use clippy_utils::{expr_or_init, get_builtin_attr, peel_hir_expr_unary, sym}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -167,7 +167,7 @@ fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { fn has_sig_drop_attr_uncached(&mut self, ty: Ty<'tcx>, depth: usize) -> bool { if let Some(adt) = ty.ty_adt_def() { - let mut iter = get_attr( + let mut iter = get_builtin_attr( self.cx.sess(), self.cx.tcx.get_all_attrs(adt.did()), sym::has_significant_drop, diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 412ca2fa4ed9..92d1b112198f 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -1,14 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr}; +use rustc_hir::{Expr, ExprKind, StructTailExpr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::DesugaringKind; use std::fmt::{self, Display, Formatter}; declare_clippy_lint! { @@ -86,19 +87,21 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { return; }; - let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else { + let ExprKind::Struct(_, [start, end], StructTailExpr::None) = inner_expr.kind else { return; }; - if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) + if inner_expr.span.is_desugaring(DesugaringKind::RangeExpr) && let ty = cx.typeck_results().expr_ty(start.expr) && let Some(snippet) = span.get_source_text(cx) // `is_from_proc_macro` will skip any `vec![]`. Let's not! && snippet.starts_with(suggested_type.starts_with()) && snippet.ends_with(suggested_type.ends_with()) - && let Some(start_snippet) = start.span.get_source_text(cx) - && let Some(end_snippet) = end.span.get_source_text(cx) { + let mut applicability = Applicability::MachineApplicable; + let (start_snippet, _) = snippet_with_context(cx, start.expr.span, span.ctxt(), "..", &mut applicability); + let (end_snippet, _) = snippet_with_context(cx, end.expr.span, span.ctxt(), "..", &mut applicability); + let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step) && implements_trait(cx, ty, step_def_id, &[]) { @@ -129,7 +132,7 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { span, "if you wanted a `Vec` that contains the entire range, try", format!("({start_snippet}..{end_snippet}).collect::>()"), - Applicability::MaybeIncorrect, + applicability, ); } @@ -138,7 +141,7 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { inner_expr.span, format!("if you wanted {suggested_type} of len {end_snippet}, try"), format!("{start_snippet}; {end_snippet}"), - Applicability::MaybeIncorrect, + applicability, ); } }, diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index 5fda388259a6..d643f7aea497 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -435,10 +435,11 @@ /// to infer a technically correct yet unexpected type. /// /// ### Example - /// ```no_run + /// ``` /// # unsafe { + /// let mut x: i32 = 0; /// // Avoid "naked" calls to `transmute()`! - /// let x: i32 = std::mem::transmute([1u16, 2u16]); + /// x = std::mem::transmute([1u16, 2u16]); /// /// // `first_answers` is intended to transmute a slice of bool to a slice of u8. /// // But the programmer forgot to index the first element of the outer slice, @@ -449,7 +450,7 @@ /// # } /// ``` /// Use instead: - /// ```no_run + /// ``` /// # unsafe { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); /// diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs index 46d9febb187f..43b38bb662dc 100644 --- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs +++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs @@ -1,115 +1,59 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qpath_generic_tys; -use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::res::MaybeResPath; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, QPath, TyKind}; +use rustc_hir::{QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use std::borrow::Cow; use super::RC_BUFFER; -pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { - let app = Applicability::Unspecified; - let name = cx.tcx.get_diagnostic_name(def_id); - if name == Some(sym::Rc) { - if let Some(alternate) = match_buffer_type(cx, qpath) { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Rc` when T is a buffer type", - |diag| { - diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app); - }, - ); - } else { - let Some(ty) = qpath_generic_tys(qpath).next() else { - return false; - }; - if !ty.basic_res().is_diag_item(cx, sym::Vec) { - return false; - } - let TyKind::Path(qpath) = &ty.kind else { return false }; - let inner_span = match qpath_generic_tys(qpath).next() { - Some(ty) => ty.span, - None => return false, - }; - span_lint_and_then( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Rc` when T is a buffer type", - |diag| { - let mut applicability = app; - diag.span_suggestion( - hir_ty.span, - "try", - format!( - "Rc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, - ); - }, - ); - return true; - } - } else if name == Some(sym::Arc) { - if let Some(alternate) = match_buffer_type(cx, qpath) { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Arc` when T is a buffer type", - |diag| { - diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app); - }, - ); - } else if let Some(ty) = qpath_generic_tys(qpath).next() { - if !ty.basic_res().is_diag_item(cx, sym::Vec) { - return false; - } - let TyKind::Path(qpath) = &ty.kind else { return false }; - let inner_span = match qpath_generic_tys(qpath).next() { - Some(ty) => ty.span, - None => return false, - }; - span_lint_and_then( - cx, - RC_BUFFER, - hir_ty.span, - "usage of `Arc` when T is a buffer type", - |diag| { - let mut applicability = app; - diag.span_suggestion( - hir_ty.span, - "try", - format!( - "Arc<[{}]>", - snippet_with_applicability(cx, inner_span, "..", &mut applicability) - ), - app, - ); - }, - ); - return true; - } +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { + let mut app = Applicability::Unspecified; + let rc = match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::Rc) => "Rc", + Some(sym::Arc) => "Arc", + _ => return false, + }; + if let Some(ty) = qpath_generic_tys(qpath).next() + && let Some(alternate) = match_buffer_type(cx, ty, &mut app) + { + span_lint_and_then( + cx, + RC_BUFFER, + hir_ty.span, + format!("usage of `{rc}` when `T` is a buffer type"), + |diag| { + diag.span_suggestion_verbose(ty.span, "try", alternate, app); + }, + ); + true + } else { + false } - - false } -fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { - let ty = qpath_generic_tys(qpath).next()?; +fn match_buffer_type( + cx: &LateContext<'_>, + ty: &Ty<'_>, + applicability: &mut Applicability, +) -> Option> { let id = ty.basic_res().opt_def_id()?; let path = match cx.tcx.get_diagnostic_name(id) { - Some(sym::OsString) => "std::ffi::OsStr", - Some(sym::PathBuf) => "std::path::Path", - _ if Some(id) == cx.tcx.lang_items().string() => "str", + Some(sym::OsString) => "std::ffi::OsStr".into(), + Some(sym::PathBuf) => "std::path::Path".into(), + Some(sym::Vec) => { + let TyKind::Path(vec_qpath) = &ty.kind else { + return None; + }; + let vec_generic_ty = qpath_generic_tys(vec_qpath).next()?; + let snippet = snippet_with_applicability(cx, vec_generic_ty.span, "_", applicability); + format!("[{snippet}]").into() + }, + _ if Some(id) == cx.tcx.lang_items().string() => "str".into(), _ => return None, }; Some(path) diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 11d3f33331cb..9d27a66a9ab8 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -215,7 +215,13 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &hir::Item<'tcx>) { } } -fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, (span, help_span): (Span, Span), is_doc: bool) { +#[expect(clippy::too_many_lines)] +fn check_has_safety_comment<'tcx>( + cx: &LateContext<'tcx>, + item: &hir::Item<'tcx>, + (span, help_span): (Span, Span), + is_doc: bool, +) { match &item.kind { ItemKind::Impl(Impl { of_trait: Some(of_trait), @@ -236,12 +242,14 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx> ItemKind::Impl(_) => {}, // const and static items only need a safety comment if their body is an unsafe block, lint otherwise &ItemKind::Const(.., ct_rhs) => { - if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, ct_rhs.hir_id()) { let expr = const_item_rhs_to_expr(cx.tcx, ct_rhs); - if let Some(expr) = expr && !matches!( - expr.kind, hir::ExprKind::Block(block, _) - if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - ) { + if let Some(expr) = expr + && !matches!( + expr.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) + { span_lint_and_then( cx, UNNECESSARY_SAFETY_COMMENT, @@ -256,8 +264,8 @@ fn check_has_safety_comment<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx> ); } } - } - &ItemKind::Static(.., body) => { + }, + &ItemKind::Static(.., body) => { if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { let body = cx.tcx.hir_body(body); if !matches!( diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs index af9f291f5deb..fba530d0dfca 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_map_on_constructor.rs @@ -35,18 +35,17 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMapOnConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if expr.span.from_expansion() { - return; - } - if let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind + if !expr.span.from_expansion() + && let hir::ExprKind::MethodCall(path, recv, [map_arg], ..) = expr.kind + && !map_arg.span.from_expansion() + && let hir::ExprKind::Path(fun) = map_arg.kind && let Some(sym::Option | sym::Result) = cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { let (constructor_path, constructor_item) = if let hir::ExprKind::Call(constructor, [arg, ..]) = recv.kind && let hir::ExprKind::Path(constructor_path) = constructor.kind + && !constructor.span.from_expansion() + && !arg.span.from_expansion() { - if constructor.span.from_expansion() || arg.span.from_expansion() { - return; - } (constructor_path, arg) } else { return; @@ -67,29 +66,22 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tc _ => return, } - if let hir::ExprKind::Path(fun) = map_arg.kind { - if map_arg.span.from_expansion() { - return; - } - let mut applicability = Applicability::MachineApplicable; - let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut applicability); - let constructor_snippet = - snippet_with_applicability(cx, constructor_path.span(), "_", &mut applicability); - let constructor_arg_snippet = - snippet_with_applicability(cx, constructor_item.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - UNNECESSARY_MAP_ON_CONSTRUCTOR, - expr.span, - format!( - "unnecessary {} on constructor {constructor_snippet}(_)", - path.ident.name - ), - "try", - format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"), - applicability, - ); - } + let mut app = Applicability::MachineApplicable; + let fun_snippet = snippet_with_applicability(cx, fun.span(), "_", &mut app); + let constructor_snippet = snippet_with_applicability(cx, constructor_path.span(), "_", &mut app); + let constructor_arg_snippet = snippet_with_applicability(cx, constructor_item.span, "_", &mut app); + span_lint_and_sugg( + cx, + UNNECESSARY_MAP_ON_CONSTRUCTOR, + expr.span, + format!( + "unnecessary `{}` on constructor `{constructor_snippet}(_)`", + path.ident.name + ), + "try", + format!("{constructor_snippet}({fun_snippet}({constructor_arg_snippet}))"), + app, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 4621c22d6b53..975dd332ad06 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -7,9 +7,10 @@ use clippy_utils::over; use rustc_ast::PatKind::*; use rustc_ast::mut_visit::*; -use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind}; +use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind, Pinnedness}; use rustc_ast_pretty::pprust; use rustc_data_structures::thin_vec::{ThinVec, thin_vec}; +use rustc_data_structures::thinvec::ExtractIf; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; @@ -98,7 +99,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { return; } - let mut pat = Box::new(pat.clone()); + let mut pat = pat.clone(); // Nix all the paren patterns everywhere so that they aren't in our way. remove_all_parens(&mut pat); @@ -120,7 +121,7 @@ fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) { } /// Remove all `(p)` patterns in `pat`. -fn remove_all_parens(pat: &mut Box) { +fn remove_all_parens(pat: &mut Pat) { #[derive(Default)] struct Visitor { /// If is not in the outer most pattern. This is needed to avoid removing the outermost @@ -143,7 +144,7 @@ fn visit_pat(&mut self, pat: &mut Pat) { } /// Insert parens where necessary according to Rust's precedence rules for patterns. -fn insert_necessary_parens(pat: &mut Box) { +fn insert_necessary_parens(pat: &mut Pat) { struct Visitor; impl MutVisitor for Visitor { fn visit_pat(&mut self, pat: &mut Pat) { @@ -151,8 +152,9 @@ fn visit_pat(&mut self, pat: &mut Pat) { walk_pat(self, pat); let target = match &mut pat.kind { // `i @ a | b`, `box a | b`, and `& mut? a | b`. - Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, - Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, // `&(mut x)` + Ident(.., Some(p)) | Box(p) | Ref(p, _, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p, + // `&(mut x)` + Ref(p, Pinnedness::Not, Mutability::Not) if matches!(p.kind, Ident(BindingMode::MUT, ..)) => p, _ => return, }; target.kind = Paren(Box::new(take_pat(target))); @@ -163,7 +165,7 @@ fn visit_pat(&mut self, pat: &mut Pat) { /// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`. /// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`. -fn unnest_or_patterns(pat: &mut Box) -> bool { +fn unnest_or_patterns(pat: &mut Pat) -> bool { struct Visitor { changed: bool, } @@ -241,7 +243,8 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec, focus_idx: usize // Skip immutable refs, as grouping them saves few characters, // and almost always requires adding parens (increasing noisiness). // In the case of only two patterns, replacement adds net characters. - | Ref(_, Mutability::Not) + // FIXME(pin_ergonomics): handle pinned patterns + | Ref(_, _, Mutability::Not) // Dealt with elsewhere. | Or(_) | Paren(_) | Deref(_) | Guard(..) => false, // Transform `box x | ... | box y` into `box (x | y)`. @@ -254,10 +257,10 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec, focus_idx: usize |k| always_pat!(k, Box(p) => *p), ), // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. - Ref(target, Mutability::Mut) => extend_with_matching( + Ref(target, _, Mutability::Mut) => extend_with_matching( target, start, alternatives, - |k| matches!(k, Ref(_, Mutability::Mut)), - |k| always_pat!(k, Ref(p, _) => *p), + |k| matches!(k, Ref(_, _, Mutability::Mut)), + |k| always_pat!(k, Ref(p, _, _) => *p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. Ident(b1, i1, Some(target)) => extend_with_matching( @@ -384,15 +387,14 @@ fn take_pat(from: &mut Pat) -> Pat { /// in `tail_or` if there are any and return if there were. fn extend_with_tail_or(target: &mut Pat, tail_or: ThinVec) -> bool { fn extend(target: &mut Pat, mut tail_or: ThinVec) { - match target { - // On an existing or-pattern in the target, append to it. - Pat { kind: Or(ps), .. } => ps.append(&mut tail_or), - // Otherwise convert the target to an or-pattern. - target => { - let mut init_or = thin_vec![take_pat(target)]; - init_or.append(&mut tail_or); - target.kind = Or(init_or); - }, + // On an existing or-pattern in the target, append to it, + // otherwise convert the target to an or-pattern. + if let Or(ps) = &mut target.kind { + ps.append(&mut tail_or); + } else { + let mut init_or = thin_vec![take_pat(target)]; + init_or.append(&mut tail_or); + target.kind = Or(init_or); } } @@ -415,26 +417,14 @@ fn drain_matching( let mut tail_or = ThinVec::new(); let mut idx = 0; - // If `ThinVec` had the `drain_filter` method, this loop could be rewritten - // like so: - // - // for pat in alternatives.drain_filter(|p| { - // // Check if we should extract, but only if `idx >= start`. - // idx += 1; - // idx > start && predicate(&p.kind) - // }) { - // tail_or.push(extract(pat.into_inner().kind)); - // } - let mut i = 0; - while i < alternatives.len() { - idx += 1; + // FIXME: once `thin-vec` releases a new version, change this to `alternatives.extract_if()` + // See https://github.com/mozilla/thin-vec/issues/77 + for pat in ExtractIf::new(alternatives, |p| { // Check if we should extract, but only if `idx >= start`. - if idx > start && predicate(&alternatives[i].kind) { - let pat = alternatives.remove(i); - tail_or.push(extract(pat.kind)); - } else { - i += 1; - } + idx += 1; + idx > start && predicate(&p.kind) + }) { + tail_or.push(extract(pat.kind)); } tail_or diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 7f9dbdd885a1..03cbb0311c6c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,5 +1,5 @@ use clippy_utils::res::MaybeQPath; -use clippy_utils::{get_attr, higher, sym}; +use clippy_utils::{get_builtin_attr, higher, sym}; use itertools::Itertools; use rustc_ast::LitIntType; use rustc_ast::ast::{LitFloatType, LitKind}; @@ -793,9 +793,9 @@ macro_rules! kind { kind!("Deref({pat})"); self.pat(pat); }, - PatKind::Ref(pat, muta) => { + PatKind::Ref(pat, pinn, muta) => { bind!(self, pat); - kind!("Ref({pat}, Mutability::{muta:?})"); + kind!("Ref({pat}, Pinning::{pinn:?}, Mutability::{muta:?})"); self.pat(pat); }, PatKind::Guard(pat, cond) => { @@ -859,5 +859,5 @@ macro_rules! kind { fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, sym::author).count() > 0 + get_builtin_attr(cx.sess(), attrs, sym::author).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs index d6cf07fdaf3f..b490866f0a11 100644 --- a/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs +++ b/src/tools/clippy/clippy_lints/src/utils/dump_hir.rs @@ -1,4 +1,4 @@ -use clippy_utils::{get_attr, sym}; +use clippy_utils::{get_builtin_attr, sym}; use hir::TraitItem; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -60,5 +60,5 @@ fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { let attrs = cx.tcx.hir_attrs(hir_id); - get_attr(cx.sess(), attrs, sym::dump).count() > 0 + get_builtin_attr(cx.sess(), attrs, sym::dump).count() > 0 } diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs deleted file mode 100644 index c39e4a4cc956..000000000000 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ /dev/null @@ -1,734 +0,0 @@ -use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; -use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; -use clippy_utils::{is_in_test, sym}; -use rustc_ast::token::LitKind; -use rustc_ast::{ - FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions, - FormatPlaceholder, FormatTrait, -}; -use rustc_errors::Applicability; -use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::impl_lint_pass; -use rustc_span::{BytePos, Span}; - -declare_clippy_lint! { - /// ### What it does - /// This lint warns when you use `println!("")` to - /// print a newline. - /// - /// ### Why is this bad? - /// You should use `println!()`, which is simpler. - /// - /// ### Example - /// ```no_run - /// println!(""); - /// ``` - /// - /// Use instead: - /// ```no_run - /// println!(); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub PRINTLN_EMPTY_STRING, - style, - "using `println!(\"\")` with an empty string" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint warns when you use `print!()` with a format - /// string that ends in a newline. - /// - /// ### Why is this bad? - /// You should use `println!()` instead, which appends the - /// newline. - /// - /// ### Example - /// ```no_run - /// # let name = "World"; - /// print!("Hello {}!\n", name); - /// ``` - /// use println!() instead - /// ```no_run - /// # let name = "World"; - /// println!("Hello {}!", name); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub PRINT_WITH_NEWLINE, - style, - "using `print!()` with a format string that ends in a single newline" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for printing on *stdout*. The purpose of this lint - /// is to catch debugging remnants. - /// - /// ### Why restrict this? - /// People often print on *stdout* while debugging an - /// application and might forget to remove those prints afterward. - /// - /// ### Known problems - /// Only catches `print!` and `println!` calls. - /// - /// ### Example - /// ```no_run - /// println!("Hello world!"); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub PRINT_STDOUT, - restriction, - "printing on stdout" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for printing on *stderr*. The purpose of this lint - /// is to catch debugging remnants. - /// - /// ### Why restrict this? - /// People often print on *stderr* while debugging an - /// application and might forget to remove those prints afterward. - /// - /// ### Known problems - /// Only catches `eprint!` and `eprintln!` calls. - /// - /// ### Example - /// ```no_run - /// eprintln!("Hello world!"); - /// ``` - #[clippy::version = "1.50.0"] - pub PRINT_STDERR, - restriction, - "printing on stderr" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of `Debug` formatting. The purpose of this - /// lint is to catch debugging remnants. - /// - /// ### Why restrict this? - /// The purpose of the `Debug` trait is to facilitate debugging Rust code, - /// and [no guarantees are made about its output][stability]. - /// It should not be used in user-facing output. - /// - /// ### Example - /// ```no_run - /// # let foo = "bar"; - /// println!("{:?}", foo); - /// ``` - /// - /// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability - #[clippy::version = "pre 1.29.0"] - pub USE_DEBUG, - restriction, - "use of `Debug`-based formatting" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint warns about the use of literals as `print!`/`println!` args. - /// - /// ### Why is this bad? - /// Using literals as `println!` args is inefficient - /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary - /// (i.e., just put the literal in the format string) - /// - /// ### Example - /// ```no_run - /// println!("{}", "foo"); - /// ``` - /// use the literal without formatting: - /// ```no_run - /// println!("foo"); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub PRINT_LITERAL, - style, - "printing a literal with a format string" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint warns when you use `writeln!(buf, "")` to - /// print a newline. - /// - /// ### Why is this bad? - /// You should use `writeln!(buf)`, which is simpler. - /// - /// ### Example - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// writeln!(buf, ""); - /// ``` - /// - /// Use instead: - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// writeln!(buf); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub WRITELN_EMPTY_STRING, - style, - "using `writeln!(buf, \"\")` with an empty string" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint warns when you use `write!()` with a format - /// string that - /// ends in a newline. - /// - /// ### Why is this bad? - /// You should use `writeln!()` instead, which appends the - /// newline. - /// - /// ### Example - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// # let name = "World"; - /// write!(buf, "Hello {}!\n", name); - /// ``` - /// - /// Use instead: - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// # let name = "World"; - /// writeln!(buf, "Hello {}!", name); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub WRITE_WITH_NEWLINE, - style, - "using `write!()` with a format string that ends in a single newline" -} - -declare_clippy_lint! { - /// ### What it does - /// This lint warns about the use of literals as `write!`/`writeln!` args. - /// - /// ### Why is this bad? - /// Using literals as `writeln!` args is inefficient - /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary - /// (i.e., just put the literal in the format string) - /// - /// ### Example - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// writeln!(buf, "{}", "foo"); - /// ``` - /// - /// Use instead: - /// ```no_run - /// # use std::fmt::Write; - /// # let mut buf = String::new(); - /// writeln!(buf, "foo"); - /// ``` - #[clippy::version = "pre 1.29.0"] - pub WRITE_LITERAL, - style, - "writing a literal with a format string" -} - -pub struct Write { - format_args: FormatArgsStorage, - // The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated - outermost_debug_impl: Option, - allow_print_in_tests: bool, -} - -impl Write { - pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { - Self { - format_args, - outermost_debug_impl: None, - allow_print_in_tests: conf.allow_print_in_tests, - } - } - - fn in_debug_impl(&self) -> bool { - self.outermost_debug_impl.is_some() - } -} - -impl_lint_pass!(Write => [ - PRINT_WITH_NEWLINE, - PRINTLN_EMPTY_STRING, - PRINT_STDOUT, - PRINT_STDERR, - USE_DEBUG, - PRINT_LITERAL, - WRITE_WITH_NEWLINE, - WRITELN_EMPTY_STRING, - WRITE_LITERAL, -]); - -impl<'tcx> LateLintPass<'tcx> for Write { - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - // Only check for `impl Debug`s if we're not already in one - if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) { - self.outermost_debug_impl = Some(item.owner_id); - } - } - - fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { - // Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl - if self.outermost_debug_impl == Some(item.owner_id) { - self.outermost_debug_impl = None; - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { - return; - }; - let Some(name) = diag_name.as_str().strip_suffix("_macro") else { - return; - }; - - let is_build_script = cx - .sess() - .opts - .crate_name - .as_ref() - .is_some_and(|crate_name| crate_name == "build_script_build"); - - let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id); - match diag_name { - sym::print_macro | sym::println_macro if !allowed_in_tests => { - if !is_build_script { - span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`")); - } - }, - sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { - span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`")); - }, - sym::write_macro | sym::writeln_macro => {}, - _ => return, - } - - if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { - // ignore `writeln!(w)` and `write!(v, some_macro!())` - if format_args.span.from_expansion() { - return; - } - - match diag_name { - sym::print_macro | sym::eprint_macro | sym::write_macro => { - check_newline(cx, format_args, ¯o_call, name); - }, - sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { - check_empty_string(cx, format_args, ¯o_call, name); - }, - _ => {}, - } - - check_literal(cx, format_args, name); - - if !self.in_debug_impl() { - for piece in &format_args.template { - if let &FormatArgsPiece::Placeholder(FormatPlaceholder { - span: Some(span), - format_trait: FormatTrait::Debug, - .. - }) = piece - { - span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); - } - } - } - } - } -} - -fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { - if let ItemKind::Impl(Impl { - of_trait: Some(of_trait), - .. - }) = &item.kind - && let Some(trait_id) = of_trait.trait_ref.trait_def_id() - { - cx.tcx.is_diagnostic_item(sym::Debug, trait_id) - } else { - false - } -} - -fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { - return; - }; - - let count_vertical_whitespace = || { - format_args - .template - .iter() - .filter_map(|piece| match piece { - FormatArgsPiece::Literal(literal) => Some(literal), - FormatArgsPiece::Placeholder(_) => None, - }) - .flat_map(|literal| literal.as_str().chars()) - .filter(|ch| matches!(ch, '\r' | '\n')) - .count() - }; - - if last.as_str().ends_with('\n') - // ignore format strings with other internal vertical whitespace - && count_vertical_whitespace() == 1 - { - let mut format_string_span = format_args.span; - - let lint = if name == "write" { - format_string_span = expand_past_previous_comma(cx, format_string_span); - - WRITE_WITH_NEWLINE - } else { - PRINT_WITH_NEWLINE - }; - - span_lint_and_then( - cx, - lint, - macro_call.span, - format!("using `{name}!()` with a format string that ends in a single newline"), - |diag| { - let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = format_string_span.get_source_text(cx) else { - return; - }; - - if format_args.template.len() == 1 && last == sym::LF { - // print!("\n"), write!(f, "\n") - - diag.multipart_suggestion( - format!("use `{name}ln!` instead"), - vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], - Applicability::MachineApplicable, - ); - } else if format_snippet.ends_with("\\n\"") { - // print!("...\n"), write!(f, "...\n") - - let hi = format_string_span.hi(); - let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); - - diag.multipart_suggestion( - format!("use `{name}ln!` instead"), - vec![(name_span, format!("{name}ln")), (newline_span, String::new())], - Applicability::MachineApplicable, - ); - } - }, - ); - } -} - -fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { - if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { - let mut span = format_args.span; - - let lint = if name == "writeln" { - span = expand_past_previous_comma(cx, span); - - WRITELN_EMPTY_STRING - } else { - PRINTLN_EMPTY_STRING - }; - - span_lint_and_then( - cx, - lint, - macro_call.span, - format!("empty string literal in `{name}!`"), - |diag| { - diag.span_suggestion( - span, - "remove the empty string", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - } -} - -fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { - let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); - - let lint_name = if name.starts_with("write") { - WRITE_LITERAL - } else { - PRINT_LITERAL - }; - - let mut counts = vec![0u32; format_args.arguments.all_args().len()]; - for piece in &format_args.template { - if let FormatArgsPiece::Placeholder(placeholder) = piece { - counts[arg_index(&placeholder.argument)] += 1; - } - } - - let mut suggestion: Vec<(Span, String)> = vec![]; - // holds index of replaced positional arguments; used to decrement the index of the remaining - // positional arguments. - let mut replaced_position: Vec = vec![]; - let mut sug_span: Option = None; - - for piece in &format_args.template { - if let FormatArgsPiece::Placeholder(FormatPlaceholder { - argument, - span: Some(placeholder_span), - format_trait: FormatTrait::Display, - format_options, - }) = piece - && *format_options == FormatOptions::default() - && let index = arg_index(argument) - && counts[index] == 1 - && let Some(arg) = format_args.arguments.by_index(index) - && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind - && !arg.expr.span.from_expansion() - && let Some(value_string) = arg.expr.span.get_source_text(cx) - { - let (replacement, replace_raw) = match lit.kind { - LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { - Some(extracted) => extracted, - None => return, - }, - LitKind::Char => ( - match lit.symbol { - sym::DOUBLE_QUOTE => "\\\"", - sym::BACKSLASH_SINGLE_QUOTE => "'", - _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { - Some(stripped) => stripped, - None => return, - }, - } - .to_string(), - false, - ), - LitKind::Bool => (lit.symbol.to_string(), false), - _ => continue, - }; - - let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { - continue; - }; - let format_string_is_raw = format_string_snippet.starts_with('r'); - - let replacement = match (format_string_is_raw, replace_raw) { - (false, false) => Some(replacement), - (false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")), - (true, false) => match conservative_unescape(&replacement) { - Ok(unescaped) => Some(unescaped), - Err(UnescapeErr::Lint) => None, - Err(UnescapeErr::Ignore) => continue, - }, - (true, true) => { - if replacement.contains(['#', '"']) { - None - } else { - Some(replacement) - } - }, - }; - - sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); - - if let Some((_, index)) = format_arg_piece_span(piece) { - replaced_position.push(index); - } - - if let Some(replacement) = replacement - // `format!("{}", "a")`, `format!("{named}", named = "b") - // ~~~~~ ~~~~~~~~~~~~~ - && let Some(removal_span) = format_arg_removal_span(format_args, index) - { - let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw); - suggestion.push((*placeholder_span, replacement)); - suggestion.push((removal_span, String::new())); - } - } - } - - // Decrement the index of the remaining by the number of replaced positional arguments - if !suggestion.is_empty() { - for piece in &format_args.template { - relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position); - } - } - - if let Some(span) = sug_span { - span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| { - if !suggestion.is_empty() { - diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable); - } - }); - } -} - -/// Extract Span and its index from the given `piece` -fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { - match piece { - FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: FormatArgPosition { index: Ok(index), .. }, - span: Some(span), - .. - }) => Some((*span, *index)), - _ => None, - } -} - -/// Relocalizes the indexes of positional arguments in the format string -fn relocalize_format_args_indexes( - piece: &FormatArgsPiece, - suggestion: &mut Vec<(Span, String)>, - replaced_position: &[usize], -) { - if let FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: - FormatArgPosition { - index: Ok(index), - // Only consider positional arguments - kind: FormatArgPositionKind::Number, - span: Some(span), - }, - format_options, - .. - }) = piece - { - if suggestion.iter().any(|(s, _)| s.overlaps(*span)) { - // If the span is already in the suggestion, we don't need to process it again - return; - } - - // lambda to get the decremented index based on the replaced positions - let decremented_index = |index: usize| -> usize { - let decrement = replaced_position.iter().filter(|&&i| i < index).count(); - index - decrement - }; - - suggestion.push((*span, decremented_index(*index).to_string())); - - // If there are format options, we need to handle them as well - if *format_options != FormatOptions::default() { - // lambda to process width and precision format counts and add them to the suggestion - let mut process_format_count = |count: &Option, formatter: &dyn Fn(usize) -> String| { - if let Some(FormatCount::Argument(FormatArgPosition { - index: Ok(format_arg_index), - kind: FormatArgPositionKind::Number, - span: Some(format_arg_span), - })) = count - { - suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index)))); - } - }; - - process_format_count(&format_options.width, &|index: usize| format!("{index}$")); - process_format_count(&format_options.precision, &|index: usize| format!(".{index}$")); - } - } -} - -/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw -/// -/// `r#"a"#` -> (`a`, true) -/// -/// `"b"` -> (`b`, false) -fn extract_str_literal(literal: &str) -> Option<(String, bool)> { - let (literal, raw) = match literal.strip_prefix('r') { - Some(stripped) => (stripped.trim_matches('#'), true), - None => (literal, false), - }; - - Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw)) -} - -enum UnescapeErr { - /// Should still be linted, can be manually resolved by author, e.g. - /// - /// ```ignore - /// print!(r"{}", '"'); - /// ``` - Lint, - /// Should not be linted, e.g. - /// - /// ```ignore - /// print!(r"{}", '\r'); - /// ``` - Ignore, -} - -/// Unescape a normal string into a raw string -fn conservative_unescape(literal: &str) -> Result { - let mut unescaped = String::with_capacity(literal.len()); - let mut chars = literal.chars(); - let mut err = false; - - while let Some(ch) = chars.next() { - match ch { - '#' => err = true, - '\\' => match chars.next() { - Some('\\') => unescaped.push('\\'), - Some('"') => err = true, - _ => return Err(UnescapeErr::Ignore), - }, - _ => unescaped.push(ch), - } - } - - if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } -} - -/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces in -/// `\u{xxxx}` are left unmodified -#[expect(clippy::match_same_arms)] -fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String { - #[derive(Clone, Copy)] - enum State { - Normal, - Backslash, - UnicodeEscape, - } - - let mut escaped = String::with_capacity(literal.len()); - let mut state = State::Normal; - - for ch in literal.chars() { - state = match (ch, state) { - // Escape braces outside of unicode escapes by doubling them up - ('{' | '}', State::Normal) => { - escaped.push(ch); - State::Normal - }, - // If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise: - // - // \u{aaaa} \\ \x01 - // ^ ^ ^ - ('\\', State::Normal) if preserve_unicode_escapes => State::Backslash, - // \u{aaaa} - // ^ - ('u', State::Backslash) => State::UnicodeEscape, - // \xAA \\ - // ^ ^ - (_, State::Backslash) => State::Normal, - // \u{aaaa} - // ^ - ('}', State::UnicodeEscape) => State::Normal, - _ => state, - }; - - escaped.push(ch); - } - - escaped -} diff --git a/src/tools/clippy/clippy_lints/src/write/empty_string.rs b/src/tools/clippy/clippy_lints/src/write/empty_string.rs new file mode 100644 index 000000000000..e7eb99eb34ec --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write/empty_string.rs @@ -0,0 +1,38 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::MacroCall; +use clippy_utils::source::expand_past_previous_comma; +use clippy_utils::sym; +use rustc_ast::{FormatArgs, FormatArgsPiece}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; + +use super::{PRINTLN_EMPTY_STRING, WRITELN_EMPTY_STRING}; + +pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + if let [FormatArgsPiece::Literal(sym::LF)] = &format_args.template[..] { + let mut span = format_args.span; + + let lint = if name == "writeln" { + span = expand_past_previous_comma(cx, span); + + WRITELN_EMPTY_STRING + } else { + PRINTLN_EMPTY_STRING + }; + + span_lint_and_then( + cx, + lint, + macro_call.span, + format!("empty string literal in `{name}!`"), + |diag| { + diag.span_suggestion( + span, + "remove the empty string", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/write/literal.rs b/src/tools/clippy/clippy_lints/src/write/literal.rs new file mode 100644 index 000000000000..699ac7ea7a5c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write/literal.rs @@ -0,0 +1,285 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::format_arg_removal_span; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sym; +use rustc_ast::token::LitKind; +use rustc_ast::{ + FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatCount, FormatOptions, + FormatPlaceholder, FormatTrait, +}; +use rustc_errors::Applicability; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::{PRINT_LITERAL, WRITE_LITERAL}; + +pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { + let arg_index = |argument: &FormatArgPosition| argument.index.unwrap_or_else(|pos| pos); + + let lint_name = if name.starts_with("write") { + WRITE_LITERAL + } else { + PRINT_LITERAL + }; + + let mut counts = vec![0u32; format_args.arguments.all_args().len()]; + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(placeholder) = piece { + counts[arg_index(&placeholder.argument)] += 1; + } + } + + let mut suggestion: Vec<(Span, String)> = vec![]; + // holds index of replaced positional arguments; used to decrement the index of the remaining + // positional arguments. + let mut replaced_position: Vec = vec![]; + let mut sug_span: Option = None; + + for piece in &format_args.template { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span: Some(placeholder_span), + format_trait: FormatTrait::Display, + format_options, + }) = piece + && *format_options == FormatOptions::default() + && let index = arg_index(argument) + && counts[index] == 1 + && let Some(arg) = format_args.arguments.by_index(index) + && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind + && !arg.expr.span.from_expansion() + && let Some(value_string) = arg.expr.span.get_source_text(cx) + { + let (replacement, replace_raw) = match lit.kind { + LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { + Some(extracted) => extracted, + None => return, + }, + LitKind::Char => ( + match lit.symbol { + sym::DOUBLE_QUOTE => "\\\"", + sym::BACKSLASH_SINGLE_QUOTE => "'", + _ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { + Some(stripped) => stripped, + None => return, + }, + } + .to_string(), + false, + ), + LitKind::Bool => (lit.symbol.to_string(), false), + _ => continue, + }; + + let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { + continue; + }; + let format_string_is_raw = format_string_snippet.starts_with('r'); + + let replacement = match (format_string_is_raw, replace_raw) { + (false, false) => Some(replacement), + (false, true) => Some(replacement.replace('\\', "\\\\").replace('"', "\\\"")), + (true, false) => match conservative_unescape(&replacement) { + Ok(unescaped) => Some(unescaped), + Err(UnescapeErr::Lint) => None, + Err(UnescapeErr::Ignore) => continue, + }, + (true, true) => { + if replacement.contains(['#', '"']) { + None + } else { + Some(replacement) + } + }, + }; + + sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); + + if let Some((_, index)) = format_arg_piece_span(piece) { + replaced_position.push(index); + } + + if let Some(replacement) = replacement + // `format!("{}", "a")`, `format!("{named}", named = "b") + // ~~~~~ ~~~~~~~~~~~~~ + && let Some(removal_span) = format_arg_removal_span(format_args, index) + { + let replacement = escape_braces(&replacement, !format_string_is_raw && !replace_raw); + suggestion.push((*placeholder_span, replacement)); + suggestion.push((removal_span, String::new())); + } + } + } + + // Decrement the index of the remaining by the number of replaced positional arguments + if !suggestion.is_empty() { + for piece in &format_args.template { + relocalize_format_args_indexes(piece, &mut suggestion, &replaced_position); + } + } + + if let Some(span) = sug_span { + span_lint_and_then(cx, lint_name, span, "literal with an empty format string", |diag| { + if !suggestion.is_empty() { + diag.multipart_suggestion("try", suggestion, Applicability::MachineApplicable); + } + }); + } +} + +/// Extract Span and its index from the given `piece` +fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { + match piece { + FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: FormatArgPosition { index: Ok(index), .. }, + span: Some(span), + .. + }) => Some((*span, *index)), + _ => None, + } +} + +/// Relocalizes the indexes of positional arguments in the format string +fn relocalize_format_args_indexes( + piece: &FormatArgsPiece, + suggestion: &mut Vec<(Span, String)>, + replaced_position: &[usize], +) { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: + FormatArgPosition { + index: Ok(index), + // Only consider positional arguments + kind: FormatArgPositionKind::Number, + span: Some(span), + }, + format_options, + .. + }) = piece + { + if suggestion.iter().any(|(s, _)| s.overlaps(*span)) { + // If the span is already in the suggestion, we don't need to process it again + return; + } + + // lambda to get the decremented index based on the replaced positions + let decremented_index = |index: usize| -> usize { + let decrement = replaced_position.iter().filter(|&&i| i < index).count(); + index - decrement + }; + + suggestion.push((*span, decremented_index(*index).to_string())); + + // If there are format options, we need to handle them as well + if *format_options != FormatOptions::default() { + // lambda to process width and precision format counts and add them to the suggestion + let mut process_format_count = |count: &Option, formatter: &dyn Fn(usize) -> String| { + if let Some(FormatCount::Argument(FormatArgPosition { + index: Ok(format_arg_index), + kind: FormatArgPositionKind::Number, + span: Some(format_arg_span), + })) = count + { + suggestion.push((*format_arg_span, formatter(decremented_index(*format_arg_index)))); + } + }; + + process_format_count(&format_options.width, &|index: usize| format!("{index}$")); + process_format_count(&format_options.precision, &|index: usize| format!(".{index}$")); + } + } +} + +/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw +/// +/// `r#"a"#` -> (`a`, true) +/// +/// `"b"` -> (`b`, false) +fn extract_str_literal(literal: &str) -> Option<(String, bool)> { + let (literal, raw) = match literal.strip_prefix('r') { + Some(stripped) => (stripped.trim_matches('#'), true), + None => (literal, false), + }; + + Some((literal.strip_prefix('"')?.strip_suffix('"')?.to_string(), raw)) +} + +enum UnescapeErr { + /// Should still be linted, can be manually resolved by author, e.g. + /// + /// ```ignore + /// print!(r"{}", '"'); + /// ``` + Lint, + /// Should not be linted, e.g. + /// + /// ```ignore + /// print!(r"{}", '\r'); + /// ``` + Ignore, +} + +/// Unescape a normal string into a raw string +fn conservative_unescape(literal: &str) -> Result { + let mut unescaped = String::with_capacity(literal.len()); + let mut chars = literal.chars(); + let mut err = false; + + while let Some(ch) = chars.next() { + match ch { + '#' => err = true, + '\\' => match chars.next() { + Some('\\') => unescaped.push('\\'), + Some('"') => err = true, + _ => return Err(UnescapeErr::Ignore), + }, + _ => unescaped.push(ch), + } + } + + if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) } +} + +/// Replaces `{` with `{{` and `}` with `}}`. If `preserve_unicode_escapes` is `true` the braces +/// in `\u{xxxx}` are left unmodified +#[expect(clippy::match_same_arms)] +fn escape_braces(literal: &str, preserve_unicode_escapes: bool) -> String { + #[derive(Clone, Copy)] + enum State { + Normal, + Backslash, + UnicodeEscape, + } + + let mut escaped = String::with_capacity(literal.len()); + let mut state = State::Normal; + + for ch in literal.chars() { + state = match (ch, state) { + // Escape braces outside of unicode escapes by doubling them up + ('{' | '}', State::Normal) => { + escaped.push(ch); + State::Normal + }, + // If `preserve_unicode_escapes` isn't enabled stay in `State::Normal`, otherwise: + // + // \u{aaaa} \\ \x01 + // ^ ^ ^ + ('\\', State::Normal) if preserve_unicode_escapes => State::Backslash, + // \u{aaaa} + // ^ + ('u', State::Backslash) => State::UnicodeEscape, + // \xAA \\ + // ^ ^ + (_, State::Backslash) => State::Normal, + // \u{aaaa} + // ^ + ('}', State::UnicodeEscape) => State::Normal, + _ => state, + }; + + escaped.push(ch); + } + + escaped +} diff --git a/src/tools/clippy/clippy_lints/src/write/mod.rs b/src/tools/clippy/clippy_lints/src/write/mod.rs new file mode 100644 index 000000000000..c42c047745bb --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write/mod.rs @@ -0,0 +1,354 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{FormatArgsStorage, root_macro_call_first_node}; +use clippy_utils::{is_in_test, sym}; +use rustc_hir::{Expr, Impl, Item, ItemKind, OwnerId}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::impl_lint_pass; + +mod empty_string; +mod literal; +mod use_debug; +mod with_newline; + +declare_clippy_lint! { + /// ### What it does + /// This lint warns when you use `println!("")` to + /// print a newline. + /// + /// ### Why is this bad? + /// You should use `println!()`, which is simpler. + /// + /// ### Example + /// ```no_run + /// println!(""); + /// ``` + /// + /// Use instead: + /// ```no_run + /// println!(); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PRINTLN_EMPTY_STRING, + style, + "using `println!(\"\")` with an empty string" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint warns when you use `print!()` with a format + /// string that ends in a newline. + /// + /// ### Why is this bad? + /// You should use `println!()` instead, which appends the + /// newline. + /// + /// ### Example + /// ```no_run + /// # let name = "World"; + /// print!("Hello {}!\n", name); + /// ``` + /// use println!() instead + /// ```no_run + /// # let name = "World"; + /// println!("Hello {}!", name); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PRINT_WITH_NEWLINE, + style, + "using `print!()` with a format string that ends in a single newline" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for printing on *stdout*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// ### Why restrict this? + /// People often print on *stdout* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// ### Known problems + /// Only catches `print!` and `println!` calls. + /// + /// ### Example + /// ```no_run + /// println!("Hello world!"); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PRINT_STDOUT, + restriction, + "printing on stdout" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for printing on *stderr*. The purpose of this lint + /// is to catch debugging remnants. + /// + /// ### Why restrict this? + /// People often print on *stderr* while debugging an + /// application and might forget to remove those prints afterward. + /// + /// ### Known problems + /// Only catches `eprint!` and `eprintln!` calls. + /// + /// ### Example + /// ```no_run + /// eprintln!("Hello world!"); + /// ``` + #[clippy::version = "1.50.0"] + pub PRINT_STDERR, + restriction, + "printing on stderr" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `Debug` formatting. The purpose of this + /// lint is to catch debugging remnants. + /// + /// ### Why restrict this? + /// The purpose of the `Debug` trait is to facilitate debugging Rust code, + /// and [no guarantees are made about its output][stability]. + /// It should not be used in user-facing output. + /// + /// ### Example + /// ```no_run + /// # let foo = "bar"; + /// println!("{:?}", foo); + /// ``` + /// + /// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability + #[clippy::version = "pre 1.29.0"] + pub USE_DEBUG, + restriction, + "use of `Debug`-based formatting" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint warns about the use of literals as `print!`/`println!` args. + /// + /// ### Why is this bad? + /// Using literals as `println!` args is inefficient + /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary + /// (i.e., just put the literal in the format string) + /// + /// ### Example + /// ```no_run + /// println!("{}", "foo"); + /// ``` + /// use the literal without formatting: + /// ```no_run + /// println!("foo"); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub PRINT_LITERAL, + style, + "printing a literal with a format string" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint warns when you use `writeln!(buf, "")` to + /// print a newline. + /// + /// ### Why is this bad? + /// You should use `writeln!(buf)`, which is simpler. + /// + /// ### Example + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf, ""); + /// ``` + /// + /// Use instead: + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub WRITELN_EMPTY_STRING, + style, + "using `writeln!(buf, \"\")` with an empty string" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint warns when you use `write!()` with a format + /// string that + /// ends in a newline. + /// + /// ### Why is this bad? + /// You should use `writeln!()` instead, which appends the + /// newline. + /// + /// ### Example + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let name = "World"; + /// write!(buf, "Hello {}!\n", name); + /// ``` + /// + /// Use instead: + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// # let name = "World"; + /// writeln!(buf, "Hello {}!", name); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub WRITE_WITH_NEWLINE, + style, + "using `write!()` with a format string that ends in a single newline" +} + +declare_clippy_lint! { + /// ### What it does + /// This lint warns about the use of literals as `write!`/`writeln!` args. + /// + /// ### Why is this bad? + /// Using literals as `writeln!` args is inefficient + /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary + /// (i.e., just put the literal in the format string) + /// + /// ### Example + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf, "{}", "foo"); + /// ``` + /// + /// Use instead: + /// ```no_run + /// # use std::fmt::Write; + /// # let mut buf = String::new(); + /// writeln!(buf, "foo"); + /// ``` + #[clippy::version = "pre 1.29.0"] + pub WRITE_LITERAL, + style, + "writing a literal with a format string" +} + +pub struct Write { + format_args: FormatArgsStorage, + // The outermost `impl Debug` we're currently in. While we're in one, `USE_DEBUG` is deactivated + outermost_debug_impl: Option, + allow_print_in_tests: bool, +} + +impl Write { + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { + Self { + format_args, + outermost_debug_impl: None, + allow_print_in_tests: conf.allow_print_in_tests, + } + } + + fn in_debug_impl(&self) -> bool { + self.outermost_debug_impl.is_some() + } +} + +impl_lint_pass!(Write => [ + PRINT_WITH_NEWLINE, + PRINTLN_EMPTY_STRING, + PRINT_STDOUT, + PRINT_STDERR, + USE_DEBUG, + PRINT_LITERAL, + WRITE_WITH_NEWLINE, + WRITELN_EMPTY_STRING, + WRITE_LITERAL, +]); + +impl<'tcx> LateLintPass<'tcx> for Write { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + // Only check for `impl Debug`s if we're not already in one + if self.outermost_debug_impl.is_none() && is_debug_impl(cx, item) { + self.outermost_debug_impl = Some(item.owner_id); + } + } + + fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) { + // Only clear `self.outermost_debug_impl` if we're escaping the _outermost_ debug impl + if self.outermost_debug_impl == Some(item.owner_id) { + self.outermost_debug_impl = None; + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { + return; + }; + let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { + return; + }; + let Some(name) = diag_name.as_str().strip_suffix("_macro") else { + return; + }; + + let is_build_script = cx + .sess() + .opts + .crate_name + .as_ref() + .is_some_and(|crate_name| crate_name == "build_script_build"); + + let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id); + match diag_name { + sym::print_macro | sym::println_macro if !allowed_in_tests => { + if !is_build_script { + span_lint(cx, PRINT_STDOUT, macro_call.span, format!("use of `{name}!`")); + } + }, + sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { + span_lint(cx, PRINT_STDERR, macro_call.span, format!("use of `{name}!`")); + }, + sym::write_macro | sym::writeln_macro => {}, + _ => return, + } + + if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) { + // ignore `writeln!(w)` and `write!(v, some_macro!())` + if format_args.span.from_expansion() { + return; + } + + match diag_name { + sym::print_macro | sym::eprint_macro | sym::write_macro => { + with_newline::check(cx, format_args, ¯o_call, name); + }, + sym::println_macro | sym::eprintln_macro | sym::writeln_macro => { + empty_string::check(cx, format_args, ¯o_call, name); + }, + _ => {}, + } + + literal::check(cx, format_args, name); + + if !self.in_debug_impl() { + use_debug::check(cx, format_args); + } + } + } +} + +fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool { + if let ItemKind::Impl(Impl { + of_trait: Some(of_trait), + .. + }) = &item.kind + && let Some(trait_id) = of_trait.trait_ref.trait_def_id() + { + cx.tcx.is_diagnostic_item(sym::Debug, trait_id) + } else { + false + } +} diff --git a/src/tools/clippy/clippy_lints/src/write/use_debug.rs b/src/tools/clippy/clippy_lints/src/write/use_debug.rs new file mode 100644 index 000000000000..75dddeb5d2a7 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write/use_debug.rs @@ -0,0 +1,18 @@ +use clippy_utils::diagnostics::span_lint; +use rustc_ast::{FormatArgs, FormatArgsPiece, FormatPlaceholder, FormatTrait}; +use rustc_lint::LateContext; + +use super::USE_DEBUG; + +pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs) { + for piece in &format_args.template { + if let &FormatArgsPiece::Placeholder(FormatPlaceholder { + span: Some(span), + format_trait: FormatTrait::Debug, + .. + }) = piece + { + span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting"); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/write/with_newline.rs b/src/tools/clippy/clippy_lints/src/write/with_newline.rs new file mode 100644 index 000000000000..e4b51da3cadc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/write/with_newline.rs @@ -0,0 +1,78 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::MacroCall; +use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::sym; +use rustc_ast::{FormatArgs, FormatArgsPiece}; +use rustc_errors::Applicability; +use rustc_lint::{LateContext, LintContext}; +use rustc_span::BytePos; + +use super::{PRINT_WITH_NEWLINE, WRITE_WITH_NEWLINE}; + +pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &MacroCall, name: &str) { + let Some(&FormatArgsPiece::Literal(last)) = format_args.template.last() else { + return; + }; + + let count_vertical_whitespace = || { + format_args + .template + .iter() + .filter_map(|piece| match piece { + FormatArgsPiece::Literal(literal) => Some(literal), + FormatArgsPiece::Placeholder(_) => None, + }) + .flat_map(|literal| literal.as_str().chars()) + .filter(|ch| matches!(ch, '\r' | '\n')) + .count() + }; + + if last.as_str().ends_with('\n') + // ignore format strings with other internal vertical whitespace + && count_vertical_whitespace() == 1 + { + let mut format_string_span = format_args.span; + + let lint = if name == "write" { + format_string_span = expand_past_previous_comma(cx, format_string_span); + + WRITE_WITH_NEWLINE + } else { + PRINT_WITH_NEWLINE + }; + + span_lint_and_then( + cx, + lint, + macro_call.span, + format!("using `{name}!()` with a format string that ends in a single newline"), + |diag| { + let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); + let Some(format_snippet) = format_string_span.get_source_text(cx) else { + return; + }; + + if format_args.template.len() == 1 && last == sym::LF { + // print!("\n"), write!(f, "\n") + + diag.multipart_suggestion( + format!("use `{name}ln!` instead"), + vec![(name_span, format!("{name}ln")), (format_string_span, String::new())], + Applicability::MachineApplicable, + ); + } else if format_snippet.ends_with("\\n\"") { + // print!("...\n"), write!(f, "...\n") + + let hi = format_string_span.hi(); + let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1)); + + diag.multipart_suggestion( + format!("use `{name}ln!` instead"), + vec![(name_span, format!("{name}ln")), (newline_span, String::new())], + Applicability::MachineApplicable, + ); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 45463b4fa1db..6f976094fc2d 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-10-31 +nightly-2025-11-14 ``` diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 771862ee5b96..9c08f7b4d80f 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -45,9 +45,8 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { && eq_expr_opt(lt.as_deref(), rt.as_deref()) && eq_range_end(&le.node, &re.node) }, - (Box(l), Box(r)) - | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) - | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), + (Box(l), Box(r)) => eq_pat(l, r), + (Ref(l, l_pin, l_mut), Ref(r, r_pin, r_mut)) => l_pin == r_pin && l_mut == r_mut && eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, eq_pat), (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { @@ -372,7 +371,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_ty(lt, rt) - && both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r)) + && both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs) }, ( Fn(box ast::Fn { @@ -626,7 +625,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { && eq_id(*li, *ri) && eq_generics(lg, rg) && eq_ty(lt, rt) - && both(lb.as_ref(), rb.as_ref(), |l, r| eq_const_item_rhs(l, r)) + && both(lb.as_ref(), rb.as_ref(), eq_const_item_rhs) }, ( Fn(box ast::Fn { diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 2d42e76dcbc9..671b266ba008 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,3 +1,5 @@ +//! Utility functions for attributes, including Clippy's built-in ones + use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr; @@ -12,131 +14,59 @@ use rustc_span::{Span, Symbol}; use std::str::FromStr; -/// Deprecation status of attributes known by Clippy. -pub enum DeprecationStatus { - /// Attribute is deprecated - Deprecated, - /// Attribute is deprecated and was replaced by the named attribute - Replaced(&'static str), - None, -} - -#[rustfmt::skip] -pub const BUILTIN_ATTRIBUTES: &[(Symbol, DeprecationStatus)] = &[ - (sym::author, DeprecationStatus::None), - (sym::version, DeprecationStatus::None), - (sym::cognitive_complexity, DeprecationStatus::None), - (sym::cyclomatic_complexity, DeprecationStatus::Replaced("cognitive_complexity")), - (sym::dump, DeprecationStatus::None), - (sym::msrv, DeprecationStatus::None), - // The following attributes are for the 3rd party crate authors. - // See book/src/attribs.md - (sym::has_significant_drop, DeprecationStatus::None), - (sym::format_args, DeprecationStatus::None), -]; - -pub struct LimitStack { - stack: Vec, -} - -impl Drop for LimitStack { - fn drop(&mut self) { - assert_eq!(self.stack.len(), 1); - } -} - -impl LimitStack { - #[must_use] - pub fn new(limit: u64) -> Self { - Self { stack: vec![limit] } - } - pub fn limit(&self) -> u64 { - *self.stack.last().expect("there should always be a value in the stack") - } - pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { - let stack = &mut self.stack; - parse_attrs(sess, attrs, name, |val| stack.push(val)); - } - pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { - let stack = &mut self.stack; - parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); - } -} - -pub fn get_attr<'a, A: AttributeExt + 'a>( +/// Given `attrs`, extract all the instances of a built-in Clippy attribute called `name` +pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( sess: &'a Session, attrs: &'a [A], name: Symbol, ) -> impl Iterator { attrs.iter().filter(move |attr| { - let Some(attr_segments) = attr.ident_path() else { - return false; - }; + if let Some([clippy, segment2]) = attr.ident_path().as_deref() + && clippy.name == sym::clippy + { + let new_name = match segment2.name { + sym::cyclomatic_complexity => Some("cognitive_complexity"), + sym::author + | sym::version + | sym::cognitive_complexity + | sym::dump + | sym::msrv + // The following attributes are for the 3rd party crate authors. + // See book/src/attribs.md + | sym::has_significant_drop + | sym::format_args => None, + _ => { + sess.dcx().span_err(segment2.span, "usage of unknown attribute"); + return false; + }, + }; - if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy { - BUILTIN_ATTRIBUTES - .iter() - .find_map(|(builtin_name, deprecation_status)| { - if attr_segments[1].name == *builtin_name { - Some(deprecation_status) - } else { - None - } - }) - .map_or_else( - || { - sess.dcx().span_err(attr_segments[1].span, "usage of unknown attribute"); - false - }, - |deprecation_status| { - let mut diag = sess - .dcx() - .struct_span_err(attr_segments[1].span, "usage of deprecated attribute"); - match *deprecation_status { - DeprecationStatus::Deprecated => { - diag.emit(); - false - }, - DeprecationStatus::Replaced(new_name) => { - diag.span_suggestion( - attr_segments[1].span, - "consider using", - new_name, - Applicability::MachineApplicable, - ); - diag.emit(); - false - }, - DeprecationStatus::None => { - diag.cancel(); - attr_segments[1].name == name - }, - } - }, - ) + match new_name { + Some(new_name) => { + sess.dcx() + .struct_span_err(segment2.span, "usage of deprecated attribute") + .with_span_suggestion( + segment2.span, + "consider using", + new_name, + Applicability::MachineApplicable, + ) + .emit(); + false + }, + None => segment2.name == name, + } } else { false } }) } -fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) { - for attr in get_attr(sess, attrs, name) { - if let Some(value) = attr.value_str() { - if let Ok(value) = FromStr::from_str(value.as_str()) { - f(value); - } else { - sess.dcx().span_err(attr.span(), "not a number"); - } - } else { - sess.dcx().span_err(attr.span(), "bad clippy attribute"); - } - } -} - -pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> { +/// If `attrs` contain exactly one instance of a built-in Clippy attribute called `name`, +/// returns that attribute, and `None` otherwise +pub fn get_unique_builtin_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], name: Symbol) -> Option<&'a A> { let mut unique_attr: Option<&A> = None; - for attr in get_attr(sess, attrs, name) { + for attr in get_builtin_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { sess.dcx() .struct_span_err(attr.span(), format!("`{name}` is defined multiple times")) @@ -149,13 +79,13 @@ pub fn get_unique_attr<'a, A: AttributeExt>(sess: &'a Session, attrs: &'a [A], n unique_attr } -/// Returns true if the attributes contain any of `proc_macro`, -/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise +/// Checks whether `attrs` contain any of `proc_macro`, `proc_macro_derive` or +/// `proc_macro_attribute` pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool { attrs.iter().any(AttributeExt::is_proc_macro_attr) } -/// Returns true if the attributes contain `#[doc(hidden)]` +/// Checks whether `attrs` contain `#[doc(hidden)]` pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { attrs .iter() @@ -164,6 +94,7 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { .any(|l| attr::list_contains_name(&l, sym::hidden)) } +/// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]` pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..)) @@ -176,7 +107,7 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { .any(|field_def| find_attr!(tcx.get_all_attrs(field_def.did), AttributeKind::NonExhaustive(..))) } -/// Checks if the given span contains a `#[cfg(..)]` attribute +/// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { s.check_source_text(cx, |src| { let mut iter = tokenize_with_text(src); @@ -198,3 +129,52 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { false }) } + +/// Currently used to keep track of the current value of `#[clippy::cognitive_complexity(N)]` +pub struct LimitStack { + default: u64, + stack: Vec, +} + +impl Drop for LimitStack { + fn drop(&mut self) { + debug_assert_eq!(self.stack, Vec::::new()); // avoid `.is_empty()`, for a nicer error message + } +} + +#[expect(missing_docs, reason = "they're all trivial...")] +impl LimitStack { + #[must_use] + /// Initialize the stack starting with a default value, which usually comes from configuration + pub fn new(limit: u64) -> Self { + Self { + default: limit, + stack: vec![], + } + } + pub fn limit(&self) -> u64 { + self.stack.last().copied().unwrap_or(self.default) + } + pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { + let stack = &mut self.stack; + parse_attrs(sess, attrs, name, |val| stack.push(val)); + } + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: Symbol) { + let stack = &mut self.stack; + parse_attrs(sess, attrs, name, |val| debug_assert_eq!(stack.pop(), Some(val))); + } +} + +fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: Symbol, mut f: F) { + for attr in get_builtin_attr(sess, attrs, name) { + let Some(value) = attr.value_str() else { + sess.dcx().span_err(attr.span(), "bad clippy attribute"); + continue; + }; + let Ok(value) = u64::from_str(value.as_str()) else { + sess.dcx().span_err(attr.span(), "not a number"); + continue; + }; + f(value); + } +} diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index ac408a1b59e5..7e3fa4f9909b 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -1138,9 +1138,8 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx match ct_rhs { ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value), ConstItemRhs::TypeConst(const_arg) => match const_arg.kind { - ConstArgKind::Path(_) => None, ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value), - ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None, + ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => None, }, } } diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index dd411fe21bdd..b286701fbed1 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -8,9 +8,9 @@ use rustc_hir::MatchSource::TryDesugar; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, - ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind, - Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, + AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, ByRef, Closure, ConstArg, ConstArgKind, Expr, + ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, + LifetimeKind, Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -480,10 +480,8 @@ fn eq_const_arg(&mut self, left: &ConstArg<'_>, right: &ConstArg<'_>) -> bool { // Use explicit match for now since ConstArg is undergoing flux. (ConstArgKind::Path(..), ConstArgKind::Anon(..)) | (ConstArgKind::Anon(..), ConstArgKind::Path(..)) - | (ConstArgKind::Infer(..), _) - | (_, ConstArgKind::Infer(..)) - | (ConstArgKind::Error(..), _) - | (_, ConstArgKind::Error(..)) => false, + | (ConstArgKind::Infer(..) | ConstArgKind::Error(..), _) + | (_, ConstArgKind::Infer(..) | ConstArgKind::Error(..)) => false, } } @@ -538,7 +536,7 @@ fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool { && both(le.as_ref(), re.as_ref(), |a, b| self.eq_pat_expr(a, b)) && (li == ri) }, - (PatKind::Ref(le, lm), PatKind::Ref(re, rm)) => lm == rm && self.eq_pat(le, re), + (PatKind::Ref(le, lp, lm), PatKind::Ref(re, rp, rm)) => lp == rp && lm == rm && self.eq_pat(le, re), (PatKind::Slice(ls, li, le), PatKind::Slice(rs, ri, re)) => { over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) @@ -1131,6 +1129,10 @@ pub fn hash_pat(&mut self, pat: &Pat<'_>) { PatKind::Missing => unreachable!(), PatKind::Binding(BindingMode(by_ref, mutability), _, _, pat) => { std::mem::discriminant(by_ref).hash(&mut self.s); + if let ByRef::Yes(pi, mu) = by_ref { + std::mem::discriminant(pi).hash(&mut self.s); + std::mem::discriminant(mu).hash(&mut self.s); + } std::mem::discriminant(mutability).hash(&mut self.s); if let Some(pat) = pat { self.hash_pat(pat); @@ -1152,8 +1154,9 @@ pub fn hash_pat(&mut self, pat: &Pat<'_>) { } std::mem::discriminant(i).hash(&mut self.s); }, - PatKind::Ref(pat, mu) => { + PatKind::Ref(pat, pi, mu) => { self.hash_pat(pat); + std::mem::discriminant(pi).hash(&mut self.s); std::mem::discriminant(mu).hash(&mut self.s); }, PatKind::Guard(pat, guard) => { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 4c0d70d320b9..c9302b17eb7e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -50,6 +50,7 @@ extern crate rustc_trait_selection; pub mod ast_utils; +#[deny(missing_docs)] pub mod attrs; mod check_proc_macro; pub mod comparisons; @@ -131,7 +132,7 @@ use crate::consts::{ConstEvalCtxt, Constant}; use crate::higher::Range; use crate::msrvs::Msrv; -use crate::res::{MaybeDef, MaybeResPath}; +use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -300,6 +301,22 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> cx.tcx.lang_items().get(item) == Some(did) } +/// Checks is `expr` is `None` +pub fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + expr.res(cx).ctor_parent(cx).is_lang_item(cx, OptionNone) +} + +/// If `expr` is `Some(inner)`, returns `inner` +pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { + if let ExprKind::Call(e, [arg]) = expr.kind + && e.res(cx).ctor_parent(cx).is_lang_item(cx, OptionSome) + { + Some(arg) + } else { + None + } +} + /// Checks if `expr` is an empty block or an empty tuple. pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( @@ -320,6 +337,25 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } +/// If `pat` is: +/// - `Some(inner)`, returns `inner` +/// - it will _usually_ contain just one element, but could have two, given patterns like +/// `Some(inner, ..)` or `Some(.., inner)` +/// - `Some`, returns `[]` +/// - otherwise, returns `None` +pub fn as_some_pattern<'a, 'hir>(cx: &LateContext<'_>, pat: &'a Pat<'hir>) -> Option<&'a [Pat<'hir>]> { + if let PatKind::TupleStruct(ref qpath, inner, _) = pat.kind + && cx + .qpath_res(qpath, pat.hir_id) + .ctor_parent(cx) + .is_lang_item(cx, OptionSome) + { + Some(inner) + } else { + None + } +} + /// Checks if the `pat` is `None`. pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { matches!(pat.kind, @@ -1462,7 +1498,7 @@ fn are_refutable<'a, I: IntoIterator>>(cx: &LateContext<'_>, PatKind::Missing => unreachable!(), PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), - PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), + PatKind::Box(pat) | PatKind::Ref(pat, _, _) => is_refutable(cx, pat), PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, @@ -1612,7 +1648,7 @@ pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> } pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { - while let PatKind::Ref(subpat, _) = pat.kind { + while let PatKind::Ref(subpat, _, _) = pat.kind { pat = subpat; } pat @@ -2157,7 +2193,7 @@ pub fn search_same(exprs: &[T], mut hash: Hash, mut eq: Eq) -> Vec< /// references removed. pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) { fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) { - if let PatKind::Ref(pat, _) = pat.kind { + if let PatKind::Ref(pat, _, _) = pat.kind { peel(pat, count + 1) } else { (pat, count) @@ -2782,11 +2818,7 @@ pub fn pat_and_expr_can_be_question_mark<'a, 'hir>( pat: &'a Pat<'hir>, else_body: &Expr<'_>, ) -> Option<&'a Pat<'hir>> { - if let PatKind::TupleStruct(pat_path, [inner_pat], _) = pat.kind - && cx - .qpath_res(&pat_path, pat.hir_id) - .ctor_parent(cx) - .is_lang_item(cx, OptionSome) + if let Some([inner_pat]) = as_some_pattern(cx, pat) && !is_refutable(cx, inner_pat) && let else_body = peel_blocks(else_body) && let ExprKind::Ret(Some(ret_val)) = else_body.kind diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 7cd5a16f5b46..4e06f010bd59 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, OnceLock}; use crate::visitors::{Descend, for_each_expr_without_closures}; -use crate::{get_unique_attr, sym}; +use crate::{get_unique_builtin_attr, sym}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -42,7 +42,7 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { } else { // Allow users to tag any macro as being format!-like // TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method - get_unique_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some() + get_unique_builtin_attr(cx.sess(), cx.tcx.get_all_attrs(macro_def_id), sym::format_args).is_some() } } diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index bb2a62821100..b5fffab13b1d 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -1,11 +1,16 @@ use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use std::iter; +/// Represents the base of a numeric literal, used for parsing and formatting. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Radix { + /// A binary literal (e.g., `0b1010`) Binary, + /// An octal literal (e.g., `0o670`) Octal, + /// A decimal literal (e.g., `123`) Decimal, + /// A hexadecimal literal (e.g., `0xFF`) Hexadecimal, } @@ -46,6 +51,7 @@ pub struct NumericLiteral<'a> { } impl<'a> NumericLiteral<'a> { + /// Attempts to parse a `NumericLiteral` from the source string of an `ast::LitKind`. pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option> { let unsigned_src = src.strip_prefix('-').map_or(src, |s| s); if lit_kind.is_numeric() @@ -63,6 +69,7 @@ pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option, float: bool) -> Self { let unsigned_lit = lit.trim_start_matches('-'); @@ -102,11 +109,12 @@ pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { } } + /// Checks if the literal's radix is `Radix::Decimal` pub fn is_decimal(&self) -> bool { self.radix == Radix::Decimal } - pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { + fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) { let mut integer = digits; let mut fraction = None; let mut exponent = None; @@ -180,7 +188,7 @@ pub fn format(&self) -> String { output } - pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) { + fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, zero_pad: bool) { debug_assert!(group_size > 0); let mut digits = input.chars().filter(|&c| c != '_'); @@ -196,7 +204,7 @@ pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial if partial_group_first { first_group_size = (digits.clone().count() - 1) % group_size + 1; - if pad { + if zero_pad { for _ in 0..group_size - first_group_size { output.push('0'); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 0cf1ad348953..296da9fec8c0 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -194,8 +194,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) - | Rvalue::ShallowInitBox(_, _) => Ok(()), + Rvalue::NullaryOp(NullOp::OffsetOf(_) | NullOp::RuntimeChecks(_), _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, cx.tcx); if ty.is_integral() || ty.is_bool() { diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 8e8a80a6a9c9..1d1537dd0e91 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -126,6 +126,7 @@ macro_rules! generate { copy_from_nonoverlapping, copy_to, copy_to_nonoverlapping, + core_arch, count_ones, create, create_new, @@ -166,7 +167,6 @@ macro_rules! generate { from_ne_bytes, from_ptr, from_raw, - from_str, from_str_radix, fs, fuse, @@ -331,6 +331,7 @@ macro_rules! generate { splitn_mut, sqrt, starts_with, + std_detect, step_by, strlen, style, diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index d23fd74d9acc..f6809da98f2b 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-10-31" +channel = "nightly-2025-11-14" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 3245b2c983e1..b7ed596d811e 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -17,6 +17,7 @@ extern crate proc_macro_derive; use core::num::{NonZero, Saturating, Wrapping}; +use core::time::Duration; const ONE: i32 = 1; const ZERO: i32 = 0; @@ -687,4 +688,59 @@ pub fn explicit_methods() { //~^ arithmetic_side_effects } +pub fn issue_15943(days: u8) -> Duration { + Duration::from_secs(86400 * u64::from(days)) +} + +pub fn type_conversion_add() { + let _ = u128::MAX + u128::from(1u8); + //~^ arithmetic_side_effects + let _ = 1u128 + u128::from(1u16); + let _ = 1u128 + u128::from(1u32); + let _ = 1u128 + u128::from(1u64); + + let _ = 1u64 + u64::from(1u8); + let _ = 1u64 + u64::from(1u16); + let _ = 1u64 + u64::from(1u32); + + let _ = 1u32 + u32::from(1u8); + let _ = 1u32 + u32::from(1u16); + + let _ = 1u16 + u16::from(1u8); +} + +pub fn type_conversion_mul() { + let _ = u128::MAX * u128::from(1u8); + //~^ arithmetic_side_effects + let _ = 1u128 * u128::from(1u16); + let _ = 1u128 * u128::from(1u32); + let _ = 1u128 * u128::from(1u64); + + let _ = 1u64 * u64::from(1u8); + let _ = 1u64 * u64::from(1u16); + let _ = 1u64 * u64::from(1u32); + + let _ = 1u32 * u32::from(1u8); + let _ = 1u32 * u32::from(1u16); + + let _ = 1u16 * u16::from(1u8); +} + +pub fn type_conversion_does_not_escape_its_context() { + struct Foo; + impl Foo { + fn from(n: u8) -> u64 { + u64::from(n) + } + } + let _ = Duration::from_secs(86400 * Foo::from(1)); + //~^ arithmetic_side_effects + + fn shift(x: u8) -> u64 { + 1 << u64::from(x) + } + let _ = Duration::from_secs(86400 * shift(1)); + //~^ arithmetic_side_effects +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 4150493ba94a..22742a82601a 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:166:13 + --> tests/ui/arithmetic_side_effects.rs:167:13 | LL | let _ = 1f16 + 1f16; | ^^^^^^^^^^^ @@ -8,766 +8,790 @@ LL | let _ = 1f16 + 1f16; = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:170:13 + --> tests/ui/arithmetic_side_effects.rs:171:13 | LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:175:13 + --> tests/ui/arithmetic_side_effects.rs:176:13 | LL | let _ = String::new() + &String::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:355:5 + --> tests/ui/arithmetic_side_effects.rs:356:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:357:5 + --> tests/ui/arithmetic_side_effects.rs:358:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:5 + --> tests/ui/arithmetic_side_effects.rs:360:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:5 + --> tests/ui/arithmetic_side_effects.rs:362:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:5 + --> tests/ui/arithmetic_side_effects.rs:364:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:5 + --> tests/ui/arithmetic_side_effects.rs:366:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:5 + --> tests/ui/arithmetic_side_effects.rs:368:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:5 + --> tests/ui/arithmetic_side_effects.rs:370:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:5 + --> tests/ui/arithmetic_side_effects.rs:372:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:5 + --> tests/ui/arithmetic_side_effects.rs:374:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:5 + --> tests/ui/arithmetic_side_effects.rs:376:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:5 + --> tests/ui/arithmetic_side_effects.rs:378:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:5 + --> tests/ui/arithmetic_side_effects.rs:380:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:5 + --> tests/ui/arithmetic_side_effects.rs:382:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:5 + --> tests/ui/arithmetic_side_effects.rs:384:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:5 + --> tests/ui/arithmetic_side_effects.rs:386:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:5 + --> tests/ui/arithmetic_side_effects.rs:388:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:5 + --> tests/ui/arithmetic_side_effects.rs:390:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:5 + --> tests/ui/arithmetic_side_effects.rs:392:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:5 + --> tests/ui/arithmetic_side_effects.rs:394:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:5 + --> tests/ui/arithmetic_side_effects.rs:396:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:5 + --> tests/ui/arithmetic_side_effects.rs:398:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:399:5 + --> tests/ui/arithmetic_side_effects.rs:400:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:401:5 + --> tests/ui/arithmetic_side_effects.rs:402:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:5 + --> tests/ui/arithmetic_side_effects.rs:404:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:405:5 + --> tests/ui/arithmetic_side_effects.rs:406:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:10 + --> tests/ui/arithmetic_side_effects.rs:410:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:411:10 + --> tests/ui/arithmetic_side_effects.rs:412:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:414:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:10 + --> tests/ui/arithmetic_side_effects.rs:416:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:417:10 + --> tests/ui/arithmetic_side_effects.rs:418:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:419:10 + --> tests/ui/arithmetic_side_effects.rs:420:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:421:10 + --> tests/ui/arithmetic_side_effects.rs:422:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:423:10 + --> tests/ui/arithmetic_side_effects.rs:424:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:10 + --> tests/ui/arithmetic_side_effects.rs:426:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:10 + --> tests/ui/arithmetic_side_effects.rs:428:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:429:10 + --> tests/ui/arithmetic_side_effects.rs:430:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:431:10 + --> tests/ui/arithmetic_side_effects.rs:432:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:433:10 + --> tests/ui/arithmetic_side_effects.rs:434:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:435:10 + --> tests/ui/arithmetic_side_effects.rs:436:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:437:10 + --> tests/ui/arithmetic_side_effects.rs:438:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:439:10 + --> tests/ui/arithmetic_side_effects.rs:440:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:10 + --> tests/ui/arithmetic_side_effects.rs:442:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:443:10 + --> tests/ui/arithmetic_side_effects.rs:444:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:445:10 + --> tests/ui/arithmetic_side_effects.rs:446:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:447:15 + --> tests/ui/arithmetic_side_effects.rs:448:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:449:15 + --> tests/ui/arithmetic_side_effects.rs:450:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:15 + --> tests/ui/arithmetic_side_effects.rs:452:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:453:15 + --> tests/ui/arithmetic_side_effects.rs:454:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:455:15 + --> tests/ui/arithmetic_side_effects.rs:456:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:457:15 + --> tests/ui/arithmetic_side_effects.rs:458:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:459:15 + --> tests/ui/arithmetic_side_effects.rs:460:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:461:15 + --> tests/ui/arithmetic_side_effects.rs:462:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:463:15 + --> tests/ui/arithmetic_side_effects.rs:464:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:465:15 + --> tests/ui/arithmetic_side_effects.rs:466:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:467:15 + --> tests/ui/arithmetic_side_effects.rs:468:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:469:15 + --> tests/ui/arithmetic_side_effects.rs:470:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:471:15 + --> tests/ui/arithmetic_side_effects.rs:472:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:473:15 + --> tests/ui/arithmetic_side_effects.rs:474:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:475:15 + --> tests/ui/arithmetic_side_effects.rs:476:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:477:15 + --> tests/ui/arithmetic_side_effects.rs:478:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:479:15 + --> tests/ui/arithmetic_side_effects.rs:480:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:481:15 + --> tests/ui/arithmetic_side_effects.rs:482:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:483:15 + --> tests/ui/arithmetic_side_effects.rs:484:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:485:15 + --> tests/ui/arithmetic_side_effects.rs:486:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:487:15 + --> tests/ui/arithmetic_side_effects.rs:488:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:489:15 + --> tests/ui/arithmetic_side_effects.rs:490:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:491:15 + --> tests/ui/arithmetic_side_effects.rs:492:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:495:23 + --> tests/ui/arithmetic_side_effects.rs:496:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:497:21 + --> tests/ui/arithmetic_side_effects.rs:498:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:499:21 + --> tests/ui/arithmetic_side_effects.rs:500:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:501:28 + --> tests/ui/arithmetic_side_effects.rs:502:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:504:23 + --> tests/ui/arithmetic_side_effects.rs:505:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:506:21 + --> tests/ui/arithmetic_side_effects.rs:507:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:508:21 + --> tests/ui/arithmetic_side_effects.rs:509:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:510:28 + --> tests/ui/arithmetic_side_effects.rs:511:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:513:23 + --> tests/ui/arithmetic_side_effects.rs:514:23 | LL | _n.saturating_div(*Box::new(_n)); | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:517:10 + --> tests/ui/arithmetic_side_effects.rs:518:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:519:10 + --> tests/ui/arithmetic_side_effects.rs:520:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:521:15 + --> tests/ui/arithmetic_side_effects.rs:522:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:523:15 + --> tests/ui/arithmetic_side_effects.rs:524:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:525:9 + --> tests/ui/arithmetic_side_effects.rs:526:9 | LL | _ = -*Box::new(_n); | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:535:5 + --> tests/ui/arithmetic_side_effects.rs:536:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:537:5 + --> tests/ui/arithmetic_side_effects.rs:538:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:539:5 + --> tests/ui/arithmetic_side_effects.rs:540:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:541:5 + --> tests/ui/arithmetic_side_effects.rs:542:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:543:5 + --> tests/ui/arithmetic_side_effects.rs:544:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:555:5 + --> tests/ui/arithmetic_side_effects.rs:556:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:557:5 + --> tests/ui/arithmetic_side_effects.rs:558:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:559:5 + --> tests/ui/arithmetic_side_effects.rs:560:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:562:5 + --> tests/ui/arithmetic_side_effects.rs:563:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:565:5 + --> tests/ui/arithmetic_side_effects.rs:566:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:567:5 + --> tests/ui/arithmetic_side_effects.rs:568:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:570:5 + --> tests/ui/arithmetic_side_effects.rs:571:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:573:5 + --> tests/ui/arithmetic_side_effects.rs:574:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:575:5 + --> tests/ui/arithmetic_side_effects.rs:576:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:586:5 + --> tests/ui/arithmetic_side_effects.rs:587:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:641:9 + --> tests/ui/arithmetic_side_effects.rs:642:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:646:9 + --> tests/ui/arithmetic_side_effects.rs:647:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:658:5 + --> tests/ui/arithmetic_side_effects.rs:659:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:663:5 + --> tests/ui/arithmetic_side_effects.rs:664:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:684:5 + --> tests/ui/arithmetic_side_effects.rs:685:5 | LL | one.add(&one); | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:686:5 + --> tests/ui/arithmetic_side_effects.rs:687:5 | LL | Box::new(one).add(one); | ^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 128 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:696:13 + | +LL | let _ = u128::MAX + u128::from(1u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:713:13 + | +LL | let _ = u128::MAX * u128::from(1u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:736:33 + | +LL | let _ = Duration::from_secs(86400 * Foo::from(1)); + | ^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:742:33 + | +LL | let _ = Duration::from_secs(86400 * shift(1)); + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 132 previous errors diff --git a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout index 786c61e0c018..43753c3288c0 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_closure.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout @@ -30,19 +30,16 @@ if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local2.pat.kind && name1.as_str() == "args" && let Some(trailing_expr) = block1.expr - && let ExprKind::Call(func2, args2) = trailing_expr.kind - && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed + && let ExprKind::Block(block2, None) = trailing_expr.kind + && block2.stmts.is_empty() + && let Some(trailing_expr1) = block2.expr + && let ExprKind::Call(func2, args2) = trailing_expr1.kind + && paths::CORE_FMT_ARGUMENTS_NEW.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements2) = inner1.kind - && elements2.len() == 2 - && let ExprKind::Lit(ref lit) = elements2[0].kind - && let LitKind::Str(s, _) = lit.node - && s.as_str() == "" - && let ExprKind::Lit(ref lit1) = elements2[1].kind - && let LitKind::Str(s1, _) = lit1.node - && s1.as_str() == "\n" - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind + && let ExprKind::Lit(ref lit) = args2[0].kind + && let LitKind::ByteStr(ref vec) = lit.node + && let [[192, 1, 10, 0]] = **vec + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[1].kind && block.expr.is_none() && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local.pat.kind && name2.as_str() == "print_text" diff --git a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout index ba3b7e244204..561a2dcbd7fe 100644 --- a/src/tools/clippy/tests/ui/author/macro_in_loop.stdout +++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout @@ -41,19 +41,16 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo && let PatKind::Binding(BindingMode::NONE, _, name2, None) = local1.pat.kind && name2.as_str() == "args" && let Some(trailing_expr) = block2.expr - && let ExprKind::Call(func2, args2) = trailing_expr.kind - && paths::CORE_FMT_RT_NEW_V1.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed + && let ExprKind::Block(block3, None) = trailing_expr.kind + && block3.stmts.is_empty() + && let Some(trailing_expr1) = block3.expr + && let ExprKind::Call(func2, args2) = trailing_expr1.kind + && paths::CORE_FMT_ARGUMENTS_NEW.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed && args2.len() == 2 - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[0].kind - && let ExprKind::Array(elements2) = inner1.kind - && elements2.len() == 2 - && let ExprKind::Lit(ref lit2) = elements2[0].kind - && let LitKind::Str(s, _) = lit2.node - && s.as_str() == "" - && let ExprKind::Lit(ref lit3) = elements2[1].kind - && let LitKind::Str(s1, _) = lit3.node - && s1.as_str() == "\n" - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[1].kind + && let ExprKind::Lit(ref lit2) = args2[0].kind + && let LitKind::ByteStr(ref vec) = lit2.node + && let [[192, 1, 10, 0]] = **vec + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[1].kind && block1.expr.is_none() && block.expr.is_none() { diff --git a/src/tools/clippy/tests/ui/cast_enum_constructor.rs b/src/tools/clippy/tests/ui/cast_enum_constructor.rs index eecf56f71a33..6d70387b6ce2 100644 --- a/src/tools/clippy/tests/ui/cast_enum_constructor.rs +++ b/src/tools/clippy/tests/ui/cast_enum_constructor.rs @@ -1,5 +1,5 @@ #![warn(clippy::cast_enum_constructor)] -#![allow(clippy::fn_to_numeric_cast)] +#![allow(clippy::fn_to_numeric_cast, function_casts_as_integer)] fn main() { enum Foo { diff --git a/src/tools/clippy/tests/ui/cmp_null.fixed b/src/tools/clippy/tests/ui/cmp_null.fixed index 04b8ec50160b..c12279cf12e6 100644 --- a/src/tools/clippy/tests/ui/cmp_null.fixed +++ b/src/tools/clippy/tests/ui/cmp_null.fixed @@ -1,5 +1,4 @@ #![warn(clippy::cmp_null)] -#![allow(unused_mut)] use std::ptr; @@ -18,7 +17,7 @@ fn main() { } let mut y = 0; - let mut m: *mut usize = &mut y; + let m: *mut usize = &mut y; if m.is_null() { //~^ cmp_null diff --git a/src/tools/clippy/tests/ui/cmp_null.rs b/src/tools/clippy/tests/ui/cmp_null.rs index 6f7762e6ae83..2771a16e00c5 100644 --- a/src/tools/clippy/tests/ui/cmp_null.rs +++ b/src/tools/clippy/tests/ui/cmp_null.rs @@ -1,5 +1,4 @@ #![warn(clippy::cmp_null)] -#![allow(unused_mut)] use std::ptr; @@ -18,7 +17,7 @@ fn main() { } let mut y = 0; - let mut m: *mut usize = &mut y; + let m: *mut usize = &mut y; if m == ptr::null_mut() { //~^ cmp_null diff --git a/src/tools/clippy/tests/ui/cmp_null.stderr b/src/tools/clippy/tests/ui/cmp_null.stderr index 8a75b0501119..381747cb3c65 100644 --- a/src/tools/clippy/tests/ui/cmp_null.stderr +++ b/src/tools/clippy/tests/ui/cmp_null.stderr @@ -1,5 +1,5 @@ error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:9:8 + --> tests/ui/cmp_null.rs:8:8 | LL | if p == ptr::null() { | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` @@ -8,31 +8,31 @@ LL | if p == ptr::null() { = help: to override `-D warnings` add `#[allow(clippy::cmp_null)]` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:14:8 + --> tests/ui/cmp_null.rs:13:8 | LL | if ptr::null() == p { | ^^^^^^^^^^^^^^^^ help: try: `p.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:22:8 + --> tests/ui/cmp_null.rs:21:8 | LL | if m == ptr::null_mut() { | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:27:8 + --> tests/ui/cmp_null.rs:26:8 | LL | if ptr::null_mut() == m { | ^^^^^^^^^^^^^^^^^^^^ help: try: `m.is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:33:13 + --> tests/ui/cmp_null.rs:32:13 | LL | let _ = x as *const () == ptr::null(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(x as *const ()).is_null()` error: comparing with null is better expressed by the `.is_null()` method - --> tests/ui/cmp_null.rs:39:19 + --> tests/ui/cmp_null.rs:38:19 | LL | debug_assert!(f != std::ptr::null_mut()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!f.is_null()` diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed index e698b99edd5c..b9691a3284ac 100644 --- a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.fixed @@ -1,5 +1,6 @@ #![feature(float_minimum_maximum)] #![warn(clippy::confusing_method_to_numeric_cast)] +#![allow(function_casts_as_integer)] fn main() { let _ = u16::MAX as usize; //~ confusing_method_to_numeric_cast diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs index ef65c21563d9..b402cbf92cb2 100644 --- a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.rs @@ -1,5 +1,6 @@ #![feature(float_minimum_maximum)] #![warn(clippy::confusing_method_to_numeric_cast)] +#![allow(function_casts_as_integer)] fn main() { let _ = u16::max as usize; //~ confusing_method_to_numeric_cast diff --git a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr index ba90df2059af..0d5e08f88077 100644 --- a/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr +++ b/src/tools/clippy/tests/ui/confusing_method_to_numeric_cast.stderr @@ -1,5 +1,5 @@ error: casting function pointer `u16::max` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:5:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 | LL | let _ = u16::max as usize; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let _ = u16::MAX as usize; | error: casting function pointer `u16::min` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:6:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 | LL | let _ = u16::min as usize; | ^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + let _ = u16::MIN as usize; | error: casting function pointer `u16::max_value` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:7:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 | LL | let _ = u16::max_value as usize; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + let _ = u16::MAX as usize; | error: casting function pointer `u16::min_value` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:8:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:9:13 | LL | let _ = u16::min_value as usize; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + let _ = u16::MIN as usize; | error: casting function pointer `f32::maximum` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:10:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 | LL | let _ = f32::maximum as usize; | ^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + let _ = f32::MAX as usize; | error: casting function pointer `f32::max` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:11:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 | LL | let _ = f32::max as usize; | ^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + let _ = f32::MAX as usize; | error: casting function pointer `f32::minimum` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:12:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 | LL | let _ = f32::minimum as usize; | ^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + let _ = f32::MIN as usize; | error: casting function pointer `f32::min` to `usize` - --> tests/ui/confusing_method_to_numeric_cast.rs:13:13 + --> tests/ui/confusing_method_to_numeric_cast.rs:14:13 | LL | let _ = f32::min as usize; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/derive.rs b/src/tools/clippy/tests/ui/derive.rs index 6b60421c3549..036f6c444b64 100644 --- a/src/tools/clippy/tests/ui/derive.rs +++ b/src/tools/clippy/tests/ui/derive.rs @@ -9,7 +9,6 @@ #![expect(incomplete_features)] // `unsafe_fields` is incomplete for the time being #![feature(unsafe_fields)] // `clone()` cannot be derived automatically on unsafe fields - #[derive(Copy)] struct Qux; diff --git a/src/tools/clippy/tests/ui/derive.stderr b/src/tools/clippy/tests/ui/derive.stderr index 2b97a58e9d6f..2701680e788d 100644 --- a/src/tools/clippy/tests/ui/derive.stderr +++ b/src/tools/clippy/tests/ui/derive.stderr @@ -1,5 +1,5 @@ error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:16:1 + --> tests/ui/derive.rs:15:1 | LL | / impl Clone for Qux { LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::expl_impl_clone_on_copy)]` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:42:1 + --> tests/ui/derive.rs:41:1 | LL | / impl<'a> Clone for Lt<'a> { LL | | @@ -27,7 +27,7 @@ LL | | } = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:55:1 + --> tests/ui/derive.rs:54:1 | LL | / impl Clone for BigArray { LL | | @@ -40,7 +40,7 @@ LL | | } = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:68:1 + --> tests/ui/derive.rs:67:1 | LL | / impl Clone for FnPtr { LL | | @@ -53,7 +53,7 @@ LL | | } = help: consider deriving `Clone` or removing `Copy` error: you are implementing `Clone` explicitly on a `Copy` type - --> tests/ui/derive.rs:90:1 + --> tests/ui/derive.rs:89:1 | LL | / impl Clone for Generic2 { LL | | diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr index 2affd0b7d6e9..86c189cb44cb 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr @@ -1,5 +1,5 @@ error: casting function pointer `foo` to `i8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:12:13 + --> tests/ui/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i8; | ^^^^^^^^^ help: try: `foo as usize` @@ -8,13 +8,13 @@ LL | let _ = foo as i8; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:14:13 + --> tests/ui/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i32` - --> tests/ui/fn_to_numeric_cast.rs:16:13 + --> tests/ui/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` @@ -23,121 +23,121 @@ LL | let _ = foo as i32; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i64` - --> tests/ui/fn_to_numeric_cast.rs:19:13 + --> tests/ui/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i128` - --> tests/ui/fn_to_numeric_cast.rs:21:13 + --> tests/ui/fn_to_numeric_cast.rs:22:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> tests/ui/fn_to_numeric_cast.rs:23:13 + --> tests/ui/fn_to_numeric_cast.rs:24:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:26:13 + --> tests/ui/fn_to_numeric_cast.rs:27:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:28:13 + --> tests/ui/fn_to_numeric_cast.rs:29:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u32` - --> tests/ui/fn_to_numeric_cast.rs:30:13 + --> tests/ui/fn_to_numeric_cast.rs:31:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> tests/ui/fn_to_numeric_cast.rs:33:13 + --> tests/ui/fn_to_numeric_cast.rs:34:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> tests/ui/fn_to_numeric_cast.rs:35:13 + --> tests/ui/fn_to_numeric_cast.rs:36:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:49:13 + --> tests/ui/fn_to_numeric_cast.rs:50:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:51:13 + --> tests/ui/fn_to_numeric_cast.rs:52:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i32` - --> tests/ui/fn_to_numeric_cast.rs:53:13 + --> tests/ui/fn_to_numeric_cast.rs:54:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> tests/ui/fn_to_numeric_cast.rs:56:13 + --> tests/ui/fn_to_numeric_cast.rs:57:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> tests/ui/fn_to_numeric_cast.rs:58:13 + --> tests/ui/fn_to_numeric_cast.rs:59:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> tests/ui/fn_to_numeric_cast.rs:60:13 + --> tests/ui/fn_to_numeric_cast.rs:61:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:63:13 + --> tests/ui/fn_to_numeric_cast.rs:64:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:65:13 + --> tests/ui/fn_to_numeric_cast.rs:66:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u32` - --> tests/ui/fn_to_numeric_cast.rs:67:13 + --> tests/ui/fn_to_numeric_cast.rs:68:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> tests/ui/fn_to_numeric_cast.rs:70:13 + --> tests/ui/fn_to_numeric_cast.rs:71:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> tests/ui/fn_to_numeric_cast.rs:72:13 + --> tests/ui/fn_to_numeric_cast.rs:73:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `f` to `i32` - --> tests/ui/fn_to_numeric_cast.rs:80:5 + --> tests/ui/fn_to_numeric_cast.rs:81:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr index 48961d14f2bb..694690ae5bfa 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr @@ -1,5 +1,5 @@ error: casting function pointer `foo` to `i8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:12:13 + --> tests/ui/fn_to_numeric_cast.rs:13:13 | LL | let _ = foo as i8; | ^^^^^^^^^ help: try: `foo as usize` @@ -8,19 +8,19 @@ LL | let _ = foo as i8; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_with_truncation)]` error: casting function pointer `foo` to `i16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:14:13 + --> tests/ui/fn_to_numeric_cast.rs:15:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i32`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:16:13 + --> tests/ui/fn_to_numeric_cast.rs:17:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `i64` - --> tests/ui/fn_to_numeric_cast.rs:19:13 + --> tests/ui/fn_to_numeric_cast.rs:20:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ help: try: `foo as usize` @@ -29,115 +29,115 @@ LL | let _ = foo as i64; = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast)]` error: casting function pointer `foo` to `i128` - --> tests/ui/fn_to_numeric_cast.rs:21:13 + --> tests/ui/fn_to_numeric_cast.rs:22:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `isize` - --> tests/ui/fn_to_numeric_cast.rs:23:13 + --> tests/ui/fn_to_numeric_cast.rs:24:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:26:13 + --> tests/ui/fn_to_numeric_cast.rs:27:13 | LL | let _ = foo as u8; | ^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:28:13 + --> tests/ui/fn_to_numeric_cast.rs:29:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u32`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:30:13 + --> tests/ui/fn_to_numeric_cast.rs:31:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u64` - --> tests/ui/fn_to_numeric_cast.rs:33:13 + --> tests/ui/fn_to_numeric_cast.rs:34:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `foo` to `u128` - --> tests/ui/fn_to_numeric_cast.rs:35:13 + --> tests/ui/fn_to_numeric_cast.rs:36:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ help: try: `foo as usize` error: casting function pointer `abc` to `i8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:49:13 + --> tests/ui/fn_to_numeric_cast.rs:50:13 | LL | let _ = abc as i8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:51:13 + --> tests/ui/fn_to_numeric_cast.rs:52:13 | LL | let _ = abc as i16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i32`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:53:13 + --> tests/ui/fn_to_numeric_cast.rs:54:13 | LL | let _ = abc as i32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i64` - --> tests/ui/fn_to_numeric_cast.rs:56:13 + --> tests/ui/fn_to_numeric_cast.rs:57:13 | LL | let _ = abc as i64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `i128` - --> tests/ui/fn_to_numeric_cast.rs:58:13 + --> tests/ui/fn_to_numeric_cast.rs:59:13 | LL | let _ = abc as i128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `isize` - --> tests/ui/fn_to_numeric_cast.rs:60:13 + --> tests/ui/fn_to_numeric_cast.rs:61:13 | LL | let _ = abc as isize; | ^^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u8`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:63:13 + --> tests/ui/fn_to_numeric_cast.rs:64:13 | LL | let _ = abc as u8; | ^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u16`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:65:13 + --> tests/ui/fn_to_numeric_cast.rs:66:13 | LL | let _ = abc as u16; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u32`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:67:13 + --> tests/ui/fn_to_numeric_cast.rs:68:13 | LL | let _ = abc as u32; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u64` - --> tests/ui/fn_to_numeric_cast.rs:70:13 + --> tests/ui/fn_to_numeric_cast.rs:71:13 | LL | let _ = abc as u64; | ^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `abc` to `u128` - --> tests/ui/fn_to_numeric_cast.rs:72:13 + --> tests/ui/fn_to_numeric_cast.rs:73:13 | LL | let _ = abc as u128; | ^^^^^^^^^^^ help: try: `abc as usize` error: casting function pointer `f` to `i32`, which truncates the value - --> tests/ui/fn_to_numeric_cast.rs:80:5 + --> tests/ui/fn_to_numeric_cast.rs:81:5 | LL | f as i32 | ^^^^^^^^ help: try: `f as usize` diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs index f53cbacdb377..0a07aeff366e 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs @@ -3,6 +3,7 @@ //@[64bit]ignore-bitwidth: 32 //@no-rustfix #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] +#![allow(function_casts_as_integer)] fn foo() -> String { String::new() diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs index 42f2128cd378..83c1e9a8387e 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs @@ -1,5 +1,6 @@ #![warn(clippy::fn_to_numeric_cast_any)] #![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] +#![allow(function_casts_as_integer)] //@no-rustfix fn foo() -> u8 { 0 diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr index 58fac2d406a0..f7c49b8ff88b 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr @@ -1,5 +1,5 @@ error: casting function pointer `foo` to `i8` - --> tests/ui/fn_to_numeric_cast_any.rs:23:13 + --> tests/ui/fn_to_numeric_cast_any.rs:24:13 | LL | let _ = foo as i8; | ^^^^^^^^^ @@ -12,7 +12,7 @@ LL | let _ = foo() as i8; | ++ error: casting function pointer `foo` to `i16` - --> tests/ui/fn_to_numeric_cast_any.rs:26:13 + --> tests/ui/fn_to_numeric_cast_any.rs:27:13 | LL | let _ = foo as i16; | ^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | let _ = foo() as i16; | ++ error: casting function pointer `foo` to `i32` - --> tests/ui/fn_to_numeric_cast_any.rs:29:13 + --> tests/ui/fn_to_numeric_cast_any.rs:30:13 | LL | let _ = foo as i32; | ^^^^^^^^^^ @@ -34,7 +34,7 @@ LL | let _ = foo() as i32; | ++ error: casting function pointer `foo` to `i64` - --> tests/ui/fn_to_numeric_cast_any.rs:32:13 + --> tests/ui/fn_to_numeric_cast_any.rs:33:13 | LL | let _ = foo as i64; | ^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let _ = foo() as i64; | ++ error: casting function pointer `foo` to `i128` - --> tests/ui/fn_to_numeric_cast_any.rs:35:13 + --> tests/ui/fn_to_numeric_cast_any.rs:36:13 | LL | let _ = foo as i128; | ^^^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = foo() as i128; | ++ error: casting function pointer `foo` to `isize` - --> tests/ui/fn_to_numeric_cast_any.rs:38:13 + --> tests/ui/fn_to_numeric_cast_any.rs:39:13 | LL | let _ = foo as isize; | ^^^^^^^^^^^^ @@ -67,7 +67,7 @@ LL | let _ = foo() as isize; | ++ error: casting function pointer `foo` to `u8` - --> tests/ui/fn_to_numeric_cast_any.rs:41:13 + --> tests/ui/fn_to_numeric_cast_any.rs:42:13 | LL | let _ = foo as u8; | ^^^^^^^^^ @@ -78,7 +78,7 @@ LL | let _ = foo() as u8; | ++ error: casting function pointer `foo` to `u16` - --> tests/ui/fn_to_numeric_cast_any.rs:44:13 + --> tests/ui/fn_to_numeric_cast_any.rs:45:13 | LL | let _ = foo as u16; | ^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | let _ = foo() as u16; | ++ error: casting function pointer `foo` to `u32` - --> tests/ui/fn_to_numeric_cast_any.rs:47:13 + --> tests/ui/fn_to_numeric_cast_any.rs:48:13 | LL | let _ = foo as u32; | ^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | let _ = foo() as u32; | ++ error: casting function pointer `foo` to `u64` - --> tests/ui/fn_to_numeric_cast_any.rs:50:13 + --> tests/ui/fn_to_numeric_cast_any.rs:51:13 | LL | let _ = foo as u64; | ^^^^^^^^^^ @@ -111,7 +111,7 @@ LL | let _ = foo() as u64; | ++ error: casting function pointer `foo` to `u128` - --> tests/ui/fn_to_numeric_cast_any.rs:53:13 + --> tests/ui/fn_to_numeric_cast_any.rs:54:13 | LL | let _ = foo as u128; | ^^^^^^^^^^^ @@ -122,7 +122,7 @@ LL | let _ = foo() as u128; | ++ error: casting function pointer `foo` to `usize` - --> tests/ui/fn_to_numeric_cast_any.rs:56:13 + --> tests/ui/fn_to_numeric_cast_any.rs:57:13 | LL | let _ = foo as usize; | ^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL | let _ = foo() as usize; | ++ error: casting function pointer `Struct::static_method` to `usize` - --> tests/ui/fn_to_numeric_cast_any.rs:61:13 + --> tests/ui/fn_to_numeric_cast_any.rs:62:13 | LL | let _ = Struct::static_method as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -144,7 +144,7 @@ LL | let _ = Struct::static_method() as usize; | ++ error: casting function pointer `f` to `usize` - --> tests/ui/fn_to_numeric_cast_any.rs:66:5 + --> tests/ui/fn_to_numeric_cast_any.rs:67:5 | LL | f as usize | ^^^^^^^^^^ @@ -155,7 +155,7 @@ LL | f() as usize | ++ error: casting function pointer `T::static_method` to `usize` - --> tests/ui/fn_to_numeric_cast_any.rs:71:5 + --> tests/ui/fn_to_numeric_cast_any.rs:72:5 | LL | T::static_method as usize | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL | T::static_method() as usize | ++ error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` - --> tests/ui/fn_to_numeric_cast_any.rs:78:13 + --> tests/ui/fn_to_numeric_cast_any.rs:79:13 | LL | let _ = (clos as fn(u32) -> u32) as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | let _ = (clos as fn(u32) -> u32)() as usize; | ++ error: casting function pointer `foo` to `*const ()` - --> tests/ui/fn_to_numeric_cast_any.rs:83:13 + --> tests/ui/fn_to_numeric_cast_any.rs:84:13 | LL | let _ = foo as *const (); | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index 3069c8139abe..e08828b46c36 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -178,4 +178,11 @@ const fn uncalled_len() { //~^ incompatible_msrv } +#[clippy::msrv = "1.0.0"] +fn vec_macro() { + let _: Vec = vec![]; + let _: Vec = vec![1; 3]; + let _: Vec = vec![1, 2]; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed b/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed index 70d503018e0f..e89e4476bf82 100644 --- a/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.edition2021.fixed @@ -261,4 +261,14 @@ fn issue14164() -> Result { //~[edition2024]^ let_and_return } +fn issue15987() -> i32 { + macro_rules! sample { + ( $( $args:expr ),+ ) => {}; + } + + let r = 5; + sample!(r); + r +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed b/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed index 9990c3b71205..d2c76673ca03 100644 --- a/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed +++ b/src/tools/clippy/tests/ui/let_and_return.edition2024.fixed @@ -261,4 +261,14 @@ fn issue14164() -> Result { //~[edition2024]^ let_and_return } +fn issue15987() -> i32 { + macro_rules! sample { + ( $( $args:expr ),+ ) => {}; + } + + let r = 5; + sample!(r); + r +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs index 48c20cdd60db..1af5f8ba5c16 100644 --- a/src/tools/clippy/tests/ui/let_and_return.rs +++ b/src/tools/clippy/tests/ui/let_and_return.rs @@ -261,4 +261,14 @@ fn issue14164() -> Result { //~[edition2024]^ let_and_return } +fn issue15987() -> i32 { + macro_rules! sample { + ( $( $args:expr ),+ ) => {}; + } + + let r = 5; + sample!(r); + r +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/let_if_seq.rs b/src/tools/clippy/tests/ui/let_if_seq.rs index 2db206212aa5..69d6319fa8bf 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.rs +++ b/src/tools/clippy/tests/ui/let_if_seq.rs @@ -139,3 +139,34 @@ fn main() { } println!("{}", val.get()); } + +fn issue16062(bar: fn() -> bool) { + let foo; + //~^ useless_let_if_seq + if bar() { + foo = 42; + } else { + foo = 0; + } +} + +fn issue16064(bar: fn() -> bool) { + macro_rules! mac { + ($e:expr) => { + $e() + }; + ($base:expr, $lit:expr) => { + $lit * $base + 2 + }; + } + + let foo; + //~^ useless_let_if_seq + if mac!(bar) { + foo = mac!(10, 4); + } else { + foo = 0; + } + + let bar = 1; +} diff --git a/src/tools/clippy/tests/ui/let_if_seq.stderr b/src/tools/clippy/tests/ui/let_if_seq.stderr index f59d42bf4c8d..b86bca6b384b 100644 --- a/src/tools/clippy/tests/ui/let_if_seq.stderr +++ b/src/tools/clippy/tests/ui/let_if_seq.stderr @@ -52,5 +52,29 @@ LL | | } | = note: you might not need `mut` at all -error: aborting due to 4 previous errors +error: `if _ { .. } else { .. }` is an expression + --> tests/ui/let_if_seq.rs:144:5 + | +LL | / let foo; +LL | | +LL | | if bar() { +LL | | foo = 42; +LL | | } else { +LL | | foo = 0; +LL | | } + | |_____^ help: it is more idiomatic to write: `let foo = if bar() { 42 } else { 0 };` + +error: `if _ { .. } else { .. }` is an expression + --> tests/ui/let_if_seq.rs:163:5 + | +LL | / let foo; +LL | | +LL | | if mac!(bar) { +LL | | foo = mac!(10, 4); +LL | | } else { +LL | | foo = 0; +LL | | } + | |_____^ help: it is more idiomatic to write: `let foo = if mac!(bar) { mac!(10, 4) } else { 0 };` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/match_single_binding.fixed b/src/tools/clippy/tests/ui/match_single_binding.fixed index 7e899a476666..fa82a316d64d 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.fixed +++ b/src/tools/clippy/tests/ui/match_single_binding.fixed @@ -265,3 +265,82 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool { a < b && b < c } + +#[allow( + irrefutable_let_patterns, + clippy::blocks_in_conditions, + clippy::unused_unit, + clippy::let_unit_value, + clippy::unit_arg, + clippy::unnecessary_operation +)] +fn issue15537(a: i32) -> ((), (), ()) { + let y = ( + { todo!() }, + { + { a }; + () + }, + (), + ); + + let y = [ + { todo!() }, + { + { a }; + () + }, + (), + ]; + + fn call(x: (), y: (), z: ()) {} + let y = call( + { todo!() }, + { + { a }; + () + }, + (), + ); + + struct Foo; + impl Foo { + fn method(&self, x: (), y: (), z: ()) {} + } + let x = Foo; + x.method( + { todo!() }, + { + { a }; + () + }, + (), + ); + + -{ + { a }; + 1 + }; + + _ = { a }; + 1; + + if let x = { + { a }; + 1 + } {} + + if { + { a }; + true + } { + todo!() + } + + [1, 2, 3][{ + { a }; + 1usize + }]; + + todo!() +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.rs b/src/tools/clippy/tests/ui/match_single_binding.rs index 37a96f2287c8..6c1fae89e230 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.rs +++ b/src/tools/clippy/tests/ui/match_single_binding.rs @@ -342,3 +342,84 @@ fn issue15269(a: usize, b: usize, c: usize) -> bool { (a, b) => b < c, } } + +#[allow( + irrefutable_let_patterns, + clippy::blocks_in_conditions, + clippy::unused_unit, + clippy::let_unit_value, + clippy::unit_arg, + clippy::unnecessary_operation +)] +fn issue15537(a: i32) -> ((), (), ()) { + let y = ( + { todo!() }, + match { a } { + //~^ match_single_binding + _ => (), + }, + (), + ); + + let y = [ + { todo!() }, + match { a } { + //~^ match_single_binding + _ => (), + }, + (), + ]; + + fn call(x: (), y: (), z: ()) {} + let y = call( + { todo!() }, + match { a } { + //~^ match_single_binding + _ => (), + }, + (), + ); + + struct Foo; + impl Foo { + fn method(&self, x: (), y: (), z: ()) {} + } + let x = Foo; + x.method( + { todo!() }, + match { a } { + //~^ match_single_binding + _ => (), + }, + (), + ); + + -match { a } { + //~^ match_single_binding + _ => 1, + }; + + _ = match { a } { + //~^ match_single_binding + _ => 1, + }; + + if let x = match { a } { + //~^ match_single_binding + _ => 1, + } {} + + if match { a } { + //~^ match_single_binding + _ => true, + } { + todo!() + } + + [1, 2, 3][match { a } { + //~^ match_single_binding + _ => 1usize, + }]; + + todo!() +} diff --git a/src/tools/clippy/tests/ui/match_single_binding.stderr b/src/tools/clippy/tests/ui/match_single_binding.stderr index 82fc43aaa5ea..8a402dcca847 100644 --- a/src/tools/clippy/tests/ui/match_single_binding.stderr +++ b/src/tools/clippy/tests/ui/match_single_binding.stderr @@ -525,5 +525,161 @@ LL | | (a, b) => b < c, LL | | } | |_________^ help: consider using the match body instead: `b < c` -error: aborting due to 37 previous errors +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:357:9 + | +LL | / match { a } { +LL | | +LL | | _ => (), +LL | | }, + | |_________^ + | +help: consider using the scrutinee and body instead + | +LL ~ { +LL + { a }; +LL + () +LL ~ }, + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:366:9 + | +LL | / match { a } { +LL | | +LL | | _ => (), +LL | | }, + | |_________^ + | +help: consider using the scrutinee and body instead + | +LL ~ { +LL + { a }; +LL + () +LL ~ }, + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:376:9 + | +LL | / match { a } { +LL | | +LL | | _ => (), +LL | | }, + | |_________^ + | +help: consider using the scrutinee and body instead + | +LL ~ { +LL + { a }; +LL + () +LL ~ }, + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:390:9 + | +LL | / match { a } { +LL | | +LL | | _ => (), +LL | | }, + | |_________^ + | +help: consider using the scrutinee and body instead + | +LL ~ { +LL + { a }; +LL + () +LL ~ }, + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:397:6 + | +LL | -match { a } { + | ______^ +LL | | +LL | | _ => 1, +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ -{ +LL + { a }; +LL + 1 +LL ~ }; + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:402:9 + | +LL | _ = match { a } { + | _________^ +LL | | +LL | | _ => 1, +LL | | }; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ _ = { a }; +LL ~ 1; + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:407:16 + | +LL | if let x = match { a } { + | ________________^ +LL | | +LL | | _ => 1, +LL | | } {} + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ if let x = { +LL + { a }; +LL + 1 +LL ~ } {} + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:412:8 + | +LL | if match { a } { + | ________^ +LL | | +LL | | _ => true, +LL | | } { + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ if { +LL + { a }; +LL + true +LL ~ } { + | + +error: this match could be replaced by its scrutinee and body + --> tests/ui/match_single_binding.rs:419:15 + | +LL | [1, 2, 3][match { a } { + | _______________^ +LL | | +LL | | _ => 1usize, +LL | | }]; + | |_____^ + | +help: consider using the scrutinee and body instead + | +LL ~ [1, 2, 3][{ +LL + { a }; +LL + 1usize +LL ~ }]; + | + +error: aborting due to 46 previous errors diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed index 9018f38100ef..50bc576dd1e2 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.fixed @@ -150,9 +150,9 @@ fn highest_index_first(v1: &[u8]) { } fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { - assert!(v1.len() == 3); + assert_eq!(v1.len(), 3); assert_eq!(v2.len(), 4); - assert!(v3.len() == 3); + assert_eq!(v3.len(), 3); assert_eq!(4, v4.len()); let _ = v1[0] + v1[1] + v1[2]; @@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +mod issue15988 { + fn assert_eq_len(v: &[i32]) { + assert_eq!(v.len(), 3); + let _ = v[0] + v[1] + v[2]; + //~^ missing_asserts_for_indexing + } + + fn debug_assert_eq_len(v: &[i32]) { + debug_assert_eq!(v.len(), 3); + let _ = v[0] + v[1] + v[2]; + //~^ missing_asserts_for_indexing + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs index 44c5eddf3d8b..9e219a2af073 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.rs @@ -166,4 +166,18 @@ fn issue14255(v1: &[u8], v2: &[u8], v3: &[u8], v4: &[u8]) { let _ = v4[0] + v4[1] + v4[2]; } +mod issue15988 { + fn assert_eq_len(v: &[i32]) { + assert_eq!(v.len(), 2); + let _ = v[0] + v[1] + v[2]; + //~^ missing_asserts_for_indexing + } + + fn debug_assert_eq_len(v: &[i32]) { + debug_assert_eq!(v.len(), 2); + let _ = v[0] + v[1] + v[2]; + //~^ missing_asserts_for_indexing + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr index b610de94b530..b686eda7530a 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing.stderr @@ -305,7 +305,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover --> tests/ui/missing_asserts_for_indexing.rs:158:13 | LL | assert_eq!(v1.len(), 2); - | ----------------------- help: provide the highest index that is indexed with: `assert!(v1.len() == 3)` + | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v1.len(), 3)` ... LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -331,7 +331,7 @@ error: indexing into a slice multiple times with an `assert` that does not cover --> tests/ui/missing_asserts_for_indexing.rs:163:13 | LL | assert_eq!(2, v3.len()); - | ----------------------- help: provide the highest index that is indexed with: `assert!(v3.len() == 3)` + | ----------------------- help: provide the highest index that is indexed with: `assert_eq!(v3.len(), 3)` ... LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^^^^^^^^^^^^^^^^^ @@ -353,5 +353,55 @@ LL | let _ = v3[0] + v3[1] + v3[2]; | ^^^^^ = note: asserting the length before indexing will elide bounds checks -error: aborting due to 13 previous errors +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:172:17 + | +LL | assert_eq!(v.len(), 2); + | ---------------------- help: provide the highest index that is indexed with: `assert_eq!(v.len(), 3)` +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:172:17 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:172:24 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:172:31 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: indexing into a slice multiple times with an `assert` that does not cover the highest index + --> tests/ui/missing_asserts_for_indexing.rs:178:17 + | +LL | debug_assert_eq!(v.len(), 2); + | ---------------------------- help: provide the highest index that is indexed with: `debug_assert_eq!(v.len(), 3)` +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^^^^^^^^^^^^^^^ + | +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:178:17 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:178:24 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ +note: slice indexed here + --> tests/ui/missing_asserts_for_indexing.rs:178:31 + | +LL | let _ = v[0] + v[1] + v[2]; + | ^^^^ + = note: asserting the length before indexing will elide bounds checks + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.rs b/src/tools/clippy/tests/ui/missing_inline_executable.rs index 444a7f1c964f..2ab3e3b7825d 100644 --- a/src/tools/clippy/tests/ui/missing_inline_executable.rs +++ b/src/tools/clippy/tests/ui/missing_inline_executable.rs @@ -1,7 +1,6 @@ -//@ check-pass - #![warn(clippy::missing_inline_in_public_items)] pub fn foo() {} +//~^ missing_inline_in_public_items fn main() {} diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.stderr b/src/tools/clippy/tests/ui/missing_inline_executable.stderr new file mode 100644 index 000000000000..3108e4e49065 --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline_executable.stderr @@ -0,0 +1,11 @@ +error: missing `#[inline]` for a function + --> tests/ui/missing_inline_executable.rs:3:1 + | +LL | pub fn foo() {} + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/missing_inline_test_crate.rs b/src/tools/clippy/tests/ui/missing_inline_test_crate.rs new file mode 100644 index 000000000000..728292a0ee2b --- /dev/null +++ b/src/tools/clippy/tests/ui/missing_inline_test_crate.rs @@ -0,0 +1,10 @@ +//@compile-flags: --test +//@check-pass +#![warn(clippy::missing_inline_in_public_items)] + +#[expect(clippy::missing_inline_in_public_items)] +pub fn foo() -> u32 { + 0 +} + +fn private_function() {} diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs index 15c8b7c50878..4b973d762ed9 100644 --- a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.rs @@ -13,8 +13,7 @@ impl A {} //~^ multiple_inherent_impl #[cfg(test)] -impl A {} // false positive -//~^ multiple_inherent_impl +impl A {} #[cfg(test)] impl A {} @@ -25,8 +24,7 @@ impl A {} impl B {} #[cfg(test)] -impl B {} // false positive -//~^ multiple_inherent_impl +impl B {} impl B {} //~^ multiple_inherent_impl diff --git a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr index 9d408ce3dec3..991ceb0ff967 100644 --- a/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr +++ b/src/tools/clippy/tests/ui/multiple_inherent_impl_cfg.stderr @@ -16,76 +16,52 @@ LL | #![deny(clippy::multiple_inherent_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: multiple implementations of this structure + --> tests/ui/multiple_inherent_impl_cfg.rs:19:1 + | +LL | impl A {} + | ^^^^^^^^^ + | +note: first implementation here --> tests/ui/multiple_inherent_impl_cfg.rs:16:1 | -LL | impl A {} // false positive - | ^^^^^^^^^ - | -note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 - | LL | impl A {} | ^^^^^^^^^ error: multiple implementations of this structure - --> tests/ui/multiple_inherent_impl_cfg.rs:20:1 - | -LL | impl A {} - | ^^^^^^^^^ - | -note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:10:1 - | -LL | impl A {} - | ^^^^^^^^^ - -error: multiple implementations of this structure - --> tests/ui/multiple_inherent_impl_cfg.rs:28:1 - | -LL | impl B {} // false positive - | ^^^^^^^^^ - | -note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 - | -LL | impl B {} - | ^^^^^^^^^ - -error: multiple implementations of this structure - --> tests/ui/multiple_inherent_impl_cfg.rs:31:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:29:1 | LL | impl B {} | ^^^^^^^^^ | note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:24:1 | LL | impl B {} | ^^^^^^^^^ error: multiple implementations of this structure - --> tests/ui/multiple_inherent_impl_cfg.rs:35:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:33:1 | LL | impl B {} | ^^^^^^^^^ | note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:25:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:27:1 | LL | impl B {} | ^^^^^^^^^ error: multiple implementations of this structure - --> tests/ui/multiple_inherent_impl_cfg.rs:45:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:43:1 | LL | impl C {} | ^^^^^^^^^ | note: first implementation here - --> tests/ui/multiple_inherent_impl_cfg.rs:42:1 + --> tests/ui/multiple_inherent_impl_cfg.rs:40:1 | LL | impl C {} | ^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr index b5c0aae8310f..bb42e5ea63f5 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.stderr @@ -2,52 +2,99 @@ error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:10:16 | LL | pub fn bad(self: Self) { - | ^^^^^^^^^^ help: consider to change this parameter to: `self` + | ^^^^^^^^^^ | = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_arbitrary_self_type)]` +help: remove the type + | +LL - pub fn bad(self: Self) { +LL + pub fn bad(self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:19:20 | LL | pub fn mut_bad(mut self: Self) { - | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `mut self` + | ^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - pub fn mut_bad(mut self: Self) { +LL + pub fn mut_bad(mut self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:28:20 | LL | pub fn ref_bad(self: &Self) { - | ^^^^^^^^^^^ help: consider to change this parameter to: `&self` + | ^^^^^^^^^^^ + | +help: remove the type + | +LL - pub fn ref_bad(self: &Self) { +LL + pub fn ref_bad(&self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:37:38 | LL | pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { - | ^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a self` + | ^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - pub fn ref_bad_with_lifetime<'a>(self: &'a Self) { +LL + pub fn ref_bad_with_lifetime<'a>(&'a self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:46:24 | LL | pub fn mut_ref_bad(self: &mut Self) { - | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + | ^^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - pub fn mut_ref_bad(self: &mut Self) { +LL + pub fn mut_ref_bad(&mut self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:55:42 | LL | pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { - | ^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'a mut self` + | ^^^^^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - pub fn mut_ref_bad_with_lifetime<'a>(self: &'a mut Self) { +LL + pub fn mut_ref_bad_with_lifetime<'a>(&'a mut self) { + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:74:11 | LL | fn f1(self: &'r#struct Self) {} - | ^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct self` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - fn f1(self: &'r#struct Self) {} +LL + fn f1(&'r#struct self) {} + | error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type.rs:76:11 | LL | fn f2(self: &'r#struct mut Self) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&'r#struct mut self` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove the type + | +LL - fn f2(self: &'r#struct mut Self) {} +LL + fn f2(&'r#struct mut self) {} + | error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr index b50e00575629..4f8f001fc5e4 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type_unfixable.stderr @@ -2,10 +2,15 @@ error: the type of the `self` parameter does not need to be arbitrary --> tests/ui/needless_arbitrary_self_type_unfixable.rs:42:31 | LL | fn call_with_mut_self(self: &mut Self) {} - | ^^^^^^^^^^^^^^^ help: consider to change this parameter to: `&mut self` + | ^^^^^^^^^^^^^^^ | = note: `-D clippy::needless-arbitrary-self-type` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_arbitrary_self_type)]` +help: remove the type + | +LL - fn call_with_mut_self(self: &mut Self) {} +LL + fn call_with_mut_self(&mut self) {} + | error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/needless_collect.fixed b/src/tools/clippy/tests/ui/needless_collect.fixed index 842d77dbc8c5..ba1451bf9704 100644 --- a/src/tools/clippy/tests/ui/needless_collect.fixed +++ b/src/tools/clippy/tests/ui/needless_collect.fixed @@ -20,6 +20,10 @@ fn main() { } sample.iter().cloned().any(|x| x == 1); //~^ needless_collect + + let _ = sample.iter().cloned().nth(1).unwrap(); + //~^ needless_collect + // #7164 HashMap's and BTreeMap's `len` usage should not be linted sample.iter().map(|x| (x, x)).collect::>().len(); sample.iter().map(|x| (x, x)).collect::>().len(); diff --git a/src/tools/clippy/tests/ui/needless_collect.rs b/src/tools/clippy/tests/ui/needless_collect.rs index 98d8d27321d2..e054cd01e6f5 100644 --- a/src/tools/clippy/tests/ui/needless_collect.rs +++ b/src/tools/clippy/tests/ui/needless_collect.rs @@ -20,6 +20,10 @@ fn main() { } sample.iter().cloned().collect::>().contains(&1); //~^ needless_collect + + let _ = sample.iter().cloned().collect::>()[1]; + //~^ needless_collect + // #7164 HashMap's and BTreeMap's `len` usage should not be linted sample.iter().map(|x| (x, x)).collect::>().len(); sample.iter().map(|x| (x, x)).collect::>().len(); diff --git a/src/tools/clippy/tests/ui/needless_collect.stderr b/src/tools/clippy/tests/ui/needless_collect.stderr index 00745eb2923c..c77674dc55d4 100644 --- a/src/tools/clippy/tests/ui/needless_collect.stderr +++ b/src/tools/clippy/tests/ui/needless_collect.stderr @@ -20,100 +20,106 @@ LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:27:35 + --> tests/ui/needless_collect.rs:24:36 + | +LL | let _ = sample.iter().cloned().collect::>()[1]; + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `nth(1).unwrap()` + +error: avoid using `collect()` when not needed + --> tests/ui/needless_collect.rs:31:35 | LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:29:35 + --> tests/ui/needless_collect.rs:33:35 | LL | sample.iter().map(|x| (x, x)).collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:37:19 + --> tests/ui/needless_collect.rs:41:19 | LL | sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:39:19 + --> tests/ui/needless_collect.rs:43:19 | LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:41:28 + --> tests/ui/needless_collect.rs:45:28 | LL | sample.iter().cloned().collect::>().contains(&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:43:19 + --> tests/ui/needless_collect.rs:47:19 | LL | sample.iter().collect::>().contains(&&1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:47:19 + --> tests/ui/needless_collect.rs:51:19 | LL | sample.iter().collect::>().len(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:49:19 + --> tests/ui/needless_collect.rs:53:19 | LL | sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:55:27 + --> tests/ui/needless_collect.rs:59:27 | LL | let _ = sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:57:27 + --> tests/ui/needless_collect.rs:61:27 | LL | let _ = sample.iter().collect::>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:80:27 + --> tests/ui/needless_collect.rs:84:27 | LL | let _ = sample.iter().collect::>().is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:82:27 + --> tests/ui/needless_collect.rs:86:27 | LL | let _ = sample.iter().collect::>().contains(&&0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)` error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:87:40 + --> tests/ui/needless_collect.rs:91:40 | LL | Vec::::new().extend((0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:89:20 + --> tests/ui/needless_collect.rs:93:20 | LL | foo((0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:91:49 + --> tests/ui/needless_collect.rs:95:49 | LL | bar((0..10).collect::>(), (0..10).collect::>()); | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:93:37 + --> tests/ui/needless_collect.rs:97:37 | LL | baz((0..10), (), ('a'..='z').collect::>()) | ^^^^^^^^^^^^^^^^^^^^ help: remove this call -error: aborting due to 19 previous errors +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed index f50af147c60c..0de944f9edcf 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed @@ -242,4 +242,9 @@ fn issue_13436() { } } +fn issue16014() { + (vec![1, 2, 3] > vec![1, 2, 3, 3]); + //~^ nonminimal_bool +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs index 0ecd4775035b..ac0bd6d8a491 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs @@ -242,4 +242,9 @@ fn before_stabilization() { } } +fn issue16014() { + !(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + //~^ nonminimal_bool +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr index b5155b3b1696..568e88007727 100644 --- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr +++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr @@ -247,5 +247,11 @@ error: this boolean expression can be simplified LL | _ = !opt.is_none_or(|x| x.is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())` -error: aborting due to 41 previous errors +error: this boolean expression can be simplified + --> tests/ui/nonminimal_bool_methods.rs:246:5 + | +LL | !(vec![1, 2, 3] <= vec![1, 2, 3, 3]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(vec![1, 2, 3] > vec![1, 2, 3, 3])` + +error: aborting due to 42 previous errors diff --git a/src/tools/clippy/tests/ui/ok_expect.fixed b/src/tools/clippy/tests/ui/ok_expect.fixed new file mode 100644 index 000000000000..2a05b8805e4d --- /dev/null +++ b/src/tools/clippy/tests/ui/ok_expect.fixed @@ -0,0 +1,51 @@ +#![allow(clippy::unnecessary_literal_unwrap)] + +use std::io; + +struct MyError(()); // doesn't implement Debug + +#[derive(Debug)] +struct MyErrorWithParam { + x: T, +} + +fn main() { + let res: Result = Ok(0); + let _ = res.unwrap(); + + res.expect("disaster!"); + //~^ ok_expect + + res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + //~^^ ok_expect + + let resres = res; + resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + //~^^^ ok_expect + + // this one has a suboptimal suggestion, but oh well + std::process::Command::new("rustc") + .arg("-vV") + .output().expect("failed to get rustc version"); + //~^^^^^ ok_expect + + // the following should not warn, since `expect` isn't implemented unless + // the error type implements `Debug` + let res2: Result = Ok(0); + res2.ok().expect("oh noes!"); + let res3: Result> = Ok(0); + res3.expect("whoof"); + //~^ ok_expect + + let res4: Result = Ok(0); + res4.expect("argh"); + //~^ ok_expect + + let res5: io::Result = Ok(0); + res5.expect("oops"); + //~^ ok_expect + + let res6: Result = Ok(0); + res6.expect("meh"); + //~^ ok_expect +} diff --git a/src/tools/clippy/tests/ui/ok_expect.rs b/src/tools/clippy/tests/ui/ok_expect.rs index efb56f242a74..3761aa26f6e8 100644 --- a/src/tools/clippy/tests/ui/ok_expect.rs +++ b/src/tools/clippy/tests/ui/ok_expect.rs @@ -16,6 +16,24 @@ fn main() { res.ok().expect("disaster!"); //~^ ok_expect + res.ok() + .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + //~^^ ok_expect + + let resres = res; + resres + .ok() + .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + //~^^^ ok_expect + + // this one has a suboptimal suggestion, but oh well + std::process::Command::new("rustc") + .arg("-vV") + .output() + .ok() + .expect("failed to get rustc version"); + //~^^^^^ ok_expect + // the following should not warn, since `expect` isn't implemented unless // the error type implements `Debug` let res2: Result = Ok(0); diff --git a/src/tools/clippy/tests/ui/ok_expect.stderr b/src/tools/clippy/tests/ui/ok_expect.stderr index a9e3533d8ca1..848a10e671db 100644 --- a/src/tools/clippy/tests/ui/ok_expect.stderr +++ b/src/tools/clippy/tests/ui/ok_expect.stderr @@ -4,41 +4,109 @@ error: called `ok().expect()` on a `Result` value LL | res.ok().expect("disaster!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: you can call `expect()` directly on the `Result` = note: `-D clippy::ok-expect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ok_expect)]` +help: call `expect()` directly on the `Result` + | +LL - res.ok().expect("disaster!"); +LL + res.expect("disaster!"); + | + +error: called `ok().expect()` on a `Result` value + --> tests/ui/ok_expect.rs:19:5 + | +LL | / res.ok() +LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + | |___________________________________________________________________________^ + | +help: call `expect()` directly on the `Result` + | +LL - res.ok() +LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); +LL + res.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + | error: called `ok().expect()` on a `Result` value --> tests/ui/ok_expect.rs:24:5 | +LL | / resres +LL | | .ok() +LL | | .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + | |___________________________________________________________________________^ + | +help: call `expect()` directly on the `Result` + | +LL - resres +LL - .ok() +LL - .expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); +LL + resres.expect("longlonglonglonglonglonglonglonglonglonglonglonglonglong"); + | + +error: called `ok().expect()` on a `Result` value + --> tests/ui/ok_expect.rs:30:5 + | +LL | / std::process::Command::new("rustc") +LL | | .arg("-vV") +LL | | .output() +LL | | .ok() +LL | | .expect("failed to get rustc version"); + | |______________________________________________^ + | +help: call `expect()` directly on the `Result` + | +LL - .output() +LL - .ok() +LL - .expect("failed to get rustc version"); +LL + .output().expect("failed to get rustc version"); + | + +error: called `ok().expect()` on a `Result` value + --> tests/ui/ok_expect.rs:42:5 + | LL | res3.ok().expect("whoof"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: you can call `expect()` directly on the `Result` +help: call `expect()` directly on the `Result` + | +LL - res3.ok().expect("whoof"); +LL + res3.expect("whoof"); + | error: called `ok().expect()` on a `Result` value - --> tests/ui/ok_expect.rs:28:5 + --> tests/ui/ok_expect.rs:46:5 | LL | res4.ok().expect("argh"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: you can call `expect()` directly on the `Result` +help: call `expect()` directly on the `Result` + | +LL - res4.ok().expect("argh"); +LL + res4.expect("argh"); + | error: called `ok().expect()` on a `Result` value - --> tests/ui/ok_expect.rs:32:5 + --> tests/ui/ok_expect.rs:50:5 | LL | res5.ok().expect("oops"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: you can call `expect()` directly on the `Result` +help: call `expect()` directly on the `Result` + | +LL - res5.ok().expect("oops"); +LL + res5.expect("oops"); + | error: called `ok().expect()` on a `Result` value - --> tests/ui/ok_expect.rs:36:5 + --> tests/ui/ok_expect.rs:54:5 | LL | res6.ok().expect("meh"); | ^^^^^^^^^^^^^^^^^^^^^^^ | - = help: you can call `expect()` directly on the `Result` +help: call `expect()` directly on the `Result` + | +LL - res6.ok().expect("meh"); +LL + res6.expect("meh"); + | -error: aborting due to 5 previous errors +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_eq.fixed b/src/tools/clippy/tests/ui/ptr_eq.fixed index 9629b3eea587..d3624a129b5f 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.fixed +++ b/src/tools/clippy/tests/ui/ptr_eq.fixed @@ -1,4 +1,5 @@ #![warn(clippy::ptr_eq)] +#![allow(function_casts_as_integer)] macro_rules! mac { ($a:expr, $b:expr) => { diff --git a/src/tools/clippy/tests/ui/ptr_eq.rs b/src/tools/clippy/tests/ui/ptr_eq.rs index 2b741d8df468..f06a99cabc81 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.rs +++ b/src/tools/clippy/tests/ui/ptr_eq.rs @@ -1,4 +1,5 @@ #![warn(clippy::ptr_eq)] +#![allow(function_casts_as_integer)] macro_rules! mac { ($a:expr, $b:expr) => { diff --git a/src/tools/clippy/tests/ui/ptr_eq.stderr b/src/tools/clippy/tests/ui/ptr_eq.stderr index e7340624b595..f6be4c3f016b 100644 --- a/src/tools/clippy/tests/ui/ptr_eq.stderr +++ b/src/tools/clippy/tests/ui/ptr_eq.stderr @@ -1,5 +1,5 @@ error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:22:13 + --> tests/ui/ptr_eq.rs:23:13 | LL | let _ = a as *const _ as usize == b as *const _ as usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` @@ -8,31 +8,31 @@ LL | let _ = a as *const _ as usize == b as *const _ as usize; = help: to override `-D warnings` add `#[allow(clippy::ptr_eq)]` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:24:13 + --> tests/ui/ptr_eq.rs:25:13 | LL | let _ = a as *const _ == b as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(a, b)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:50:13 + --> tests/ui/ptr_eq.rs:51:13 | LL | let _ = x as *const u32 == y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:53:13 + --> tests/ui/ptr_eq.rs:54:13 | LL | let _ = x as *const u32 != y as *mut u32 as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!std::ptr::eq(x, y)` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:61:13 + --> tests/ui/ptr_eq.rs:62:13 | LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` error: use `std::ptr::eq` when comparing raw pointers - --> tests/ui/ptr_eq.rs:65:13 + --> tests/ui/ptr_eq.rs:66:13 | LL | let _ = mac!(cast a) as *const _ == mac!(cast b) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::eq(mac!(cast a), mac!(cast b))` diff --git a/src/tools/clippy/tests/ui/rc_buffer.fixed b/src/tools/clippy/tests/ui/rc_buffer.fixed index c71a4072b962..a41f98c8fa35 100644 --- a/src/tools/clippy/tests/ui/rc_buffer.fixed +++ b/src/tools/clippy/tests/ui/rc_buffer.fixed @@ -1,5 +1,4 @@ #![warn(clippy::rc_buffer)] -#![allow(dead_code, unused_imports)] use std::cell::RefCell; use std::ffi::OsString; @@ -32,4 +31,9 @@ fn func_bad4(_: Rc) {} // does not trigger lint fn func_good1(_: Rc>) {} +mod issue_15802 { + fn foo(_: std::rc::Rc<[u8]>) {} + //~^ rc_buffer +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/rc_buffer.rs b/src/tools/clippy/tests/ui/rc_buffer.rs index 686c2644da17..879f60647472 100644 --- a/src/tools/clippy/tests/ui/rc_buffer.rs +++ b/src/tools/clippy/tests/ui/rc_buffer.rs @@ -1,5 +1,4 @@ #![warn(clippy::rc_buffer)] -#![allow(dead_code, unused_imports)] use std::cell::RefCell; use std::ffi::OsString; @@ -32,4 +31,9 @@ fn func_bad4(_: Rc) {} // does not trigger lint fn func_good1(_: Rc>) {} +mod issue_15802 { + fn foo(_: std::rc::Rc>) {} + //~^ rc_buffer +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/rc_buffer.stderr b/src/tools/clippy/tests/ui/rc_buffer.stderr index 7500523ab4ac..e31e9c9c8fdf 100644 --- a/src/tools/clippy/tests/ui/rc_buffer.stderr +++ b/src/tools/clippy/tests/ui/rc_buffer.stderr @@ -1,53 +1,112 @@ -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:11:11 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:10:11 | LL | bad1: Rc, - | ^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^ | = note: `-D clippy::rc-buffer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rc_buffer)]` +help: try + | +LL - bad1: Rc, +LL + bad1: Rc, + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:13:11 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:12:11 | LL | bad2: Rc, - | ^^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^^ + | +help: try + | +LL - bad2: Rc, +LL + bad2: Rc, + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:15:11 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:14:11 | LL | bad3: Rc>, - | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + | ^^^^^^^^^^^ + | +help: try + | +LL - bad3: Rc>, +LL + bad3: Rc<[u8]>, + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:17:11 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:16:11 | LL | bad4: Rc, - | ^^^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^^^ + | +help: try + | +LL - bad4: Rc, +LL + bad4: Rc, + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:24:17 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:23:17 | LL | fn func_bad1(_: Rc) {} - | ^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^ + | +help: try + | +LL - fn func_bad1(_: Rc) {} +LL + fn func_bad1(_: Rc) {} + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:26:17 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:25:17 | LL | fn func_bad2(_: Rc) {} - | ^^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad2(_: Rc) {} +LL + fn func_bad2(_: Rc) {} + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:28:17 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:27:17 | LL | fn func_bad3(_: Rc>) {} - | ^^^^^^^^^^^ help: try: `Rc<[u8]>` + | ^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad3(_: Rc>) {} +LL + fn func_bad3(_: Rc<[u8]>) {} + | -error: usage of `Rc` when T is a buffer type - --> tests/ui/rc_buffer.rs:30:17 +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:29:17 | LL | fn func_bad4(_: Rc) {} - | ^^^^^^^^^^^^ help: try: `Rc` + | ^^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad4(_: Rc) {} +LL + fn func_bad4(_: Rc) {} + | -error: aborting due to 8 previous errors +error: usage of `Rc` when `T` is a buffer type + --> tests/ui/rc_buffer.rs:35:15 + | +LL | fn foo(_: std::rc::Rc>) {} + | ^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - fn foo(_: std::rc::Rc>) {} +LL + fn foo(_: std::rc::Rc<[u8]>) {} + | + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.fixed b/src/tools/clippy/tests/ui/rc_buffer_arc.fixed index 27059e3f2e1f..36b989ec1b60 100644 --- a/src/tools/clippy/tests/ui/rc_buffer_arc.fixed +++ b/src/tools/clippy/tests/ui/rc_buffer_arc.fixed @@ -1,5 +1,4 @@ #![warn(clippy::rc_buffer)] -#![allow(dead_code, unused_imports)] use std::ffi::OsString; use std::path::PathBuf; @@ -31,4 +30,9 @@ fn func_bad4(_: Arc) {} // does not trigger lint fn func_good1(_: Arc>) {} +mod issue_15802 { + fn foo(_: std::sync::Arc<[u8]>) {} + //~^ rc_buffer +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.rs b/src/tools/clippy/tests/ui/rc_buffer_arc.rs index 5261eae2f26a..f8e78dc5c18f 100644 --- a/src/tools/clippy/tests/ui/rc_buffer_arc.rs +++ b/src/tools/clippy/tests/ui/rc_buffer_arc.rs @@ -1,5 +1,4 @@ #![warn(clippy::rc_buffer)] -#![allow(dead_code, unused_imports)] use std::ffi::OsString; use std::path::PathBuf; @@ -31,4 +30,9 @@ fn func_bad4(_: Arc) {} // does not trigger lint fn func_good1(_: Arc>) {} +mod issue_15802 { + fn foo(_: std::sync::Arc>) {} + //~^ rc_buffer +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/rc_buffer_arc.stderr b/src/tools/clippy/tests/ui/rc_buffer_arc.stderr index 786715463232..043f7a15ec00 100644 --- a/src/tools/clippy/tests/ui/rc_buffer_arc.stderr +++ b/src/tools/clippy/tests/ui/rc_buffer_arc.stderr @@ -1,53 +1,112 @@ -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:10:11 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:9:11 | LL | bad1: Arc, - | ^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^ | = note: `-D clippy::rc-buffer` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rc_buffer)]` +help: try + | +LL - bad1: Arc, +LL + bad1: Arc, + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:12:11 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:11:11 | LL | bad2: Arc, - | ^^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^^ + | +help: try + | +LL - bad2: Arc, +LL + bad2: Arc, + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:14:11 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:13:11 | LL | bad3: Arc>, - | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + | ^^^^^^^^^^^^ + | +help: try + | +LL - bad3: Arc>, +LL + bad3: Arc<[u8]>, + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:16:11 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:15:11 | LL | bad4: Arc, - | ^^^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^^^ + | +help: try + | +LL - bad4: Arc, +LL + bad4: Arc, + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:23:17 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:22:17 | LL | fn func_bad1(_: Arc) {} - | ^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad1(_: Arc) {} +LL + fn func_bad1(_: Arc) {} + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:25:17 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:24:17 | LL | fn func_bad2(_: Arc) {} - | ^^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad2(_: Arc) {} +LL + fn func_bad2(_: Arc) {} + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:27:17 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:26:17 | LL | fn func_bad3(_: Arc>) {} - | ^^^^^^^^^^^^ help: try: `Arc<[u8]>` + | ^^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad3(_: Arc>) {} +LL + fn func_bad3(_: Arc<[u8]>) {} + | -error: usage of `Arc` when T is a buffer type - --> tests/ui/rc_buffer_arc.rs:29:17 +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:28:17 | LL | fn func_bad4(_: Arc) {} - | ^^^^^^^^^^^^^ help: try: `Arc` + | ^^^^^^^^^^^^^ + | +help: try + | +LL - fn func_bad4(_: Arc) {} +LL + fn func_bad4(_: Arc) {} + | -error: aborting due to 8 previous errors +error: usage of `Arc` when `T` is a buffer type + --> tests/ui/rc_buffer_arc.rs:34:15 + | +LL | fn foo(_: std::sync::Arc>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - fn foo(_: std::sync::Arc>) {} +LL + fn foo(_: std::sync::Arc<[u8]>) {} + | + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/replace_box.fixed b/src/tools/clippy/tests/ui/replace_box.fixed index 58c8ed1691d7..e3fc7190a9c9 100644 --- a/src/tools/clippy/tests/ui/replace_box.fixed +++ b/src/tools/clippy/tests/ui/replace_box.fixed @@ -70,3 +70,74 @@ fn main() { let bb: Box; bb = Default::default(); } + +fn issue15951() { + struct Foo { + inner: String, + } + + fn embedded_body() { + let mut x = Box::new(()); + let y = x; + x = Box::new(()); + + let mut x = Box::new(Foo { inner: String::new() }); + let y = x.inner; + *x = Foo { inner: String::new() }; + //~^ replace_box + } + + let mut x = Box::new(Foo { inner: String::new() }); + let in_closure = || { + *x = Foo { inner: String::new() }; + //~^ replace_box + }; +} + +static R: fn(&mut Box) = |x| **x = String::new(); +//~^ replace_box + +fn field() { + struct T { + content: String, + } + + impl T { + fn new() -> Self { + Self { content: String::new() } + } + } + + struct S { + b: Box, + } + + let mut s = S { b: Box::new(T::new()) }; + let _b = s.b; + s.b = Box::new(T::new()); + + // Interestingly, the lint and fix are valid here as `s.b` is not really moved + let mut s = S { b: Box::new(T::new()) }; + _ = s.b; + *s.b = T::new(); + //~^ replace_box + + let mut s = S { b: Box::new(T::new()) }; + *s.b = T::new(); + //~^ replace_box + + struct Q(Box); + let mut q = Q(Box::new(T::new())); + let _b = q.0; + q.0 = Box::new(T::new()); + + let mut q = Q(Box::new(T::new())); + _ = q.0; + *q.0 = T::new(); + //~^ replace_box + + // This one is a false negative, but it will need MIR analysis to work properly + let mut x = Box::new(String::new()); + x = Box::new(String::new()); + x; +} diff --git a/src/tools/clippy/tests/ui/replace_box.rs b/src/tools/clippy/tests/ui/replace_box.rs index e1fb223e4f21..1d5ca1b24994 100644 --- a/src/tools/clippy/tests/ui/replace_box.rs +++ b/src/tools/clippy/tests/ui/replace_box.rs @@ -70,3 +70,74 @@ fn main() { let bb: Box; bb = Default::default(); } + +fn issue15951() { + struct Foo { + inner: String, + } + + fn embedded_body() { + let mut x = Box::new(()); + let y = x; + x = Box::new(()); + + let mut x = Box::new(Foo { inner: String::new() }); + let y = x.inner; + x = Box::new(Foo { inner: String::new() }); + //~^ replace_box + } + + let mut x = Box::new(Foo { inner: String::new() }); + let in_closure = || { + x = Box::new(Foo { inner: String::new() }); + //~^ replace_box + }; +} + +static R: fn(&mut Box) = |x| *x = Box::new(String::new()); +//~^ replace_box + +fn field() { + struct T { + content: String, + } + + impl T { + fn new() -> Self { + Self { content: String::new() } + } + } + + struct S { + b: Box, + } + + let mut s = S { b: Box::new(T::new()) }; + let _b = s.b; + s.b = Box::new(T::new()); + + // Interestingly, the lint and fix are valid here as `s.b` is not really moved + let mut s = S { b: Box::new(T::new()) }; + _ = s.b; + s.b = Box::new(T::new()); + //~^ replace_box + + let mut s = S { b: Box::new(T::new()) }; + s.b = Box::new(T::new()); + //~^ replace_box + + struct Q(Box); + let mut q = Q(Box::new(T::new())); + let _b = q.0; + q.0 = Box::new(T::new()); + + let mut q = Q(Box::new(T::new())); + _ = q.0; + q.0 = Box::new(T::new()); + //~^ replace_box + + // This one is a false negative, but it will need MIR analysis to work properly + let mut x = Box::new(String::new()); + x = Box::new(String::new()); + x; +} diff --git a/src/tools/clippy/tests/ui/replace_box.stderr b/src/tools/clippy/tests/ui/replace_box.stderr index 7d9c85da1731..4b7bd4a0eeae 100644 --- a/src/tools/clippy/tests/ui/replace_box.stderr +++ b/src/tools/clippy/tests/ui/replace_box.stderr @@ -48,5 +48,53 @@ LL | b = Box::new(mac!(three)); | = note: this creates a needless allocation -error: aborting due to 6 previous errors +error: creating a new box + --> tests/ui/replace_box.rs:86:9 + | +LL | x = Box::new(Foo { inner: String::new() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*x = Foo { inner: String::new() }` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:92:9 + | +LL | x = Box::new(Foo { inner: String::new() }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*x = Foo { inner: String::new() }` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:97:38 + | +LL | static R: fn(&mut Box) = |x| *x = Box::new(String::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `**x = String::new()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:122:5 + | +LL | s.b = Box::new(T::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*s.b = T::new()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:126:5 + | +LL | s.b = Box::new(T::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*s.b = T::new()` + | + = note: this creates a needless allocation + +error: creating a new box + --> tests/ui/replace_box.rs:136:5 + | +LL | q.0 = Box::new(T::new()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace existing content with inner value instead: `*q.0 = T::new()` + | + = note: this creates a needless allocation + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.1.fixed b/src/tools/clippy/tests/ui/single_range_in_vec_init.1.fixed new file mode 100644 index 000000000000..0af91907ad05 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.1.fixed @@ -0,0 +1,84 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] +#![warn(clippy::single_range_in_vec_init)] + +#[macro_use] +extern crate proc_macros; + +macro_rules! a { + () => { + vec![0..200]; + }; +} + +fn awa(start: T, end: T) { + [start..end]; +} + +fn awa_vec(start: T, end: T) { + vec![start..end]; +} + +fn main() { + // Lint + (0..200).collect::>(); + //~^ single_range_in_vec_init + (0..200).collect::>(); + //~^ single_range_in_vec_init + (0u8..200).collect::>(); + //~^ single_range_in_vec_init + (0usize..200).collect::>(); + //~^ single_range_in_vec_init + (0..200usize).collect::>(); + //~^ single_range_in_vec_init + (0u8..200).collect::>(); + //~^ single_range_in_vec_init + (0usize..200).collect::>(); + //~^ single_range_in_vec_init + (0..200usize).collect::>(); + //~^ single_range_in_vec_init + // Only suggest collect + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + // Do not lint + [0..200, 0..100]; + vec![0..200, 0..100]; + [0.0..200.0]; + vec![0.0..200.0]; + // `Copy` is not implemented for `Range`, so this doesn't matter + // FIXME: [0..200; 2]; + // FIXME: [vec!0..200; 2]; + + // Unfortunately skips any macros + a!(); + + // Skip external macros and procedural macros + external! { + [0..200]; + vec![0..200]; + } + with_span! { + span + [0..200]; + vec![0..200]; + } +} + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = (0..as_i32!(10)).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.2.fixed b/src/tools/clippy/tests/ui/single_range_in_vec_init.2.fixed new file mode 100644 index 000000000000..fd6b91360aeb --- /dev/null +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.2.fixed @@ -0,0 +1,84 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] +#![warn(clippy::single_range_in_vec_init)] + +#[macro_use] +extern crate proc_macros; + +macro_rules! a { + () => { + vec![0..200]; + }; +} + +fn awa(start: T, end: T) { + [start..end]; +} + +fn awa_vec(start: T, end: T) { + vec![start..end]; +} + +fn main() { + // Lint + [0; 200]; + //~^ single_range_in_vec_init + vec![0; 200]; + //~^ single_range_in_vec_init + [0u8; 200]; + //~^ single_range_in_vec_init + [0usize; 200]; + //~^ single_range_in_vec_init + [0; 200usize]; + //~^ single_range_in_vec_init + vec![0u8; 200]; + //~^ single_range_in_vec_init + vec![0usize; 200]; + //~^ single_range_in_vec_init + vec![0; 200usize]; + //~^ single_range_in_vec_init + // Only suggest collect + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + // Do not lint + [0..200, 0..100]; + vec![0..200, 0..100]; + [0.0..200.0]; + vec![0.0..200.0]; + // `Copy` is not implemented for `Range`, so this doesn't matter + // FIXME: [0..200; 2]; + // FIXME: [vec!0..200; 2]; + + // Unfortunately skips any macros + a!(); + + // Skip external macros and procedural macros + external! { + [0..200]; + vec![0..200]; + } + with_span! { + span + [0..200]; + vec![0..200]; + } +} + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = (0..as_i32!(10)).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs index 0888019e101c..1cc2b894c034 100644 --- a/src/tools/clippy/tests/ui/single_range_in_vec_init.rs +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -//@no-rustfix: overlapping suggestions #![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] #![warn(clippy::single_range_in_vec_init)] @@ -66,3 +65,20 @@ fn main() { vec![0..200]; } } + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = vec![0..as_i32!(10)]; + //~^ single_range_in_vec_init +} diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr index b21338e38a3c..d93379777d39 100644 --- a/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init.stderr @@ -1,5 +1,5 @@ error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:25:5 + --> tests/ui/single_range_in_vec_init.rs:24:5 | LL | [0..200]; | ^^^^^^^^ @@ -18,7 +18,7 @@ LL + [0; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:27:5 + --> tests/ui/single_range_in_vec_init.rs:26:5 | LL | vec![0..200]; | ^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + vec![0; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:29:5 + --> tests/ui/single_range_in_vec_init.rs:28:5 | LL | [0u8..200]; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + [0u8; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:31:5 + --> tests/ui/single_range_in_vec_init.rs:30:5 | LL | [0usize..200]; | ^^^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + [0usize; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:33:5 + --> tests/ui/single_range_in_vec_init.rs:32:5 | LL | [0..200usize]; | ^^^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + [0; 200usize]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:35:5 + --> tests/ui/single_range_in_vec_init.rs:34:5 | LL | vec![0u8..200]; | ^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + vec![0u8; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:37:5 + --> tests/ui/single_range_in_vec_init.rs:36:5 | LL | vec![0usize..200]; | ^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + vec![0usize; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:39:5 + --> tests/ui/single_range_in_vec_init.rs:38:5 | LL | vec![0..200usize]; | ^^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + vec![0; 200usize]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:42:5 + --> tests/ui/single_range_in_vec_init.rs:41:5 | LL | [0..200isize]; | ^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL + (0..200isize).collect::>(); | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:44:5 + --> tests/ui/single_range_in_vec_init.rs:43:5 | LL | vec![0..200isize]; | ^^^^^^^^^^^^^^^^^ @@ -160,5 +160,17 @@ LL - vec![0..200isize]; LL + (0..200isize).collect::>(); | -error: aborting due to 10 previous errors +error: a `Vec` of `Range` that is only one element + --> tests/ui/single_range_in_vec_init.rs:82:17 + | +LL | let input = vec![0..as_i32!(10)]; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: if you wanted a `Vec` that contains the entire range, try + | +LL - let input = vec![0..as_i32!(10)]; +LL + let input = (0..as_i32!(10)).collect::>(); + | + +error: aborting due to 11 previous errors diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 02f67f79e2b1..f332e02a2d32 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -4,6 +4,7 @@ #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] #![allow(unused, clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] +#![allow(function_casts_as_integer)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs index c5e156405ebc..c29a42ddca53 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -4,6 +4,7 @@ #![warn(clippy::useless_transmute)] #![warn(clippy::transmute_ptr_to_ptr)] #![allow(unused, clippy::borrow_as_ptr, clippy::missing_transmute_annotations)] +#![allow(function_casts_as_integer)] use std::mem::{size_of, transmute}; diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr index f39a64d57eb4..5ddc3de6a039 100644 --- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:19:38 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:20:38 | LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + let _ptr_i8_transmute = unsafe { ptr_i32.cast::() }; | error: transmute from a pointer to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:26:46 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:27:46 | LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] }; | error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:34:50 | LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize` @@ -34,7 +34,7 @@ LL | let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us = help: to override `-D warnings` add `#[allow(clippy::transmutes_expressible_as_ptr_casts)]` error: transmute from a reference to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:40:41 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:41:41 | LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]` @@ -43,31 +43,31 @@ LL | let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:49:41 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:50:41 | LL | let _usize_ptr_transmute = unsafe { transmute:: u8, *const usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize` error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:54:49 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:55:49 | LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:58:36 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:59:36 | LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize` error: transmute from a reference to a pointer - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:70:14 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:71:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` error: transmute from `fn()` to `*const u8` which could be expressed as a pointer cast instead - --> tests/ui/transmutes_expressible_as_ptr_casts.rs:89:28 + --> tests/ui/transmutes_expressible_as_ptr_casts.rs:90:28 | LL | let _x: u8 = unsafe { *std::mem::transmute::(f) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(f as *const u8)` diff --git a/src/tools/clippy/tests/ui/unnecessary_map_on_constructor.stderr b/src/tools/clippy/tests/ui/unnecessary_map_on_constructor.stderr index f29bfec60f72..a19116820808 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_on_constructor.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_on_constructor.stderr @@ -1,4 +1,4 @@ -error: unnecessary map on constructor Some(_) +error: unnecessary `map` on constructor `Some(_)` --> tests/ui/unnecessary_map_on_constructor.rs:32:13 | LL | let a = Some(x).map(fun); @@ -7,43 +7,43 @@ LL | let a = Some(x).map(fun); = note: `-D clippy::unnecessary-map-on-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_on_constructor)]` -error: unnecessary map on constructor Ok(_) +error: unnecessary `map` on constructor `Ok(_)` --> tests/ui/unnecessary_map_on_constructor.rs:34:27 | LL | let b: SimpleResult = Ok(x).map(fun); | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` -error: unnecessary map_err on constructor Err(_) +error: unnecessary `map_err` on constructor `Err(_)` --> tests/ui/unnecessary_map_on_constructor.rs:36:27 | LL | let c: SimpleResult = Err(err).map_err(notfun); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Err(notfun(err))` -error: unnecessary map on constructor Option::Some(_) +error: unnecessary `map` on constructor `Option::Some(_)` --> tests/ui/unnecessary_map_on_constructor.rs:39:13 | LL | let a = Option::Some(x).map(fun); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Option::Some(fun(x))` -error: unnecessary map on constructor SimpleResult::Ok(_) +error: unnecessary `map` on constructor `SimpleResult::Ok(_)` --> tests/ui/unnecessary_map_on_constructor.rs:41:27 | LL | let b: SimpleResult = SimpleResult::Ok(x).map(fun); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Ok(fun(x))` -error: unnecessary map_err on constructor SimpleResult::Err(_) +error: unnecessary `map_err` on constructor `SimpleResult::Err(_)` --> tests/ui/unnecessary_map_on_constructor.rs:43:27 | LL | let c: SimpleResult = SimpleResult::Err(err).map_err(notfun); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `SimpleResult::Err(notfun(err))` -error: unnecessary map on constructor Ok(_) +error: unnecessary `map` on constructor `Ok(_)` --> tests/ui/unnecessary_map_on_constructor.rs:45:52 | LL | let b: std::result::Result = Ok(x).map(fun); | ^^^^^^^^^^^^^^ help: try: `Ok(fun(x))` -error: unnecessary map_err on constructor Err(_) +error: unnecessary `map_err` on constructor `Err(_)` --> tests/ui/unnecessary_map_on_constructor.rs:47:52 | LL | let c: std::result::Result = Err(err).map_err(notfun); diff --git a/src/tools/clippy/tests/ui/unnecessary_mut_passed.fixed b/src/tools/clippy/tests/ui/unnecessary_mut_passed.fixed index 63bbadb01dcb..876b61d29519 100644 --- a/src/tools/clippy/tests/ui/unnecessary_mut_passed.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_mut_passed.fixed @@ -146,7 +146,7 @@ fn main() { my_struct.takes_raw_mut(a); } -// not supported currently +// These shouldn't be linted, see https://github.com/rust-lang/rust-clippy/pull/15962#issuecomment-3503704832 fn raw_ptrs(my_struct: MyStruct) { let mut n = 42; diff --git a/src/tools/clippy/tests/ui/unnecessary_mut_passed.rs b/src/tools/clippy/tests/ui/unnecessary_mut_passed.rs index b719ca1871b2..e92368bfffeb 100644 --- a/src/tools/clippy/tests/ui/unnecessary_mut_passed.rs +++ b/src/tools/clippy/tests/ui/unnecessary_mut_passed.rs @@ -146,7 +146,7 @@ fn main() { my_struct.takes_raw_mut(a); } -// not supported currently +// These shouldn't be linted, see https://github.com/rust-lang/rust-clippy/pull/15962#issuecomment-3503704832 fn raw_ptrs(my_struct: MyStruct) { let mut n = 42; diff --git a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr index 14d1d20a66e4..c742cc8a85ba 100644 --- a/src/tools/clippy/tests/ui/unused_enumerate_index.stderr +++ b/src/tools/clippy/tests/ui/unused_enumerate_index.stderr @@ -1,8 +1,8 @@ error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:12:19 + --> tests/ui/unused_enumerate_index.rs:12:27 | LL | for (_, x) in v.iter().enumerate() { - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = note: `-D clippy::unused-enumerate-index` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unused_enumerate_index)]` @@ -13,10 +13,10 @@ LL + for x in v.iter() { | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:60:19 + --> tests/ui/unused_enumerate_index.rs:60:24 | LL | for (_, x) in dummy.enumerate() { - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -25,10 +25,10 @@ LL + for x in dummy { | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:65:39 + --> tests/ui/unused_enumerate_index.rs:65:38 | LL | let _ = vec![1, 2, 3].into_iter().enumerate().map(|(_, x)| println!("{x}")); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -37,10 +37,10 @@ LL + let _ = vec![1, 2, 3].into_iter().map(|x| println!("{x}")); | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:68:39 + --> tests/ui/unused_enumerate_index.rs:68:38 | LL | let p = vec![1, 2, 3].into_iter().enumerate(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -50,10 +50,10 @@ LL ~ p.map(|x| println!("{x}")); | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:90:17 + --> tests/ui/unused_enumerate_index.rs:90:16 | LL | _ = mac2!().enumerate().map(|(_, _v)| {}); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -62,10 +62,10 @@ LL + _ = mac2!().map(|_v| {}); | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:99:39 + --> tests/ui/unused_enumerate_index.rs:99:38 | LL | let v = [1, 2, 3].iter().copied().enumerate(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -75,10 +75,10 @@ LL ~ let x = v.map(|x: i32| x).sum::(); | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:105:39 + --> tests/ui/unused_enumerate_index.rs:105:38 | LL | let v = [1, 2, 3].iter().copied().enumerate(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | @@ -88,10 +88,10 @@ LL ~ let x = v.map(|x: i32| x).sum::(); | error: you seem to use `.enumerate()` and immediately discard the index - --> tests/ui/unused_enumerate_index.rs:110:39 + --> tests/ui/unused_enumerate_index.rs:110:38 | LL | let v = [1, 2, 3].iter().copied().enumerate(); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^ | help: remove the `.enumerate()` call | diff --git a/src/tools/clippy/tests/ui/write_literal.fixed b/src/tools/clippy/tests/ui/write_literal.fixed index 29352fd468ea..ae29f3a57462 100644 --- a/src/tools/clippy/tests/ui/write_literal.fixed +++ b/src/tools/clippy/tests/ui/write_literal.fixed @@ -70,6 +70,55 @@ fn main() { //~^ write_literal } +fn escaping() { + let mut v = Vec::new(); + + writeln!(v, "{{hello}}"); + //~^ write_literal + + writeln!(v, r"{{hello}}"); + //~^ write_literal + + writeln!(v, "'"); + //~^ write_literal + + writeln!(v, "\""); + //~^ write_literal + + writeln!(v, r"'"); + //~^ write_literal + + writeln!( + v, + "some hello \ + //~^ write_literal + world!", + ); + writeln!( + v, + "some 1\ + 2 \\ 3", + //~^^^ write_literal + ); + writeln!(v, "\\"); + //~^ write_literal + + writeln!(v, r"\"); + //~^ write_literal + + writeln!(v, r#"\"#); + //~^ write_literal + + writeln!(v, "\\"); + //~^ write_literal + + writeln!(v, "\r"); + //~^ write_literal + + // should not lint + writeln!(v, r"{}", "\r"); +} + fn issue_13959() { let mut v = Vec::new(); writeln!(v, "\""); diff --git a/src/tools/clippy/tests/ui/write_literal.rs b/src/tools/clippy/tests/ui/write_literal.rs index 928727527592..d930339e106c 100644 --- a/src/tools/clippy/tests/ui/write_literal.rs +++ b/src/tools/clippy/tests/ui/write_literal.rs @@ -70,6 +70,59 @@ fn main() { //~^ write_literal } +fn escaping() { + let mut v = Vec::new(); + + writeln!(v, "{}", "{hello}"); + //~^ write_literal + + writeln!(v, r"{}", r"{hello}"); + //~^ write_literal + + writeln!(v, "{}", '\''); + //~^ write_literal + + writeln!(v, "{}", '"'); + //~^ write_literal + + writeln!(v, r"{}", '\''); + //~^ write_literal + + writeln!( + v, + "some {}", + "hello \ + //~^ write_literal + world!", + ); + writeln!( + v, + "some {}\ + {} \\ {}", + "1", + "2", + "3", + //~^^^ write_literal + ); + writeln!(v, "{}", "\\"); + //~^ write_literal + + writeln!(v, r"{}", "\\"); + //~^ write_literal + + writeln!(v, r#"{}"#, "\\"); + //~^ write_literal + + writeln!(v, "{}", r"\"); + //~^ write_literal + + writeln!(v, "{}", "\r"); + //~^ write_literal + + // should not lint + writeln!(v, r"{}", "\r"); +} + fn issue_13959() { let mut v = Vec::new(); writeln!(v, "{}", r#"""#); diff --git a/src/tools/clippy/tests/ui/write_literal.stderr b/src/tools/clippy/tests/ui/write_literal.stderr index ca37406c8114..374098fa2b14 100644 --- a/src/tools/clippy/tests/ui/write_literal.stderr +++ b/src/tools/clippy/tests/ui/write_literal.stderr @@ -145,7 +145,156 @@ LL + writeln!(v, "hello {0} {1}, world {2}", 2, 3, 4); | error: literal with an empty format string - --> tests/ui/write_literal.rs:75:23 + --> tests/ui/write_literal.rs:76:23 + | +LL | writeln!(v, "{}", "{hello}"); + | ^^^^^^^^^ + | +help: try + | +LL - writeln!(v, "{}", "{hello}"); +LL + writeln!(v, "{{hello}}"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:79:24 + | +LL | writeln!(v, r"{}", r"{hello}"); + | ^^^^^^^^^^ + | +help: try + | +LL - writeln!(v, r"{}", r"{hello}"); +LL + writeln!(v, r"{{hello}}"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:82:23 + | +LL | writeln!(v, "{}", '\''); + | ^^^^ + | +help: try + | +LL - writeln!(v, "{}", '\''); +LL + writeln!(v, "'"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:85:23 + | +LL | writeln!(v, "{}", '"'); + | ^^^ + | +help: try + | +LL - writeln!(v, "{}", '"'); +LL + writeln!(v, "\""); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:88:24 + | +LL | writeln!(v, r"{}", '\''); + | ^^^^ + | +help: try + | +LL - writeln!(v, r"{}", '\''); +LL + writeln!(v, r"'"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:94:9 + | +LL | / "hello \ +LL | | +LL | | world!", + | |_______________^ + | +help: try + | +LL ~ "some hello \ +LL + +LL ~ world!", + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:102:9 + | +LL | / "1", +LL | | "2", +LL | | "3", + | |___________^ + | +help: try + | +LL ~ "some 1\ +LL ~ 2 \\ 3", + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:107:23 + | +LL | writeln!(v, "{}", "\\"); + | ^^^^ + | +help: try + | +LL - writeln!(v, "{}", "\\"); +LL + writeln!(v, "\\"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:110:24 + | +LL | writeln!(v, r"{}", "\\"); + | ^^^^ + | +help: try + | +LL - writeln!(v, r"{}", "\\"); +LL + writeln!(v, r"\"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:113:26 + | +LL | writeln!(v, r#"{}"#, "\\"); + | ^^^^ + | +help: try + | +LL - writeln!(v, r#"{}"#, "\\"); +LL + writeln!(v, r#"\"#); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:116:23 + | +LL | writeln!(v, "{}", r"\"); + | ^^^^ + | +help: try + | +LL - writeln!(v, "{}", r"\"); +LL + writeln!(v, "\\"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:119:23 + | +LL | writeln!(v, "{}", "\r"); + | ^^^^ + | +help: try + | +LL - writeln!(v, "{}", "\r"); +LL + writeln!(v, "\r"); + | + +error: literal with an empty format string + --> tests/ui/write_literal.rs:128:23 | LL | writeln!(v, "{}", r#"""#); | ^^^^^^ @@ -157,7 +306,7 @@ LL + writeln!(v, "\""); | error: literal with an empty format string - --> tests/ui/write_literal.rs:80:9 + --> tests/ui/write_literal.rs:133:9 | LL | / r#" LL | | @@ -182,7 +331,7 @@ LL ~ " | error: literal with an empty format string - --> tests/ui/write_literal.rs:94:55 + --> tests/ui/write_literal.rs:147:55 | LL | writeln!(v, "Hello {3} is {0:2$.1$}", 0.01, 2, 3, "x"); | ^^^ @@ -194,7 +343,7 @@ LL + writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3); | error: literal with an empty format string - --> tests/ui/write_literal.rs:96:52 + --> tests/ui/write_literal.rs:149:52 | LL | writeln!(v, "Hello {2} is {0:3$.1$}", 0.01, 2, "x", 3); | ^^^ @@ -206,7 +355,7 @@ LL + writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3); | error: literal with an empty format string - --> tests/ui/write_literal.rs:98:49 + --> tests/ui/write_literal.rs:151:49 | LL | writeln!(v, "Hello {1} is {0:3$.2$}", 0.01, "x", 2, 3); | ^^^ @@ -218,7 +367,7 @@ LL + writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3); | error: literal with an empty format string - --> tests/ui/write_literal.rs:100:43 + --> tests/ui/write_literal.rs:153:43 | LL | writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); | ^^^ @@ -229,5 +378,5 @@ LL - writeln!(v, "Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); LL + writeln!(v, "Hello x is {0:2$.1$}", 0.01, 2, 3); | -error: aborting due to 18 previous errors +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/write_literal_2.rs b/src/tools/clippy/tests/ui/write_literal_2.rs deleted file mode 100644 index f896782aaf3b..000000000000 --- a/src/tools/clippy/tests/ui/write_literal_2.rs +++ /dev/null @@ -1,65 +0,0 @@ -//@no-rustfix: overlapping suggestions -#![allow(unused_must_use)] -#![warn(clippy::write_literal)] - -use std::io::Write; - -fn main() { - let mut v = Vec::new(); - - writeln!(v, "{}", "{hello}"); - //~^ write_literal - - writeln!(v, r"{}", r"{hello}"); - //~^ write_literal - - writeln!(v, "{}", '\''); - //~^ write_literal - - writeln!(v, "{}", '"'); - //~^ write_literal - - writeln!(v, r"{}", '"'); - //~^ write_literal - - writeln!(v, r"{}", '\''); - //~^ write_literal - - writeln!( - v, - "some {}", - "hello \ - //~^ write_literal - world!", - ); - writeln!( - v, - "some {}\ - {} \\ {}", - "1", - "2", - "3", - //~^^^ write_literal - ); - writeln!(v, "{}", "\\"); - //~^ write_literal - - writeln!(v, r"{}", "\\"); - //~^ write_literal - - writeln!(v, r#"{}"#, "\\"); - //~^ write_literal - - writeln!(v, "{}", r"\"); - //~^ write_literal - - writeln!(v, "{}", "\r"); - //~^ write_literal - - // hard mode - writeln!(v, r#"{}{}"#, '#', '"'); - //~^ write_literal - - // should not lint - writeln!(v, r"{}", "\r"); -} diff --git a/src/tools/clippy/tests/ui/write_literal_2.stderr b/src/tools/clippy/tests/ui/write_literal_2.stderr deleted file mode 100644 index 29803d6a8b18..000000000000 --- a/src/tools/clippy/tests/ui/write_literal_2.stderr +++ /dev/null @@ -1,165 +0,0 @@ -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:10:23 - | -LL | writeln!(v, "{}", "{hello}"); - | ^^^^^^^^^ - | - = note: `-D clippy::write-literal` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::write_literal)]` -help: try - | -LL - writeln!(v, "{}", "{hello}"); -LL + writeln!(v, "{{hello}}"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:13:24 - | -LL | writeln!(v, r"{}", r"{hello}"); - | ^^^^^^^^^^ - | -help: try - | -LL - writeln!(v, r"{}", r"{hello}"); -LL + writeln!(v, r"{{hello}}"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:16:23 - | -LL | writeln!(v, "{}", '\''); - | ^^^^ - | -help: try - | -LL - writeln!(v, "{}", '\''); -LL + writeln!(v, "'"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:19:23 - | -LL | writeln!(v, "{}", '"'); - | ^^^ - | -help: try - | -LL - writeln!(v, "{}", '"'); -LL + writeln!(v, "\""); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:22:24 - | -LL | writeln!(v, r"{}", '"'); - | ^^^ - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:25:24 - | -LL | writeln!(v, r"{}", '\''); - | ^^^^ - | -help: try - | -LL - writeln!(v, r"{}", '\''); -LL + writeln!(v, r"'"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:31:9 - | -LL | / "hello \ -LL | | -LL | | world!", - | |_______________^ - | -help: try - | -LL ~ "some hello \ -LL + -LL ~ world!", - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:39:9 - | -LL | / "1", -LL | | "2", -LL | | "3", - | |___________^ - | -help: try - | -LL ~ "some 1\ -LL ~ 2 \\ 3", - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:44:23 - | -LL | writeln!(v, "{}", "\\"); - | ^^^^ - | -help: try - | -LL - writeln!(v, "{}", "\\"); -LL + writeln!(v, "\\"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:47:24 - | -LL | writeln!(v, r"{}", "\\"); - | ^^^^ - | -help: try - | -LL - writeln!(v, r"{}", "\\"); -LL + writeln!(v, r"\"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:50:26 - | -LL | writeln!(v, r#"{}"#, "\\"); - | ^^^^ - | -help: try - | -LL - writeln!(v, r#"{}"#, "\\"); -LL + writeln!(v, r#"\"#); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:53:23 - | -LL | writeln!(v, "{}", r"\"); - | ^^^^ - | -help: try - | -LL - writeln!(v, "{}", r"\"); -LL + writeln!(v, "\\"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:56:23 - | -LL | writeln!(v, "{}", "\r"); - | ^^^^ - | -help: try - | -LL - writeln!(v, "{}", "\r"); -LL + writeln!(v, "\r"); - | - -error: literal with an empty format string - --> tests/ui/write_literal_2.rs:60:28 - | -LL | writeln!(v, r#"{}{}"#, '#', '"'); - | ^^^^^^^^ - -error: aborting due to 14 previous errors - diff --git a/src/tools/clippy/tests/ui/write_literal_unfixable.rs b/src/tools/clippy/tests/ui/write_literal_unfixable.rs new file mode 100644 index 000000000000..3a5660180779 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal_unfixable.rs @@ -0,0 +1,16 @@ +//@no-rustfix +#![allow(unused_must_use)] +#![warn(clippy::write_literal)] + +use std::io::Write; + +fn escaping() { + let mut v = vec![]; + + writeln!(v, r"{}", '"'); + //~^ write_literal + + // hard mode + writeln!(v, r#"{}{}"#, '#', '"'); + //~^ write_literal +} diff --git a/src/tools/clippy/tests/ui/write_literal_unfixable.stderr b/src/tools/clippy/tests/ui/write_literal_unfixable.stderr new file mode 100644 index 000000000000..0dd40e891893 --- /dev/null +++ b/src/tools/clippy/tests/ui/write_literal_unfixable.stderr @@ -0,0 +1,17 @@ +error: literal with an empty format string + --> tests/ui/write_literal_unfixable.rs:10:24 + | +LL | writeln!(v, r"{}", '"'); + | ^^^ + | + = note: `-D clippy::write-literal` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::write_literal)]` + +error: literal with an empty format string + --> tests/ui/write_literal_unfixable.rs:14:28 + | +LL | writeln!(v, r#"{}{}"#, '#', '"'); + | ^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index d34ff0a59732..e443baff0808 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -28,7 +28,7 @@ Otherwise, have a great day =^.^= {# #} {# #} {# #} -

{# #} +