diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb6bc325a3bb..8963039dd50b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -289,7 +289,7 @@ jobs: fi # Get closest bors merge commit - PARENT_COMMIT=`git rev-list --author='bors ' -n1 --first-parent HEAD^1` + PARENT_COMMIT=`git rev-list --author='122020455+rust-bors\[bot\]@users.noreply.github.com' -n1 --first-parent HEAD^1` ./build/citool/debug/citool postprocess-metrics \ --job-name ${CI_JOB_NAME} \ diff --git a/Cargo.lock b/Cargo.lock index 427e93e56cd8..9c791babc399 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,19 +182,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "askama" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" -dependencies = [ - "askama_derive 0.14.0", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - [[package]] name = "askama" version = "0.15.1" @@ -208,30 +195,13 @@ dependencies = [ "serde_json", ] -[[package]] -name = "askama_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" -dependencies = [ - "askama_parser 0.14.0", - "basic-toml", - "memchr", - "proc-macro2", - "quote", - "rustc-hash 2.1.1", - "serde", - "serde_derive", - "syn 2.0.110", -] - [[package]] name = "askama_derive" version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" dependencies = [ - "askama_parser 0.15.1", + "askama_parser", "basic-toml", "memchr", "proc-macro2", @@ -248,19 +218,7 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" dependencies = [ - "askama_derive 0.15.1", -] - -[[package]] -name = "askama_parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" -dependencies = [ - "memchr", - "serde", - "serde_derive", - "winnow 0.7.13", + "askama_derive", ] [[package]] @@ -675,7 +633,7 @@ name = "clippy" version = "0.1.94" dependencies = [ "anstream", - "askama 0.14.0", + "askama", "cargo_metadata 0.18.1", "clippy_config", "clippy_lints", @@ -1566,7 +1524,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", - "askama 0.15.1", + "askama", "cargo_metadata 0.21.0", "serde", "serde_json", @@ -3412,9 +3370,9 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc-literal-escaper" -version = "0.0.5" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" +checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" [[package]] name = "rustc-main" @@ -4421,6 +4379,7 @@ dependencies = [ "rustc_errors", "rustc_fluent_macro", "rustc_hir", + "rustc_index", "rustc_macros", "rustc_middle", "rustc_session", @@ -4912,7 +4871,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama 0.15.1", + "askama", "base64", "expect-test", "indexmap", diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index a0350dd6ff31..5e81ec28ee35 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -618,7 +618,7 @@ impl DroplessArena { /// - Types that are `!Copy` and `Drop`: these must be specified in the /// arguments. The `TypedArena` will be used for them. /// -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) { #[derive(Default)] pub struct Arena<'tcx> { diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml index 34d90adf5cb3..471a6bf1df13 100644 --- a/compiler/rustc_ast/Cargo.toml +++ b/compiler/rustc_ast/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start bitflags = "2.4.1" memchr = "2.7.6" -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" rustc_ast_ir = { path = "../rustc_ast_ir" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d4407dbf7be7..fb2ccefe12a1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2117,10 +2117,9 @@ pub struct MacroDef { #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic, Walkable)] pub struct EiiExternTarget { - /// path to the extern item we're targetting + /// path to the extern item we're targeting pub extern_item_path: Path, pub impl_unsafe: bool, - pub span: Span, } #[derive(Clone, Encodable, Decodable, Debug, Copy, Hash, Eq, PartialEq)] @@ -3813,6 +3812,19 @@ pub struct Fn { pub struct EiiImpl { pub node_id: NodeId, pub eii_macro_path: Path, + /// This field is an implementation detail that prevents a lot of bugs. + /// See for an example. + /// + /// The problem is, that if we generate a declaration *together* with its default, + /// we generate both a declaration and an implementation. The generated implementation + /// uses the same mechanism to register itself as a user-defined implementation would, + /// despite being invisible to users. What does happen is a name resolution step. + /// The invisible default implementation has to find the declaration. + /// Both are generated at the same time, so we can skip that name resolution step. + /// + /// This field is that shortcut: we prefill the extern target to skip a name resolution step, + /// making sure it never fails. It'd be awful UX if we fail name resolution in code invisible to the user. + pub known_eii_macro_resolution: Option, pub impl_safety: Safety, pub span: Span, pub inner_span: Span, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 6ecba865c815..0a2a34d932f6 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -235,6 +235,34 @@ impl AttributeExt for Attribute { } } + fn deprecation_note(&self) -> Option { + match &self.kind { + AttrKind::Normal(normal) if normal.item.path == sym::deprecated => { + let meta = &normal.item; + + // #[deprecated = "..."] + if let Some(s) = meta.value_str() { + return Some(s); + } + + // #[deprecated(note = "...")] + if let Some(list) = meta.meta_item_list() { + for nested in list { + if let Some(mi) = nested.meta_item() + && mi.path == sym::note + && let Some(s) = mi.value_str() + { + return Some(s); + } + } + } + + None + } + _ => None, + } + } + fn doc_resolution_scope(&self) -> Option { match &self.kind { AttrKind::DocComment(..) => Some(self.style), @@ -277,6 +305,7 @@ impl Attribute { pub fn may_have_doc_links(&self) -> bool { self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) + || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str())) } /// Extracts the MetaItem from inside this Attribute. @@ -873,6 +902,11 @@ pub trait AttributeExt: Debug { /// * `#[doc(...)]` returns `None`. fn doc_str(&self) -> Option; + /// Returns the deprecation note if this is deprecation attribute. + /// * `#[deprecated = "note"]` returns `Some("note")`. + /// * `#[deprecated(note = "note", ...)]` returns `Some("note")`. + fn deprecation_note(&self) -> Option; + fn is_proc_macro_attr(&self) -> bool { [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] .iter() diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f8b98a5ece40..3a7308ef7c97 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -2,7 +2,7 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiDecl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImplResolution}; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; use rustc_hir::{ @@ -134,6 +134,56 @@ impl<'hir> LoweringContext<'_, 'hir> { } } + fn lower_eii_extern_target( + &mut self, + id: NodeId, + eii_name: Ident, + EiiExternTarget { extern_item_path, impl_unsafe }: &EiiExternTarget, + ) -> Option { + self.lower_path_simple_eii(id, extern_item_path).map(|did| EiiDecl { + eii_extern_target: did, + impl_unsafe: *impl_unsafe, + name: eii_name, + }) + } + + fn lower_eii_impl( + &mut self, + EiiImpl { + node_id, + eii_macro_path, + impl_safety, + span, + inner_span, + is_default, + known_eii_macro_resolution, + }: &EiiImpl, + ) -> hir::attrs::EiiImpl { + let resolution = if let Some(target) = known_eii_macro_resolution + && let Some(decl) = self.lower_eii_extern_target( + *node_id, + // the expect is ok here since we always generate this path in the eii macro. + eii_macro_path.segments.last().expect("at least one segment").ident, + target, + ) { + EiiImplResolution::Known(decl) + } else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) { + EiiImplResolution::Macro(macro_did) + } else { + EiiImplResolution::Error( + self.dcx().span_delayed_bug(*span, "eii never resolved without errors given"), + ) + }; + + hir::attrs::EiiImpl { + span: self.lower_span(*span), + inner_span: self.lower_span(*inner_span), + impl_marked_unsafe: self.lower_safety(*impl_safety, hir::Safety::Safe).is_unsafe(), + is_default: *is_default, + resolution, + } + } + fn generate_extra_attrs_for_item_kind( &mut self, id: NodeId, @@ -143,49 +193,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ItemKind::Fn(box Fn { eii_impls, .. }) if eii_impls.is_empty() => Vec::new(), ItemKind::Fn(box Fn { eii_impls, .. }) => { vec![hir::Attribute::Parsed(AttributeKind::EiiImpls( - eii_impls - .iter() - .flat_map( - |EiiImpl { - node_id, - eii_macro_path, - impl_safety, - span, - inner_span, - is_default, - }| { - self.lower_path_simple_eii(*node_id, eii_macro_path).map(|did| { - hir::attrs::EiiImpl { - eii_macro: did, - span: self.lower_span(*span), - inner_span: self.lower_span(*inner_span), - impl_marked_unsafe: self - .lower_safety(*impl_safety, hir::Safety::Safe) - .is_unsafe(), - is_default: *is_default, - } - }) - }, - ) - .collect(), + eii_impls.iter().map(|i| self.lower_eii_impl(i)).collect(), ))] } - ItemKind::MacroDef( - _, - MacroDef { - eii_extern_target: Some(EiiExternTarget { extern_item_path, impl_unsafe, span }), - .. - }, - ) => self - .lower_path_simple_eii(id, extern_item_path) - .map(|did| { - vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(EiiDecl { - eii_extern_target: did, - impl_unsafe: *impl_unsafe, - span: self.lower_span(*span), - }))] - }) + ItemKind::MacroDef(name, MacroDef { eii_extern_target: Some(target), .. }) => self + .lower_eii_extern_target(id, *name, target) + .map(|decl| vec![hir::Attribute::Parsed(AttributeKind::EiiExternTarget(decl))]) .unwrap_or_default(), + ItemKind::ExternCrate(..) | ItemKind::Use(..) | ItemKind::Static(..) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a81d2297ef85..42eb8673f30c 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1986,8 +1986,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (name, kind) = self.lower_generic_param_kind(param, source); let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs, param.span(), Target::Param); - hir::GenericParam { + let param_attrs = ¶m.attrs; + let param_span = param.span(); + let param = hir::GenericParam { hir_id, def_id: self.local_def_id(param.id), name, @@ -1996,7 +1997,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { kind, colon_span: param.colon_span.map(|s| self.lower_span(s)), source, - } + }; + self.lower_attrs(hir_id, param_attrs, param_span, Target::from_generic_param(¶m)); + param } fn lower_generic_param_kind( @@ -2536,6 +2539,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { overly_complex_const(self) } + ExprKind::Lit(literal) => { + let span = expr.span; + let literal = self.lower_lit(literal, span); + + ConstArg { + hir_id: self.lower_node_id(expr.id), + kind: hir::ConstArgKind::Literal(literal.node), + span, + } + } _ => overly_complex_const(self), } } diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 01c503357fc7..5604fbd25edc 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -9,7 +9,7 @@ impl SingleAttributeParser for CrateNameParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(n) = args else { @@ -33,7 +33,7 @@ impl SingleAttributeParser for RecursionLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { @@ -56,7 +56,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { @@ -79,7 +79,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { @@ -102,7 +102,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let ArgParser::NameValue(nv) = args else { @@ -123,7 +123,7 @@ pub(crate) struct NoCoreParser; impl NoArgsAttributeParser for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; } @@ -132,7 +132,7 @@ pub(crate) struct NoStdParser; impl NoArgsAttributeParser for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; } @@ -141,7 +141,7 @@ pub(crate) struct RustcCoherenceIsCoreParser; impl NoArgsAttributeParser for RustcCoherenceIsCoreParser { const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore; } @@ -151,7 +151,7 @@ impl SingleAttributeParser for WindowsSubsystemParser { const PATH: &[Symbol] = &[sym::windows_subsystem]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: ["windows", "console"], "https://doc.rust-lang.org/reference/runtime.html#the-windows_subsystem-attribute"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 922a5bd297ac..834b1d988cb4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -305,3 +305,18 @@ impl SingleAttributeParser for RustcScalableVectorParser { Some(AttributeKind::RustcScalableVector { element_count: Some(n), span: cx.attr_span }) } } + +pub(crate) struct RustcHasIncoherentInherentImplsParser; + +impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParser { + const PATH: &[Symbol] = &[sym::rustc_has_incoherent_inherent_impls]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Trait), + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::Union), + Allow(Target::ForeignTy), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6d4f77ef1751..8d27e2e276dd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -1,6 +1,7 @@ use std::num::NonZero; use rustc_errors::ErrorGuaranteed; +use rustc_hir::target::GenericParamKind; use rustc_hir::{ DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel, StableSince, Target, UnstableReason, VERSION_PLACEHOLDER, @@ -43,7 +44,7 @@ const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ Allow(Target::TyAlias), Allow(Target::Variant), Allow(Target::Field), - Allow(Target::Param), + Allow(Target::GenericParam { kind: GenericParamKind::Type, has_default: true }), Allow(Target::Static), Allow(Target::ForeignFn), Allow(Target::ForeignStatic), diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index 52aa42b1085a..7db84f8f2d95 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -15,7 +15,7 @@ impl SingleAttributeParser for TransparencyParser { }); const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const TEMPLATE: AttributeTemplate = - template!(NameValueStr: ["transparent", "semitransparent", "opaque"]); + template!(NameValueStr: ["transparent", "semiopaque", "opaque"]); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option { let Some(nv) = args.name_value() else { @@ -24,12 +24,12 @@ impl SingleAttributeParser for TransparencyParser { }; match nv.value_as_str() { Some(sym::transparent) => Some(Transparency::Transparent), - Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque), + Some(sym::semiopaque) => Some(Transparency::SemiOpaque), Some(sym::opaque) => Some(Transparency::Opaque), Some(_) => { cx.expected_specific_argument_strings( nv.value_span, - &[sym::transparent, sym::semitransparent, sym::opaque], + &[sym::transparent, sym::semiopaque, sym::opaque], ); None } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b85bb6c6c89d..732bf111b256 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -4,12 +4,12 @@ use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use private::Sealed; -use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; +use rustc_ast::{AttrStyle, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; -use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; +use rustc_hir::{AttrPath, HirId}; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -62,12 +62,12 @@ use crate::attributes::proc_macro_attrs::{ use crate::attributes::prototype::CustomMirParser; use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ - RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, - RustcLegacyConstGenericsParser, RustcLintDiagnosticsParser, RustcLintOptDenyFieldAccessParser, - RustcLintOptTyParser, RustcLintQueryInstabilityParser, - RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, - RustcObjectLifetimeDefaultParser, RustcScalableVectorParser, + RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser, + RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, + RustcLintDiagnosticsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, + RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, + RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, + RustcNoImplicitAutorefsParser, RustcObjectLifetimeDefaultParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; @@ -264,6 +264,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, @@ -303,8 +304,6 @@ pub trait Stage: Sized + 'static + Sealed { ) -> ErrorGuaranteed; fn should_emit(&self) -> ShouldEmit; - - fn id_is_crate_root(id: Self::Id) -> bool; } // allow because it's a sealed trait @@ -326,10 +325,6 @@ impl Stage for Early { fn should_emit(&self) -> ShouldEmit { self.emit_errors } - - fn id_is_crate_root(id: Self::Id) -> bool { - id == CRATE_NODE_ID - } } // allow because it's a sealed trait @@ -351,10 +346,6 @@ impl Stage for Late { fn should_emit(&self) -> ShouldEmit { ShouldEmit::ErrorsAndLints } - - fn id_is_crate_root(id: Self::Id) -> bool { - id == CRATE_HIR_ID - } } /// used when parsing attributes for miscellaneous things *before* ast lowering diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index e86ecb451fc2..52c2d10f4797 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -15,11 +15,6 @@ use crate::session_diagnostics::InvalidTarget; pub(crate) enum AllowedTargets { AllowList(&'static [Policy]), AllowListWarnRest(&'static [Policy]), - /// Special, and not the same as `AllowList(&[Allow(Target::Crate)])`. - /// For crate-level attributes we emit a specific set of lints to warn - /// people about accidentally not using them on the crate. - /// Only use this for attributes that are *exclusively* valid at the crate level. - CrateLevel, } pub(crate) enum AllowedResult { @@ -53,7 +48,6 @@ impl AllowedTargets { AllowedResult::Warn } } - AllowedTargets::CrateLevel => AllowedResult::Allowed, } } @@ -61,7 +55,6 @@ impl AllowedTargets { match self { AllowedTargets::AllowList(list) => list, AllowedTargets::AllowListWarnRest(list) => list, - AllowedTargets::CrateLevel => ALL_TARGETS, } .iter() .filter_map(|target| match target { @@ -95,7 +88,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { - Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx); + if allowed_targets.allowed_targets() == &[Target::Crate] { + Self::check_crate_level(target, cx); + return; + } match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} @@ -149,18 +145,10 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } } - pub(crate) fn check_type( - crate_level: bool, - target: Target, - cx: &mut AcceptContext<'_, 'sess, S>, - ) { - let is_crate_root = S::id_is_crate_root(cx.target_id); - - if is_crate_root { - return; - } - - if !crate_level { + pub(crate) fn check_crate_level(target: Target, cx: &mut AcceptContext<'_, 'sess, S>) { + // For crate-level attributes we emit a specific set of lints to warn + // people about accidentally not using them on the crate. + if target == Target::Crate { return; } @@ -310,5 +298,29 @@ pub(crate) const ALL_TARGETS: &'static [Policy] = { Allow(Target::Crate), Allow(Target::Delegation { mac: false }), Allow(Target::Delegation { mac: true }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Const, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Const, + has_default: true, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Lifetime, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Lifetime, + has_default: true, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Type, + has_default: false, + }), + Allow(Target::GenericParam { + kind: rustc_hir::target::GenericParamKind::Type, + has_default: true, + }), ] }; diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index f0f6f2dcf82c..e4fded0cb01e 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -182,6 +182,8 @@ builtin_macros_expected_other = expected operand, {$is_inline_asm -> builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently +builtin_macros_format_add_missing_colon = add a colon before the format specifier + builtin_macros_format_duplicate_arg = duplicate argument named `{$ident}` .label1 = previously here .label2 = duplicate argument diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 9049639925dd..0ebd3dc826d4 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -103,19 +103,19 @@ fn eii_( // span of the declaring item without attributes let item_span = func.sig.span; - // span of the eii attribute and the item below it, i.e. the full declaration - let decl_span = eii_attr_span.to(item_span); let foreign_item_name = func.ident; let mut return_items = Vec::new(); if func.body.is_some() { return_items.push(Box::new(generate_default_impl( + ecx, &func, impl_unsafe, macro_name, eii_attr_span, item_span, + foreign_item_name, ))) } @@ -133,7 +133,7 @@ fn eii_( macro_name, foreign_item_name, impl_unsafe, - decl_span, + &attrs_from_decl, ))); return_items.into_iter().map(wrap_item).collect() @@ -187,11 +187,13 @@ fn filter_attrs_for_multiple_eii_attr( } fn generate_default_impl( + ecx: &mut ExtCtxt<'_>, func: &ast::Fn, impl_unsafe: bool, macro_name: Ident, eii_attr_span: Span, item_span: Span, + foreign_item_name: Ident, ) -> ast::Item { // FIXME: re-add some original attrs let attrs = ThinVec::new(); @@ -208,6 +210,21 @@ fn generate_default_impl( }, span: eii_attr_span, is_default: true, + known_eii_macro_resolution: Some(ast::EiiExternTarget { + extern_item_path: ast::Path { + span: foreign_item_name.span, + segments: thin_vec![ + ast::PathSegment { + ident: Ident::from_str_and_span("super", foreign_item_name.span,), + id: DUMMY_NODE_ID, + args: None + }, + ast::PathSegment { ident: foreign_item_name, id: DUMMY_NODE_ID, args: None }, + ], + tokens: None, + }, + impl_unsafe, + }), }); ast::Item { @@ -236,18 +253,66 @@ fn generate_default_impl( stmts: thin_vec![ast::Stmt { id: DUMMY_NODE_ID, kind: ast::StmtKind::Item(Box::new(ast::Item { - attrs, + attrs: ThinVec::new(), id: DUMMY_NODE_ID, span: item_span, vis: ast::Visibility { - span: eii_attr_span, + span: item_span, kind: ast::VisibilityKind::Inherited, tokens: None }, - kind: ItemKind::Fn(Box::new(default_func)), + kind: ItemKind::Mod( + ast::Safety::Default, + Ident::from_str_and_span("dflt", item_span), + ast::ModKind::Loaded( + thin_vec![ + Box::new(ast::Item { + attrs: thin_vec![ecx.attr_nested_word( + sym::allow, + sym::unused_imports, + item_span + ),], + id: DUMMY_NODE_ID, + span: item_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None + }, + kind: ItemKind::Use(ast::UseTree { + prefix: ast::Path::from_ident( + Ident::from_str_and_span( + "super", item_span, + ) + ), + kind: ast::UseTreeKind::Glob, + span: item_span, + }), + tokens: None, + }), + Box::new(ast::Item { + attrs, + id: DUMMY_NODE_ID, + span: item_span, + vis: ast::Visibility { + span: eii_attr_span, + kind: ast::VisibilityKind::Inherited, + tokens: None + }, + kind: ItemKind::Fn(Box::new(default_func)), + tokens: None, + }), + ], + ast::Inline::Yes, + ast::ModSpans { + inner_span: item_span, + inject_use_span: item_span, + } + ) + ), tokens: None, })), - span: eii_attr_span + span: eii_attr_span, }], id: DUMMY_NODE_ID, rules: ast::BlockCheckMode::Default, @@ -352,10 +417,14 @@ fn generate_attribute_macro_to_implement( macro_name: Ident, foreign_item_name: Ident, impl_unsafe: bool, - decl_span: Span, + attrs_from_decl: &[Attribute], ) -> ast::Item { let mut macro_attrs = ThinVec::new(); + // To avoid e.g. `error: attribute macro has missing stability attribute` + // errors for eii's in std. + macro_attrs.extend_from_slice(attrs_from_decl); + // #[builtin_macro(eii_shared_macro)] macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span)); @@ -394,7 +463,6 @@ fn generate_attribute_macro_to_implement( eii_extern_target: Some(ast::EiiExternTarget { extern_item_path: ast::Path::from_ident(foreign_item_name), impl_unsafe, - span: decl_span, }), }, ), @@ -451,7 +519,7 @@ pub(crate) fn eii_extern_target( false }; - d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span }); + d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe }); // Return the original item and the new methods. vec![item] @@ -508,6 +576,7 @@ pub(crate) fn eii_shared_macro( impl_safety: meta_item.unsafety, span, is_default, + known_eii_macro_resolution: None, }); vec![item] diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index ad31eae10f60..e673ad3f8a64 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -643,6 +643,15 @@ pub(crate) enum InvalidFormatStringSuggestion { span: Span, replacement: String, }, + #[suggestion( + builtin_macros_format_add_missing_colon, + code = ":?", + applicability = "machine-applicable" + )] + AddMissingColon { + #[primary_span] + span: Span, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index a0ee7ac19899..12cb2cd00694 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -329,6 +329,10 @@ fn make_format_args( replacement, }); } + parse::Suggestion::AddMissingColon(span) => { + let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end)); + e.sugg_ = Some(errors::InvalidFormatStringSuggestion::AddMissingColon { span }); + } } let guar = ecx.dcx().emit_err(e); return ExpandResult::Ready(Err(guar)); diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs index b522ea193716..301547cadaf7 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -744,43 +744,43 @@ unsafe extern "C" { pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro stringify($($t:tt)*) { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro file() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro line() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro cfg() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro asm() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro global_asm() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro naked_asm() { /* compiler built-in */ } diff --git a/compiler/rustc_codegen_cranelift/src/global_asm.rs b/compiler/rustc_codegen_cranelift/src/global_asm.rs index 8d8cdb14dbc6..97d6cecf6848 100644 --- a/compiler/rustc_codegen_cranelift/src/global_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/global_asm.rs @@ -233,7 +233,7 @@ pub(crate) fn compile_global_asm( #![allow(internal_features)] #![no_core] #[rustc_builtin_macro] - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro global_asm() { /* compiler built-in */ } global_asm!(r###" "####, diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 64a5b431bd07..0aba44a88c5a 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -748,25 +748,25 @@ extern "C" { pub struct VaList<'a>(&'a mut VaListImpl); #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro stringify($($t:tt)*) { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro file() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro line() { /* compiler built-in */ } #[rustc_builtin_macro] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro cfg() { /* compiler built-in */ } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 08c53179bc14..2b04f81c267f 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -187,6 +187,10 @@ fn check_and_apply_linkage<'ll, 'tcx>( }; llvm::set_linkage(g1, base::linkage_to_llvm(linkage)); + // Normally this is done in `get_static_inner`, but when as we generate an internal global, + // it will apply the dso_local to the internal global instead, so do it here, too. + cx.assume_dso_local(g1, true); + // Declare an internal global `extern_with_linkage_foo` which // is initialized with the address of `foo`. If `foo` is // discarded during linking (for example, if `foo` has weak diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 9b08f4e9869c..63f820dc2918 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -379,19 +379,19 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { { false } - // Unsupported - (Arch::Arm64EC, _) => false, + // Unsupported (fixed in llvm22) + (Arch::Arm64EC, _) if major < 22 => false, // Selection failure (fixed in llvm21) (Arch::S390x, _) if major < 21 => false, // MinGW ABI bugs (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // Infinite recursion - (Arch::CSky, _) => false, + (Arch::CSky, _) if major < 22 => false, // (fixed in llvm22) (Arch::Hexagon, _) if major < 21 => false, // (fixed in llvm21) (Arch::LoongArch32 | Arch::LoongArch64, _) if major < 21 => false, // (fixed in llvm21) - (Arch::PowerPC | Arch::PowerPC64, _) => false, - (Arch::Sparc | Arch::Sparc64, _) => false, - (Arch::Wasm32 | Arch::Wasm64, _) => false, + (Arch::PowerPC | Arch::PowerPC64, _) if major < 22 => false, // (fixed in llvm22) + (Arch::Sparc | Arch::Sparc64, _) if major < 22 => false, // (fixed in llvm22) + (Arch::Wasm32 | Arch::Wasm64, _) if major < 22 => false, // (fixed in llvm22) // `f16` support only requires that symbols converting to and from `f32` are available. We // provide these in `compiler-builtins`, so `f16` should be available on all platforms that // do not have other ABI issues or LLVM crashes. diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index 41cf92390b0d..1878f4043ee8 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -10,7 +10,6 @@ use rustc_middle::mir::mono::Visibility; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, TypeVisitableExt}; use rustc_session::config::CrateType; -use rustc_span::Symbol; use rustc_target::spec::{Arch, RelocModel}; use tracing::debug; @@ -92,17 +91,19 @@ impl<'tcx> PreDefineCodegenMethods<'tcx> for CodegenCx<'_, 'tcx> { } impl CodegenCx<'_, '_> { - fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(Symbol, Linkage, Visibility)]) { + fn add_aliases(&self, aliasee: &llvm::Value, aliases: &[(DefId, Linkage, Visibility)]) { let ty = self.get_type_of_global(aliasee); for (alias, linkage, visibility) in aliases { + let symbol_name = self.tcx.symbol_name(Instance::mono(self.tcx, *alias)); + tracing::debug!("ALIAS: {alias:?} {linkage:?} {visibility:?}"); let lldecl = llvm::add_alias( self.llmod, ty, AddressSpace::ZERO, aliasee, - &CString::new(alias.as_str()).unwrap(), + &CString::new(symbol_name.name).unwrap(), ); llvm::set_visibility(lldecl, base::visibility_to_llvm(*visibility)); @@ -110,7 +111,7 @@ impl CodegenCx<'_, '_> { } } - /// Whether a definition or declaration can be assumed to be local to a group of + /// A definition or declaration can be assumed to be local to a group of /// libraries that form a single DSO or executable. /// Marks the local as DSO if so. pub(crate) fn assume_dso_local(&self, llval: &llvm::Value, is_declaration: bool) -> bool { @@ -152,7 +153,7 @@ impl CodegenCx<'_, '_> { return false; } - // With pie relocation model calls of functions defined in the translation + // With pie relocation model, calls of functions defined in the translation // unit can use copy relocations. if self.tcx.sess.relocation_model() == RelocModel::Pie && !is_declaration { return true; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b23415a732cc..688f461e7478 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,12 +1,13 @@ -use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size, TyAndLayout}; +use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size}; use rustc_codegen_ssa::MemFlags; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{ BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods, }; +use rustc_middle::bug; use rustc_middle::ty::Ty; -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout}; use rustc_target::spec::{Abi, Arch, Env}; use crate::builder::Builder; @@ -82,6 +83,7 @@ enum PassMode { enum SlotSize { Bytes8 = 8, Bytes4 = 4, + Bytes1 = 1, } enum AllowHigherAlign { @@ -728,7 +730,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, reg_addr: &'ll Value, - layout: TyAndLayout<'tcx, Ty<'tcx>>, + layout: TyAndLayout<'tcx>, src_align: Align, ) -> &'ll Value { if layout.layout.align.abi > src_align { @@ -751,7 +753,7 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, va_list_addr: &'ll Value, - layout: TyAndLayout<'tcx, Ty<'tcx>>, + layout: TyAndLayout<'tcx>, ) -> &'ll Value { let dl = bx.cx.data_layout(); let ptr_align_abi = dl.data_layout().pointer_align().abi; @@ -1003,15 +1005,17 @@ fn emit_xtensa_va_arg<'ll, 'tcx>( return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi); } +/// Determine the va_arg implementation to use. The LLVM va_arg instruction +/// is lacking in some instances, so we should only use it as a fallback. pub(super) fn emit_va_arg<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, addr: OperandRef<'tcx, &'ll Value>, target_ty: Ty<'tcx>, ) -> &'ll Value { - // Determine the va_arg implementation to use. The LLVM va_arg instruction - // is lacking in some instances, so we should only use it as a fallback. - let target = &bx.cx.tcx.sess.target; + let layout = bx.cx.layout_of(target_ty); + let target_ty_size = layout.layout.size().bytes(); + let target = &bx.cx.tcx.sess.target; match target.arch { Arch::X86 => emit_ptr_va_arg( bx, @@ -1069,23 +1073,79 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( AllowHigherAlign::Yes, ForceRightAdjust::No, ), + Arch::LoongArch32 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct }, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::LoongArch64 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, + SlotSize::Bytes8, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::AmdGpu => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::No, + ForceRightAdjust::No, + ), + Arch::Nvptx64 => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes1, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::Wasm32 => emit_ptr_va_arg( + bx, + addr, + target_ty, + if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() { + PassMode::Indirect + } else { + PassMode::Direct + }, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), + Arch::Wasm64 => bug!("c-variadic functions are not fully implemented for wasm64"), + Arch::CSky => emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ), // Windows x86_64 - Arch::X86_64 if target.is_like_windows => { - let target_ty_size = bx.cx.size_of(target_ty).bytes(); - emit_ptr_va_arg( - bx, - addr, - target_ty, - if target_ty_size > 8 || !target_ty_size.is_power_of_two() { - PassMode::Indirect - } else { - PassMode::Direct - }, - SlotSize::Bytes8, - AllowHigherAlign::No, - ForceRightAdjust::No, - ) - } + Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg( + bx, + addr, + target_ty, + if target_ty_size > 8 || !target_ty_size.is_power_of_two() { + PassMode::Indirect + } else { + PassMode::Direct + }, + SlotSize::Bytes8, + AllowHigherAlign::No, + ForceRightAdjust::No, + ), // This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64. Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty), Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty), diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 1d87dc5da8d2..4875f309cca5 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -10,6 +10,8 @@ codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$erro codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty +codegen_ssa_bpf_staticlib_not_supported = linking static libraries is not supported for BPF + codegen_ssa_cgu_not_recorded = CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 93f6cb3b87e6..3f12e857391b 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -22,10 +22,11 @@ use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; use crate::common; -// Re-exporting for rustc_codegen_llvm::back::archive -pub use crate::errors::{ArchiveBuildFailure, ExtractBundledLibsError, UnknownArchiveKind}; +// Public for ArchiveBuilderBuilder::extract_bundled_libs +pub use crate::errors::ExtractBundledLibsError; use crate::errors::{ - DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, ErrorWritingDEFFile, + ArchiveBuildFailure, DlltoolFailImportLibrary, ErrorCallingDllTool, ErrorCreatingImportLibrary, + ErrorWritingDEFFile, UnknownArchiveKind, }; /// An item to be included in an import library. diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index c73e950bed40..637d54dd06c6 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -839,6 +839,11 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::LibDefWriteFailure { error }); } self.link_arg(path); + } else if self.sess.target.is_like_wasm { + self.link_arg("--no-export-dynamic"); + for (sym, _) in symbols { + self.link_arg("--export").link_arg(sym); + } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { let res: io::Result<()> = try { let mut f = File::create_buffered(&path)?; @@ -853,11 +858,6 @@ impl<'a> Linker for GccLinker<'a> { self.sess.dcx().emit_fatal(errors::VersionScriptWriteFailure { error }); } self.link_arg("--dynamic-list").link_arg(path); - } else if self.sess.target.is_like_wasm { - self.link_arg("--no-export-dynamic"); - for (sym, _) in symbols { - self.link_arg("--export").link_arg(sym); - } } else { // Write an LD version script let res: io::Result<()> = try { @@ -2075,7 +2075,7 @@ impl<'a> Linker for BpfLinker<'a> { } fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) { - panic!("staticlibs not supported") + self.sess.dcx().emit_fatal(errors::BpfStaticlibNotSupported) } fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 94dc3f44a68b..7dda824e2b18 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; -use rustc_hir::attrs::{AttributeKind, InlineAttr, Linkage, RtsanSetting, UsedBy}; +use rustc_hir::attrs::{ + AttributeKind, EiiImplResolution, InlineAttr, Linkage, RtsanSetting, UsedBy, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, LangItem, find_attr, lang_items}; @@ -13,10 +15,10 @@ use rustc_middle::middle::codegen_fn_attrs::{ use rustc_middle::mir::mono::Visibility; use rustc_middle::query::Providers; use rustc_middle::span_bug; -use rustc_middle::ty::{self as ty, Instance, TyCtxt}; +use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::lint; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use rustc_target::spec::Os; use crate::errors; @@ -285,13 +287,23 @@ fn process_builtin_attrs( } AttributeKind::EiiImpls(impls) => { for i in impls { - let extern_item = find_attr!( - tcx.get_all_attrs(i.eii_macro), - AttributeKind::EiiExternTarget(target) => target.eii_extern_target - ) - .expect("eii should have declaration macro with extern target attribute"); - - let symbol_name = tcx.symbol_name(Instance::mono(tcx, extern_item)); + let extern_item = match i.resolution { + EiiImplResolution::Macro(def_id) => { + let Some(extern_item) = find_attr!( + tcx.get_all_attrs(def_id), + AttributeKind::EiiExternTarget(target) => target.eii_extern_target + ) else { + tcx.dcx().span_delayed_bug( + i.span, + "resolved to something that's not an EII", + ); + continue; + }; + extern_item + } + EiiImplResolution::Known(decl) => decl.eii_extern_target, + EiiImplResolution::Error(_eg) => continue, + }; // this is to prevent a bug where a single crate defines both the default and explicit implementation // for an EII. In that case, both of them may be part of the same final object file. I'm not 100% sure @@ -304,13 +316,13 @@ fn process_builtin_attrs( // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&i.eii_macro).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) + && tcx.externally_implementable_items(LOCAL_CRATE).get(&extern_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) { continue; } codegen_fn_attrs.foreign_item_symbol_aliases.push(( - Symbol::intern(symbol_name.name), + extern_item, if i.is_default { Linkage::LinkOnceAny } else { Linkage::External }, Visibility::Default, )); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 95306c140895..39727685aec1 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -661,7 +661,7 @@ pub(crate) struct RlibArchiveBuildFailure { } #[derive(Diagnostic)] -// Public for rustc_codegen_llvm::back::archive +// Public for ArchiveBuilderBuilder::extract_bundled_libs pub enum ExtractBundledLibsError<'a> { #[diag(codegen_ssa_extract_bundled_libs_open_file)] OpenFile { rlib: &'a Path, error: Box }, @@ -700,19 +700,21 @@ pub(crate) struct UnsupportedLinkSelfContained; #[derive(Diagnostic)] #[diag(codegen_ssa_archive_build_failure)] -// Public for rustc_codegen_llvm::back::archive -pub struct ArchiveBuildFailure { +pub(crate) struct ArchiveBuildFailure { pub path: PathBuf, pub error: std::io::Error, } #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_archive_kind)] -// Public for rustc_codegen_llvm::back::archive -pub struct UnknownArchiveKind<'a> { +pub(crate) struct UnknownArchiveKind<'a> { pub kind: &'a str, } +#[derive(Diagnostic)] +#[diag(codegen_ssa_bpf_staticlib_not_supported)] +pub(crate) struct BpfStaticlibNotSupported; + #[derive(Diagnostic)] #[diag(codegen_ssa_multiple_main_functions)] #[help] diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 113e0d66c48a..02615e3bbc18 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -6,9 +6,7 @@ // having basically only two use-cases that act in different ways. use rustc_errors::ErrorGuaranteed; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::DefKind; -use rustc_hir::{LangItem, find_attr}; +use rustc_hir::LangItem; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::*; use rustc_middle::ty::{self, AdtDef, Ty}; @@ -366,14 +364,10 @@ where // check performed after the promotion. Verify that with an assertion. assert!(promoted.is_none() || Q::ALLOW_PROMOTED); - // Avoid looking at attrs of anon consts as that will ICE - let is_type_const_item = - matches!(cx.tcx.def_kind(def), DefKind::Const | DefKind::AssocConst) - && find_attr!(cx.tcx.get_all_attrs(def), AttributeKind::TypeConst(_)); - // Don't peak inside trait associated constants, also `#[type_const] const` items // don't have bodies so there's nothing to look at - if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() && !is_type_const_item { + if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() && !cx.tcx.is_type_const(def) + { let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def); if !Q::in_qualifs(&qualifs) { diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7538130e9d92..719187b99012 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, IndexEntry}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem}; use rustc_middle::mir::AssertMessage; -use rustc_middle::mir::interpret::ReportedErrorInfo; +use rustc_middle::mir::interpret::{Pointer, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -22,7 +22,7 @@ use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, - GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar, + GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, RangeSet, Scalar, compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; @@ -586,6 +586,11 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { } } + sym::type_of => { + let ty = ecx.read_type_id(&args[0])?; + ecx.write_type_info(ty, dest)?; + } + _ => { // We haven't handled the intrinsic, let's see if we can use a fallback body. if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden { diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 624ca1dd2da0..e70488b81c4c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -13,6 +13,7 @@ mod error; mod eval_queries; mod fn_queries; mod machine; +mod type_info; mod valtrees; pub use self::dummy_machine::*; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs new file mode 100644 index 000000000000..f932b198b426 --- /dev/null +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -0,0 +1,175 @@ +use rustc_abi::FieldIdx; +use rustc_hir::LangItem; +use rustc_middle::mir::interpret::CtfeProvenance; +use rustc_middle::span_bug; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{self, ScalarInt, Ty}; +use rustc_span::{Symbol, sym}; + +use crate::const_eval::CompileTimeMachine; +use crate::interpret::{ + Immediate, InterpCx, InterpResult, MPlaceTy, MemoryKind, Writeable, interp_ok, +}; + +impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { + /// Writes a `core::mem::type_info::TypeInfo` for a given type, `ty` to the given place. + pub(crate) fn write_type_info( + &mut self, + ty: Ty<'tcx>, + dest: &impl Writeable<'tcx, CtfeProvenance>, + ) -> InterpResult<'tcx> { + let ty_struct = self.tcx.require_lang_item(LangItem::Type, self.tcx.span); + let ty_struct = self.tcx.type_of(ty_struct).no_bound_vars().unwrap(); + assert_eq!(ty_struct, dest.layout().ty); + let ty_struct = ty_struct.ty_adt_def().unwrap().non_enum_variant(); + // Fill all fields of the `TypeInfo` struct. + for (idx, field) in ty_struct.fields.iter_enumerated() { + let field_dest = self.project_field(dest, idx)?; + let downcast = |name: Symbol| { + let variants = field_dest.layout().ty.ty_adt_def().unwrap().variants(); + let variant_id = variants + .iter_enumerated() + .find(|(_idx, var)| var.name == name) + .unwrap_or_else(|| panic!("got {name} but expected one of {variants:#?}")) + .0; + + interp_ok((variant_id, self.project_downcast(&field_dest, variant_id)?)) + }; + match field.name { + sym::kind => { + let variant_index = match ty.kind() { + ty::Tuple(fields) => { + let (variant, variant_place) = downcast(sym::Tuple)?; + // project to the single tuple variant field of `type_info::Tuple` struct type + let tuple_place = self.project_field(&variant_place, FieldIdx::ZERO)?; + assert_eq!( + 1, + tuple_place + .layout() + .ty + .ty_adt_def() + .unwrap() + .non_enum_variant() + .fields + .len() + ); + self.write_tuple_fields(tuple_place, fields, ty)?; + variant + } + // For now just merge all primitives into one `Leaf` variant with no data + ty::Uint(_) | ty::Int(_) | ty::Float(_) | ty::Char | ty::Bool => { + downcast(sym::Leaf)?.0 + } + ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Pat(_, _) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::UnsafeBinder(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Alias(..) + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(_) + | ty::Infer(..) + | ty::Error(_) => downcast(sym::Other)?.0, + }; + self.write_discriminant(variant_index, &field_dest)? + } + sym::size => { + let layout = self.layout_of(ty)?; + let variant_index = if layout.is_sized() { + let (variant, variant_place) = downcast(sym::Some)?; + let size_field_place = + self.project_field(&variant_place, FieldIdx::ZERO)?; + self.write_scalar( + ScalarInt::try_from_target_usize(layout.size.bytes(), self.tcx.tcx) + .unwrap(), + &size_field_place, + )?; + variant + } else { + downcast(sym::None)?.0 + }; + self.write_discriminant(variant_index, &field_dest)?; + } + other => span_bug!(self.tcx.span, "unknown `Type` field {other}"), + } + } + + interp_ok(()) + } + + pub(crate) fn write_tuple_fields( + &mut self, + tuple_place: impl Writeable<'tcx, CtfeProvenance>, + fields: &[Ty<'tcx>], + tuple_ty: Ty<'tcx>, + ) -> InterpResult<'tcx> { + // project into the `type_info::Tuple::fields` field + let fields_slice_place = self.project_field(&tuple_place, FieldIdx::ZERO)?; + // get the `type_info::Field` type from `fields: &[Field]` + let field_type = fields_slice_place + .layout() + .ty + .builtin_deref(false) + .unwrap() + .sequence_element_type(self.tcx.tcx); + // Create an array with as many elements as the number of fields in the inspected tuple + let fields_layout = + self.layout_of(Ty::new_array(self.tcx.tcx, field_type, fields.len() as u64))?; + let fields_place = self.allocate(fields_layout, MemoryKind::Stack)?; + let mut fields_places = self.project_array_fields(&fields_place)?; + + let tuple_layout = self.layout_of(tuple_ty)?; + + while let Some((i, place)) = fields_places.next(self)? { + let field_ty = fields[i as usize]; + self.write_field(field_ty, place, tuple_layout, i)?; + } + + let fields_place = fields_place.map_provenance(CtfeProvenance::as_immutable); + + let ptr = Immediate::new_slice(fields_place.ptr(), fields.len() as u64, self); + + self.write_immediate(ptr, &fields_slice_place) + } + + fn write_field( + &mut self, + field_ty: Ty<'tcx>, + place: MPlaceTy<'tcx>, + layout: TyAndLayout<'tcx>, + idx: u64, + ) -> InterpResult<'tcx> { + for (field_idx, field_ty_field) in + place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() + { + let field_place = self.project_field(&place, field_idx)?; + match field_ty_field.name { + sym::ty => self.write_type_id(field_ty, &field_place)?, + sym::offset => { + let offset = layout.fields.offset(idx as usize); + self.write_scalar( + ScalarInt::try_from_target_usize(offset.bytes(), self.tcx.tcx).unwrap(), + &field_place, + )?; + } + other => { + span_bug!(self.tcx.def_span(field_ty_field.did), "unimplemented field {other}") + } + } + } + interp_ok(()) + } +} diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index d70d157d8808..94832c0c2577 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -27,6 +27,7 @@ use super::{ throw_ub_custom, throw_ub_format, }; use crate::fluent_generated as fluent; +use crate::interpret::Writeable; #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum MulAddType { @@ -68,10 +69,10 @@ pub(crate) fn alloc_type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> (AllocId } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Generates a value of `TypeId` for `ty` in-place. - fn write_type_id( + pub(crate) fn write_type_id( &mut self, ty: Ty<'tcx>, - dest: &PlaceTy<'tcx, M::Provenance>, + dest: &impl Writeable<'tcx, M::Provenance>, ) -> InterpResult<'tcx, ()> { let tcx = self.tcx; let type_id_hash = tcx.type_id_hash(ty).as_u128(); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 13c1f2219bed..43032bf938c0 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -235,7 +235,7 @@ declare_features! ( (accepted, generic_param_attrs, "1.27.0", Some(48848)), /// Allows the `#[global_allocator]` attribute. (accepted, global_allocator, "1.28.0", Some(27389)), - // FIXME: explain `globs`. + /// Allows globs imports (`use module::*;`) to import all public items from a module. (accepted, globs, "1.0.0", None), /// Allows using `..=X` as a pattern. (accepted, half_open_range_patterns, "1.66.0", Some(67264)), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 387a01967ae5..8959bc586af7 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -233,8 +233,6 @@ declare_features! ( (internal, link_cfg, "1.14.0", None), /// Allows using `?Trait` trait bounds in more contexts. (internal, more_maybe_bounds, "1.82.0", None), - /// Allows the `multiple_supertrait_upcastable` lint. - (unstable, multiple_supertrait_upcastable, "1.69.0", None), /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! (internal, negative_bounds, "1.71.0", None), /// Set the maximum pattern complexity allowed (not limited by default). @@ -569,6 +567,8 @@ declare_features! ( (unstable, more_qualified_paths, "1.54.0", Some(86935)), /// The `movrs` target feature on x86. (unstable, movrs_target_feature, "1.88.0", Some(137976)), + /// Allows the `multiple_supertrait_upcastable` lint. + (unstable, multiple_supertrait_upcastable, "1.69.0", Some(150833)), /// Allows the `#[must_not_suspend]` attribute. (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fa8998f0546d..c791f54902d1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -11,7 +11,7 @@ use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; use rustc_span::def_id::DefId; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; @@ -19,9 +19,22 @@ use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; +#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum EiiImplResolution { + /// Usually, finding the extern item that an EII implementation implements means finding + /// the defid of the associated attribute macro, and looking at *its* attributes to find + /// what foreign item its associated with. + Macro(DefId), + /// Sometimes though, we already know statically and can skip some name resolution. + /// Stored together with the eii's name for diagnostics. + Known(EiiDecl), + /// For when resolution failed, but we want to continue compilation + Error(ErrorGuaranteed), +} + #[derive(Copy, Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub struct EiiImpl { - pub eii_macro: DefId, + pub resolution: EiiImplResolution, pub impl_marked_unsafe: bool, pub span: Span, pub inner_span: Span, @@ -33,7 +46,7 @@ pub struct EiiDecl { pub eii_extern_target: DefId, /// whether or not it is unsafe to implement this EII pub impl_unsafe: bool, - pub span: Span, + pub name: Ident, } #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] @@ -867,6 +880,9 @@ pub enum AttributeKind { /// Represents `#[rustc_coherence_is_core]` RustcCoherenceIsCore(Span), + /// Represents `#[rustc_has_incoherent_inherent_impls]` + RustcHasIncoherentInherentImpls, + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 3efa876ed6a9..4e1e9a074e66 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -95,6 +95,7 @@ impl AttributeKind { Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcCoherenceIsCore(..) => No, + RustcHasIncoherentInherentImpls => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcLegacyConstGenerics { .. } => Yes, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index aacd6324bb03..2fb6daf6469b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -518,6 +518,7 @@ pub enum ConstArgKind<'hir, Unambig = ()> { /// This variant is not always used to represent inference consts, sometimes /// [`GenericArg::Infer`] is used instead. Infer(Unambig), + Literal(LitKind), } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -1400,6 +1401,14 @@ impl AttributeExt for Attribute { } } + #[inline] + fn deprecation_note(&self) -> Option { + match &self { + Attribute::Parsed(AttributeKind::Deprecation { deprecation, .. }) => deprecation.note, + _ => None, + } + } + fn is_automatically_derived_attr(&self) -> bool { matches!(self, Attribute::Parsed(AttributeKind::AutomaticallyDerived(..))) } @@ -2616,6 +2625,12 @@ impl Expr<'_> { // them being used only for its side-effects. base.can_have_side_effects() } + ExprKind::Binary(_, lhs, rhs) => { + // This isn't exactly true for all `Binary`, but we are using this + // method exclusively for diagnostics and there's a *cultural* pressure against + // them being used only for its side-effects. + lhs.can_have_side_effects() || rhs.can_have_side_effects() + } ExprKind::Struct(_, fields, init) => { let init_side_effects = match init { StructTailExpr::Base(init) => init.can_have_side_effects(), @@ -2638,13 +2653,13 @@ impl Expr<'_> { }, args, ) => args.iter().any(|arg| arg.can_have_side_effects()), + ExprKind::Repeat(arg, _) => arg.can_have_side_effects(), ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) | ExprKind::Call(..) | ExprKind::Closure { .. } | ExprKind::Block(..) - | ExprKind::Repeat(..) | ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) @@ -2655,7 +2670,6 @@ impl Expr<'_> { | ExprKind::InlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::ConstBlock(..) - | ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::DropTemps(..) | ExprKind::Err(_) => true, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 59db60fdc55c..e5e6fc7b1d8d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1104,6 +1104,7 @@ pub fn walk_const_arg<'v, V: Visitor<'v>>( ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, *hir_id, qpath.span()), ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), ConstArgKind::Error(_) => V::Result::output(), // errors and spans are not important + ConstArgKind::Literal(..) => V::Result::output(), // FIXME(mcga) } } diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 4ac3e4e83e80..557f76208bfe 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -278,6 +278,7 @@ language_item_table! { PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1); CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None; + Type, sym::type_info, type_struct, Target::Struct, GenericRequirement::None; TypeId, sym::type_id, type_id, Target::Struct, GenericRequirement::None; // A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index fa46c0f085a2..9c2fec867785 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -32,7 +32,7 @@ pub mod lints; pub mod pat_util; mod stability; mod stable_hash_impls; -mod target; +pub mod target; pub mod weak_lang_items; #[cfg(test)] diff --git a/compiler/rustc_hir_analysis/src/check/compare_eii.rs b/compiler/rustc_hir_analysis/src/check/compare_eii.rs index 4e7f47a92108..d8afee9aafc9 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_eii.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_eii.rs @@ -32,21 +32,21 @@ use crate::errors::{EiiWithGenerics, LifetimesOrBoundsMismatchOnEii}; pub(crate) fn compare_eii_function_types<'tcx>( tcx: TyCtxt<'tcx>, external_impl: LocalDefId, - declaration: DefId, + foreign_item: DefId, eii_name: Symbol, eii_attr_span: Span, ) -> Result<(), ErrorGuaranteed> { - check_is_structurally_compatible(tcx, external_impl, declaration, eii_name, eii_attr_span)?; + check_is_structurally_compatible(tcx, external_impl, foreign_item, eii_name, eii_attr_span)?; let external_impl_span = tcx.def_span(external_impl); let cause = ObligationCause::new( external_impl_span, external_impl, - ObligationCauseCode::CompareEii { external_impl, declaration }, + ObligationCauseCode::CompareEii { external_impl, declaration: foreign_item }, ); // FIXME(eii): even if we don't support generic functions, we should support explicit outlive bounds here - let param_env = tcx.param_env(declaration); + let param_env = tcx.param_env(foreign_item); let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(infcx); @@ -62,7 +62,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( let mut wf_tys = FxIndexSet::default(); let norm_cause = ObligationCause::misc(external_impl_span, external_impl); - let declaration_sig = tcx.fn_sig(declaration).instantiate_identity(); + let declaration_sig = tcx.fn_sig(foreign_item).instantiate_identity(); let declaration_sig = tcx.liberate_late_bound_regions(external_impl.into(), declaration_sig); debug!(?declaration_sig); @@ -103,7 +103,7 @@ pub(crate) fn compare_eii_function_types<'tcx>( cause, param_env, terr, - (declaration, declaration_sig), + (foreign_item, declaration_sig), (external_impl, external_impl_sig), eii_attr_span, eii_name, diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 9eaf5319cb04..c84c1a8ca16d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -213,6 +213,7 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id | sym::type_id_eq | sym::type_name + | sym::type_of | sym::ub_checks | sym::variant_count | sym::vtable_for @@ -308,13 +309,22 @@ pub(crate) fn check_intrinsic_type( sym::needs_drop => (1, 0, vec![], tcx.types.bool), sym::type_name => (1, 0, vec![], Ty::new_static_str(tcx)), - sym::type_id => { - (1, 0, vec![], tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity()) - } + sym::type_id => ( + 1, + 0, + vec![], + tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(), + ), sym::type_id_eq => { - let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).instantiate_identity(); + let type_id = tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap(); (0, 0, vec![type_id, type_id], tcx.types.bool) } + sym::type_of => ( + 0, + 0, + vec![tcx.type_of(tcx.lang_items().type_id().unwrap()).no_bound_vars().unwrap()], + tcx.type_of(tcx.lang_items().type_struct().unwrap()).no_bound_vars().unwrap(), + ), sym::offload => ( 3, 0, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 725294dfd377..7ec4110f80b9 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -6,7 +6,7 @@ use rustc_abi::{ExternAbi, ScalableElt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::lang_items::LangItem; @@ -1196,27 +1196,28 @@ fn check_item_fn( fn check_eiis(tcx: TyCtxt<'_>, def_id: LocalDefId) { // does the function have an EiiImpl attribute? that contains the defid of a *macro* // that was used to mark the implementation. This is a two step process. - for EiiImpl { eii_macro, span, .. } in + for EiiImpl { resolution, span, .. } in find_attr!(tcx.get_all_attrs(def_id), AttributeKind::EiiImpls(impls) => impls) .into_iter() .flatten() { - // we expect this macro to have the `EiiMacroFor` attribute, that points to a function - // signature that we'd like to compare the function we're currently checking with - if let Some(eii_extern_target) = find_attr!(tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target, ..}) => *eii_extern_target) - { - let _ = compare_eii_function_types( - tcx, - def_id, - eii_extern_target, - tcx.item_name(*eii_macro), - *span, - ); - } else { - panic!( - "EII impl macro {eii_macro:?} did not have an eii extern target attribute pointing to a foreign function" - ) - } + let (foreign_item, name) = match resolution { + EiiImplResolution::Macro(def_id) => { + // we expect this macro to have the `EiiMacroFor` attribute, that points to a function + // signature that we'd like to compare the function we're currently checking with + if let Some(foreign_item) = find_attr!(tcx.get_all_attrs(*def_id), AttributeKind::EiiExternTarget(EiiDecl {eii_extern_target: t, ..}) => *t) + { + (foreign_item, tcx.item_name(*def_id)) + } else { + tcx.dcx().span_delayed_bug(*span, "resolved to something that's not an EII"); + continue; + } + } + EiiImplResolution::Known(decl) => (decl.eii_extern_target, decl.name.name), + EiiImplResolution::Error(_eg) => continue, + }; + + let _ = compare_eii_function_types(tcx, def_id, foreign_item, name, *span); } } diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 38ae7852ca99..fe47f3258846 100644 --- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -15,7 +15,7 @@ use rustc_hir::find_attr; use rustc_middle::bug; use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams, simplify_type}; use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; -use rustc_span::{ErrorGuaranteed, sym}; +use rustc_span::ErrorGuaranteed; use crate::errors; @@ -79,13 +79,15 @@ impl<'tcx> InherentCollect<'tcx> { } if self.tcx.features().rustc_attrs() { - let items = self.tcx.associated_item_def_ids(impl_def_id); - - if !self.tcx.has_attr(ty_def_id, sym::rustc_has_incoherent_inherent_impls) { + if !find_attr!( + self.tcx.get_all_attrs(ty_def_id), + AttributeKind::RustcHasIncoherentInherentImpls + ) { let impl_span = self.tcx.def_span(impl_def_id); return Err(self.tcx.dcx().emit_err(errors::InherentTyOutside { span: impl_span })); } + let items = self.tcx.associated_item_def_ids(impl_def_id); for &impl_item in items { if !find_attr!( self.tcx.get_all_attrs(impl_item), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index aa0e5c7fd710..910176a0689c 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -420,7 +420,22 @@ fn infer_placeholder_type<'tcx>( kind: &'static str, ) -> Ty<'tcx> { let tcx = cx.tcx(); - let ty = tcx.typeck(def_id).node_type(hir_id); + // If the type is omitted on a #[type_const] we can't run + // type check on since that requires the const have a body + // which type_consts don't. + let ty = if tcx.is_type_const(def_id.to_def_id()) { + if let Some(trait_item_def_id) = tcx.trait_item_of(def_id.to_def_id()) { + tcx.type_of(trait_item_def_id).instantiate_identity() + } else { + Ty::new_error_with_message( + tcx, + ty_span, + "constant with #[type_const] requires an explicit type", + ) + } + } else { + tcx.typeck(def_id).node_type(hir_id) + }; // If this came from a free `const` or `static mut?` item, // then the user may have written e.g. `const A = 42;`. 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 aaa566760013..1dd9bff42575 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -22,6 +22,7 @@ pub mod generics; use std::assert_matches::assert_matches; use std::slice; +use rustc_ast::LitKind; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2391,6 +2392,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::ConstArgKind::Anon(anon) => self.lower_const_arg_anon(anon), hir::ConstArgKind::Infer(()) => self.ct_infer(None, const_arg.span), hir::ConstArgKind::Error(e) => ty::Const::new_error(tcx, e), + hir::ConstArgKind::Literal(kind) if let FeedConstTy::WithTy(anon_const_type) = feed => { + self.lower_const_arg_literal(&kind, anon_const_type, const_arg.span) + } + hir::ConstArgKind::Literal(..) => { + let e = self.dcx().span_err(const_arg.span, "literal of unknown type"); + ty::Const::new_error(tcx, e) + } } } @@ -2773,6 +2781,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + #[instrument(skip(self), level = "debug")] + fn lower_const_arg_literal(&self, kind: &LitKind, ty: Ty<'tcx>, span: Span) -> Const<'tcx> { + let tcx = self.tcx(); + let input = LitToConstInput { lit: *kind, ty, neg: false }; + tcx.at(span).lit_to_const(input) + } + #[instrument(skip(self), level = "debug")] fn try_lower_anon_const_lit( &self, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 2c160ccef2b6..37fa18c00887 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -21,7 +21,7 @@ use rustc_hir::{ GenericParam, GenericParamKind, HirId, ImplicitSelfKind, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TyPatKind, }; -use rustc_span::source_map::SourceMap; +use rustc_span::source_map::{SourceMap, Spanned}; use rustc_span::{DUMMY_SP, FileName, Ident, Span, Symbol, kw, sym}; use {rustc_ast as ast, rustc_hir as hir}; @@ -1157,6 +1157,10 @@ impl<'a> State<'a> { ConstArgKind::Anon(anon) => self.print_anon_const(anon), ConstArgKind::Error(_) => self.word("/*ERROR*/"), ConstArgKind::Infer(..) => self.word("_"), + ConstArgKind::Literal(node) => { + let span = const_arg.span; + self.print_literal(&Spanned { span, node: *node }) + } } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 5e1e567d103e..52ea6cdeaa0e 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -54,7 +54,7 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span}; +use rustc_span::{BytePos, DUMMY_SP, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::solve::inspect::{self, InferCtxtProofTreeExt, ProofTreeVisitor}; use rustc_trait_selection::solve::{Certainty, Goal, NoSolution}; @@ -1828,10 +1828,9 @@ impl<'tcx> CoerceMany<'tcx> { // If the block is from an external macro or try (`?`) desugaring, then // do not suggest adding a semicolon, because there's nowhere to put it. // See issues #81943 and #87051. - && matches!( - cond_expr.span.desugaring_kind(), - None | Some(DesugaringKind::WhileLoop) - ) + // Similarly, if the block is from a loop desugaring, then also do not + // suggest adding a semicolon. See issue #150850. + && cond_expr.span.desugaring_kind().is_none() && !cond_expr.span.in_external_macro(fcx.tcx.sess.source_map()) && !matches!( cond_expr.kind, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d51b052e0d1b..3e4c194147f9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,8 +10,8 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, - GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, - WherePredicateKind, expr_needs_parens, is_range_literal, + GenericBound, HirId, LoopSource, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, + TyKind, WherePredicateKind, expr_needs_parens, is_range_literal, }; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::suggest_impl_trait; @@ -1170,15 +1170,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let found = self.resolve_vars_if_possible(found); - let in_loop = self.is_loop(id) - || self - .tcx + let innermost_loop = if self.is_loop(id) { + Some(self.tcx.hir_node(id)) + } else { + self.tcx .hir_parent_iter(id) .take_while(|(_, node)| { // look at parents until we find the first body owner node.body_id().is_none() }) - .any(|(parent_id, _)| self.is_loop(parent_id)); + .find_map(|(parent_id, node)| self.is_loop(parent_id).then_some(node)) + }; + let can_break_with_value = innermost_loop.is_some_and(|node| { + matches!( + node, + Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::Loop, ..), .. }) + ) + }); let in_local_statement = self.is_local_statement(id) || self @@ -1186,7 +1194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .hir_parent_iter(id) .any(|(parent_id, _)| self.is_local_statement(parent_id)); - if in_loop && in_local_statement { + if can_break_with_value && in_local_statement { err.multipart_suggestion( "you might have meant to break the loop with this value", vec![ diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index c07b41b56caa..23f6fee406a5 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -289,7 +289,7 @@ struct Canonicalizer<'cx, 'tcx> { /// Set to `None` to disable the resolution of inference variables. infcx: Option<&'cx InferCtxt<'tcx>>, tcx: TyCtxt<'tcx>, - variables: SmallVec<[CanonicalVarKind<'tcx>; 8]>, + var_kinds: SmallVec<[CanonicalVarKind<'tcx>; 8]>, query_state: &'cx mut OriginalQueryValues<'tcx>, // Note that indices is only used once `var_values` is big enough to be // heap-allocated. @@ -507,7 +507,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { { let base = Canonical { max_universe: ty::UniverseIndex::ROOT, - variables: List::empty(), + var_kinds: List::empty(), value: (), }; Canonicalizer::canonicalize_with_base( @@ -548,7 +548,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { tcx, canonicalize_mode: canonicalize_region_mode, needs_canonical_flags, - variables: SmallVec::from_slice(base.variables), + var_kinds: SmallVec::from_slice(base.var_kinds), query_state, indices: FxHashMap::default(), sub_root_lookup_table: Default::default(), @@ -569,16 +569,16 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { // anymore. debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); - let canonical_variables = - tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_variables()); + let canonical_var_kinds = + tcx.mk_canonical_var_kinds(&canonicalizer.universe_canonicalized_var_kinds()); - let max_universe = canonical_variables + let max_universe = canonical_var_kinds .iter() .map(|cvar| cvar.universe()) .max() .unwrap_or(ty::UniverseIndex::ROOT); - Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + Canonical { max_universe, var_kinds: canonical_var_kinds, value: (base.value, out_value) } } /// Creates a canonical variable replacing `kind` from the input, @@ -590,7 +590,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { var_kind: CanonicalVarKind<'tcx>, value: GenericArg<'tcx>, ) -> BoundVar { - let Canonicalizer { variables, query_state, indices, .. } = self; + let Canonicalizer { var_kinds, query_state, indices, .. } = self; let var_values = &mut query_state.var_values; @@ -607,7 +607,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } - // This code is hot. `variables` and `var_values` are usually small + // This code is hot. `var_kinds` and `var_values` are usually small // (fewer than 8 elements ~95% of the time). They are SmallVec's to // avoid allocations in those cases. We also don't use `indices` to // determine if a kind has been seen before until the limit of 8 has @@ -620,10 +620,10 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { BoundVar::new(idx) } else { // `kind` isn't present in `var_values`. Append it. Likewise - // for `var_kind` and `variables`. - variables.push(var_kind); + // for `var_kind` and `var_kinds`. + var_kinds.push(var_kind); var_values.push(value); - assert_eq!(variables.len(), var_values.len()); + assert_eq!(var_kinds.len(), var_values.len()); // If `var_values` has become big enough to be heap-allocated, // fill up `indices` to facilitate subsequent lookups. @@ -641,10 +641,10 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } else { // `var_values` is large. Do a hashmap search via `indices`. *indices.entry(value).or_insert_with(|| { - variables.push(var_kind); + var_kinds.push(var_kind); var_values.push(value); - assert_eq!(variables.len(), var_values.len()); - BoundVar::new(variables.len() - 1) + assert_eq!(var_kinds.len(), var_values.len()); + BoundVar::new(var_kinds.len() - 1) }) } } @@ -652,16 +652,16 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { let root_vid = self.infcx.unwrap().sub_unification_table_root_var(vid); let idx = - *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.var_kinds.len()); ty::BoundVar::from(idx) } /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. - fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> { + fn universe_canonicalized_var_kinds(self) -> SmallVec<[CanonicalVarKind<'tcx>; 8]> { if self.query_state.universe_map.len() == 1 { - return self.variables; + return self.var_kinds; } let reverse_universe_map: FxHashMap = self @@ -672,7 +672,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx))) .collect(); - self.variables + self.var_kinds .iter() .map(|&kind| match kind { CanonicalVarKind::Int | CanonicalVarKind::Float => { diff --git a/compiler/rustc_infer/src/infer/canonical/instantiate.rs b/compiler/rustc_infer/src/infer/canonical/instantiate.rs index c215a9db2a0a..66a7bd2fc636 100644 --- a/compiler/rustc_infer/src/infer/canonical/instantiate.rs +++ b/compiler/rustc_infer/src/infer/canonical/instantiate.rs @@ -43,7 +43,7 @@ impl<'tcx, V> Canonical<'tcx, V> { where T: TypeFoldable>, { - assert_eq!(self.variables.len(), var_values.len()); + assert_eq!(self.var_kinds.len(), var_values.len()); let value = projection_fn(&self.value); instantiate_value(tcx, var_values, value) } diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 9af0e17be4e2..c6826d774216 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -68,7 +68,7 @@ impl<'tcx> InferCtxt<'tcx> { .collect(); let var_values = - CanonicalVarValues::instantiate(self.tcx, &canonical.variables, |var_values, info| { + CanonicalVarValues::instantiate(self.tcx, &canonical.var_kinds, |var_values, info| { self.instantiate_canonical_var(span, info, &var_values, |ui| universes[ui]) }); let result = canonical.instantiate(self.tcx, &var_values); diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index b4952e7bfe15..65b0f8211432 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -430,7 +430,7 @@ impl<'tcx> InferCtxt<'tcx> { // result, then we can type the corresponding value from the // input. See the example above. let mut opt_values: IndexVec>> = - IndexVec::from_elem_n(None, query_response.variables.len()); + IndexVec::from_elem_n(None, query_response.var_kinds.len()); for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) { @@ -442,7 +442,7 @@ impl<'tcx> InferCtxt<'tcx> { // more involved. They are also a lot rarer than region variables. if let ty::Bound(index_kind, b) = *result_value.kind() && !matches!( - query_response.variables[b.var.as_usize()], + query_response.var_kinds[b.var.as_usize()], CanonicalVarKind::Ty { .. } ) { @@ -472,8 +472,8 @@ impl<'tcx> InferCtxt<'tcx> { // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. let tcx = self.tcx; - let variables = query_response.variables; - let var_values = CanonicalVarValues::instantiate(tcx, variables, |var_values, kind| { + let var_kinds = query_response.var_kinds; + let var_values = CanonicalVarValues::instantiate(tcx, var_kinds, |var_values, kind| { if kind.universe() != ty::UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all, we have to deal with them for now. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 0ba54e3829f5..506a16355e22 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1190,6 +1190,8 @@ impl UnusedParens { // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume // that if there are unnecessary parens they serve a purpose of readability. PatKind::Range(..) => return, + // Parentheses may be necessary to disambiguate precedence in guard patterns. + PatKind::Guard(..) => return, // Avoid `p0 | .. | pn` if we should. PatKind::Or(..) if avoid_or => return, // Avoid `mut x` and `mut x @ p` if we should: diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 5558ff930851..b06ac8481397 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -1,5 +1,5 @@ use rustc_data_structures::fx::FxIndexMap; -use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl}; +use rustc_hir::attrs::{AttributeKind, EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_middle::query::LocalCrate; @@ -9,7 +9,7 @@ use rustc_middle::ty::TyCtxt; pub(crate) type EiiMapEncodedKeyValue = (DefId, (EiiDecl, Vec<(DefId, EiiImpl)>)); pub(crate) type EiiMap = FxIndexMap< - DefId, // the defid of the macro that declared the eii + DefId, // the defid of the foreign item associated with the eii ( // the corresponding declaration EiiDecl, @@ -28,18 +28,34 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap for i in find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiImpls(e) => e).into_iter().flatten() { - eiis.entry(i.eii_macro) - .or_insert_with(|| { + let decl = match i.resolution { + EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) - (find_attr!(tcx.get_all_attrs(i.eii_macro), AttributeKind::EiiExternTarget(d) => *d).unwrap(), Default::default()) - }).1.insert(id.into(), *i); + let Some(decl) = find_attr!(tcx.get_all_attrs(macro_defid), AttributeKind::EiiExternTarget(d) => *d) + else { + // skip if it doesn't have eii_extern_target (if we resolved to another macro that's not an EII) + tcx.dcx() + .span_delayed_bug(i.span, "resolved to something that's not an EII"); + continue; + }; + decl + } + EiiImplResolution::Known(decl) => decl, + EiiImplResolution::Error(_eg) => continue, + }; + + // FIXME(eii) remove extern target from encoded decl + eiis.entry(decl.eii_extern_target) + .or_insert_with(|| (decl, Default::default())) + .1 + .insert(id.into(), *i); } // if we find a new declaration, add it to the list without a known implementation if let Some(decl) = find_attr!(tcx.get_all_attrs(id), AttributeKind::EiiExternTarget(d) => *d) { - eiis.entry(id.into()).or_insert((decl, Default::default())); + eiis.entry(decl.eii_extern_target).or_insert((decl, Default::default())); } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d0e8bc50c495..eedb88783ec0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1444,6 +1444,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { | hir::ConstArgKind::TupleCall(..) | hir::ConstArgKind::Tup(..) | hir::ConstArgKind::Path(..) + | hir::ConstArgKind::Literal(..) | hir::ConstArgKind::Infer(..) => true, hir::ConstArgKind::Anon(..) => false, }, @@ -1656,9 +1657,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { empty_proc_macro!(self); let externally_implementable_items = self.tcx.externally_implementable_items(LOCAL_CRATE); - self.lazy_array(externally_implementable_items.iter().map(|(decl_did, (decl, impls))| { - (*decl_did, (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect())) - })) + self.lazy_array(externally_implementable_items.iter().map( + |(foreign_item, (decl, impls))| { + ( + *foreign_item, + (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect()), + ) + }, + )) } #[instrument(level = "trace", skip(self))] diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 153605ee7f81..32c6b6e9c0ba 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -169,7 +169,7 @@ impl<'tcx> CanonicalParamEnvCache<'tcx> { ) { return Canonical { max_universe: ty::UniverseIndex::ROOT, - variables: List::empty(), + var_kinds: List::empty(), value: key, }; } diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 9033d9c46d54..4f600af0cbfc 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use rustc_abi::Align; use rustc_hir::attrs::{InlineAttr, InstructionSetAttr, Linkage, OptimizeAttr, RtsanSetting}; +use rustc_hir::def_id::DefId; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Symbol; use rustc_target::spec::SanitizerSet; @@ -72,7 +73,7 @@ pub struct CodegenFnAttrs { /// generate this function under its real name, /// but *also* under the same name as this foreign function so that the foreign function has an implementation. // FIXME: make "SymbolName<'tcx>" - pub foreign_item_symbol_aliases: Vec<(Symbol, Linkage, Visibility)>, + pub foreign_item_symbol_aliases: Vec<(DefId, Linkage, Visibility)>, /// The `#[link_ordinal = "..."]` attribute, indicating an ordinal an /// imported function has in the dynamic library. Note that this must not /// be set when `link_name` is set. This is for foreign items with the diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index c882d5d499bd..29cceb708850 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -20,8 +20,8 @@ use rustc_span::hygiene::{ }; use rustc_span::source_map::Spanned; use rustc_span::{ - BlobDecoder, BytePos, ByteSymbol, CachingSourceMapView, ExpnData, ExpnHash, Pos, - RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, + BlobDecoder, BytePos, ByteSymbol, CachingSourceMapView, ExpnData, ExpnHash, RelativeBytePos, + SourceFile, Span, SpanDecoder, SpanEncoder, StableSourceFileId, Symbol, }; use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; @@ -652,7 +652,10 @@ impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> { let dto = u32::decode(self); let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked(); - (enclosing.lo + BytePos::from_u32(dlo), enclosing.lo + BytePos::from_u32(dto)) + ( + BytePos(enclosing.lo.0.wrapping_add(dlo)), + BytePos(enclosing.lo.0.wrapping_add(dto)), + ) } TAG_FULL_SPAN => { let file_lo_index = SourceFileIndex::decode(self); @@ -894,30 +897,33 @@ impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> { return TAG_PARTIAL_SPAN.encode(self); } - if let Some(parent) = span_data.parent { - let enclosing = self.tcx.source_span_untracked(parent).data_untracked(); - if enclosing.contains(span_data) { - TAG_RELATIVE_SPAN.encode(self); - (span_data.lo - enclosing.lo).to_u32().encode(self); - (span_data.hi - enclosing.lo).to_u32().encode(self); - return; - } + let parent = + span_data.parent.map(|parent| self.tcx.source_span_untracked(parent).data_untracked()); + if let Some(parent) = parent + && parent.contains(span_data) + { + TAG_RELATIVE_SPAN.encode(self); + (span_data.lo.0.wrapping_sub(parent.lo.0)).encode(self); + (span_data.hi.0.wrapping_sub(parent.lo.0)).encode(self); + return; } - let pos = self.source_map.byte_pos_to_line_and_col(span_data.lo); - let partial_span = match &pos { - Some((file_lo, _, _)) => !file_lo.contains(span_data.hi), - None => true, + let Some((file_lo, line_lo, col_lo)) = + self.source_map.byte_pos_to_line_and_col(span_data.lo) + else { + return TAG_PARTIAL_SPAN.encode(self); }; - if partial_span { - return TAG_PARTIAL_SPAN.encode(self); + if let Some(parent) = parent + && file_lo.contains(parent.lo) + { + TAG_RELATIVE_SPAN.encode(self); + (span_data.lo.0.wrapping_sub(parent.lo.0)).encode(self); + (span_data.hi.0.wrapping_sub(parent.lo.0)).encode(self); + return; } - let (file_lo, line_lo, col_lo) = pos.unwrap(); - let len = span_data.hi - span_data.lo; - let source_file_index = self.source_file_index(file_lo); TAG_FULL_SPAN.encode(self); diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 3683c1cfb7dd..e538c2182924 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -643,10 +643,26 @@ pub struct FieldPat<'tcx> { pub pattern: Pat<'tcx>, } +/// Additional per-node data that is not present on most THIR pattern nodes. +#[derive(Clone, Debug, Default, HashStable, TypeVisitable)] +pub struct PatExtra<'tcx> { + /// If present, this node represents a named constant that was lowered to + /// a pattern using `const_to_pat`. + /// + /// This is used by some diagnostics for non-exhaustive matches, to map + /// the pattern node back to the `DefId` of its original constant. + pub expanded_const: Option, + + /// User-written types that must be preserved into MIR so that they can be + /// checked. + pub ascriptions: Vec>, +} + #[derive(Clone, Debug, HashStable, TypeVisitable)] pub struct Pat<'tcx> { pub ty: Ty<'tcx>, pub span: Span, + pub extra: Option>>, pub kind: PatKind<'tcx>, } @@ -762,11 +778,6 @@ pub enum PatKind<'tcx> { /// A wildcard pattern: `_`. Wild, - AscribeUserType { - ascription: Ascription<'tcx>, - subpattern: Box>, - }, - /// `x`, `ref x`, `x @ P`, etc. Binding { name: Symbol, @@ -831,21 +842,6 @@ pub enum PatKind<'tcx> { value: ty::Value<'tcx>, }, - /// Wrapper node representing a named constant that was lowered to a pattern - /// using `const_to_pat`. - /// - /// This is used by some diagnostics for non-exhaustive matches, to map - /// the pattern node back to the `DefId` of its original constant. - /// - /// FIXME(#150498): Can we make this an `Option` field on `Pat` - /// instead, so that non-diagnostic code can ignore it more easily? - ExpandedConstant { - /// [DefId] of the constant item. - def_id: DefId, - /// The pattern that the constant lowered to. - subpattern: Box>, - }, - Range(Arc>), /// Matches against a slice, checking the length and extracting elements. @@ -1119,7 +1115,7 @@ mod size_asserts { static_assert_size!(Block, 48); static_assert_size!(Expr<'_>, 64); static_assert_size!(ExprKind<'_>, 40); - static_assert_size!(Pat<'_>, 64); + static_assert_size!(Pat<'_>, 72); static_assert_size!(PatKind<'_>, 48); static_assert_size!(Stmt<'_>, 48); static_assert_size!(StmtKind<'_>, 48); diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index d792bbe60c88..b611b23e5261 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -259,7 +259,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( pat: &'a Pat<'tcx>, mut callback: impl FnMut(&'a Pat<'tcx>), ) { - let Pat { kind, ty: _, span: _ } = pat; + let Pat { kind, ty: _, span: _, extra: _ } = pat; match kind { PatKind::Missing | PatKind::Wild @@ -269,11 +269,9 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( | PatKind::Never | PatKind::Error(_) => {} - PatKind::AscribeUserType { subpattern, .. } - | PatKind::Binding { subpattern: Some(subpattern), .. } + PatKind::Binding { subpattern: Some(subpattern), .. } | PatKind::Deref { subpattern } - | PatKind::DerefPattern { subpattern, .. } - | PatKind::ExpandedConstant { subpattern, .. } => callback(subpattern), + | PatKind::DerefPattern { subpattern, .. } => callback(subpattern), PatKind::Variant { subpatterns, .. } | PatKind::Leaf { subpatterns } => { for field_pat in subpatterns { diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 6501ddeed6fa..5a5bf7d38ea4 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -191,8 +191,7 @@ impl<'tcx> Value<'tcx> { } } - /// Destructures array, ADT or tuple constants into the constants - /// of their fields. + /// Destructures ADT constants into the constants of their fields. pub fn destructure_adt_const(&self) -> ty::DestructuredAdtConst<'tcx> { let fields = self.to_branch(); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a2f4714d1b2c..a3eec72214c0 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1891,6 +1891,12 @@ impl<'tcx> TyCtxt<'tcx> { self.is_lang_item(self.parent(def_id), LangItem::AsyncDropInPlace) } + /// Check if the given `def_id` is a const with the `#[type_const]` attribute. + pub fn is_type_const(self, def_id: DefId) -> bool { + matches!(self.def_kind(def_id), DefKind::Const | DefKind::AssocConst) + && find_attr!(self.get_all_attrs(def_id), AttributeKind::TypeConst(_)) + } + /// Returns the movability of the coroutine of `def_id`, or panics /// if given a `def_id` that is not a coroutine. pub fn coroutine_movability(self, def_id: DefId) -> hir::Movability { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 1a5a3f3965fa..314d2ba39632 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -586,11 +586,18 @@ impl<'tcx> TypeSuperFoldable> for ty::Predicate<'tcx> { self, folder: &mut F, ) -> Result { + // This method looks different to `Ty::try_super_fold_with` and `Const::super_fold_with`. + // Why is that? `PredicateKind` provides little scope for optimized folding, unlike + // `TyKind` and `ConstKind` (which have common variants that don't require recursive + // `fold_with` calls on their fields). So we just derive the `TypeFoldable` impl for + // `PredicateKind` and call it here because the derived code is as fast as hand-written + // code would be. let new = self.kind().try_fold_with(folder)?; Ok(folder.cx().reuse_or_mk_predicate(self, new)) } fn super_fold_with>>(self, folder: &mut F) -> Self { + // See comment in `Predicate::try_super_fold_with`. let new = self.kind().fold_with(folder); folder.cx().reuse_or_mk_predicate(self, new) } @@ -598,6 +605,7 @@ impl<'tcx> TypeSuperFoldable> for ty::Predicate<'tcx> { impl<'tcx> TypeSuperVisitable> for ty::Predicate<'tcx> { fn super_visit_with>>(&self, visitor: &mut V) -> V::Result { + // See comment in `Predicate::try_super_fold_with`. self.kind().visit_with(visitor) } } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 691cb43b724a..2f6b38a619d2 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -2,11 +2,11 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::{self as hir, find_attr}; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::symbol::sym; use tracing::debug; use crate::query::LocalCrate; @@ -241,7 +241,7 @@ pub(super) fn trait_impls_of_provider(tcx: TyCtxt<'_>, trait_id: DefId) -> Trait /// Query provider for `incoherent_impls`. pub(super) fn incoherent_impls_provider(tcx: TyCtxt<'_>, simp: SimplifiedType) -> &[DefId] { if let Some(def_id) = simp.def() - && !tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) + && !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::RustcHasIncoherentInherentImpls) { return &[]; } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs index c6ef362c6ea5..3ae4b1fadee9 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs @@ -287,22 +287,14 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { self.parse_var(pattern) } - fn parse_var(&mut self, mut pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { - // Make sure we throw out any `AscribeUserType` we find - loop { - match &pat.kind { - PatKind::Binding { var, ty, .. } => break Ok((*var, *ty, pat.span)), - PatKind::AscribeUserType { subpattern, .. } => { - pat = subpattern; - } - _ => { - break Err(ParseError { - span: pat.span, - item_description: format!("{:?}", pat.kind), - expected: "local".to_string(), - }); - } - } + fn parse_var(&mut self, pat: &Pat<'tcx>) -> PResult<(LocalVarId, Ty<'tcx>, Span)> { + match &pat.kind { + PatKind::Binding { var, ty, .. } => Ok((*var, *ty, pat.span)), + _ => Err(ParseError { + span: pat.span, + item_description: format!("{:?}", pat.kind), + expected: "local".to_string(), + }), } } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index ddaa61c6cc91..13082d408ec0 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -144,11 +144,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let arm = &self.thir[*arm]; let value = match arm.pattern.kind { PatKind::Constant { value } => value, - PatKind::ExpandedConstant { ref subpattern, def_id: _ } - if let PatKind::Constant { value } = subpattern.kind => - { - value - } _ => { return Err(ParseError { span: arm.pattern.span, 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 186fde4883df..1772d66f5285 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -1,7 +1,7 @@ //! See docs in build/expr/mod.rs use rustc_abi::Size; -use rustc_ast as ast; +use rustc_ast::{self as ast}; use rustc_hir::LangItem; use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, LitToConstInput, Scalar}; use rustc_middle::mir::*; @@ -47,6 +47,7 @@ pub(crate) fn as_constant_inner<'tcx>( tcx: TyCtxt<'tcx>, ) -> ConstOperand<'tcx> { 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 }); @@ -69,6 +70,13 @@ pub(crate) fn as_constant_inner<'tcx>( } ExprKind::NamedConst { def_id, args, ref user_ty } => { let user_ty = user_ty.as_ref().and_then(push_cuta); + if tcx.is_type_const(def_id) { + let uneval = ty::UnevaluatedConst::new(def_id, args); + let ct = ty::Const::new_unevaluated(tcx, uneval); + + let const_ = Const::Ty(ty, ct); + return ConstOperand { span, user_ty, const_ }; + } let uneval = mir::UnevaluatedConst::new(def_id, args); let const_ = Const::Unevaluated(uneval, ty); 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 f0114c2193c3..8cee3ff27e8f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -133,6 +133,20 @@ impl<'tcx> MatchPairTree<'tcx> { } let place = place_builder.try_to_place(cx); + + // Apply any type ascriptions to the value at `match_pair.place`. + if let Some(place) = place + && let Some(extra) = &pattern.extra + { + for &Ascription { ref annotation, variance } in &extra.ascriptions { + extra_data.ascriptions.push(super::Ascription { + source: place, + annotation: annotation.clone(), + variance, + }); + } + } + let mut subpairs = Vec::new(); let testable_case = match pattern.kind { PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, @@ -195,28 +209,6 @@ impl<'tcx> MatchPairTree<'tcx> { Some(TestableCase::Constant { value, kind: const_kind }) } - PatKind::AscribeUserType { - ascription: Ascription { ref annotation, variance }, - ref subpattern, - .. - } => { - MatchPairTree::for_pattern( - place_builder, - subpattern, - cx, - &mut subpairs, - extra_data, - ); - - // Apply the type ascription to the value at `match_pair.place` - if let Some(source) = place { - let annotation = annotation.clone(); - extra_data.ascriptions.push(super::Ascription { source, annotation, variance }); - } - - None - } - PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { // In order to please the borrow checker, when lowering a pattern // like `x @ subpat` we must establish any bindings in `subpat` @@ -263,11 +255,6 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::ExpandedConstant { subpattern: ref pattern, .. } => { - MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data); - None - } - PatKind::Array { ref prefix, ref slice, ref suffix } => { cx.prefix_slice_suffix( &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 9080e2ba801b..590316a47554 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -576,7 +576,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { initializer_id: ExprId, ) -> BlockAnd<()> { match irrefutable_pat.kind { - // Optimize the case of `let x = ...` to write directly into `x` + // Optimize `let x = ...` and `let x: T = ...` to write directly into `x`, + // and then require that `T == typeof(x)` if present. PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => { let place = self.storage_live_binding( block, @@ -592,43 +593,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = self.source_info(irrefutable_pat.span); self.cfg.push_fake_read(block, source_info, FakeReadCause::ForLet(None), place); - self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); - block.unit() - } + let ascriptions: &[_] = + try { irrefutable_pat.extra.as_deref()?.ascriptions.as_slice() } + .unwrap_or_default(); + for thir::Ascription { annotation, variance: _ } in ascriptions { + let ty_source_info = self.source_info(annotation.span); - // Optimize the case of `let x: T = ...` to write directly - // into `x` and then require that `T == typeof(x)`. - PatKind::AscribeUserType { - ref subpattern, - ascription: thir::Ascription { ref annotation, variance: _ }, - } if let PatKind::Binding { - mode: BindingMode(ByRef::No, _), - var, - subpattern: None, - .. - } = subpattern.kind => - { - let place = self.storage_live_binding( - block, - var, - irrefutable_pat.span, - false, - OutsideGuard, - ScheduleDrops::Yes, - ); - block = self.expr_into_dest(place, block, initializer_id).into_block(); - - // Inject a fake read, see comments on `FakeReadCause::ForLet`. - let pattern_source_info = self.source_info(irrefutable_pat.span); - let cause_let = FakeReadCause::ForLet(None); - self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); - - let ty_source_info = self.source_info(annotation.span); - - let base = self.canonical_user_type_annotations.push(annotation.clone()); - self.cfg.push( - block, - Statement::new( + let base = self.canonical_user_type_annotations.push(annotation.clone()); + let stmt = Statement::new( ty_source_info, StatementKind::AscribeUserType( Box::new((place, UserTypeProjection { base, projs: Vec::new() })), @@ -648,8 +620,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``. ty::Invariant, ), - ), - ); + ); + self.cfg.push(block, stmt); + } self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() @@ -879,6 +852,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &ProjectedUserTypesNode<'_>, ), ) { + // Ascriptions correspond to user-written types like `let A::<'a>(_): A<'static> = ...;`. + // + // Caution: Pushing user types here is load-bearing even for + // patterns containing no bindings, to ensure that the type ends + // up represented in MIR _somewhere_. + let user_tys = match pattern.extra.as_deref() { + Some(PatExtra { ascriptions, .. }) if !ascriptions.is_empty() => { + let base_user_tys = ascriptions + .iter() + .map(|thir::Ascription { annotation, variance: _ }| { + // Note that the variance doesn't apply here, as we are tracking the effect + // of user types on any bindings contained with subpattern. + self.canonical_user_type_annotations.push(annotation.clone()) + }) + .collect(); + &user_tys.push_user_types(base_user_tys) + } + _ => user_tys, + }; + // Avoid having to write the full method name at each recursive call. let visit_subpat = |this: &mut Self, subpat, user_tys: &_, f: &mut _| { this.visit_primary_bindings_special(subpat, user_tys, f) @@ -924,31 +917,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f); } - PatKind::AscribeUserType { - ref subpattern, - ascription: thir::Ascription { ref annotation, variance: _ }, - } => { - // This corresponds to something like - // - // ``` - // let A::<'a>(_): A<'static> = ...; - // ``` - // - // Note that the variance doesn't apply here, as we are tracking the effect - // of `user_ty` on any bindings contained with subpattern. - - // Caution: Pushing this user type here is load-bearing even for - // patterns containing no bindings, to ensure that the type ends - // up represented in MIR _somewhere_. - let base_user_ty = self.canonical_user_type_annotations.push(annotation.clone()); - let subpattern_user_tys = user_tys.push_user_type(base_user_ty); - visit_subpat(self, subpattern, &subpattern_user_tys, f) - } - - PatKind::ExpandedConstant { ref subpattern, .. } => { - visit_subpat(self, subpattern, user_tys, f) - } - PatKind::Leaf { ref subpatterns } => { for subpattern in subpatterns { let subpattern_user_tys = user_tys.leaf(subpattern.field); diff --git a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs index df9f93ac328a..2dcfd3772902 100644 --- a/compiler/rustc_mir_build/src/builder/matches/user_ty.rs +++ b/compiler/rustc_mir_build/src/builder/matches/user_ty.rs @@ -8,15 +8,20 @@ use std::assert_matches::assert_matches; use std::iter; use rustc_abi::{FieldIdx, VariantIdx}; +use rustc_data_structures::smallvec::SmallVec; use rustc_middle::mir::{ProjectionElem, UserTypeProjection, UserTypeProjections}; use rustc_middle::ty::{AdtDef, UserTypeAnnotationIndex}; use rustc_span::Symbol; +/// A single `thir::Pat` node should almost never have more than 0-2 user types. +/// We can store up to 4 inline in the same size as an ordinary `Vec`. +pub(crate) type UserTypeIndices = SmallVec<[UserTypeAnnotationIndex; 4]>; + /// One of a list of "operations" that can be used to lazily build projections /// of user-specified types. -#[derive(Clone, Debug)] +#[derive(Debug)] pub(crate) enum ProjectedUserTypesOp { - PushUserType { base: UserTypeAnnotationIndex }, + PushUserTypes { base_types: UserTypeIndices }, Index, Subslice { from: u64, to: u64 }, @@ -32,9 +37,10 @@ pub(crate) enum ProjectedUserTypesNode<'a> { } impl<'a> ProjectedUserTypesNode<'a> { - pub(crate) fn push_user_type(&'a self, base: UserTypeAnnotationIndex) -> Self { - // Pushing a base user type always causes the chain to become non-empty. - Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserType { base } } + pub(crate) fn push_user_types(&'a self, base_types: UserTypeIndices) -> Self { + assert!(!base_types.is_empty()); + // Pushing one or more base user types always causes the chain to become non-empty. + Self::Chain { parent: self, op: ProjectedUserTypesOp::PushUserTypes { base_types } } } /// Push another projection op onto the chain, but only if it is already non-empty. @@ -94,16 +100,19 @@ impl<'a> ProjectedUserTypesNode<'a> { return None; } - let ops_reversed = self.iter_ops_reversed().cloned().collect::>(); + let ops_reversed = self.iter_ops_reversed().collect::>(); // The "first" op should always be `PushUserType`. // Other projections are only added if there is at least one user type. - assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserType { .. })); + assert_matches!(ops_reversed.last(), Some(ProjectedUserTypesOp::PushUserTypes { .. })); let mut projections = vec![]; for op in ops_reversed.into_iter().rev() { - match op { - ProjectedUserTypesOp::PushUserType { base } => { - projections.push(UserTypeProjection { base, projs: vec![] }) + match *op { + ProjectedUserTypesOp::PushUserTypes { ref base_types } => { + assert!(!base_types.is_empty()); + for &base in base_types { + projections.push(UserTypeProjection { base, projs: vec![] }) + } } ProjectedUserTypesOp::Index => { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 4f03e3d965c6..68b8a842a2a6 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -342,8 +342,6 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { PatKind::Wild | // these just wrap other patterns, which we recurse on below. PatKind::Or { .. } | - PatKind::ExpandedConstant { .. } | - PatKind::AscribeUserType { .. } | PatKind::Error(_) => {} } }; diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index bf480cf601ee..d9a06de32bb0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -680,20 +680,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { let mut interpreted_as_const = None; let mut interpreted_as_const_sugg = None; - // These next few matches want to peek through `AscribeUserType` to see - // the underlying pattern. - let mut unpeeled_pat = pat; - while let PatKind::AscribeUserType { ref subpattern, .. } = unpeeled_pat.kind { - unpeeled_pat = subpattern; - } - - if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, unpeeled_pat) { + if let Some(def_id) = is_const_pat_that_looks_like_binding(self.tcx, pat) { let span = self.tcx.def_span(def_id); let variable = self.tcx.item_name(def_id).to_string(); // When we encounter a constant as the binding name, point at the `const` definition. interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() }); interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable }); - } else if let PatKind::Constant { .. } = unpeeled_pat.kind + } else if let PatKind::Constant { .. } = pat.kind && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span) { // If the pattern to match is an integer literal: @@ -1213,7 +1206,7 @@ fn is_const_pat_that_looks_like_binding<'tcx>(tcx: TyCtxt<'tcx>, pat: &Pat<'tcx> // The pattern must be a named constant, and the name that appears in // the pattern's source text must resemble a plain identifier without any // `::` namespace separators or other non-identifier characters. - if let PatKind::ExpandedConstant { def_id, .. } = pat.kind + if let Some(def_id) = try { pat.extra.as_deref()?.expanded_const? } && matches!(tcx.def_kind(def_id), DefKind::Const) && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(pat.span) && snippet.chars().all(|c| c.is_alphanumeric() || c == '_') diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 2da9a43f71d8..02409d2bae9f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -90,7 +90,7 @@ impl<'tcx> ConstToPat<'tcx> { ); } } - Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()) }) + Box::new(Pat { span: self.span, ty, kind: PatKind::Error(err.emit()), extra: None }) } fn unevaluated_to_pat( @@ -174,10 +174,10 @@ impl<'tcx> ConstToPat<'tcx> { } }; - // Convert the valtree to a const. - let inlined_const_as_pat = self.valtree_to_pat(valtree, ty); + // Lower the valtree to a THIR pattern. + let mut thir_pat = self.valtree_to_pat(valtree, ty); - if !inlined_const_as_pat.references_error() { + if !thir_pat.references_error() { // Always check for `PartialEq` if we had no other errors yet. if !type_has_partial_eq_impl(self.tcx, typing_env, ty).has_impl { let mut err = self.tcx.dcx().create_err(TypeNotPartialEq { span: self.span, ty }); @@ -186,10 +186,10 @@ impl<'tcx> ConstToPat<'tcx> { } } - // Wrap the pattern in a marker node to indicate that it is the result of lowering a + // Mark the pattern to indicate that it is the result of lowering a named // constant. This is used for diagnostics. - let kind = PatKind::ExpandedConstant { subpattern: inlined_const_as_pat, def_id: uv.def }; - Box::new(Pat { kind, ty, span: self.span }) + thir_pat.extra.get_or_insert_default().expanded_const = Some(uv.def); + thir_pat } fn field_pats( @@ -351,7 +351,7 @@ impl<'tcx> ConstToPat<'tcx> { } }; - Box::new(Pat { span, ty, kind }) + Box::new(Pat { span, ty, kind, extra: None }) } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 4128508955cb..650650cbaac9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -71,14 +71,11 @@ pub(super) fn pat_from_hir<'tcx>( span: let_stmt_type.span, inferred_ty: typeck_results.node_type(let_stmt_type.hir_id), }; - thir_pat = Box::new(Pat { - ty: thir_pat.ty, - span: thir_pat.span, - kind: PatKind::AscribeUserType { - ascription: Ascription { annotation, variance: ty::Covariant }, - subpattern: thir_pat, - }, - }); + thir_pat + .extra + .get_or_insert_default() + .ascriptions + .push(Ascription { annotation, variance: ty::Covariant }); } if let Some(m) = pcx.rust_2024_migration { @@ -142,7 +139,7 @@ impl<'tcx> PatCtxt<'tcx> { } PatAdjust::PinDeref => PatKind::Deref { subpattern: thir_pat }, }; - Box::new(Pat { span, ty: adjust.source, kind }) + Box::new(Pat { span, ty: adjust.source, kind, extra: None }) }); if let Some(s) = &mut self.rust_2024_migration @@ -167,25 +164,14 @@ impl<'tcx> PatCtxt<'tcx> { // Return None in that case; the caller will use NegInfinity or PosInfinity instead. let Some(expr) = expr else { return Ok(None) }; - // Lower the endpoint into a temporary `PatKind` that will then be + // Lower the endpoint into a temporary `thir::Pat` that will then be // deconstructed to obtain the constant value and other data. - let mut kind: PatKind<'tcx> = self.lower_pat_expr(pat, expr); + let endpoint_pat: Box> = self.lower_pat_expr(pat, expr); + let box Pat { ref kind, extra, .. } = endpoint_pat; - // Unpeel any ascription or inline-const wrapper nodes. - loop { - match kind { - PatKind::AscribeUserType { ascription, subpattern } => { - ascriptions.push(ascription); - kind = subpattern.kind; - } - PatKind::ExpandedConstant { def_id: _, subpattern } => { - // Expanded-constant nodes are currently only needed by - // diagnostics that don't apply to range patterns, so we - // can just discard them here. - kind = subpattern.kind; - } - _ => break, - } + // Preserve any ascriptions from endpoint constants. + if let Some(extra) = extra { + ascriptions.extend(extra.ascriptions); } // The unpeeled kind should now be a constant, giving us the endpoint value. @@ -250,7 +236,7 @@ impl<'tcx> PatCtxt<'tcx> { lo_expr: Option<&'tcx hir::PatExpr<'tcx>>, hi_expr: Option<&'tcx hir::PatExpr<'tcx>>, end: RangeEnd, - ) -> Result, ErrorGuaranteed> { + ) -> Result>, ErrorGuaranteed> { let ty = self.typeck_results.node_type(pat.hir_id); let span = pat.span; @@ -306,27 +292,28 @@ impl<'tcx> PatCtxt<'tcx> { return Err(e); } } + let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None }); // If we are handling a range with associated constants (e.g. // `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated // constants somewhere. Have them on the range pattern. - for ascription in ascriptions { - let subpattern = Box::new(Pat { span, ty, kind }); - kind = PatKind::AscribeUserType { ascription, subpattern }; - } - // `PatKind::ExpandedConstant` wrappers from range endpoints used to + thir_pat.extra.get_or_insert_default().ascriptions.extend(ascriptions); + // IDs of expanded constants from range endpoints used to // also be preserved here, but that was only needed for unsafeck of // inline `const { .. }` patterns, which were removed by // . - Ok(kind) + Ok(thir_pat) } #[instrument(skip(self), level = "debug")] fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { - let mut ty = self.typeck_results.node_type(pat.hir_id); - let mut span = pat.span; + let ty = self.typeck_results.node_type(pat.hir_id); + let span = pat.span; + // Some of these match arms return a `Box` early, while others + // evaluate to a `PatKind` that will become a `Box` at the end of + // this function. let kind = match pat.kind { hir::PatKind::Missing => PatKind::Missing, @@ -334,10 +321,13 @@ impl<'tcx> PatCtxt<'tcx> { hir::PatKind::Never => PatKind::Never, - hir::PatKind::Expr(value) => self.lower_pat_expr(pat, value), + hir::PatKind::Expr(value) => return self.lower_pat_expr(pat, value), hir::PatKind::Range(lo_expr, hi_expr, end) => { - self.lower_pattern_range(pat, lo_expr, hi_expr, end).unwrap_or_else(PatKind::Error) + match self.lower_pattern_range(pat, lo_expr, hi_expr, end) { + Ok(thir_pat) => return thir_pat, + Err(e) => PatKind::Error(e), + } } hir::PatKind::Deref(subpattern) => { @@ -360,7 +350,7 @@ impl<'tcx> PatCtxt<'tcx> { }, hir::PatKind::Slice(prefix, slice, suffix) => { - self.slice_or_array_pattern(pat, prefix, slice, suffix) + return self.slice_or_array_pattern(pat, prefix, slice, suffix); } hir::PatKind::Tuple(pats, ddpos) => { @@ -372,8 +362,9 @@ impl<'tcx> PatCtxt<'tcx> { } hir::PatKind::Binding(explicit_ba, id, ident, sub) => { + let mut thir_pat_span = span; if let Some(ident_span) = ident.span.find_ancestor_inside(span) { - span = span.with_hi(ident_span.hi()); + thir_pat_span = span.with_hi(ident_span.hi()); } let mode = *self @@ -389,22 +380,23 @@ impl<'tcx> PatCtxt<'tcx> { // A ref x pattern is the same node used for x, and as such it has // x's type, which is &T, where we want T (the type being matched). let var_ty = ty; + let mut thir_pat_ty = ty; if let hir::ByRef::Yes(pinnedness, _) = mode.0 { match pinnedness { hir::Pinnedness::Pinned if let Some(pty) = ty.pinned_ty() && let &ty::Ref(_, rty, _) = pty.kind() => { - ty = rty; + thir_pat_ty = rty; } hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => { - ty = rty; + thir_pat_ty = rty; } _ => bug!("`ref {}` has wrong type {}", ident, ty), } }; - PatKind::Binding { + let kind = PatKind::Binding { mode, name: ident.name, var: LocalVarId(id), @@ -412,7 +404,10 @@ impl<'tcx> PatCtxt<'tcx> { subpattern: self.lower_opt_pattern(sub), is_primary: id == pat.hir_id, is_shorthand: false, - } + }; + // We might have modified the type or span, so use the modified + // values in the THIR pattern node. + return Box::new(Pat { ty: thir_pat_ty, span: thir_pat_span, kind, extra: None }); } hir::PatKind::TupleStruct(ref qpath, pats, ddpos) => { @@ -422,7 +417,7 @@ impl<'tcx> PatCtxt<'tcx> { }; let variant_def = adt_def.variant_of_res(res); let subpatterns = self.lower_tuple_subpats(pats, variant_def.fields.len(), ddpos); - self.lower_variant_or_leaf(pat, None, res, subpatterns) + return self.lower_variant_or_leaf(pat, None, res, subpatterns); } hir::PatKind::Struct(ref qpath, fields, _) => { @@ -439,7 +434,7 @@ impl<'tcx> PatCtxt<'tcx> { }) .collect(); - self.lower_variant_or_leaf(pat, None, res, subpatterns) + return self.lower_variant_or_leaf(pat, None, res, subpatterns); } hir::PatKind::Or(pats) => PatKind::Or { pats: self.lower_patterns(pats) }, @@ -450,7 +445,9 @@ impl<'tcx> PatCtxt<'tcx> { hir::PatKind::Err(guar) => PatKind::Error(guar), }; - Box::new(Pat { span, ty, kind }) + // For pattern kinds that haven't already returned, create a `thir::Pat` + // with the HIR pattern node's type and span. + Box::new(Pat { span, ty, kind, extra: None }) } fn lower_tuple_subpats( @@ -482,13 +479,14 @@ impl<'tcx> PatCtxt<'tcx> { prefix: &'tcx [hir::Pat<'tcx>], slice: Option<&'tcx hir::Pat<'tcx>>, suffix: &'tcx [hir::Pat<'tcx>], - ) -> PatKind<'tcx> { + ) -> Box> { let ty = self.typeck_results.node_type(pat.hir_id); + let span = pat.span; let prefix = self.lower_patterns(prefix); let slice = self.lower_opt_pattern(slice); let suffix = self.lower_patterns(suffix); - match ty.kind() { + let kind = match ty.kind() { // Matching a slice, `[T]`. ty::Slice(..) => PatKind::Slice { prefix, slice, suffix }, // Fixed-length array, `[T; len]`. @@ -499,8 +497,9 @@ impl<'tcx> PatCtxt<'tcx> { assert!(len >= prefix.len() as u64 + suffix.len() as u64); PatKind::Array { prefix, slice, suffix } } - _ => span_bug!(pat.span, "bad slice pattern type {ty:?}"), - } + _ => span_bug!(span, "bad slice pattern type {ty:?}"), + }; + Box::new(Pat { ty, span, kind, extra: None }) } fn lower_variant_or_leaf( @@ -509,7 +508,7 @@ impl<'tcx> PatCtxt<'tcx> { expr: Option<&'tcx hir::PatExpr<'tcx>>, res: Res, subpatterns: Vec>, - ) -> PatKind<'tcx> { + ) -> Box> { // Check whether the caller should have provided an `expr` for this pattern kind. assert_matches!( (pat.kind, expr), @@ -533,7 +532,7 @@ impl<'tcx> PatCtxt<'tcx> { res => res, }; - let mut kind = match res { + let kind = match res { Res::Def(DefKind::Variant, variant_id) => { let enum_id = self.tcx.parent(variant_id); let adt_def = self.tcx.adt_def(enum_id); @@ -542,7 +541,12 @@ impl<'tcx> PatCtxt<'tcx> { ty::Adt(_, args) | ty::FnDef(_, args) => args, ty::Error(e) => { // Avoid ICE (#50585) - return PatKind::Error(*e); + return Box::new(Pat { + ty, + span, + kind: PatKind::Error(*e), + extra: None, + }); } _ => bug!("inappropriate type for def: {:?}", ty), }; @@ -583,21 +587,23 @@ impl<'tcx> PatCtxt<'tcx> { PatKind::Error(e) } }; + let mut thir_pat = Box::new(Pat { ty, span, kind, extra: None }); if let Some(user_ty) = self.user_args_applied_to_ty_of_hir_id(hir_id) { - debug!("lower_variant_or_leaf: kind={:?} user_ty={:?} span={:?}", kind, user_ty, span); + debug!(?thir_pat, ?user_ty, ?span, "lower_variant_or_leaf: applying ascription"); let annotation = CanonicalUserTypeAnnotation { user_ty: Box::new(user_ty), span, inferred_ty: self.typeck_results.node_type(hir_id), }; - kind = PatKind::AscribeUserType { - subpattern: Box::new(Pat { span, ty, kind }), - ascription: Ascription { annotation, variance: ty::Covariant }, - }; + thir_pat + .extra + .get_or_insert_default() + .ascriptions + .push(Ascription { annotation, variance: ty::Covariant }); } - kind + thir_pat } fn user_args_applied_to_ty_of_hir_id( @@ -632,8 +638,7 @@ impl<'tcx> PatCtxt<'tcx> { _ => { // The path isn't the name of a constant, so it must actually // be a unit struct or unit variant (e.g. `Option::None`). - let kind = self.lower_variant_or_leaf(pat, Some(expr), res, vec![]); - return Box::new(Pat { span, ty, kind }); + return self.lower_variant_or_leaf(pat, Some(expr), res, vec![]); } }; @@ -652,16 +657,13 @@ impl<'tcx> PatCtxt<'tcx> { span, inferred_ty: self.typeck_results.node_type(id), }; - let kind = PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - annotation, - // Note that we use `Contravariant` here. See the - // `variance` field documentation for details. - variance: ty::Contravariant, - }, - }; - pattern = Box::new(Pat { span, kind, ty }); + // Note that we use `Contravariant` here. See the + // `variance` field documentation for details. + pattern + .extra + .get_or_insert_default() + .ascriptions + .push(Ascription { annotation, variance: ty::Contravariant }); } pattern @@ -674,10 +676,10 @@ impl<'tcx> PatCtxt<'tcx> { &mut self, pat: &'tcx hir::Pat<'tcx>, // Pattern that directly contains `expr` expr: &'tcx hir::PatExpr<'tcx>, - ) -> PatKind<'tcx> { + ) -> Box> { assert_matches!(pat.kind, hir::PatKind::Expr(..) | hir::PatKind::Range(..)); match &expr.kind { - hir::PatExprKind::Path(qpath) => self.lower_path(pat, expr, qpath).kind, + hir::PatExprKind::Path(qpath) => self.lower_path(pat, expr, qpath), hir::PatExprKind::Lit { lit, negated } => { // We handle byte string literal patterns by using the pattern's type instead of the // literal's type in `const_to_pat`: if the literal `b"..."` matches on a slice reference, @@ -691,7 +693,7 @@ impl<'tcx> PatCtxt<'tcx> { let pat_ty = self.typeck_results.node_type(pat.hir_id); let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated }; let constant = self.tcx.at(expr.span).lit_to_const(lit_input); - self.const_to_pat(constant, pat_ty, expr.hir_id, lit.span).kind + self.const_to_pat(constant, pat_ty, expr.hir_id, lit.span) } } } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 5a2d6cfef1cc..e12909305961 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -73,6 +73,24 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.fmt } + fn print_list( + &mut self, + label: &str, + list: &[T], + depth_lvl: usize, + print_fn: impl Fn(&mut Self, &T, usize), + ) { + if list.is_empty() { + print_indented!(self, format_args!("{label}: []"), depth_lvl); + } else { + print_indented!(self, format_args!("{label}: ["), depth_lvl); + for item in list { + print_fn(self, item, depth_lvl + 1) + } + print_indented!(self, "]", depth_lvl); + } + } + fn print_param(&mut self, param: &Param<'tcx>, depth_lvl: usize) { let Param { pat, ty, ty_span, self_kind, hir_id } = param; @@ -663,15 +681,37 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { } fn print_pat(&mut self, pat: &Pat<'tcx>, depth_lvl: usize) { - let &Pat { ty, span, ref kind } = pat; + let &Pat { ty, span, ref kind, ref extra } = pat; print_indented!(self, "Pat: {", depth_lvl); print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1); print_indented!(self, format!("span: {:?}", span), depth_lvl + 1); + self.print_pat_extra(extra.as_deref(), depth_lvl + 1); self.print_pat_kind(kind, depth_lvl + 1); print_indented!(self, "}", depth_lvl); } + fn print_pat_extra(&mut self, extra: Option<&PatExtra<'tcx>>, depth_lvl: usize) { + let Some(extra) = extra else { + // Skip printing in the common case of a pattern node with no extra data. + return; + }; + + let PatExtra { expanded_const, ascriptions } = extra; + + print_indented!(self, "extra: PatExtra {", depth_lvl); + print_indented!(self, format_args!("expanded_const: {expanded_const:?}"), depth_lvl + 1); + self.print_list( + "ascriptions", + ascriptions, + depth_lvl + 1, + |this, ascription, depth_lvl| { + print_indented!(this, format_args!("{ascription:?}"), depth_lvl); + }, + ); + print_indented!(self, "}", depth_lvl); + } + fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) { print_indented!(self, "kind: PatKind {", depth_lvl); @@ -685,13 +725,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { PatKind::Never => { print_indented!(self, "Never", depth_lvl + 1); } - PatKind::AscribeUserType { ascription, subpattern } => { - print_indented!(self, "AscribeUserType: {", depth_lvl + 1); - print_indented!(self, format!("ascription: {:?}", ascription), depth_lvl + 2); - print_indented!(self, "subpattern: ", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 3); - print_indented!(self, "}", depth_lvl + 1); - } PatKind::Binding { name, mode, var, ty, subpattern, is_primary, is_shorthand } => { print_indented!(self, "Binding {", depth_lvl + 1); print_indented!(self, format!("name: {:?}", name), depth_lvl + 2); @@ -756,13 +789,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { print_indented!(self, format!("value: {}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::ExpandedConstant { def_id, subpattern } => { - print_indented!(self, "ExpandedConstant {", depth_lvl + 1); - print_indented!(self, format!("def_id: {def_id:?}"), depth_lvl + 2); - print_indented!(self, "subpattern:", depth_lvl + 2); - self.print_pat(subpattern, depth_lvl + 2); - print_indented!(self, "}", depth_lvl + 1); - } PatKind::Range(pat_range) => { print_indented!(self, format!("Range ( {:?} )", pat_range), depth_lvl + 1); } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 800d4e406cf0..8be7d210f8df 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -177,8 +177,9 @@ pub struct MoveData<'tcx> { pub rev_lookup: MovePathLookup<'tcx>, pub inits: IndexVec, /// Each Location `l` is mapped to the Inits that are effects - /// of executing the code at `l`. - pub init_loc_map: LocationMap>, + /// of executing the code at `l`. Only very rarely (e.g. inline asm) + /// is there more than one Init at any `l`. + pub init_loc_map: LocationMap>, pub init_path_map: IndexVec>, } diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index ee21d4fbcead..21a6c4d653bc 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -19,14 +19,14 @@ pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( caller_def_id: DefId, callee_only: &[&'a str], ) { - let callee = tcx.def_path_str(callee_def_id); - let caller = tcx.def_path_str(caller_def_id); - tcx.node_span_lint( lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), call_span, |lint| { + let callee = tcx.def_path_str(callee_def_id); + let caller = tcx.def_path_str(caller_def_id); + lint.primary_message(format!( "call to `#[inline(always)]`-annotated `{callee}` \ requires the same target features to be inlined" diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 09a55f0b5f8d..0829d52283ab 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -10,6 +10,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl index edb91d8f2eda..09500ba73359 100644 --- a/compiler/rustc_monomorphize/messages.ftl +++ b/compiler/rustc_monomorphize/messages.ftl @@ -75,4 +75,8 @@ monomorphize_recursion_limit = monomorphize_start_not_found = using `fn main` requires the standard library .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` +monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}` + .label = part of this cycle + .note = cyclic static initializers are not supported for target `{$target}` + monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4b2f8e03afc1..070db1ae6b5e 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -267,7 +267,8 @@ pub(crate) struct UsageMap<'tcx> { // Maps every mono item to the mono items used by it. pub used_map: UnordMap, Vec>>, - // Maps every mono item to the mono items that use it. + // Maps each mono item with users to the mono items that use it. + // Be careful: subsets `used_map`, so unused items are vacant. user_map: UnordMap, Vec>>, } diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 4949d9ae3922..723649f22117 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -117,3 +117,15 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { /// Whether this is a problem at a call site or at a declaration. pub is_call: bool, } + +#[derive(Diagnostic)] +#[diag(monomorphize_static_initializer_cyclic)] +#[note] +pub(crate) struct StaticInitializerCyclic<'a> { + #[primary_span] + pub span: Span, + #[label] + pub labels: Vec, + pub head: &'a str, + pub target: &'a str, +} diff --git a/compiler/rustc_monomorphize/src/graph_checks/mod.rs b/compiler/rustc_monomorphize/src/graph_checks/mod.rs new file mode 100644 index 000000000000..2b9b7cfff0b2 --- /dev/null +++ b/compiler/rustc_monomorphize/src/graph_checks/mod.rs @@ -0,0 +1,18 @@ +//! Checks that need to operate on the entire mono item graph +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::TyCtxt; + +use crate::collector::UsageMap; +use crate::graph_checks::statics::check_static_initializers_are_acyclic; + +mod statics; + +pub(super) fn target_specific_checks<'tcx, 'a, 'b>( + tcx: TyCtxt<'tcx>, + mono_items: &'a [MonoItem<'tcx>], + usage_map: &'b UsageMap<'tcx>, +) { + if tcx.sess.target.options.static_initializer_must_be_acyclic { + check_static_initializers_are_acyclic(tcx, mono_items, usage_map); + } +} diff --git a/compiler/rustc_monomorphize/src/graph_checks/statics.rs b/compiler/rustc_monomorphize/src/graph_checks/statics.rs new file mode 100644 index 000000000000..a764d307b3d4 --- /dev/null +++ b/compiler/rustc_monomorphize/src/graph_checks/statics.rs @@ -0,0 +1,115 @@ +use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::graph::scc::Sccs; +use rustc_data_structures::graph::{DirectedGraph, Successors}; +use rustc_data_structures::unord::UnordMap; +use rustc_hir::def_id::DefId; +use rustc_index::{Idx, IndexVec, newtype_index}; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::TyCtxt; + +use crate::collector::UsageMap; +use crate::errors; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct StaticNodeIdx(usize); + +impl Idx for StaticNodeIdx { + fn new(idx: usize) -> Self { + Self(idx) + } + + fn index(self) -> usize { + self.0 + } +} + +impl From for StaticNodeIdx { + fn from(value: usize) -> Self { + StaticNodeIdx(value) + } +} + +newtype_index! { + #[derive(Ord, PartialOrd)] + struct StaticSccIdx {} +} + +// Adjacency-list graph for statics using `StaticNodeIdx` as node type. +// We cannot use `DefId` as the node type directly because each node must be +// represented by an index in the range `0..num_nodes`. +struct StaticRefGraph<'a, 'b, 'tcx> { + // maps from `StaticNodeIdx` to `DefId` and vice versa + statics: &'a FxIndexSet, + // contains for each `MonoItem` the `MonoItem`s it uses + used_map: &'b UnordMap, Vec>>, +} + +impl<'a, 'b, 'tcx> DirectedGraph for StaticRefGraph<'a, 'b, 'tcx> { + type Node = StaticNodeIdx; + + fn num_nodes(&self) -> usize { + self.statics.len() + } +} + +impl<'a, 'b, 'tcx> Successors for StaticRefGraph<'a, 'b, 'tcx> { + fn successors(&self, node_idx: StaticNodeIdx) -> impl Iterator { + let def_id = self.statics[node_idx.index()]; + self.used_map[&MonoItem::Static(def_id)].iter().filter_map(|&mono_item| match mono_item { + MonoItem::Static(def_id) => self.statics.get_index_of(&def_id).map(|idx| idx.into()), + _ => None, + }) + } +} + +pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>( + tcx: TyCtxt<'tcx>, + mono_items: &'a [MonoItem<'tcx>], + usage_map: &'b UsageMap<'tcx>, +) { + // Collect statics + let statics: FxIndexSet = mono_items + .iter() + .filter_map(|&mono_item| match mono_item { + MonoItem::Static(def_id) => Some(def_id), + _ => None, + }) + .collect(); + + // If we don't have any statics the check is not necessary + if statics.is_empty() { + return; + } + // Create a subgraph from the mono item graph, which only contains statics + let graph = StaticRefGraph { statics: &statics, used_map: &usage_map.used_map }; + // Calculate its SCCs + let sccs: Sccs = Sccs::new(&graph); + // Group statics by SCCs + let mut nodes_of_sccs: IndexVec> = + IndexVec::from_elem_n(Vec::new(), sccs.num_sccs()); + for i in graph.iter_nodes() { + nodes_of_sccs[sccs.scc(i)].push(i); + } + let is_cyclic = |nodes_of_scc: &[StaticNodeIdx]| -> bool { + match nodes_of_scc.len() { + 0 => false, + 1 => graph.successors(nodes_of_scc[0]).any(|x| x == nodes_of_scc[0]), + 2.. => true, + } + }; + // Emit errors for all cycles + for nodes in nodes_of_sccs.iter_mut().filter(|nodes| is_cyclic(nodes)) { + // We sort the nodes by their Span to have consistent error line numbers + nodes.sort_by_key(|node| tcx.def_span(statics[node.index()])); + + let head_def = statics[nodes[0].index()]; + let head_span = tcx.def_span(head_def); + + tcx.dcx().emit_err(errors::StaticInitializerCyclic { + span: head_span, + labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(), + head: &tcx.def_path_str(head_def), + target: &tcx.sess.target.llvm_target, + }); + } +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 8b48cf5a6501..5b4f74ca6a70 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -16,6 +16,7 @@ use rustc_span::ErrorGuaranteed; mod collector; mod errors; +mod graph_checks; mod mono_checks; mod partitioning; mod util; diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index e7af1d45cff8..13d7f6a25f76 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -124,6 +124,7 @@ use tracing::debug; use crate::collector::{self, MonoItemCollectionStrategy, UsageMap}; use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined}; +use crate::graph_checks::target_specific_checks; struct PartitioningCx<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -1135,6 +1136,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio }; let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_strategy); + // Perform checks that need to operate on the entire mono item graph + target_specific_checks(tcx, &items, &usage_map); // If there was an error during collection (e.g. from one of the constants we evaluated), // then we stop here. This way codegen does not have to worry about failing constants. diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 9162284422d0..8dfa875ca6f9 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -64,7 +64,7 @@ pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner canonicalize_mode: CanonicalizeMode, // Mutable fields. - variables: &'a mut Vec, + variables: Vec, var_kinds: Vec>, variable_lookup_table: HashMap, /// Maps each `sub_unification_table_root_var` to the index of the first @@ -84,14 +84,13 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { pub(super) fn canonicalize_response>( delegate: &'a D, max_input_universe: ty::UniverseIndex, - variables: &'a mut Vec, value: T, ) -> ty::Canonical { let mut canonicalizer = Canonicalizer { delegate, canonicalize_mode: CanonicalizeMode::Response { max_input_universe }, - variables, + variables: Vec::new(), variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), @@ -106,16 +105,17 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }; debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); - let (max_universe, variables) = canonicalizer.finalize(); - Canonical { max_universe, variables, value } + let (max_universe, _variables, var_kinds) = canonicalizer.finalize(); + Canonical { max_universe, var_kinds, value } } + fn canonicalize_param_env( delegate: &'a D, - variables: &'a mut Vec, param_env: I::ParamEnv, - ) -> (I::ParamEnv, HashMap, Vec>) { + ) -> (I::ParamEnv, Vec, Vec>, HashMap) + { if !param_env.has_type_flags(NEEDS_CANONICAL) { - return (param_env, Default::default(), Vec::new()); + return (param_env, Vec::new(), Vec::new(), Default::default()); } // Check whether we can use the global cache for this param_env. As we only use @@ -129,12 +129,11 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { delegate.cx().canonical_param_env_cache_get_or_insert( param_env, || { - let mut variables = Vec::new(); let mut env_canonicalizer = Canonicalizer { delegate, canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), - variables: &mut variables, + variables: Vec::new(), variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), @@ -147,7 +146,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { param_env, variable_lookup_table: env_canonicalizer.variable_lookup_table, var_kinds: env_canonicalizer.var_kinds, - variables, + variables: env_canonicalizer.variables, } }, |&CanonicalParamEnvCacheEntry { @@ -156,9 +155,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ref variable_lookup_table, ref var_kinds, }| { - debug_assert!(variables.is_empty()); + // FIXME(nnethercote): for reasons I don't understand, this `new`+`extend` + // combination is faster than `variables.clone()`, because it somehow avoids + // some allocations. + let mut variables = Vec::new(); variables.extend(cache_variables.iter().copied()); - (param_env, variable_lookup_table.clone(), var_kinds.clone()) + (param_env, variables, var_kinds.clone(), variable_lookup_table.clone()) }, ) } else { @@ -166,7 +168,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { delegate, canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), - variables, + variables: Vec::new(), variable_lookup_table: Default::default(), sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), @@ -175,7 +177,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }; let param_env = param_env.fold_with(&mut env_canonicalizer); debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); - (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds) + ( + param_env, + env_canonicalizer.variables, + env_canonicalizer.var_kinds, + env_canonicalizer.variable_lookup_table, + ) } } @@ -189,12 +196,11 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { /// variable in the future by changing the way we detect global where-bounds. pub(super) fn canonicalize_input>( delegate: &'a D, - variables: &'a mut Vec, input: QueryInput, - ) -> ty::Canonical> { + ) -> (Vec, ty::Canonical>) { // First canonicalize the `param_env` while keeping `'static` - let (param_env, variable_lookup_table, var_kinds) = - Canonicalizer::canonicalize_param_env(delegate, variables, input.goal.param_env); + let (param_env, variables, var_kinds, variable_lookup_table) = + Canonicalizer::canonicalize_param_env(delegate, input.goal.param_env); // Then canonicalize the rest of the input without keeping `'static` // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { @@ -234,8 +240,8 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { debug_assert!(!value.has_infer(), "unexpected infer in {value:?}"); debug_assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}"); - let (max_universe, variables) = rest_canonicalizer.finalize(); - Canonical { max_universe, variables, value } + let (max_universe, variables, var_kinds) = rest_canonicalizer.finalize(); + (variables, Canonical { max_universe, var_kinds, value }) } fn get_or_insert_bound_var( @@ -243,8 +249,9 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { arg: impl Into, kind: CanonicalVarKind, ) -> ty::BoundVar { - // FIXME: 16 is made up and arbitrary. We should look at some - // perf data here. + // The exact value of 16 here doesn't matter that much (8 and 32 give extremely similar + // results). So long as we have protection against the rare cases where the length reaches + // 1000+ (e.g. `wg-grammar`). let arg = arg.into(); let idx = if self.variables.len() > 16 { if self.variable_lookup_table.is_empty() { @@ -276,19 +283,18 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ty::BoundVar::from(idx) } - fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) { + fn finalize(self) -> (ty::UniverseIndex, Vec, I::CanonicalVarKinds) { let mut var_kinds = self.var_kinds; // See the rustc-dev-guide section about how we deal with universes // during canonicalization in the new solver. - match self.canonicalize_mode { + let max_universe = match self.canonicalize_mode { // All placeholders and vars are canonicalized in the root universe. CanonicalizeMode::Input { .. } => { debug_assert!( var_kinds.iter().all(|var| var.universe() == ty::UniverseIndex::ROOT), "expected all vars to be canonicalized in root universe: {var_kinds:#?}" ); - let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds); - (ty::UniverseIndex::ROOT, var_kinds) + ty::UniverseIndex::ROOT } // When canonicalizing a response we map a universes already entered // by the caller to the root universe and only return useful universe @@ -302,15 +308,15 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ); *var = var.with_updated_universe(new_uv); } - let max_universe = var_kinds + var_kinds .iter() .map(|kind| kind.universe()) .max() - .unwrap_or(ty::UniverseIndex::ROOT); - let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds); - (max_universe, var_kinds) + .unwrap_or(ty::UniverseIndex::ROOT) } - } + }; + let var_kinds = self.delegate.cx().mk_canonical_var_kinds(&var_kinds); + (max_universe, self.variables, var_kinds) } fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty { @@ -417,7 +423,7 @@ impl, I: Interner> TypeFolder for Canonicaliz // We don't canonicalize `ReStatic` in the `param_env` as we use it // when checking whether a `ParamEnv` candidate is global. ty::ReStatic => match self.canonicalize_mode { - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => { CanonicalVarKind::Region(ty::UniverseIndex::ROOT) } CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) @@ -545,7 +551,7 @@ impl, I: Interner> TypeFolder for Canonicaliz match self.canonicalize_mode { CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { max_input_universe: _ } => {} - CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate) => { panic!("erasing 'static in env") } } diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index b4cea8701d82..96fea09013a1 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -59,10 +59,8 @@ where D: SolverDelegate, I: Interner, { - let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize_input( + let (orig_values, canonical) = Canonicalizer::canonicalize_input( delegate, - &mut orig_values, QueryInput { goal, predefined_opaques_in_body: delegate.cx().mk_predefined_opaques_in_body(opaque_types), @@ -82,10 +80,7 @@ where I: Interner, T: TypeFoldable, { - let mut orig_values = Default::default(); - let canonical = - Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value); - canonical + Canonicalizer::canonicalize_response(delegate, max_input_universe, value) } /// After calling a canonical query, we apply the constraints returned @@ -157,7 +152,7 @@ where // // We therefore instantiate the existential variable in the canonical response with the // inference variable of the input right away, which is more performant. - let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + let mut opt_values = IndexVec::from_elem_n(None, response.var_kinds.len()); for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) { match result_value.kind() { ty::GenericArgKind::Type(t) => { @@ -167,7 +162,7 @@ where // more involved. They are also a lot rarer than region variables. if let ty::Bound(index_kind, b) = t.kind() && !matches!( - response.variables.get(b.var().as_usize()).unwrap(), + response.var_kinds.get(b.var().as_usize()).unwrap(), CanonicalVarKind::Ty { .. } ) { @@ -189,7 +184,7 @@ where } } } - CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { + CanonicalVarValues::instantiate(delegate.cx(), response.var_kinds, |var_values, kind| { if kind.universe() != ty::UniverseIndex::ROOT { // A variable from inside a binder of the query. While ideally these shouldn't // exist at all (see the FIXME at the start of this method), we have to deal with @@ -308,7 +303,7 @@ where let var_values = CanonicalVarValues { var_values: delegate.cx().mk_args(var_values) }; let state = inspect::State { var_values, data }; let state = eager_resolve_vars(delegate, state); - Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut vec![], state) + Canonicalizer::canonicalize_response(delegate, max_input_universe, state) } // FIXME: needs to be pub to be accessed by downstream @@ -345,14 +340,14 @@ where pub fn response_no_constraints_raw( cx: I, max_universe: ty::UniverseIndex, - variables: I::CanonicalVarKinds, + var_kinds: I::CanonicalVarKinds, certainty: Certainty, ) -> CanonicalResponse { ty::Canonical { max_universe, - variables, + var_kinds, value: Response { - var_values: ty::CanonicalVarValues::make_identity(cx, variables), + var_values: ty::CanonicalVarValues::make_identity(cx, var_kinds), // FIXME: maybe we should store the "no response" version in cx, like // we do for cx.types and stuff. external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8d0a3ac94d5a..2fd79f593a5f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -97,7 +97,7 @@ where /// The variable info for the `var_values`, only used to make an ambiguous response /// with no constraints. - variables: I::CanonicalVarKinds, + var_kinds: I::CanonicalVarKinds, /// What kind of goal we're currently computing, see the enum definition /// for more info. @@ -325,7 +325,7 @@ where // which we don't do within this evaluation context. max_input_universe: ty::UniverseIndex::ROOT, initial_opaque_types_storage_num_entries: Default::default(), - variables: Default::default(), + var_kinds: Default::default(), var_values: CanonicalVarValues::dummy(), current_goal_kind: CurrentGoalKind::Misc, origin_span, @@ -376,7 +376,7 @@ where let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries(); let mut ecx = EvalCtxt { delegate, - variables: canonical_input.canonical.variables, + var_kinds: canonical_input.canonical.var_kinds, var_values, current_goal_kind: CurrentGoalKind::from_query_input(cx, input), max_input_universe: canonical_input.canonical.max_universe, @@ -1323,7 +1323,7 @@ where response_no_constraints_raw( self.cx(), self.max_input_universe, - self.variables, + self.var_kinds, Certainty::Maybe { cause, opaque_types_jank }, ) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs index e5658ba32ff6..edf2a5d1ba8d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs @@ -47,7 +47,7 @@ where let max_input_universe = outer.max_input_universe; let mut nested = EvalCtxt { delegate, - variables: outer.variables, + var_kinds: outer.var_kinds, var_values: outer.var_values, current_goal_kind: outer.current_goal_kind, max_input_universe, diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index ea45d5096990..73044b7943ae 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -143,7 +143,7 @@ fn response_no_constraints( Ok(response_no_constraints_raw( cx, input.canonical.max_universe, - input.canonical.variables, + input.canonical.var_kinds, certainty, )) } diff --git a/compiler/rustc_parse/Cargo.toml b/compiler/rustc_parse/Cargo.toml index 04a51c905661..f0c84e07a56f 100644 --- a/compiler/rustc_parse/Cargo.toml +++ b/compiler/rustc_parse/Cargo.toml @@ -6,7 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index d178fcda1fb9..10b41a39c3bf 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" rustc_lexer = { path = "../rustc_lexer" } # tidy-alphabetical-end diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 86326fc6536c..143fd0c4436c 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -184,6 +184,9 @@ pub enum Suggestion { /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` ReorderFormatParameter(Range, String), + /// Add missing colon: + /// `format!("{foo?}")` -> `format!("{foo:?}")` + AddMissingColon(Range), } /// The parser structure for interpreting the input format string. This is @@ -453,10 +456,11 @@ impl<'input> Parser<'input> { suggestion: Suggestion::None, }); - if let Some((_, _, c)) = self.peek() { - match c { - '?' => self.suggest_format_debug(), - '<' | '^' | '>' => self.suggest_format_align(c), + if let (Some((_, _, c)), Some((_, _, nc))) = (self.peek(), self.peek_ahead()) { + match (c, nc) { + ('?', '}') => self.missing_colon_before_debug_formatter(), + ('?', _) => self.suggest_format_debug(), + ('<' | '^' | '>', _) => self.suggest_format_align(c), _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } @@ -849,6 +853,23 @@ impl<'input> Parser<'input> { } } + fn missing_colon_before_debug_formatter(&mut self) { + if let Some((range, _)) = self.consume_pos('?') { + let span = range.clone(); + self.errors.insert( + 0, + ParseError { + description: "expected `}`, found `?`".to_owned(), + note: Some(format!("to print `{{`, you can escape it using `{{{{`",)), + label: "expected `:` before `?` to format with `Debug`".to_owned(), + span: range, + secondary_label: None, + suggestion: Suggestion::AddMissingColon(span), + }, + ); + } + } + fn suggest_format_align(&mut self, alignment: char) { if let Some((range, _)) = self.consume_pos(alignment) { self.errors.insert( diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 94996c0adb47..ba44aa3a35ab 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -244,10 +244,6 @@ passes_function_not_have_default_implementation = function doesn't have a defaul passes_functions_names_duplicated = functions names are duplicated .note = all `#[rustc_must_implement_one_of]` arguments must be unique -passes_has_incoherent_inherent_impl = - `rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits - .label = only adts, extern types and traits are supported - passes_ignored_derived_impls = `{$name}` has {$trait_list_len -> [one] a derived impl diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index f1cbb72554d2..360feacac0e9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -21,8 +21,8 @@ use rustc_feature::{ BuiltinAttribute, }; use rustc_hir::attrs::{ - AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, InlineAttr, MirDialect, MirPhase, - ReprAttr, SanitizerSet, + AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, + MirDialect, MirPhase, ReprAttr, SanitizerSet, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; @@ -306,6 +306,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::CfgAttrTrace | AttributeKind::ThreadLocal | AttributeKind::CfiEncoding { .. } + | AttributeKind::RustcHasIncoherentInherentImpls ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -325,9 +326,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), - [sym::rustc_has_incoherent_inherent_impls, ..] => { - self.check_has_incoherent_inherent_impls(attr, span, target) - } [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -506,7 +504,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_eii_impl(&self, impls: &[EiiImpl], target: Target) { - for EiiImpl { span, inner_span, eii_macro, impl_marked_unsafe, is_default: _ } in impls { + for EiiImpl { span, inner_span, resolution, impl_marked_unsafe, is_default: _ } in impls { match target { Target::Fn => {} _ => { @@ -514,7 +512,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) + if let EiiImplResolution::Macro(eii_macro) = resolution + && find_attr!(self.tcx.get_all_attrs(*eii_macro), AttributeKind::EiiExternTarget(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) && !impl_marked_unsafe { self.dcx().emit_err(errors::EiiImplRequiresUnsafe { @@ -758,9 +757,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(impls) = find_attr!(attrs, AttributeKind::EiiImpls(impls) => impls) { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); for i in impls { + let name = match i.resolution { + EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), + EiiImplResolution::Known(decl) => decl.name.name, + EiiImplResolution::Error(_eg) => continue, + }; self.dcx().emit_err(errors::EiiWithTrackCaller { attr_span, - name: self.tcx.item_name(i.eii_macro), + name, sig_span: sig.span, }); } @@ -1158,17 +1162,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_has_incoherent_inherent_impls(&self, attr: &Attribute, span: Span, target: Target) { - match target { - Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {} - _ => { - self.tcx - .dcx() - .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span(), span }); - } - } - } - fn check_ffi_pure(&self, attr_span: Span, attrs: &[Attribute]) { if find_attr!(attrs, AttributeKind::FfiConst(_)) { // `#[ffi_const]` functions cannot be `#[ffi_pure]` diff --git a/compiler/rustc_passes/src/eii.rs b/compiler/rustc_passes/src/eii.rs index ab3f9f0d2182..f3e84665f21d 100644 --- a/compiler/rustc_passes/src/eii.rs +++ b/compiler/rustc_passes/src/eii.rs @@ -81,7 +81,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): } // now we have all eiis! For each of them, choose one we want to actually generate. - for (decl_did, FoundEii { decl, decl_crate, impls }) in eiis { + for (foreign_item, FoundEii { decl, decl_crate, impls }) in eiis { let mut default_impls = Vec::new(); let mut explicit_impls = Vec::new(); @@ -97,7 +97,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): // is instantly an error. if explicit_impls.len() > 1 { tcx.dcx().emit_err(DuplicateEiiImpls { - name: tcx.item_name(decl_did), + name: decl.name.name, first_span: tcx.def_span(explicit_impls[0].0), first_crate: tcx.crate_name(explicit_impls[0].1), second_span: tcx.def_span(explicit_impls[1].0), @@ -116,7 +116,7 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): } if default_impls.len() > 1 { - let decl_span = tcx.def_ident_span(decl_did).unwrap(); + let decl_span = tcx.def_ident_span(foreign_item).unwrap(); tcx.dcx().span_delayed_bug(decl_span, "multiple not supported right now"); } @@ -139,8 +139,9 @@ pub(crate) fn check_externally_implementable_items<'tcx>(tcx: TyCtxt<'tcx>, (): tcx.dcx().emit_err(EiiWithoutImpl { current_crate_name: tcx.crate_name(LOCAL_CRATE), decl_crate_name: tcx.crate_name(decl_crate), - name: tcx.item_name(decl_did), - span: decl.span, + // FIXME: shouldn't call `item_name` + name: decl.name.name, + span: decl.name.span, help: (), }); diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index af5cb29b83d0..14555765e423 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -187,15 +187,6 @@ pub(crate) struct DocAttrNotCrateLevel<'a> { pub attr_name: &'a str, } -#[derive(Diagnostic)] -#[diag(passes_has_incoherent_inherent_impl)] -pub(crate) struct HasIncoherentInherentImpl { - #[primary_span] - pub attr_span: Span, - #[label] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(passes_both_ffi_const_and_pure, code = E0757)] pub(crate) struct BothFfiConstAndPure { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d66c303b1726..721635ed48ff 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -462,8 +462,6 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { let arity; let fields: Vec<_>; match &pat.kind { - PatKind::AscribeUserType { subpattern, .. } - | PatKind::ExpandedConstant { subpattern, .. } => return self.lower_pat(subpattern), PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; diff --git a/compiler/rustc_proc_macro/Cargo.toml b/compiler/rustc_proc_macro/Cargo.toml index beb95aa3b52f..54c765075113 100644 --- a/compiler/rustc_proc_macro/Cargo.toml +++ b/compiler/rustc_proc_macro/Cargo.toml @@ -16,7 +16,7 @@ doctest = false [dependencies] # tidy-alphabetical-start -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" # tidy-alphabetical-end [features] diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index be838f689e53..840f0b35266d 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -5,9 +5,7 @@ use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{ - BytePos, CachingSourceMapView, DUMMY_SP, Span, SpanData, StableSourceFileId, Symbol, -}; +use rustc_span::{BytePos, CachingSourceMapView, DUMMY_SP, SourceFile, Span, SpanData, Symbol}; use crate::ich; @@ -118,7 +116,7 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> { + ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)> { self.source_map().span_data_to_lines_and_cols(span) } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d56ca7c079cb..752d8c46beb4 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -49,13 +49,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn plant_decl_into_local_module( &mut self, parent: Module<'ra>, - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, decl: Decl<'ra>, ) { if let Err(old_decl) = self.try_plant_decl_into_local_module(parent, ident, ns, decl, false) { - self.report_conflict(parent, ident, ns, old_decl, decl); + self.report_conflict(parent, ident.0, ns, old_decl, decl); } } @@ -71,6 +71,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { expn_id: LocalExpnId, ) { let decl = self.arenas.new_def_decl(res, vis.to_def_id(), span, expn_id); + let ident = Macros20NormalizedIdent::new(ident); self.plant_decl_into_local_module(parent, ident, ns, decl); } @@ -78,7 +79,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn define_extern( &self, parent: Module<'ra>, - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, child_index: usize, res: Res, @@ -89,10 +90,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { let decl = self.arenas.alloc_decl(DeclData { kind: DeclKind::Def(res), - ambiguity, + ambiguity: CmCell::new(ambiguity), // External ambiguities always report the `AMBIGUOUS_GLOB_IMPORTS` lint at the moment. - warn_ambiguity: true, - vis, + warn_ambiguity: CmCell::new(true), + vis: CmCell::new(vis), span, expansion, }); @@ -280,6 +281,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) }; let ModChild { ident, res, vis, ref reexport_chain } = *child; + let ident = Macros20NormalizedIdent::new(ident); let span = child_span(self, reexport_chain, res); let res = res.expect_non_local(); let expansion = parent_scope.expansion; @@ -532,7 +534,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if target.name != kw::Underscore { self.r.per_ns(|this, ns| { if !type_ns_only || ns == TypeNS { - let key = BindingKey::new(target, ns); + let key = BindingKey::new(Macros20NormalizedIdent::new(target), ns); this.resolution_or_default(current_module, key) .borrow_mut(this) .single_imports @@ -1023,11 +1025,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } self.r.potentially_unused_imports.push(import); let import_decl = self.r.new_import_decl(decl, import); + let ident = Macros20NormalizedIdent::new(ident); if ident.name != kw::Underscore && parent == self.r.graph_root { - let norm_ident = Macros20NormalizedIdent::new(ident); // FIXME: this error is technically unnecessary now when extern prelude is split into // two scopes, remove it with lang team approval. - if let Some(entry) = self.r.extern_prelude.get(&norm_ident) + if let Some(entry) = self.r.extern_prelude.get(&ident) && expansion != LocalExpnId::ROOT && orig_name.is_some() && entry.item_decl.is_none() @@ -1038,7 +1040,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } use indexmap::map::Entry; - match self.r.extern_prelude.entry(norm_ident) { + match self.r.extern_prelude.entry(ident) { Entry::Occupied(mut occupied) => { let entry = occupied.get_mut(); if entry.item_decl.is_some() { @@ -1158,18 +1160,18 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.potentially_unused_imports.push(import); module.for_each_child_mut(self, |this, ident, ns, binding| { if ns == MacroNS { - let import = if this.r.is_accessible_from(binding.vis, this.parent_scope.module) - { - import - } else { - // FIXME: This branch is used for reporting the `private_macro_use` lint - // and should eventually be removed. - if this.r.macro_use_prelude.contains_key(&ident.name) { - // Do not override already existing entries with compatibility entries. - return; - } - macro_use_import(this, span, true) - }; + let import = + if this.r.is_accessible_from(binding.vis(), this.parent_scope.module) { + import + } else { + // FIXME: This branch is used for reporting the `private_macro_use` lint + // and should eventually be removed. + if this.r.macro_use_prelude.contains_key(&ident.name) { + // Do not override already existing entries with compatibility entries. + return; + } + macro_use_import(this, span, true) + }; let import_decl = this.r.new_import_decl(binding, import); this.add_macro_use_decl(ident.name, import_decl, span, allow_shadowing); } @@ -1290,8 +1292,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); if macro_rules { - let ident = ident.normalize_to_macros_2_0(); - self.r.macro_names.insert(ident); + let ident = Macros20NormalizedIdent::new(ident); + self.r.macro_names.insert(ident.0); let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { Visibility::Public @@ -1320,8 +1322,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let import_decl = self.r.new_import_decl(decl, import); self.r.plant_decl_into_local_module(self.r.graph_root, ident, MacroNS, import_decl); } else { - self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id); + self.r.check_reserved_macro_name(ident.0, res); + self.insert_unused_macro(ident.0, def_id, item.id); } self.r.feed_visibility(feed, vis); let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Def( @@ -1488,7 +1490,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { { // Don't add underscore names, they cannot be looked up anyway. let impl_def_id = self.r.tcx.local_parent(local_def_id); - let key = BindingKey::new(ident, ns); + let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 7c86ed91a07a..9781a77871f6 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1187,7 +1187,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .get(&expn_id) .into_iter() .flatten() - .map(|(ident, _)| TypoSuggestion::typo_from_ident(*ident, res)), + .map(|(ident, _)| TypoSuggestion::typo_from_ident(ident.0, res)), ); } } @@ -1199,7 +1199,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let res = macro_rules_def.decl.res(); if filter_fn(res) { suggestions - .push(TypoSuggestion::typo_from_ident(macro_rules_def.ident, res)) + .push(TypoSuggestion::typo_from_ident(macro_rules_def.ident.0, res)) } } } @@ -1338,7 +1338,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let child_accessible = - accessible && this.is_accessible_from(name_binding.vis, parent_scope.module); + accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); // do not venture inside inaccessible items of other crates if in_module_is_extern && !child_accessible { @@ -1358,8 +1358,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // #90113: Do not count an inaccessible reexported item as a candidate. if let DeclKind::Import { source_decl, .. } = name_binding.kind - && this.is_accessible_from(source_decl.vis, parent_scope.module) - && !this.is_accessible_from(name_binding.vis, parent_scope.module) + && this.is_accessible_from(source_decl.vis(), parent_scope.module) + && !this.is_accessible_from(name_binding.vis(), parent_scope.module) { return; } @@ -2243,7 +2243,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let first = binding == first_binding; let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis.is_public() { + if !first && binding.vis().is_public() { let desc = match binding.kind { DeclKind::Import { .. } => "re-export", _ => "directly", @@ -2892,7 +2892,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return None; } - let binding_key = BindingKey::new(ident, MacroNS); + let binding_key = BindingKey::new(Macros20NormalizedIdent::new(ident), MacroNS); let binding = self.resolution(crate_module, binding_key)?.binding()?; let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { return None; diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index e5144332f2b7..ebdb0060e1b9 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -100,7 +100,9 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { if let Some(node_id) = import.id() { r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx) } - } else if decl.ambiguity.is_some() && eff_vis.is_public_at_level(Level::Reexported) { + } else if decl.ambiguity.get().is_some() + && eff_vis.is_public_at_level(Level::Reexported) + { exported_ambiguities.insert(*decl); } } @@ -125,9 +127,10 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { // If the binding is ambiguous, put the root ambiguity binding and all reexports // leading to it into the table. They are used by the `ambiguous_glob_reexports` // lint. For all bindings added to the table this way `is_ambiguity` returns true. - let is_ambiguity = |decl: Decl<'ra>, warn: bool| decl.ambiguity.is_some() && !warn; + let is_ambiguity = + |decl: Decl<'ra>, warn: bool| decl.ambiguity.get().is_some() && !warn; let mut parent_id = ParentId::Def(module_id); - let mut warn_ambiguity = decl.warn_ambiguity; + let mut warn_ambiguity = decl.warn_ambiguity.get(); while let DeclKind::Import { source_decl, .. } = decl.kind { self.update_import(decl, parent_id); @@ -140,12 +143,12 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { parent_id = ParentId::Import(decl); decl = source_decl; - warn_ambiguity |= source_decl.warn_ambiguity; + warn_ambiguity |= source_decl.warn_ambiguity.get(); } if !is_ambiguity(decl, warn_ambiguity) && let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) { - self.update_def(def_id, decl.vis.expect_local(), parent_id); + self.update_def(def_id, decl.vis().expect_local(), parent_id); } } } @@ -188,7 +191,7 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { } fn update_import(&mut self, decl: Decl<'ra>, parent_id: ParentId<'ra>) { - let nominal_vis = decl.vis.expect_local(); + let nominal_vis = decl.vis().expect_local(); let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return }; let inherited_eff_vis = self.effective_vis_or_private(parent_id); let tcx = self.r.tcx; diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index e0acf043ffcf..91fe6741950b 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -9,7 +9,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Macros20NormalizedIdent, Span, kw, sym}; +use smallvec::SmallVec; use tracing::{debug, instrument}; use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst}; @@ -50,7 +51,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { mut self: CmResolver<'r, 'ra, 'tcx>, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, - ctxt: SyntaxContext, + orig_ctxt: SyntaxContext, derive_fallback_lint_id: Option, mut visitor: impl FnMut( &mut CmResolver<'r, 'ra, 'tcx>, @@ -100,7 +101,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // 4c. Standard library prelude (de-facto closed, controlled). // 6. Language prelude: builtin attributes (closed, controlled). - let rust_2015 = ctxt.edition().is_rust_2015(); let (ns, macro_kind) = match scope_set { ScopeSet::All(ns) | ScopeSet::Module(ns, _) @@ -123,7 +123,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ctxt = ctxt.normalize_to_macros_2_0(); + let mut ctxt = orig_ctxt.normalize_to_macros_2_0(); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -148,7 +148,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { true } Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, - Scope::MacroUsePrelude => use_prelude || rust_2015, + Scope::MacroUsePrelude => use_prelude || orig_ctxt.edition().is_rust_2015(), Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { use_prelude || module_and_extern_prelude || extern_prelude @@ -397,7 +397,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { assert!(force || finalize.is_none()); // `finalize` implies `force` // Make sure `self`, `super` etc produce an error when passed to here. - if orig_ident.is_path_segment_keyword() && !matches!(scope_set, ScopeSet::Module(..)) { + if !matches!(scope_set, ScopeSet::Module(..)) && orig_ident.is_path_segment_keyword() { return Err(Determinacy::Determined); } @@ -424,7 +424,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // } // So we have to save the innermost solution and continue searching in outer scopes // to detect potential ambiguities. - let mut innermost_results: Vec<(Decl<'_>, Scope<'_>)> = Vec::new(); + let mut innermost_results: SmallVec<[(Decl<'_>, Scope<'_>); 2]> = SmallVec::new(); let mut determinacy = Determinacy::Determined; // Go through all the scopes and try to resolve the name. @@ -434,12 +434,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { orig_ident.span.ctxt(), derive_fallback_lint_id, |this, scope, use_prelude, ctxt| { + let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); + // The passed `ctxt` is already normalized, so avoid expensive double normalization. + let ident = Macros20NormalizedIdent(ident); let res = match this.reborrow().resolve_ident_in_scope( - orig_ident, + ident, ns, scope, use_prelude, - ctxt, scope_set, parent_scope, // Shadowed decls don't need to be marked as used or non-speculatively loaded. @@ -508,11 +510,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_ident_in_scope<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, - orig_ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, scope: Scope<'ra>, use_prelude: UsePrelude, - ctxt: SyntaxContext, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, finalize: Option, @@ -520,7 +521,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_decl: Option>, ignore_import: Option>, ) -> Result, ControlFlow> { - let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt)); let ret = match scope { Scope::DeriveHelpers(expn_id) => { if let Some(decl) = self @@ -599,11 +599,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.get_mut().lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, - orig_ident.span, + ident.span, errors::ProcMacroDeriveResolutionFallback { - span: orig_ident.span, + span: ident.span, ns_descr: ns.descr(), - ident, + ident: ident.0, }, ); } @@ -649,11 +649,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.get_mut().lint_buffer.buffer_lint( PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, - orig_ident.span, + ident.span, errors::ProcMacroDeriveResolutionFallback { - span: orig_ident.span, + span: ident.span, ns_descr: ns.descr(), - ident, + ident: ident.0, }, ); } @@ -699,7 +699,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut result = Err(Determinacy::Determined); if let Some(prelude) = self.prelude && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set( - ident, + ident.0, ScopeSet::Module(ns, prelude), parent_scope, None, @@ -983,7 +983,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_ident_in_module_non_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, module: Module<'ra>, - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, parent_scope: &ParentScope<'ra>, shadowing: Shadowing, @@ -1006,7 +1006,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { return self.get_mut().finalize_module_binding( - ident, + ident.0, binding, parent_scope, module, @@ -1017,7 +1017,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Items and single imports are not shadowable, if we have one, then it's determined. if let Some(binding) = binding { - let accessible = self.is_accessible_from(binding.vis, parent_scope.module); + let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); return if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) }; } @@ -1046,7 +1046,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn resolve_ident_in_module_globs_unadjusted<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, module: Module<'ra>, - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, parent_scope: &ParentScope<'ra>, shadowing: Shadowing, @@ -1067,7 +1067,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(finalize) = finalize { return self.get_mut().finalize_module_binding( - ident, + ident.0, binding, parent_scope, module, @@ -1103,7 +1103,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // shadowing is enabled, see `macro_expanded_macro_export_errors`). if let Some(binding) = binding { return if binding.determined() || ns == MacroNS || shadowing == Shadowing::Restricted { - let accessible = self.is_accessible_from(binding.vis, parent_scope.module); + let accessible = self.is_accessible_from(binding.vis(), parent_scope.module); if accessible { Ok(binding) } else { Err(ControlFlow::Break(Determined)) } } else { Err(ControlFlow::Break(Undetermined)) @@ -1136,9 +1136,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => return Err(ControlFlow::Continue(Undetermined)), }; let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ident) = - (parent_scope, ident.normalize_to_macros_2_0()); - match ident.span.glob_adjust(module.expansion, glob_import.span) { + let (mut adjusted_parent_scope, mut ident) = (parent_scope, ident); + match ident.0.span.glob_adjust(module.expansion, glob_import.span) { Some(Some(def)) => { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; @@ -1148,7 +1147,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => continue, }; let result = self.reborrow().resolve_ident_in_scope_set( - ident, + ident.0, ScopeSet::Module(ns, module), adjusted_parent_scope, None, @@ -1160,7 +1159,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match result { Err(Determined) => continue, Ok(binding) - if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) => + if !self.is_accessible_from(binding.vis(), glob_import.parent_scope.module) => { continue; } @@ -1187,7 +1186,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return Err(ControlFlow::Continue(Determined)); }; - if !self.is_accessible_from(binding.vis, parent_scope.module) { + if !self.is_accessible_from(binding.vis(), parent_scope.module) { if report_private { self.privacy_errors.push(PrivacyError { ident, @@ -1304,7 +1303,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { Err(Determined) => continue, Ok(binding) - if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) => + if !self + .is_accessible_from(binding.vis(), single_import.parent_scope.module) => { continue; } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index a8bb53cc7f27..b98061ea8130 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -20,7 +20,7 @@ use rustc_session::lint::builtin::{ use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::LocalExpnId; -use rustc_span::{Ident, Span, Symbol, kw, sym}; +use rustc_span::{Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; use tracing::debug; use crate::Namespace::{self, *}; @@ -295,17 +295,35 @@ fn pub_use_of_private_extern_crate_hack(import: Import<'_>, decl: Decl<'_>) -> O } } +/// Removes identical import layers from two declarations. +fn remove_same_import<'ra>(d1: Decl<'ra>, d2: Decl<'ra>) -> (Decl<'ra>, Decl<'ra>) { + if let DeclKind::Import { import: import1, source_decl: d1_next } = d1.kind + && let DeclKind::Import { import: import2, source_decl: d2_next } = d2.kind + && import1 == import2 + && d1.warn_ambiguity.get() == d2.warn_ambiguity.get() + { + assert_eq!(d1.ambiguity.get(), d2.ambiguity.get()); + assert!(!d1.warn_ambiguity.get()); + assert_eq!(d1.expansion, d2.expansion); + assert_eq!(d1.span, d2.span); + assert_eq!(d1.vis(), d2.vis()); + remove_same_import(d1_next, d2_next) + } else { + (d1, d2) + } +} + impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// Given an import and the declaration that it points to, /// create the corresponding import declaration. pub(crate) fn new_import_decl(&self, decl: Decl<'ra>, import: Import<'ra>) -> Decl<'ra> { let import_vis = import.vis.to_def_id(); - let vis = if decl.vis.is_at_least(import_vis, self.tcx) + let vis = if decl.vis().is_at_least(import_vis, self.tcx) || pub_use_of_private_extern_crate_hack(import, decl).is_some() { import_vis } else { - decl.vis + decl.vis() }; if let ImportKind::Glob { ref max_vis, .. } = import.kind @@ -317,26 +335,88 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.arenas.alloc_decl(DeclData { kind: DeclKind::Import { source_decl: decl, import }, - ambiguity: None, - warn_ambiguity: false, + ambiguity: CmCell::new(None), + warn_ambiguity: CmCell::new(false), span: import.span, - vis, + vis: CmCell::new(vis), expansion: import.parent_scope.expansion, }) } + /// If `glob_decl` attempts to overwrite `old_glob_decl` in a module, + /// decide which one to keep. + fn select_glob_decl( + &self, + glob_decl: Decl<'ra>, + old_glob_decl: Decl<'ra>, + warn_ambiguity: bool, + ) -> Decl<'ra> { + assert!(glob_decl.is_glob_import()); + assert!(old_glob_decl.is_glob_import()); + assert_ne!(glob_decl, old_glob_decl); + // `best_decl` with a given key in a module may be overwritten in a + // number of cases (all of them can be seen below in the `match` in `try_define_local`), + // all these overwrites will be re-fetched by glob imports importing + // from that module without generating new ambiguities. + // - A glob decl is overwritten by a non-glob decl arriving later. + // - A glob decl is overwritten by its clone after setting ambiguity in it. + // FIXME: avoid this by removing `warn_ambiguity`, or by triggering glob re-fetch + // with the same decl in some way. + // - A glob decl is overwritten by a glob decl re-fetching an + // overwritten decl from other module (the recursive case). + // Here we are detecting all such re-fetches and overwrite old decls + // with the re-fetched decls. + // This is probably incorrect in corner cases, and the outdated decls still get + // propagated to other places and get stuck there, but that's what we have at the moment. + let (deep_decl, old_deep_decl) = remove_same_import(glob_decl, old_glob_decl); + if deep_decl != glob_decl { + // Some import layers have been removed, need to overwrite. + assert_ne!(old_deep_decl, old_glob_decl); + // FIXME: reenable the asserts when `warn_ambiguity` is removed (#149195). + // assert_ne!(old_deep_decl, deep_decl); + // assert!(old_deep_decl.is_glob_import()); + assert!(!deep_decl.is_glob_import()); + if glob_decl.is_ambiguity_recursive() { + glob_decl.warn_ambiguity.set_unchecked(true); + } + glob_decl + } else if glob_decl.res() != old_glob_decl.res() { + old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); + old_glob_decl.warn_ambiguity.set_unchecked(warn_ambiguity); + if warn_ambiguity { + old_glob_decl + } else { + // Need a fresh decl so other glob imports importing it could re-fetch it + // and set their own `warn_ambiguity` to true. + // FIXME: remove this when `warn_ambiguity` is removed (#149195). + self.arenas.alloc_decl((*old_glob_decl).clone()) + } + } else if !old_glob_decl.vis().is_at_least(glob_decl.vis(), self.tcx) { + // We are glob-importing the same item but with greater visibility. + old_glob_decl.vis.set_unchecked(glob_decl.vis()); + old_glob_decl + } else if glob_decl.is_ambiguity_recursive() && !old_glob_decl.is_ambiguity_recursive() { + // Overwriting a non-ambiguous glob import with an ambiguous glob import. + old_glob_decl.ambiguity.set_unchecked(Some(glob_decl)); + old_glob_decl.warn_ambiguity.set_unchecked(true); + old_glob_decl + } else { + old_glob_decl + } + } + /// Attempt to put the declaration with the given name and namespace into the module, /// and return existing declaration if there is a collision. pub(crate) fn try_plant_decl_into_local_module( &mut self, module: Module<'ra>, - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, decl: Decl<'ra>, warn_ambiguity: bool, ) -> Result<(), Decl<'ra>> { let res = decl.res(); - self.check_reserved_macro_name(ident, res); + self.check_reserved_macro_name(ident.0, res); self.set_decl_parent_module(decl, module); // Even if underscore names cannot be looked up, we still need to add them to modules, // because they can be fetched by glob imports from those modules, and bring traits @@ -347,52 +427,26 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); self.update_local_resolution(module, key, warn_ambiguity, |this, resolution| { if let Some(old_decl) = resolution.best_decl() { + assert_ne!(decl, old_decl); + assert!(!decl.warn_ambiguity.get()); if res == Res::Err && old_decl.res() != Res::Err { // Do not override real declarations with `Res::Err`s from error recovery. return Ok(()); } match (old_decl.is_glob_import(), decl.is_glob_import()) { (true, true) => { - let (glob_decl, old_glob_decl) = (decl, old_decl); - // FIXME: remove `!decl.is_ambiguity_recursive()` after delete the warning ambiguity. - if !decl.is_ambiguity_recursive() - && let DeclKind::Import { import: old_import, .. } = old_glob_decl.kind - && let DeclKind::Import { import, .. } = glob_decl.kind - && old_import == import - { - // When imported from the same glob-import statement, we should replace - // `old_glob_decl` with `glob_decl`, regardless of whether - // they have the same resolution or not. - resolution.glob_decl = Some(glob_decl); - } else if res != old_glob_decl.res() { - resolution.glob_decl = Some(this.new_decl_with_ambiguity( - old_glob_decl, - glob_decl, - warn_ambiguity, - )); - } else if !old_decl.vis.is_at_least(decl.vis, this.tcx) { - // We are glob-importing the same item but with greater visibility. - resolution.glob_decl = Some(glob_decl); - } else if decl.is_ambiguity_recursive() { - resolution.glob_decl = - Some(this.new_decl_with_warn_ambiguity(glob_decl)); - } + resolution.glob_decl = + Some(this.select_glob_decl(decl, old_decl, warn_ambiguity)); } (old_glob @ true, false) | (old_glob @ false, true) => { let (glob_decl, non_glob_decl) = if old_glob { (old_decl, decl) } else { (decl, old_decl) }; resolution.non_glob_decl = Some(non_glob_decl); - if let Some(old_glob_decl) = resolution.glob_decl { - assert!(old_glob_decl.is_glob_import()); - if glob_decl.res() != old_glob_decl.res() { - resolution.glob_decl = Some(this.new_decl_with_ambiguity( - old_glob_decl, - glob_decl, - false, - )); - } else if !old_glob_decl.vis.is_at_least(decl.vis, this.tcx) { - resolution.glob_decl = Some(glob_decl); - } + if let Some(old_glob_decl) = resolution.glob_decl + && old_glob_decl != glob_decl + { + resolution.glob_decl = + Some(this.select_glob_decl(glob_decl, old_glob_decl, false)); } else { resolution.glob_decl = Some(glob_decl); } @@ -413,22 +467,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) } - fn new_decl_with_ambiguity( - &self, - primary_decl: Decl<'ra>, - secondary_decl: Decl<'ra>, - warn_ambiguity: bool, - ) -> Decl<'ra> { - let ambiguity = Some(secondary_decl); - let data = DeclData { ambiguity, warn_ambiguity, ..*primary_decl }; - self.arenas.alloc_decl(data) - } - - fn new_decl_with_warn_ambiguity(&self, decl: Decl<'ra>) -> Decl<'ra> { - assert!(decl.is_ambiguity_recursive()); - self.arenas.alloc_decl(DeclData { warn_ambiguity: true, ..*decl }) - } - // Use `f` to mutate the resolution of the name in the module. // If the resolution becomes a success, define it in the module's glob importers. fn update_local_resolution( @@ -470,11 +508,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => import.parent_scope.module, None => continue, }; - if self.is_accessible_from(binding.vis, scope) { + if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, *import); let _ = self.try_plant_decl_into_local_module( import.parent_scope.module, - ident.0, + ident, key.ns, import_decl, warn_ambiguity, @@ -496,11 +534,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let dummy_decl = self.new_import_decl(dummy_decl, import); self.per_ns(|this, ns| { let module = import.parent_scope.module; - let _ = - this.try_plant_decl_into_local_module(module, target, ns, dummy_decl, false); + let ident = Macros20NormalizedIdent::new(target); + let _ = this.try_plant_decl_into_local_module(module, ident, ns, dummy_decl, false); // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { - let key = BindingKey::new(target, ns); + let key = BindingKey::new(ident, ns); this.update_local_resolution(module, key, false, |_, resolution| { resolution.single_imports.swap_remove(&import); }) @@ -634,7 +672,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Some(binding) = resolution.best_decl() else { continue }; if let DeclKind::Import { import, .. } = binding.kind - && let Some(amb_binding) = binding.ambiguity + && let Some(amb_binding) = binding.ambiguity.get() && binding.res() != Res::Err && exported_ambiguities.contains(&binding) { @@ -660,8 +698,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && let Some(glob_import_id) = glob_import.id() && let glob_import_def_id = self.local_def_id(glob_import_id) && self.effective_visibilities.is_exported(glob_import_def_id) - && glob_decl.vis.is_public() - && !binding.vis.is_public() + && glob_decl.vis().is_public() + && !binding.vis().is_public() { let binding_id = match binding.kind { DeclKind::Def(res) => { @@ -879,7 +917,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let import_decl = this.new_import_decl(binding, import); this.get_mut_unchecked().plant_decl_into_local_module( parent, - target, + Macros20NormalizedIdent::new(target), ns, import_decl, ); @@ -888,7 +926,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Err(Determinacy::Determined) => { // Don't remove underscores from `single_imports`, they were never added. if target.name != kw::Underscore { - let key = BindingKey::new(target, ns); + let key = BindingKey::new(Macros20NormalizedIdent::new(target), ns); this.get_mut_unchecked().update_local_resolution( parent, key, @@ -1291,9 +1329,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return; }; - if !binding.vis.is_at_least(import.vis, this.tcx) { + if !binding.vis().is_at_least(import.vis, this.tcx) { reexport_error = Some((ns, binding)); - if let Visibility::Restricted(binding_def_id) = binding.vis + if let Visibility::Restricted(binding_def_id) = binding.vis() && binding_def_id.is_top_level_module() { crate_private_reexport = true; @@ -1496,7 +1534,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => import.parent_scope.module, None => continue, }; - if self.is_accessible_from(binding.vis, scope) { + if self.is_accessible_from(binding.vis(), scope) { let import_decl = self.new_import_decl(binding, import); let warn_ambiguity = self .resolution(import.parent_scope.module, key) @@ -1504,7 +1542,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .is_some_and(|binding| binding.warn_ambiguity_recursive()); let _ = self.try_plant_decl_into_local_module( import.parent_scope.module, - key.ident.0, + key.ident, key.ns, import_decl, warn_ambiguity, @@ -1538,7 +1576,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let child = |reexport_chain| ModChild { ident: ident.0, res, - vis: binding.vis, + vis: binding.vis(), reexport_chain, }; if let Some((ambig_binding1, ambig_binding2)) = binding.descent_to_ambiguity() { @@ -1546,7 +1584,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let second = ModChild { ident: ident.0, res: ambig_binding2.res().expect_non_local(), - vis: ambig_binding2.vis, + vis: ambig_binding2.vis(), reexport_chain: ambig_binding2.reexport_chain(this), }; ambig_children.push(AmbigModChild { main, second }) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index b4941a6f5b99..4d2ba73c9cab 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -37,7 +37,9 @@ use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym}; +use rustc_span::{ + BytePos, DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, SyntaxContext, kw, sym, +}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; use tracing::{debug, instrument, trace}; @@ -1069,8 +1071,20 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc debug!("(resolving function) entering function"); if let FnKind::Fn(_, _, f) = fn_kind { - for EiiImpl { node_id, eii_macro_path, .. } in &f.eii_impls { - self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + for EiiImpl { node_id, eii_macro_path, known_eii_macro_resolution, .. } in &f.eii_impls + { + // See docs on the `known_eii_macro_resolution` field: + // if we already know the resolution statically, don't bother resolving it. + if let Some(target) = known_eii_macro_resolution { + self.smart_resolve_path( + *node_id, + &None, + &target.extern_item_path, + PathSource::Expr(None), + ); + } else { + self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); + } } } @@ -2917,7 +2931,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id]; } - if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _, span: _ }) = + if let Some(EiiExternTarget { extern_item_path, impl_unsafe: _ }) = ¯o_def.eii_extern_target { self.smart_resolve_path( @@ -3629,7 +3643,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { return; }; ident.span.normalize_to_macros_2_0_and_adjust(module.expansion); - let key = BindingKey::new(ident, ns); + let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); let mut decl = self.r.resolution(module, key).and_then(|r| r.best_decl()); debug!(?decl); if decl.is_none() { @@ -3640,7 +3654,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { TypeNS => ValueNS, _ => ns, }; - let key = BindingKey::new(ident, ns); + let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns); decl = self.r.resolution(module, key).and_then(|r| r.best_decl()); debug!(?decl); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 73e1a8f0c3bc..a2805497e796 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2793,7 +2793,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { in_module.for_each_child(self.r, |r, ident, _, name_binding| { // abort if the module is already found or if name_binding is private external - if result.is_some() || !name_binding.vis.is_visible_locally() { + if result.is_some() || !name_binding.vis().is_visible_locally() { return; } if let Some(module_def_id) = name_binding.res().module_like_def_id() { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 063b6b4058f0..d82fad3861d5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -572,17 +572,17 @@ struct BindingKey { } impl BindingKey { - fn new(ident: Ident, ns: Namespace) -> Self { - BindingKey { ident: Macros20NormalizedIdent::new(ident), ns, disambiguator: 0 } + fn new(ident: Macros20NormalizedIdent, ns: Namespace) -> Self { + BindingKey { ident, ns, disambiguator: 0 } } fn new_disambiguated( - ident: Ident, + ident: Macros20NormalizedIdent, ns: Namespace, disambiguator: impl FnOnce() -> u32, ) -> BindingKey { let disambiguator = if ident.name == kw::Underscore { disambiguator() } else { 0 }; - BindingKey { ident: Macros20NormalizedIdent::new(ident), ns, disambiguator } + BindingKey { ident, ns, disambiguator } } } @@ -802,16 +802,16 @@ impl<'ra> fmt::Debug for Module<'ra> { } /// Data associated with any name declaration. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] struct DeclData<'ra> { kind: DeclKind<'ra>, - ambiguity: Option>, + ambiguity: CmCell>>, /// Produce a warning instead of an error when reporting ambiguities inside this binding. /// May apply to indirect ambiguities under imports, so `ambiguity.is_some()` is not required. - warn_ambiguity: bool, + warn_ambiguity: CmCell, expansion: LocalExpnId, span: Span, - vis: Visibility, + vis: CmCell>, } /// All name declarations are unique and allocated on a same arena, @@ -923,6 +923,10 @@ struct AmbiguityError<'ra> { } impl<'ra> DeclData<'ra> { + fn vis(&self) -> Visibility { + self.vis.get() + } + fn res(&self) -> Res { match self.kind { DeclKind::Def(res) => res, @@ -938,7 +942,7 @@ impl<'ra> DeclData<'ra> { } fn descent_to_ambiguity(self: Decl<'ra>) -> Option<(Decl<'ra>, Decl<'ra>)> { - match self.ambiguity { + match self.ambiguity.get() { Some(ambig_binding) => Some((self, ambig_binding)), None => match self.kind { DeclKind::Import { source_decl, .. } => source_decl.descent_to_ambiguity(), @@ -948,7 +952,7 @@ impl<'ra> DeclData<'ra> { } fn is_ambiguity_recursive(&self) -> bool { - self.ambiguity.is_some() + self.ambiguity.get().is_some() || match self.kind { DeclKind::Import { source_decl, .. } => source_decl.is_ambiguity_recursive(), _ => false, @@ -956,7 +960,7 @@ impl<'ra> DeclData<'ra> { } fn warn_ambiguity_recursive(&self) -> bool { - self.warn_ambiguity + self.warn_ambiguity.get() || match self.kind { DeclKind::Import { source_decl, .. } => source_decl.warn_ambiguity_recursive(), _ => false, @@ -1076,7 +1080,7 @@ impl ExternPreludeEntry<'_> { struct DeriveData { resolutions: Vec, - helper_attrs: Vec<(usize, Ident)>, + helper_attrs: Vec<(usize, Macros20NormalizedIdent)>, has_derive_copy: bool, } @@ -1241,7 +1245,7 @@ pub struct Resolver<'ra, 'tcx> { /// `macro_rules` scopes produced by `macro_rules` item definitions. macro_rules_scopes: FxHashMap>, /// Helper attributes that are in scope for the given expansion. - helper_attrs: FxHashMap)>>, + helper_attrs: FxHashMap)>>, /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute /// with the given `ExpnId`. derive_data: FxHashMap, @@ -1342,9 +1346,9 @@ impl<'ra> ResolverArenas<'ra> { ) -> Decl<'ra> { self.alloc_decl(DeclData { kind: DeclKind::Def(res), - ambiguity: None, - warn_ambiguity: false, - vis, + ambiguity: CmCell::new(None), + warn_ambiguity: CmCell::new(false), + vis: CmCell::new(vis), span, expansion, }) @@ -2041,7 +2045,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn record_use(&mut self, ident: Ident, used_decl: Decl<'ra>, used: Used) { - self.record_use_inner(ident, used_decl, used, used_decl.warn_ambiguity); + self.record_use_inner(ident, used_decl, used, used_decl.warn_ambiguity.get()); } fn record_use_inner( @@ -2051,7 +2055,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { used: Used, warn_ambiguity: bool, ) { - if let Some(b2) = used_decl.ambiguity { + if let Some(b2) = used_decl.ambiguity.get() { let ambiguity_error = AmbiguityError { kind: AmbiguityKind::GlobVsGlob, ident, @@ -2112,7 +2116,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, source_decl, Used::Other, - warn_ambiguity || source_decl.warn_ambiguity, + warn_ambiguity || source_decl.warn_ambiguity.get(), ); } } @@ -2251,20 +2255,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn extern_prelude_get_item<'r>( mut self: CmResolver<'r, 'ra, 'tcx>, - ident: Ident, + ident: Macros20NormalizedIdent, finalize: bool, ) -> Option> { - let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)); - entry.and_then(|entry| entry.item_decl).map(|(binding, _)| { + let entry = self.extern_prelude.get(&ident); + entry.and_then(|entry| entry.item_decl).map(|(decl, _)| { if finalize { - self.get_mut().record_use(ident, binding, Used::Scope); + self.get_mut().record_use(ident.0, decl, Used::Scope); } - binding + decl }) } - fn extern_prelude_get_flag(&self, ident: Ident, finalize: bool) -> Option> { - let entry = self.extern_prelude.get(&Macros20NormalizedIdent::new(ident)); + fn extern_prelude_get_flag( + &self, + ident: Macros20NormalizedIdent, + finalize: bool, + ) -> Option> { + let entry = self.extern_prelude.get(&ident); entry.and_then(|entry| entry.flag_decl.as_ref()).and_then(|flag_decl| { let (pending_decl, finalized) = flag_decl.get(); let decl = match pending_decl { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index c9c754374c87..32d686d4f702 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -29,7 +29,7 @@ use rustc_session::parse::feature_err; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; +use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym}; use crate::Namespace::*; use crate::errors::{ @@ -52,7 +52,7 @@ pub(crate) struct MacroRulesDecl<'ra> { pub(crate) decl: Decl<'ra>, /// `macro_rules` scope into which the `macro_rules` item was planted. pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'ra>, - pub(crate) ident: Ident, + pub(crate) ident: Macros20NormalizedIdent, } /// The scope introduced by a `macro_rules!` macro. @@ -403,13 +403,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { - let last_seg = resolution.path.segments.last().unwrap(); - let span = last_seg.ident.span.normalize_to_macros_2_0(); - entry.helper_attrs.extend( - ext.helper_attrs - .iter() - .map(|name| (i, Ident::new(*name, span))), - ); + let span = resolution.path.segments.last().unwrap().ident.span; + entry.helper_attrs.extend(ext.helper_attrs.iter().map(|name| { + (i, Macros20NormalizedIdent::new(Ident::new(*name, span))) + })); } entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); ext @@ -530,7 +527,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { target_trait.for_each_child(self, |this, ident, ns, _binding| { // FIXME: Adjust hygiene for idents from globs, like for glob imports. if let Some(overriding_keys) = this.impl_binding_keys.get(&impl_def_id) - && overriding_keys.contains(&BindingKey::new(ident.0, ns)) + && overriding_keys.contains(&BindingKey::new(ident, ns)) { // The name is overridden, do not produce it from the glob delegation. } else { diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 7f7c423acb40..9f74a7801d2e 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -410,8 +410,17 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { /// Simplified version of `preprocessed_markdown_links` from rustdoc. /// Must return at least the same links as it, but may add some more links on top of that. pub(crate) fn attrs_to_preprocessed_links(attrs: &[A]) -> Vec> { - let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); - let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); + let (doc_fragments, other_attrs) = + attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), false); + let mut doc = + prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap_or_default(); + + for attr in other_attrs { + if let Some(note) = attr.deprecation_note() { + doc += note.as_str(); + doc += "\n"; + } + } parse_links(&doc) } diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 41cfaa3ee34e..11507a3d9e45 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -2,7 +2,7 @@ use std::ops::Range; use std::sync::Arc; use crate::source_map::SourceMap; -use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData, StableSourceFileId}; +use crate::{BytePos, Pos, RelativeBytePos, SourceFile, SpanData}; #[derive(Clone)] struct CacheEntry { @@ -114,7 +114,7 @@ impl<'sm> CachingSourceMapView<'sm> { pub fn span_data_to_lines_and_cols( &mut self, span_data: &SpanData, - ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)> { + ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)> { self.time_stamp += 1; // Check if lo and hi are in the cached lines. @@ -136,7 +136,7 @@ impl<'sm> CachingSourceMapView<'sm> { let lo = &self.line_cache[lo_cache_idx as usize]; let hi = &self.line_cache[hi_cache_idx as usize]; return Some(( - lo.file.stable_id, + &lo.file, lo.line_number, span_data.lo - lo.line.start, hi.line_number, @@ -224,7 +224,7 @@ impl<'sm> CachingSourceMapView<'sm> { assert_eq!(lo.file_index, hi.file_index); Some(( - lo.file.stable_id, + &lo.file, lo.line_number, span_data.lo - lo.line.start, hi.line_number, diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 484e626d4638..a0ccf8d7e798 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -81,7 +81,7 @@ impl fmt::Display for CrateNum { /// because it depends on the set of crates in the entire crate graph of a /// compilation session. Again, using the same crate with a different version /// number would fix the issue with a high probability -- but that might be -/// easier said then done if the crates in questions are dependencies of +/// easier said than done if the crates in questions are dependencies of /// third-party crates. /// /// That being said, given a high quality hash function, the collision diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d3e0e303d0c6..1c430099835b 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2831,7 +2831,7 @@ pub trait HashStableContext { fn span_data_to_lines_and_cols( &mut self, span: &SpanData, - ) -> Option<(StableSourceFileId, usize, BytePos, usize, BytePos)>; + ) -> Option<(&SourceFile, usize, BytePos, usize, BytePos)>; fn hashing_controls(&self) -> HashingControls; } @@ -2849,6 +2849,8 @@ where /// codepoint offsets. For the purpose of the hash that's sufficient. /// Also, hashing filenames is expensive so we avoid doing it twice when the /// span starts and ends in the same file, which is almost always the case. + /// + /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; @@ -2867,15 +2869,17 @@ where return; } - if let Some(parent) = span.parent { - let def_span = ctx.def_span(parent).data_untracked(); - if def_span.contains(span) { - // This span is enclosed in a definition: only hash the relative position. - Hash::hash(&TAG_RELATIVE_SPAN, hasher); - (span.lo - def_span.lo).to_u32().hash_stable(ctx, hasher); - (span.hi - def_span.lo).to_u32().hash_stable(ctx, hasher); - return; - } + let parent = span.parent.map(|parent| ctx.def_span(parent).data_untracked()); + if let Some(parent) = parent + && parent.contains(span) + { + // This span is enclosed in a definition: only hash the relative position. + // This catches a subset of the cases from the `file.contains(parent.lo)`, + // But we can do this check cheaply without the expensive `span_data_to_lines_and_cols` query. + Hash::hash(&TAG_RELATIVE_SPAN, hasher); + (span.lo - parent.lo).to_u32().hash_stable(ctx, hasher); + (span.hi - parent.lo).to_u32().hash_stable(ctx, hasher); + return; } // If this is not an empty or invalid span, we want to hash the last @@ -2887,8 +2891,19 @@ where return; }; + if let Some(parent) = parent + && file.contains(parent.lo) + { + // This span is relative to another span in the same file, + // only hash the relative position. + Hash::hash(&TAG_RELATIVE_SPAN, hasher); + Hash::hash(&(span.lo.0.wrapping_sub(parent.lo.0)), hasher); + Hash::hash(&(span.hi.0.wrapping_sub(parent.lo.0)), hasher); + return; + } + Hash::hash(&TAG_VALID_SPAN, hasher); - Hash::hash(&file, hasher); + Hash::hash(&file.stable_id, hasher); // Hash both the length and the end location (line/column) of a span. If we // hash only the length, for example, then two otherwise equal spans with diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c7ff28ccaffb..b473d36a45fc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -284,6 +284,7 @@ symbols! { IteratorItem, IteratorMap, Layout, + Leaf, Left, LinkedList, LintDiagnostic, @@ -302,6 +303,7 @@ symbols! { Ordering, OsStr, OsString, + Other, Output, Param, ParamSet, @@ -380,6 +382,7 @@ symbols! { TryCapturePrintable, TryFrom, TryInto, + Tuple, Ty, TyCtxt, TyKind, @@ -2041,7 +2044,6 @@ symbols! { self_in_typedefs, self_struct_ctor, semiopaque, - semitransparent, sha2, sha3, sha512_sm_x86, @@ -2319,6 +2321,7 @@ symbols! { type_const, type_id, type_id_eq, + type_info, type_ir, type_ir_infer_ctxt_like, type_ir_inherent, @@ -2326,6 +2329,7 @@ symbols! { type_length_limit, type_macros, type_name, + type_of, type_privacy_lints, typed_swap_nonoverlapping, u8, diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 092d99e91118..6faa57252ca2 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -381,15 +381,14 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn new( cx: &impl HasDataLayout, layout: TyAndLayout<'a, Ty>, - scalar_attrs: impl Fn(&TyAndLayout<'a, Ty>, Scalar, Size) -> ArgAttributes, + scalar_attrs: impl Fn(Scalar, Size) -> ArgAttributes, ) -> Self { let mode = match layout.backend_repr { - BackendRepr::Scalar(scalar) => { - PassMode::Direct(scalar_attrs(&layout, scalar, Size::ZERO)) - } + _ if layout.is_zst() => PassMode::Ignore, + BackendRepr::Scalar(scalar) => PassMode::Direct(scalar_attrs(scalar, Size::ZERO)), BackendRepr::ScalarPair(a, b) => PassMode::Pair( - scalar_attrs(&layout, a, Size::ZERO), - scalar_attrs(&layout, b, a.size(cx).align_to(b.align(cx).abi)), + scalar_attrs(a, Size::ZERO), + scalar_attrs(b, a.size(cx).align_to(b.align(cx).abi)), ), BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index a972299deeac..20fbb687b308 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -163,6 +163,7 @@ impl Target { forward!(relro_level); forward!(archive_format); forward!(allow_asm); + forward!(static_initializer_must_be_acyclic); forward!(main_needs_argc_argv); forward!(has_thread_local); forward!(obj_is_bitcode); @@ -360,6 +361,7 @@ impl ToJson for Target { target_option_val!(relro_level); target_option_val!(archive_format); target_option_val!(allow_asm); + target_option_val!(static_initializer_must_be_acyclic); target_option_val!(main_needs_argc_argv); target_option_val!(has_thread_local); target_option_val!(obj_is_bitcode); @@ -581,6 +583,7 @@ struct TargetSpecJson { relro_level: Option, archive_format: Option>, allow_asm: Option, + static_initializer_must_be_acyclic: Option, main_needs_argc_argv: Option, has_thread_local: Option, obj_is_bitcode: Option, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b06339f59425..89c9fdc935cc 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -2394,6 +2394,9 @@ pub struct TargetOptions { pub archive_format: StaticCow, /// Is asm!() allowed? Defaults to true. pub allow_asm: bool, + /// Static initializers must be acyclic. + /// Defaults to false + pub static_initializer_must_be_acyclic: bool, /// Whether the runtime startup code requires the `main` function be passed /// `argc` and `argv` values. pub main_needs_argc_argv: bool, @@ -2777,6 +2780,7 @@ impl Default for TargetOptions { archive_format: "gnu".into(), main_needs_argc_argv: true, allow_asm: true, + static_initializer_must_be_acyclic: false, has_thread_local: false, obj_is_bitcode: false, min_atomic_width: None, 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 be09681b1f35..87c2693e9877 100644 --- a/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/targets/nvptx64_nvidia_cuda.rs @@ -59,6 +59,9 @@ pub(crate) fn target() -> Target { // Support using `self-contained` linkers like the llvm-bitcode-linker link_self_contained: LinkSelfContainedDefault::True, + // Static initializers must not have cycles on this target + static_initializer_must_be_acyclic: true, + ..Default::default() }, } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index cdbf2b0aeb83..944d57bf95d1 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -144,7 +144,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { pub fn instantiate_nested_goals(&self, span: Span) -> Vec> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; - let mut orig_values = self.goal.orig_values.to_vec(); + let mut orig_values = self.goal.orig_values.clone(); let mut instantiated_goals = vec![]; for step in &self.steps { @@ -186,7 +186,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { pub fn instantiate_impl_args(&self, span: Span) -> ty::GenericArgsRef<'tcx> { let infcx = self.goal.infcx; let param_env = self.goal.goal.param_env; - let mut orig_values = self.goal.orig_values.to_vec(); + let mut orig_values = self.goal.orig_values.clone(); for step in &self.steps { match **step { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 394095508393..d383cb9d91dd 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1051,7 +1051,53 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { | ty::ConstKind::Placeholder(..) => { // These variants are trivially WF, so nothing to do here. } - ty::ConstKind::Value(..) => { + ty::ConstKind::Value(val) => { + // FIXME(mgca): no need to feature-gate once valtree lifetimes are not erased + if tcx.features().min_generic_const_args() { + match val.ty.kind() { + ty::Adt(adt_def, args) => { + let adt_val = val.destructure_adt_const(); + let variant_def = adt_def.variant(adt_val.variant); + let cause = self.cause(ObligationCauseCode::WellFormed(None)); + self.out.extend(variant_def.fields.iter().zip(adt_val.fields).map( + |(field_def, &field_val)| { + let field_ty = + tcx.type_of(field_def.did).instantiate(tcx, args); + let predicate = ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(field_val, field_ty), + ); + traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + predicate, + ) + }, + )); + } + ty::Tuple(field_tys) => { + let field_vals = val.to_branch(); + let cause = self.cause(ObligationCauseCode::WellFormed(None)); + self.out.extend(field_tys.iter().zip(field_vals).map( + |(field_ty, &field_val)| { + let predicate = ty::PredicateKind::Clause( + ty::ClauseKind::ConstArgHasType(field_val, field_ty), + ); + traits::Obligation::with_depth( + tcx, + cause.clone(), + self.recursion_depth, + self.param_env, + predicate, + ) + }, + )); + } + _ => {} + } + } + // FIXME: Enforce that values are structurally-matchable. } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 0676fa99d826..ad621c67772c 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -549,15 +549,9 @@ fn fn_abi_new_uncached<'tcx>( layout }; - let mut arg = ArgAbi::new(cx, layout, |layout, scalar, offset| { - arg_attrs_for_rust_scalar(*cx, scalar, *layout, offset, is_return, drop_target_pointee) - }); - - if arg.layout.is_zst() { - arg.mode = PassMode::Ignore; - } - - Ok(arg) + Ok(ArgAbi::new(cx, layout, |scalar, offset| { + arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, drop_target_pointee) + })) }; let mut fn_abi = FnAbi { diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index 47e753d78731..12d222258b0c 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -38,7 +38,7 @@ impl Eq for CanonicalQueryInput {} pub struct Canonical { pub value: V, pub max_universe: UniverseIndex, - pub variables: I::CanonicalVarKinds, + pub var_kinds: I::CanonicalVarKinds, } impl Eq for Canonical {} @@ -68,17 +68,17 @@ impl Canonical { /// let b: Canonical)> = a.unchecked_map(|v| (v, ty)); /// ``` pub fn unchecked_map(self, map_op: impl FnOnce(V) -> W) -> Canonical { - let Canonical { max_universe, variables, value } = self; - Canonical { max_universe, variables, value: map_op(value) } + let Canonical { max_universe, var_kinds, value } = self; + Canonical { max_universe, var_kinds, value: map_op(value) } } } impl fmt::Display for Canonical { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { value, max_universe, variables } = self; + let Self { value, max_universe, var_kinds } = self; write!( f, - "Canonical {{ value: {value}, max_universe: {max_universe:?}, variables: {variables:?} }}", + "Canonical {{ value: {value}, max_universe: {max_universe:?}, var_kinds: {var_kinds:?} }}", ) } } @@ -311,30 +311,30 @@ impl CanonicalVarValues { pub fn instantiate( cx: I, - variables: I::CanonicalVarKinds, + var_kinds: I::CanonicalVarKinds, mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind) -> I::GenericArg, ) -> CanonicalVarValues { // Instantiating `CanonicalVarValues` is really hot, but limited to less than // 4 most of the time. Avoid creating a `Vec` here. - if variables.len() <= 4 { + if var_kinds.len() <= 4 { let mut var_values = ArrayVec::<_, 4>::new(); - for info in variables.iter() { + for info in var_kinds.iter() { var_values.push(f(&var_values, info)); } CanonicalVarValues { var_values: cx.mk_args(&var_values) } } else { - CanonicalVarValues::instantiate_cold(cx, variables, f) + CanonicalVarValues::instantiate_cold(cx, var_kinds, f) } } #[cold] fn instantiate_cold( cx: I, - variables: I::CanonicalVarKinds, + var_kinds: I::CanonicalVarKinds, mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind) -> I::GenericArg, ) -> CanonicalVarValues { - let mut var_values = Vec::with_capacity(variables.len()); - for info in variables.iter() { + let mut var_values = Vec::with_capacity(var_kinds.len()); + for info in var_kinds.iter() { var_values.push(f(&var_values, info)); } CanonicalVarValues { var_values: cx.mk_args(&var_values) } diff --git a/library/Cargo.lock b/library/Cargo.lock index 5e49843dae06..f6c14bc58a04 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -283,12 +283,11 @@ dependencies = [ [[package]] name = "rustc-literal-escaper" -version = "0.0.5" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" +checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198" dependencies = [ "rustc-std-workspace-core", - "rustc-std-workspace-std", ] [[package]] diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index b4116f4988b6..aa1901314e37 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -127,6 +127,44 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`Waker`]. +/// +/// The closure gets called every time the waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(waker_fn)] +/// use std::task::waker_fn; +/// +/// let waker = waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +#[cfg(target_has_atomic = "ptr")] +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn waker_fn(f: F) -> Waker { + struct WakeFn { + f: F, + } + + impl Wake for WakeFn + where + F: Fn(), + { + fn wake(self: Arc) { + (self.f)() + } + + fn wake_by_ref(self: &Arc) { + (self.f)() + } + } + + Waker::from(Arc::new(WakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct @@ -306,6 +344,45 @@ impl From> for RawWaker { } } +/// Converts a closure into a [`LocalWaker`]. +/// +/// The closure gets called every time the local waker is woken. +/// +/// # Examples +/// +/// ``` +/// #![feature(local_waker)] +/// #![feature(waker_fn)] +/// use std::task::local_waker_fn; +/// +/// let waker = local_waker_fn(|| println!("woken")); +/// +/// waker.wake_by_ref(); // Prints "woken". +/// waker.wake(); // Prints "woken". +/// ``` +// #[unstable(feature = "local_waker", issue = "118959")] +#[unstable(feature = "waker_fn", issue = "149580")] +pub fn local_waker_fn(f: F) -> LocalWaker { + struct LocalWakeFn { + f: F, + } + + impl LocalWake for LocalWakeFn + where + F: Fn(), + { + fn wake(self: Rc) { + (self.f)() + } + + fn wake_by_ref(self: &Rc) { + (self.f)() + } + } + + LocalWaker::from(Rc::new(LocalWakeFn { f })) +} + // NB: This private function for constructing a RawWaker is used, rather than // inlining this into the `From> for RawWaker` impl, to ensure that // the safety of `From> for Waker` does not depend on the correct diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index d3da7efe4ddf..379e964f0a0c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1747,7 +1747,11 @@ impl Vec { // * We only construct `&mut` references to `self.buf` through `&mut self` methods; borrow- // check ensures that it is not possible to mutably alias `self.buf` within the // returned lifetime. - unsafe { slice::from_raw_parts(self.as_ptr(), self.len) } + unsafe { + // normally this would use `slice::from_raw_parts`, but it's + // instantiated often enough that avoiding the UB check is worth it + &*core::intrinsics::aggregate_raw_ptr::<*const [T], _, _>(self.as_ptr(), self.len) + } } /// Extracts a mutable slice of the entire vector. @@ -1779,7 +1783,11 @@ impl Vec { // * We only construct references to `self.buf` through `&self` and `&mut self` methods; // borrow-check ensures that it is not possible to construct a reference to `self.buf` // within the returned lifetime. - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), self.len) } + unsafe { + // normally this would use `slice::from_raw_parts_mut`, but it's + // instantiated often enough that avoiding the UB check is worth it + &mut *core::intrinsics::aggregate_raw_ptr::<*mut [T], _, _>(self.as_mut_ptr(), self.len) + } } /// Returns a raw pointer to the vector's buffer, or a dangling raw pointer diff --git a/library/alloctests/benches/btree/map.rs b/library/alloctests/benches/btree/map.rs index 778065fd9657..c2bf43caf131 100644 --- a/library/alloctests/benches/btree/map.rs +++ b/library/alloctests/benches/btree/map.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use std::ops::RangeBounds; use rand::Rng; +use rand::distr::{Distribution, Uniform}; use rand::seq::SliceRandom; use test::{Bencher, black_box}; @@ -106,7 +107,8 @@ macro_rules! map_find_rand_bench { // setup let mut rng = crate::bench_rng(); - let mut keys: Vec<_> = (0..n).map(|_| rng.random::() % n).collect(); + let mut keys: Vec<_> = + Uniform::new(0, n).unwrap().sample_iter(&mut rng).take(n as usize).collect(); for &k in &keys { map.insert(k, k); diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index db01cebe5b27..c7fdc2054f71 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -21,6 +21,8 @@ #![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_drain_sorted)] #![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(slice_partial_sort_unstable)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] diff --git a/library/alloctests/tests/sort/mod.rs b/library/alloctests/tests/sort/mod.rs index 0e2494ca9d34..e2e141a02b59 100644 --- a/library/alloctests/tests/sort/mod.rs +++ b/library/alloctests/tests/sort/mod.rs @@ -12,6 +12,7 @@ pub trait Sort { mod ffi_types; mod known_good_stable_sort; +mod partial; mod patterns; mod tests; mod zipf; diff --git a/library/alloctests/tests/sort/partial.rs b/library/alloctests/tests/sort/partial.rs new file mode 100644 index 000000000000..679841b91e8d --- /dev/null +++ b/library/alloctests/tests/sort/partial.rs @@ -0,0 +1,88 @@ +use std::fmt::Debug; +use std::ops::{Range, RangeBounds}; +use std::slice; + +use super::patterns; + +fn check_is_partial_sorted>(v: &mut [T], range: R) { + let Range { start, end } = slice::range(range, ..v.len()); + v.partial_sort_unstable(start..end); + + let max_before = v[..start].iter().max().into_iter(); + let sorted_range = v[start..end].into_iter(); + let min_after = v[end..].iter().min().into_iter(); + let seq = max_before.chain(sorted_range).chain(min_after); + assert!(seq.is_sorted()); +} + +fn check_is_partial_sorted_ranges(v: &[T]) { + let len = v.len(); + + check_is_partial_sorted::(&mut v.to_vec(), ..); + check_is_partial_sorted::(&mut v.to_vec(), 0..0); + check_is_partial_sorted::(&mut v.to_vec(), len..len); + + if len > 0 { + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len - 1); + check_is_partial_sorted::(&mut v.to_vec(), 0..1); + check_is_partial_sorted::(&mut v.to_vec(), len - 1..len); + + for mid in 1..len { + check_is_partial_sorted::(&mut v.to_vec(), 0..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..len); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid + 1); + check_is_partial_sorted::(&mut v.to_vec(), mid - 1..mid); + check_is_partial_sorted::(&mut v.to_vec(), mid..mid + 1); + } + + let quarters = [0, len / 4, len / 2, (3 * len) / 4, len]; + for &start in &quarters { + for &end in &quarters { + if start < end { + check_is_partial_sorted::(&mut v.to_vec(), start..end); + } + } + } + } +} + +#[test] +fn basic_impl() { + check_is_partial_sorted::(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [], ..); + check_is_partial_sorted::<(), _>(&mut [()], ..); + check_is_partial_sorted::<(), _>(&mut [(), ()], ..); + check_is_partial_sorted::<(), _>(&mut [(), (), ()], ..); + check_is_partial_sorted::(&mut [], ..); + + check_is_partial_sorted::(&mut [77], ..); + check_is_partial_sorted::(&mut [2, 3], ..); + check_is_partial_sorted::(&mut [2, 3, 6], ..); + check_is_partial_sorted::(&mut [2, 3, 99, 6], ..); + check_is_partial_sorted::(&mut [2, 7709, 400, 90932], ..); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], ..); + + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..0); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..1); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 0..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 7..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 6..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..7); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 5..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..5); + check_is_partial_sorted::(&mut [15, -1, 3, -1, -3, -1, 7], 4..6); +} + +#[test] +fn random_patterns() { + check_is_partial_sorted_ranges(&patterns::random(10)); + check_is_partial_sorted_ranges(&patterns::random(50)); + + // Longer tests would take hours to run under Miri. + if !cfg!(miri) { + check_is_partial_sorted_ranges(&patterns::random(100)); + check_is_partial_sorted_ranges(&patterns::random(1000)); + } +} diff --git a/library/alloctests/tests/sort/patterns.rs b/library/alloctests/tests/sort/patterns.rs index 0f1ec664d3d4..1ed645cf99dc 100644 --- a/library/alloctests/tests/sort/patterns.rs +++ b/library/alloctests/tests/sort/patterns.rs @@ -27,21 +27,20 @@ where { // :.:.:.:: - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); // Abstracting over ranges in Rust :( let dist = Uniform::try_from(range).unwrap(); - (0..len).map(|_| dist.sample(&mut rng)).collect() + rng.sample_iter(dist).take(len).collect() } pub fn random_zipf(len: usize, exponent: f64) -> Vec { // https://en.wikipedia.org/wiki/Zipf's_law - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); - // Abstracting over ranges in Rust :( let dist = ZipfDistribution::new(len, exponent).unwrap(); - (0..len).map(|_| dist.sample(&mut rng) as i32).collect() + rng.sample_iter(dist).map(|val| val as i32).take(len).collect() } pub fn random_sorted(len: usize, sorted_percent: f64) -> Vec { @@ -68,7 +67,7 @@ pub fn all_equal(len: usize) -> Vec { // ...... // :::::: - (0..len).map(|_| 66).collect::>() + vec![66; len] } pub fn ascending(len: usize) -> Vec { @@ -206,6 +205,6 @@ fn rand_root_seed() -> u64 { } fn random_vec(len: usize) -> Vec { - let mut rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); - (0..len).map(|_| rng.random::()).collect() + let rng: XorShiftRng = rand::SeedableRng::seed_from_u64(get_or_init_rand_seed()); + rng.random_iter().take(len).collect() } diff --git a/library/compiler-builtins/.github/workflows/main.yaml b/library/compiler-builtins/.github/workflows/main.yaml index 38995cf0f0ff..699a9c417dde 100644 --- a/library/compiler-builtins/.github/workflows/main.yaml +++ b/library/compiler-builtins/.github/workflows/main.yaml @@ -203,7 +203,7 @@ jobs: # Unlike rustfmt, stable clippy does not work on code with nightly features. - name: Install nightly `clippy` run: | - rustup set profile minimal + rustup update nightly --no-self-update rustup default nightly rustup component add clippy - uses: Swatinem/rust-cache@v2 @@ -247,16 +247,7 @@ jobs: - uses: taiki-e/install-action@cargo-binstall - name: Set up dependencies - run: | - sudo apt-get update - sudo apt-get install -y valgrind gdb libc6-dbg # Needed for gungraun - rustup update "$BENCHMARK_RUSTC" --no-self-update - rustup default "$BENCHMARK_RUSTC" - # Install the version of gungraun-runner that is specified in Cargo.toml - gungraun_version="$(cargo metadata --format-version=1 --features icount | - jq -r '.packages[] | select(.name == "gungraun").version')" - cargo binstall -y gungraun-runner --version "$gungraun_version" - sudo apt-get install valgrind + run: ./ci/install-bench-deps.sh - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.target }} @@ -276,13 +267,7 @@ jobs: path: ${{ env.BASELINE_NAME }}.tar.xz - name: Run wall time benchmarks - run: | - # Always use the same seed for benchmarks. Ideally we should switch to a - # non-random generator. - export LIBM_SEED=benchesbenchesbenchesbencheswoo! - cargo bench --package libm-test \ - --no-default-features \ - --features short-benchmarks,build-musl,libm/force-soft-floats + run: ./ci/bench-runtime.sh - name: Print test logs if available if: always() diff --git a/library/compiler-builtins/ci/bench-runtime.sh b/library/compiler-builtins/ci/bench-runtime.sh new file mode 100755 index 000000000000..d272cf33463e --- /dev/null +++ b/library/compiler-builtins/ci/bench-runtime.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Run wall time benchmarks as we do on CI. + +# Always use the same seed for benchmarks. Ideally we should switch to a +# non-random generator. +export LIBM_SEED=benchesbenchesbenchesbencheswoo! +cargo bench --package libm-test \ + --no-default-features \ + --features short-benchmarks,build-musl,libm/force-soft-floats diff --git a/library/compiler-builtins/ci/install-bench-deps.sh b/library/compiler-builtins/ci/install-bench-deps.sh new file mode 100755 index 000000000000..61f4723c0358 --- /dev/null +++ b/library/compiler-builtins/ci/install-bench-deps.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Install needed dependencies for gungraun. + +sudo apt-get update +sudo apt-get install -y valgrind gdb libc6-dbg # Needed for gungraun +rustup update "$BENCHMARK_RUSTC" --no-self-update +rustup default "$BENCHMARK_RUSTC" +# Install the version of gungraun-runner that is specified in Cargo.toml +gungraun_version="$(cargo metadata --format-version=1 --features icount | + jq -r '.packages[] | select(.name == "gungraun").version')" +cargo binstall -y gungraun-runner --version "$gungraun_version" diff --git a/library/compiler-builtins/compiler-builtins/src/aarch64.rs b/library/compiler-builtins/compiler-builtins/src/aarch64.rs index 039fab2061c5..1b230a214eef 100644 --- a/library/compiler-builtins/compiler-builtins/src/aarch64.rs +++ b/library/compiler-builtins/compiler-builtins/src/aarch64.rs @@ -4,7 +4,7 @@ use core::intrinsics; intrinsics! { #[unsafe(naked)] - #[cfg(target_os = "uefi")] + #[cfg(any(all(windows, target_env = "gnu"), target_os = "uefi"))] pub unsafe extern "custom" fn __chkstk() { core::arch::naked_asm!( ".p2align 2", diff --git a/library/compiler-builtins/libm/src/libm_helper.rs b/library/compiler-builtins/libm/src/libm_helper.rs index dfa1ff77bf2e..0bb669398657 100644 --- a/library/compiler-builtins/libm/src/libm_helper.rs +++ b/library/compiler-builtins/libm/src/libm_helper.rs @@ -168,7 +168,7 @@ libm_helper! { (fn remquo(x: f64, y: f64) -> (f64, i32); => remquo); (fn rint(x: f64) -> (f64); => rint); (fn round(x: f64) -> (f64); => round); - (fn roundevem(x: f64) -> (f64); => roundeven); + (fn roundeven(x: f64) -> (f64); => roundeven); (fn scalbn(x: f64, n: i32) -> (f64); => scalbn); (fn sin(x: f64) -> (f64); => sin); (fn sincos(x: f64) -> (f64, f64); => sincos); diff --git a/library/compiler-builtins/libm/src/math/arch/x86/detect.rs b/library/compiler-builtins/libm/src/math/arch/x86/detect.rs index 5391a68228ed..ca785470b806 100644 --- a/library/compiler-builtins/libm/src/math/arch/x86/detect.rs +++ b/library/compiler-builtins/libm/src/math/arch/x86/detect.rs @@ -39,6 +39,8 @@ pub fn get_cpu_features() -> Flags { /// Implementation is taken from [std-detect][std-detect]. /// /// [std-detect]: https://github.com/rust-lang/stdarch/blob/690b3a6334d482874163bd6fcef408e0518febe9/crates/std_detect/src/detect/os/x86.rs#L142 +// FIXME(msrv): Remove unsafe block around __cpuid once https://github.com/rust-lang/stdarch/pull/1935 is available in MSRV. +#[allow(unused_unsafe)] fn load_x86_features() -> Flags { let mut value = Flags::empty(); @@ -57,7 +59,7 @@ fn load_x86_features() -> Flags { // (in that order) let mut vendor_id = [0u8; 12]; let max_basic_leaf; - { + unsafe { let CpuidResult { eax, ebx, ecx, edx } = __cpuid(0); max_basic_leaf = eax; vendor_id[0..4].copy_from_slice(&ebx.to_ne_bytes()); @@ -72,7 +74,7 @@ fn load_x86_features() -> Flags { // EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits"; // Contains information about most x86 features. - let CpuidResult { ecx, edx, .. } = __cpuid(0x0000_0001_u32); + let CpuidResult { ecx, edx, .. } = unsafe { __cpuid(0x0000_0001_u32) }; let proc_info_ecx = Flags::from_bits(ecx); let proc_info_edx = Flags::from_bits(edx); @@ -82,23 +84,23 @@ fn load_x86_features() -> Flags { let mut extended_features_edx = Flags::empty(); let mut extended_features_eax_leaf_1 = Flags::empty(); if max_basic_leaf >= 7 { - let CpuidResult { ebx, edx, .. } = __cpuid(0x0000_0007_u32); + let CpuidResult { ebx, edx, .. } = unsafe { __cpuid(0x0000_0007_u32) }; extended_features_ebx = Flags::from_bits(ebx); extended_features_edx = Flags::from_bits(edx); - let CpuidResult { eax, .. } = __cpuid_count(0x0000_0007_u32, 0x0000_0001_u32); + let CpuidResult { eax, .. } = unsafe { __cpuid_count(0x0000_0007_u32, 0x0000_0001_u32) }; extended_features_eax_leaf_1 = Flags::from_bits(eax) } // EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported // - EAX returns the max leaf value for extended information, that is, // `cpuid` calls in range [0x8000_0000; u32::MAX]: - let extended_max_basic_leaf = __cpuid(0x8000_0000_u32).eax; + let extended_max_basic_leaf = unsafe { __cpuid(0x8000_0000_u32) }.eax; // EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature Bits" let mut extended_proc_info_ecx = Flags::empty(); if extended_max_basic_leaf >= 1 { - let CpuidResult { ecx, .. } = __cpuid(0x8000_0001_u32); + let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) }; extended_proc_info_ecx = Flags::from_bits(ecx); } diff --git a/library/compiler-builtins/rust-version b/library/compiler-builtins/rust-version index 7345c25066a8..6a2835bc2d9e 100644 --- a/library/compiler-builtins/rust-version +++ b/library/compiler-builtins/rust-version @@ -1 +1 @@ -2dc30247c5d8293aaa31e1d7dae2ed2fde908ada +23d01cd2412583491621ab1ca4f1b01e37d11e39 diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index e3d4b5c3331c..85b09ee06f1f 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -285,7 +285,7 @@ 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, trivial_clone)] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals, trivial_clone)] pub macro Clone($item:item) { /* compiler built-in */ } @@ -350,7 +350,7 @@ impl_use_cloned! { #[doc(hidden)] #[allow(missing_debug_implementations)] #[unstable( - feature = "derive_clone_copy", + feature = "derive_clone_copy_internals", reason = "deriving hack, should not be public", issue = "none" )] @@ -360,7 +360,7 @@ pub struct AssertParamIsClone { #[doc(hidden)] #[allow(missing_debug_implementations)] #[unstable( - feature = "derive_clone_copy", + feature = "derive_clone_copy_internals", reason = "deriving hack, should not be public", issue = "none" )] diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index feb9c4319604..78ea1f111325 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -351,7 +351,7 @@ pub const trait Eq: [const] PartialEq + PointeeSized { /// Derive macro generating an impl of the trait [`Eq`]. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)] +#[allow_internal_unstable(core_intrinsics, derive_eq_internals, structural_match)] #[allow_internal_unstable(coverage_attribute)] pub macro Eq($item:item) { /* compiler built-in */ @@ -363,7 +363,11 @@ pub macro Eq($item:item) { // This struct should never appear in user code. #[doc(hidden)] #[allow(missing_debug_implementations)] -#[unstable(feature = "derive_eq", reason = "deriving hack, should not be public", issue = "none")] +#[unstable( + feature = "derive_eq_internals", + reason = "deriving hack, should not be public", + issue = "none" +)] pub struct AssertParamIsEq { _field: crate::marker::PhantomData, } @@ -1186,7 +1190,7 @@ pub macro Ord($item:item) { /// every `a`. This isn't always the case for types that implement `PartialOrd`, for example: /// /// ``` -/// let a = f64::sqrt(-1.0); +/// let a = f64::NAN; /// assert_eq!(a <= a, false); /// ``` /// diff --git a/library/core/src/error.rs b/library/core/src/error.rs index 9ca91ee009ee..011d6ac4a1c7 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -205,6 +205,56 @@ pub trait Error: Debug + Display { /// assert!(request_ref::(dyn_error).is_none()); /// } /// ``` + /// + /// # Delegating Impls + /// + ///
+ /// + /// **Warning**: We recommend implementors avoid delegating implementations of `provide` to + /// source error implementations. + /// + ///
+ /// + /// This method should expose context from the current piece of the source chain only, not from + /// sources that are exposed in the chain of sources. Delegating `provide` implementations cause + /// the same context to be provided by multiple errors in the chain of sources which can cause + /// unintended duplication of information in error reports or require heuristics to deduplicate. + /// + /// In other words, the following implementation pattern for `provide` is discouraged and should + /// not be used for [`Error`] types exposed in public APIs to third parties. + /// + /// ```rust + /// # #![feature(error_generic_member_access)] + /// # use core::fmt; + /// # use core::error::Request; + /// # #[derive(Debug)] + /// struct MyError { + /// source: Error, + /// } + /// # #[derive(Debug)] + /// # struct Error; + /// # impl fmt::Display for Error { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Source Error") + /// # } + /// # } + /// # impl fmt::Display for MyError { + /// # fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + /// # write!(f, "Example Error") + /// # } + /// # } + /// # impl std::error::Error for Error { } + /// + /// impl std::error::Error for MyError { + /// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + /// Some(&self.source) + /// } + /// + /// fn provide<'a>(&'a self, request: &mut Request<'a>) { + /// self.source.provide(request) // <--- Discouraged + /// } + /// } + /// ``` #[unstable(feature = "error_generic_member_access", issue = "99301")] #[allow(unused_variables)] fn provide<'a>(&'a self, request: &mut Request<'a>) {} diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index 780e522c48eb..4f2e8a22d180 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -10,7 +10,7 @@ use crate::{cmp, ptr}; /// This is currently the default hashing function used by standard library /// (e.g., `collections::HashMap` uses it by default). /// -/// See: +/// See: #[unstable(feature = "hashmap_internals", issue = "none")] #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] #[derive(Debug, Clone, Default)] @@ -21,7 +21,7 @@ pub struct SipHasher13 { /// An implementation of SipHash 2-4. /// -/// See: +/// See: #[unstable(feature = "hashmap_internals", issue = "none")] #[deprecated(since = "1.13.0", note = "use `std::hash::DefaultHasher` instead")] #[derive(Debug, Clone, Default)] @@ -31,7 +31,7 @@ struct SipHasher24 { /// An implementation of SipHash 2-4. /// -/// See: +/// See: /// /// SipHash is a general-purpose hashing function: it runs at a good /// speed (competitive with Spooky and City) and permits strong _keyed_ diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 0ae8d3d4a4ce..20f34036b25c 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2738,10 +2738,6 @@ pub unsafe fn vtable_align(ptr: *const ()) -> usize; /// Determining whether `T` can be coerced to the trait object type `U` requires trait resolution by the compiler. /// In some cases, that resolution can exceed the recursion limit, /// and compilation will fail instead of this function returning `None`. -/// -/// # Safety -/// -/// `ptr` must point to a vtable. #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] #[rustc_intrinsic] @@ -2849,6 +2845,15 @@ pub const unsafe fn size_of_val(ptr: *const T) -> usize; #[rustc_intrinsic_const_stable_indirect] pub const unsafe fn align_of_val(ptr: *const T) -> usize; +/// Compute the type information of a concrete type. +/// It can only be called at compile time, the backends do +/// not implement it. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +pub const fn type_of(_id: crate::any::TypeId) -> crate::mem::type_info::Type { + panic!("`TypeId::info` can only be called at compile-time") +} + /// Gets a static string slice containing the name of a type. /// /// Note that, unlike most intrinsics, this can only be called at compile-time diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index ee40d2b74c64..b9bdb827209b 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -337,7 +337,6 @@ impl Peekable { /// /// Parse the leading decimal number from an iterator of characters. /// ``` - /// #![feature(peekable_next_if_map)] /// let mut iter = "125 GOTO 10".chars().peekable(); /// let mut line_num = 0_u32; /// while let Some(digit) = iter.next_if_map(|c| c.to_digit(10).ok_or(c)) { @@ -349,7 +348,6 @@ impl Peekable { /// /// Matching custom types. /// ``` - /// #![feature(peekable_next_if_map)] /// /// #[derive(Debug, PartialEq, Eq)] /// enum Node { @@ -408,7 +406,7 @@ impl Peekable { ///# ], ///# ) /// ``` - #[unstable(feature = "peekable_next_if_map", issue = "143702")] + #[stable(feature = "peekable_next_if_map", since = "CURRENT_RUSTC_VERSION")] pub fn next_if_map(&mut self, f: impl FnOnce(I::Item) -> Result) -> Option { let unpeek = if let Some(item) = self.next() { match f(item) { @@ -437,7 +435,6 @@ impl Peekable { /// /// Parse the leading decimal number from an iterator of characters. /// ``` - /// #![feature(peekable_next_if_map)] /// let mut iter = "125 GOTO 10".chars().peekable(); /// let mut line_num = 0_u32; /// while let Some(digit) = iter.next_if_map_mut(|c| c.to_digit(10)) { @@ -446,7 +443,7 @@ impl Peekable { /// assert_eq!(line_num, 125); /// assert_eq!(iter.collect::(), " GOTO 10"); /// ``` - #[unstable(feature = "peekable_next_if_map", issue = "143702")] + #[stable(feature = "peekable_next_if_map", since = "CURRENT_RUSTC_VERSION")] pub fn next_if_map_mut(&mut self, f: impl FnOnce(&mut I::Item) -> Option) -> Option { let unpeek = if let Some(mut item) = self.next() { match f(&mut item) { diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 7dabaece9556..da0b05063657 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -334,8 +334,18 @@ pub trait DoubleEndedIterator: Iterator { /// ``` /// let a = [1, 2, 3]; /// - /// assert_eq!(a.iter().rfind(|&&x| x == 2), Some(&2)); + /// assert_eq!(a.into_iter().rfind(|&x| x == 2), Some(2)); + /// assert_eq!(a.into_iter().rfind(|&x| x == 5), None); + /// ``` /// + /// Iterating over references: + /// + /// ``` + /// let a = [1, 2, 3]; + /// + /// // `iter()` yields references i.e. `&i32` and `rfind()` takes a + /// // reference to each element. + /// assert_eq!(a.iter().rfind(|&&x| x == 2), Some(&2)); /// assert_eq!(a.iter().rfind(|&&x| x == 5), None); /// ``` /// diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index f106900a3983..419f66089b10 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1906,9 +1906,12 @@ pub trait Iterator { /// Transforms an iterator into a collection. /// - /// `collect()` can take anything iterable, and turn it into a relevant - /// collection. This is one of the more powerful methods in the standard - /// library, used in a variety of contexts. + /// `collect()` takes ownership of an iterator and produces whichever + /// collection type you request. The iterator itself carries no knowledge of + /// the eventual container; the target collection is chosen entirely by the + /// type you ask `collect()` to return. This makes `collect()` one of the + /// more powerful methods in the standard library, and it shows up in a wide + /// variety of contexts. /// /// The most basic pattern in which `collect()` is used is to turn one /// collection into another. You take a collection, call [`iter`] on it, @@ -2862,6 +2865,17 @@ pub trait Iterator { /// assert_eq!(a.into_iter().find(|&x| x == 5), None); /// ``` /// + /// Iterating over references: + /// + /// ``` + /// let a = [1, 2, 3]; + /// + /// // `iter()` yields references i.e. `&i32` and `find()` takes a + /// // reference to each element. + /// assert_eq!(a.iter().find(|&&x| x == 2), Some(&2)); + /// assert_eq!(a.iter().find(|&&x| x == 5), None); + /// ``` + /// /// Stopping at the first `true`: /// /// ``` diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 962b0cea4a4f..dfc9a7b5dbc3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -124,6 +124,7 @@ #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] #![feature(str_split_remainder)] +#![feature(type_info)] #![feature(ub_checks)] #![feature(unsafe_pinned)] #![feature(utf16_extra)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index a12ee60277f0..338c5688bf10 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -168,7 +168,7 @@ macro_rules! assert_ne { /// ``` #[unstable(feature = "assert_matches", issue = "82775")] #[allow_internal_unstable(panic_internals)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro assert_matches { ($left:expr, $(|)? $( $pattern:pat_param )|+ $( if $guard: expr )? $(,)?) => { match $left { @@ -401,7 +401,7 @@ macro_rules! debug_assert_ne { /// ``` #[unstable(feature = "assert_matches", issue = "82775")] #[allow_internal_unstable(assert_matches)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro debug_assert_matches($($arg:tt)*) { if $crate::cfg!(debug_assertions) { $crate::assert_matches::assert_matches!($($arg)*); diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 5ecc2a63ea83..68f5649210de 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -466,7 +466,7 @@ pub trait Copy: Clone { /// Derive macro generating an impl of the trait `Copy`. #[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_internals)] pub macro Copy($item:item) { /* compiler built-in */ } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 4f7edce1e977..1671c8219de1 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -37,6 +37,9 @@ pub use drop_guard::DropGuard; #[doc(inline)] pub use crate::intrinsics::transmute; +#[unstable(feature = "type_info", issue = "146922")] +pub mod type_info; + /// Takes ownership and "forgets" about the value **without running its destructor**. /// /// Any resources the value manages, such as heap memory or a file handle, will linger diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs new file mode 100644 index 000000000000..7938e2b52ed0 --- /dev/null +++ b/library/core/src/mem/type_info.rs @@ -0,0 +1,71 @@ +//! MVP for exposing compile-time information about types in a +//! runtime or const-eval processable way. + +use crate::any::TypeId; +use crate::intrinsics::type_of; + +/// Compile-time type information. +#[derive(Debug)] +#[non_exhaustive] +#[lang = "type_info"] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Type { + /// Per-type information + pub kind: TypeKind, + /// Size of the type. `None` if it is unsized + pub size: Option, +} + +impl TypeId { + /// Compute the type information of a concrete type. + /// It can only be called at compile time. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + pub const fn info(self) -> Type { + type_of(self) + } +} + +impl Type { + /// Returns the type information of the generic type parameter. + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + // FIXME(reflection): don't require the 'static bound + pub const fn of() -> Self { + const { TypeId::of::().info() } + } +} + +/// Compile-time type information. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub enum TypeKind { + /// Tuples. + Tuple(Tuple), + /// Primitives + /// FIXME(#146922): disambiguate further + Leaf, + /// FIXME(#146922): add all the common types + Other, +} + +/// Compile-time type information about tuples. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Tuple { + /// All fields of a tuple. + pub fields: &'static [Field], +} + +/// Compile-time type information about fields of tuples, structs and enum variants. +#[derive(Debug)] +#[non_exhaustive] +#[unstable(feature = "type_info", issue = "146922")] +pub struct Field { + /// The field's type. + pub ty: TypeId, + /// Offset in bytes from the parent type + pub offset: usize, +} diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 81520c3ecd1b..cf0750446680 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -20,7 +20,7 @@ use crate::any::Any; #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2015_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2015 { () => ( $crate::panicking::panic("explicit panic") @@ -47,7 +47,7 @@ pub macro panic_2015 { #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(panic_internals, const_format_args)] #[rustc_diagnostic_item = "core_panic_2021_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2021 { () => ( $crate::panicking::panic("explicit panic") @@ -67,7 +67,7 @@ pub macro panic_2021 { #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(panic_internals)] #[rustc_diagnostic_item = "unreachable_2015_macro"] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro unreachable_2015 { () => ( $crate::panicking::panic("internal error: entered unreachable code") @@ -85,7 +85,7 @@ pub macro unreachable_2015 { #[doc(hidden)] #[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")] #[allow_internal_unstable(panic_internals)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro unreachable_2021 { () => ( $crate::panicking::panic("internal error: entered unreachable code") diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 74ecb5ee4946..4791f5612bfa 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -2027,7 +2027,7 @@ unsafe impl PinCoerceUnsized for *mut T {} /// /// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin #[stable(feature = "pin_macro", since = "1.68.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] #[allow_internal_unstable(super_let)] #[rustc_diagnostic_item = "pin_macro"] // `super` gets removed by rustfmt diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 335c5c6ee944..ad74a8628c61 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -2662,7 +2662,7 @@ impl fmt::Debug for F { /// same requirements apply to field projections, even inside `addr_of!`. (In particular, it makes /// no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro addr_of($place:expr) { &raw const $place } @@ -2752,7 +2752,7 @@ pub macro addr_of($place:expr) { /// same requirements apply to field projections, even inside `addr_of_mut!`. (In particular, it /// makes no difference whether the pointer is null or dangling.) #[stable(feature = "raw_ref_macros", since = "1.51.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro addr_of_mut($place:expr) { &raw mut $place } diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index fd1ca23fb79c..c3ff928a3277 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -13,13 +13,10 @@ impl const PartialEq<[U]> for [T] where T: [const] PartialEq, { + #[inline] fn eq(&self, other: &[U]) -> bool { SlicePartialEq::equal(self, other) } - - fn ne(&self, other: &[U]) -> bool { - SlicePartialEq::not_equal(self, other) - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -99,10 +96,6 @@ impl PartialOrd for [T] { #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const trait SlicePartialEq { fn equal(&self, other: &[B]) -> bool; - - fn not_equal(&self, other: &[B]) -> bool { - !self.equal(other) - } } // Generic slice equality @@ -111,6 +104,11 @@ impl const SlicePartialEq for [A] where A: [const] PartialEq, { + // It's not worth trying to inline the loops underneath here *in MIR*, + // and preventing it encourages more useful inlining upstream, + // such as in `::eq`. + // The codegen backend can still inline it later if needed. + #[rustc_no_mir_inline] default fn equal(&self, other: &[B]) -> bool { if self.len() != other.len() { return false; @@ -140,6 +138,16 @@ impl const SlicePartialEq for [A] where A: [const] BytewiseEq, { + // This is usually a pretty good backend inlining candidate because the + // intrinsic tends to just be `memcmp`. However, as of 2025-12 letting + // MIR inline this makes reuse worse because it means that, for example, + // `String::eq` doesn't inline, whereas by keeping this from inling all + // the wrappers until the call to this disappear. If the heuristics have + // changed and this is no longer fruitful, though, please do remove it. + // In the mean time, it's fine to not inline it in MIR because the backend + // will still inline it if it things it's important to do so. + #[rustc_no_mir_inline] + #[inline] fn equal(&self, other: &[B]) -> bool { if self.len() != other.len() { return false; diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1bca13b14ed4..8113e1824f27 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -3244,6 +3244,219 @@ impl [T] { sort::unstable::sort(self, &mut |a, b| f(a).lt(&f(b))); } + /// Partially sorts the slice in ascending order **without** preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `T` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable(0..0); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable(2..2); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable(2..3); + /// for i in 0..2 { + /// assert!(v[i] <= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] <= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable(1..4); + /// assert_eq!(&v[1..4], [-3, 1, 2]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable(..); + /// assert_eq!(v, [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [`sort_unstable`]: slice::sort_unstable + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable(&mut self, range: R) + where + T: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, T::lt); + } + + /// Partially sorts the slice in ascending order with a comparison function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the `compare` does not implement a total order, or if + /// the `compare` itself panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by(0..0, |a, b| b.cmp(a)); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by(2..2, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by(2..3, |a, b| b.cmp(a)); + /// for i in 0..2 { + /// assert!(v[i] >= v[2]); + /// } + /// for i in 3..v.len() { + /// assert!(v[2] >= v[i]); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by(1..4, |a, b| b.cmp(a)); + /// assert_eq!(&v[1..4], [2, 1, -3]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by(.., |a, b| b.cmp(a)); + /// assert_eq!(v, [4, 2, 1, -3, -5]); + /// ``` + /// + /// [`sort_unstable_by`]: slice::sort_unstable_by + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by(&mut self, range: R, mut compare: F) + where + F: FnMut(&T, &T) -> Ordering, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| compare(a, b) == Less); + } + + /// Partially sorts the slice in ascending order with a key extraction function, **without** + /// preserving the initial order of equal elements. + /// + /// Upon completion, for the specified range `start..end`, it's guaranteed that: + /// + /// 1. Every element in `self[..start]` is smaller than or equal to + /// 2. Every element in `self[start..end]`, which is sorted, and smaller than or equal to + /// 3. Every element in `self[end..]`. + /// + /// This partial sort is unstable, meaning it may reorder equal elements in the specified range. + /// It may reorder elements outside the specified range as well, but the guarantees above still hold. + /// + /// This partial sort is in-place (i.e., does not allocate), and *O*(*n* + *k* \* log(*k*)) worst-case, + /// where *n* is the length of the slice and *k* is the length of the specified range. + /// + /// See the documentation of [`sort_unstable_by_key`] for implementation notes. + /// + /// # Panics + /// + /// May panic if the implementation of [`Ord`] for `K` does not implement a total order, or if + /// the [`Ord`] implementation panics, or if the specified range is out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partial_sort_unstable)] + /// + /// let mut v = [4i32, -5, 1, -3, 2]; + /// + /// // empty range at the beginning, nothing changed + /// v.partial_sort_unstable_by_key(0..0, |k| k.abs()); + /// assert_eq!(v, [4, -5, 1, -3, 2]); + /// + /// // empty range in the middle, partitioning the slice + /// v.partial_sort_unstable_by_key(2..2, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // single element range, same as select_nth_unstable + /// v.partial_sort_unstable_by_key(2..3, |k| k.abs()); + /// for i in 0..2 { + /// assert!(v[i].abs() <= v[2].abs()); + /// } + /// for i in 3..v.len() { + /// assert!(v[2].abs() <= v[i].abs()); + /// } + /// + /// // partial sort a subrange + /// v.partial_sort_unstable_by_key(1..4, |k| k.abs()); + /// assert_eq!(&v[1..4], [2, -3, 4]); + /// + /// // partial sort the whole range, same as sort_unstable + /// v.partial_sort_unstable_by_key(.., |k| k.abs()); + /// assert_eq!(v, [1, 2, -3, 4, -5]); + /// ``` + /// + /// [`sort_unstable_by_key`]: slice::sort_unstable_by_key + #[unstable(feature = "slice_partial_sort_unstable", issue = "149046")] + #[inline] + pub fn partial_sort_unstable_by_key(&mut self, range: R, mut f: F) + where + F: FnMut(&T) -> K, + K: Ord, + R: RangeBounds, + { + sort::unstable::partial_sort(self, range, |a, b| f(a).lt(&f(b))); + } + /// Reorders the slice such that the element at `index` is at a sort-order position. All /// elements before `index` will be `<=` to this value, and all elements after will be `>=` to /// it. diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index d4df8d3a264d..7ca95a3b1b19 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -1,11 +1,13 @@ //! This module contains the entry points for `slice::sort_unstable`. use crate::mem::SizedTypeProperties; +use crate::ops::{Range, RangeBounds}; +use crate::slice::sort::select::partition_at_index; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; #[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; -use crate::{cfg_select, intrinsics}; +use crate::{cfg_select, intrinsics, slice}; pub(crate) mod heapsort; pub(crate) mod quicksort; @@ -17,7 +19,10 @@ pub(crate) mod quicksort; /// Upholds all safety properties outlined here: /// #[inline(always)] -pub fn sort bool>(v: &mut [T], is_less: &mut F) { +pub fn sort(v: &mut [T], is_less: &mut F) +where + F: FnMut(&T, &T) -> bool, +{ // Arrays of zero-sized types are always all-equal, and thus sorted. if T::IS_ZST { return; @@ -52,6 +57,54 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { } } +/// Unstable partial sort the range `start..end`, after which it's guaranteed that: +/// +/// 1. Every element in `v[..start]` is smaller than or equal to +/// 2. Every element in `v[start..end]`, which is sorted, and smaller than or equal to +/// 3. Every element in `v[end..]`. +#[inline] +pub fn partial_sort(v: &mut [T], range: R, mut is_less: F) +where + F: FnMut(&T, &T) -> bool, + R: RangeBounds, +{ + // Arrays of zero-sized types are always all-equal, and thus sorted. + if T::IS_ZST { + return; + } + + let len = v.len(); + let Range { start, end } = slice::range(range, ..len); + + if end - start <= 1 { + // Empty range or single element. This case can be resolved in at most + // single partition_at_index call, without further sorting. + + if end == 0 || start == len { + // Do nothing if it is an empty range at start or end: all guarantees + // are already upheld. + return; + } + + partition_at_index(v, start, &mut is_less); + return; + } + + // A heuristic factor to decide whether to partition the slice or not. + // If the range bound is close to the edges of the slice, it's not worth + // partitioning first. + const PARTITION_THRESHOLD: usize = 8; + let mut v = v; + if end + PARTITION_THRESHOLD <= len { + v = partition_at_index(v, end - 1, &mut is_less).0; + } + if start >= PARTITION_THRESHOLD { + v = partition_at_index(v, start, &mut is_less).2; + } + + sort(v, &mut is_less); +} + /// See [`sort`] /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 1eae06ebd33e..22f46ec385ce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1550,6 +1550,24 @@ impl AtomicPtr { unsafe { &*ptr.cast() } } + /// Creates a new `AtomicPtr` initialized with a null pointer. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_ptr_null)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let atomic_ptr = AtomicPtr::<()>::null(); + /// assert!(atomic_ptr.load(Ordering::Relaxed).is_null()); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "atomic_ptr_null", issue = "150733")] + pub const fn null() -> AtomicPtr { + AtomicPtr::new(crate::ptr::null_mut()) + } + /// Returns a mutable reference to the underlying pointer. /// /// This is safe because the mutable reference guarantees that no other threads are diff --git a/library/core/src/task/ready.rs b/library/core/src/task/ready.rs index 495d72fd14be..468b3b4e528e 100644 --- a/library/core/src/task/ready.rs +++ b/library/core/src/task/ready.rs @@ -46,7 +46,7 @@ /// # } /// ``` #[stable(feature = "ready_macro", since = "1.64.0")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro ready($e:expr) { match $e { $crate::task::Poll::Ready(t) => t, diff --git a/library/coretests/benches/num/int_bits/mod.rs b/library/coretests/benches/num/int_bits/mod.rs index 43531b0b5de9..c6ec51f248ba 100644 --- a/library/coretests/benches/num/int_bits/mod.rs +++ b/library/coretests/benches/num/int_bits/mod.rs @@ -4,11 +4,12 @@ macro_rules! bench_template { ($op:path, $name:ident, $mask:expr) => { #[bench] fn $name(bench: &mut ::test::Bencher) { - use ::rand::Rng; + use ::rand::distr::{Distribution, Uniform}; let mut rng = crate::bench_rng(); let mut dst = vec![0; ITERATIONS]; - let src1: Vec = (0..ITERATIONS).map(|_| rng.random_range(0..=U::MAX)).collect(); - let mut src2: Vec = (0..ITERATIONS).map(|_| rng.random_range(0..=U::MAX)).collect(); + let distr = &Uniform::try_from(0..=U::MAX).unwrap(); + let src1: Vec = distr.sample_iter(&mut rng).take(ITERATIONS).collect(); + let mut src2: Vec = distr.sample_iter(&mut rng).take(ITERATIONS).collect(); // Fix the loop invariant mask src2[0] = U::MAX / 3; let dst = dst.first_chunk_mut().unwrap(); diff --git a/library/coretests/benches/num/int_sqrt/mod.rs b/library/coretests/benches/num/int_sqrt/mod.rs index 05cb3c5383b2..56172f71dd88 100644 --- a/library/coretests/benches/num/int_sqrt/mod.rs +++ b/library/coretests/benches/num/int_sqrt/mod.rs @@ -1,3 +1,5 @@ +use std::iter; + use rand::Rng; use test::{Bencher, black_box}; @@ -20,7 +22,9 @@ macro_rules! int_sqrt_bench { let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the whole range of the type. */ let numbers: Vec<$t> = - (0..256).map(|_| rng.random::<$t>() >> rng.random_range(0..<$t>::BITS)).collect(); + iter::repeat_with(|| rng.random::<$t>() >> rng.random_range(0..<$t>::BITS)) + .take(256) + .collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); @@ -32,9 +36,10 @@ macro_rules! int_sqrt_bench { fn $random_small(bench: &mut Bencher) { let mut rng = crate::bench_rng(); /* Exponentially distributed random numbers from the range 0..256. */ - let numbers: Vec<$t> = (0..256) - .map(|_| (rng.random::() >> rng.random_range(0..u8::BITS)) as $t) - .collect(); + let numbers: Vec<$t> = + iter::repeat_with(|| (rng.random::() >> rng.random_range(0..u8::BITS)) as $t) + .take(256) + .collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); @@ -44,9 +49,9 @@ macro_rules! int_sqrt_bench { #[bench] fn $random_uniform(bench: &mut Bencher) { - let mut rng = crate::bench_rng(); + let rng = crate::bench_rng(); /* Exponentially distributed random numbers from the whole range of the type. */ - let numbers: Vec<$t> = (0..256).map(|_| rng.random::<$t>()).collect(); + let numbers: Vec<$t> = rng.random_iter().take(256).collect(); bench.iter(|| { for x in &numbers { black_box(black_box(x).isqrt()); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b28e7338859c..6a8b6d9ba154 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -90,7 +90,6 @@ #![feature(one_sided_range)] #![feature(option_reduce)] #![feature(pattern)] -#![feature(peekable_next_if_map)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] diff --git a/library/proc_macro/Cargo.toml b/library/proc_macro/Cargo.toml index 3a4840a57334..1e5046ca61c3 100644 --- a/library/proc_macro/Cargo.toml +++ b/library/proc_macro/Cargo.toml @@ -9,7 +9,7 @@ std = { path = "../std" } # `core` when resolving doc links. Without this line a different `core` will be # loaded from sysroot causing duplicate lang items and other similar errors. core = { path = "../core" } -rustc-literal-escaper = { version = "0.0.5", features = ["rustc-dep-of-std"] } +rustc-literal-escaper = { version = "0.0.7", features = ["rustc-dep-of-std"] } [features] default = ["rustc-dep-of-std"] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 43caf7734fdb..5c9ae52d9e6c 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -128,10 +128,6 @@ debug_refcell = ["core/debug_refcell"] llvm_enzyme = ["core/llvm_enzyme"] -# Enable std_detect features: -std_detect_file_io = ["std_detect/std_detect_file_io"] -std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] - # Enable using raw-dylib for Windows imports. # This will eventually be the default. windows_raw_dylib = ["windows-targets/windows_raw_dylib"] diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 251d62bd2033..ad6328f76ed6 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -4,6 +4,7 @@ mod tests; use hashbrown::hash_map as base; use self::Entry::*; +use crate::alloc::{Allocator, Global}; use crate::borrow::Borrow; use crate::collections::{TryReserveError, TryReserveErrorKind}; use crate::error::Error; @@ -243,8 +244,13 @@ use crate::ops::Index; #[cfg_attr(not(test), rustc_diagnostic_item = "HashMap")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_insignificant_dtor] -pub struct HashMap { - base: base::HashMap, +pub struct HashMap< + K, + V, + S = RandomState, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::HashMap, } impl HashMap { @@ -286,6 +292,46 @@ impl HashMap { } } +impl HashMap { + /// Creates an empty `HashMap` using the given allocator. + /// + /// The hash map is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::new(); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_in(alloc: A) -> Self { + HashMap::with_hasher_in(Default::default(), alloc) + } + + /// Creates an empty `HashMap` with at least the specified capacity using + /// the given allocator. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, i32> = HashMap::with_capacity(10); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + HashMap::with_capacity_and_hasher_in(capacity, Default::default(), alloc) + } +} + impl HashMap { /// Creates an empty `HashMap` which will use the given hash builder to hash /// keys. @@ -347,6 +393,47 @@ impl HashMap { pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashMap { HashMap { base: base::HashMap::with_capacity_and_hasher(capacity, hasher) } } +} + +impl HashMap { + /// Creates an empty `HashMap` which will use the given hash builder and + /// allocator. + /// + /// The created map has the default initial capacity. + /// + /// Warning: `hash_builder` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashMap` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_hasher_in(hash_builder: S, alloc: A) -> Self { + HashMap { base: base::HashMap::with_hasher_in(hash_builder, alloc) } + } + + /// Creates an empty `HashMap` with at least the specified capacity, using + /// `hasher` to hash the keys and `alloc` to allocate memory. + /// + /// The hash map will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash map will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow HashMaps to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hasher` passed should implement the [`BuildHasher`] trait for + /// the `HashMap` to be useful, see its documentation for details. + /// + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { + HashMap { base: base::HashMap::with_capacity_and_hasher_in(capacity, hash_builder, alloc) } + } /// Returns the number of elements the map can hold without reallocating. /// @@ -424,7 +511,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_keys(self) -> IntoKeys { + pub fn into_keys(self) -> IntoKeys { IntoKeys { inner: self.into_iter() } } @@ -519,7 +606,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "map_into_keys_values", since = "1.54.0")] - pub fn into_values(self) -> IntoValues { + pub fn into_values(self) -> IntoValues { IntoValues { inner: self.into_iter() } } @@ -648,7 +735,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, K, V> { + pub fn drain(&mut self) -> Drain<'_, K, V, A> { Drain { base: self.base.drain() } } @@ -688,7 +775,7 @@ impl HashMap { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F> + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, K, V, F, A> where F: FnMut(&K, &mut V) -> bool, { @@ -762,10 +849,11 @@ impl HashMap { } } -impl HashMap +impl HashMap where K: Eq + Hash, S: BuildHasher, + A: Allocator, { /// Reserves capacity for at least `additional` more elements to be inserted /// in the `HashMap`. The collection may reserve more space to speculatively @@ -884,7 +972,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + pub fn entry(&mut self, key: K) -> Entry<'_, K, V, A> { map_entry(self.base.rustc_entry(key)) } @@ -1232,7 +1320,7 @@ where /// assert_eq!(err.value, "b"); /// ``` #[unstable(feature = "map_try_insert", issue = "82766")] - pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V>> { + pub fn try_insert(&mut self, key: K, value: V) -> Result<&mut V, OccupiedError<'_, K, V, A>> { match self.entry(key) { Occupied(entry) => Err(OccupiedError { entry, value }), Vacant(entry) => Ok(entry.insert(value)), @@ -1298,11 +1386,12 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashMap +impl Clone for HashMap where K: Clone, V: Clone, S: Clone, + A: Allocator + Clone, { #[inline] fn clone(&self) -> Self { @@ -1316,13 +1405,14 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashMap +impl PartialEq for HashMap where K: Eq + Hash, V: PartialEq, S: BuildHasher, + A: Allocator, { - fn eq(&self, other: &HashMap) -> bool { + fn eq(&self, other: &HashMap) -> bool { if self.len() != other.len() { return false; } @@ -1332,19 +1422,21 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashMap +impl Eq for HashMap where K: Eq + Hash, V: Eq, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl Debug for HashMap +impl Debug for HashMap where K: Debug, V: Debug, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_map().entries(self.iter()).finish() @@ -1364,11 +1456,12 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Index<&Q> for HashMap +impl Index<&Q> for HashMap where K: Eq + Hash + Borrow, Q: Eq + Hash, S: BuildHasher, + A: Allocator, { type Output = V; @@ -1523,11 +1616,15 @@ impl Default for IterMut<'_, K, V> { /// let iter = map.into_iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - base: base::IntoIter, +pub struct IntoIter< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::IntoIter, } -impl IntoIter { +impl IntoIter { /// Returns an iterator of references over the remaining items. #[inline] pub(super) fn iter(&self) -> Iter<'_, K, V> { @@ -1656,11 +1753,16 @@ impl fmt::Debug for Values<'_, K, V> { /// ``` #[stable(feature = "drain", since = "1.6.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "hashmap_drain_ty")] -pub struct Drain<'a, K: 'a, V: 'a> { - base: base::Drain<'a, K, V>, +pub struct Drain< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::Drain<'a, K, V, A>, } -impl<'a, K, V> Drain<'a, K, V> { +impl<'a, K, V, A: Allocator> Drain<'a, K, V, A> { /// Returns an iterator of references over the remaining items. #[inline] pub(super) fn iter(&self) -> Iter<'_, K, V> { @@ -1687,8 +1789,14 @@ impl<'a, K, V> Drain<'a, K, V> { #[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, V, F> { - base: base::ExtractIf<'a, K, V, F>, +pub struct ExtractIf< + 'a, + K, + V, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::ExtractIf<'a, K, V, F, A>, } /// A mutable iterator over the values of a `HashMap`. @@ -1740,8 +1848,12 @@ impl Default for ValuesMut<'_, K, V> { /// let iter_keys = map.into_keys(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoKeys { - inner: IntoIter, +pub struct IntoKeys< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1770,8 +1882,12 @@ impl Default for IntoKeys { /// let iter_keys = map.into_values(); /// ``` #[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoValues { - inner: IntoIter, +pub struct IntoValues< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + inner: IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1789,14 +1905,19 @@ impl Default for IntoValues { /// [`entry`]: HashMap::entry #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "HashMapEntry")] -pub enum Entry<'a, K: 'a, V: 'a> { +pub enum Entry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// An occupied entry. #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V, A>), /// A vacant entry. #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V, A>), } #[stable(feature = "debug_hash_map", since = "1.12.0")] @@ -1812,12 +1933,17 @@ impl Debug for Entry<'_, K, V> { /// A view into an occupied entry in a `HashMap`. /// It is part of the [`Entry`] enum. #[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - base: base::RustcOccupiedEntry<'a, K, V>, +pub struct OccupiedEntry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::RustcOccupiedEntry<'a, K, V, A>, } #[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V> { +impl Debug for OccupiedEntry<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry") .field("key", self.key()) @@ -1829,12 +1955,17 @@ impl Debug for OccupiedEntry<'_, K, V> { /// A view into a vacant entry in a `HashMap`. /// It is part of the [`Entry`] enum. #[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - base: base::RustcVacantEntry<'a, K, V>, +pub struct VacantEntry< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::RustcVacantEntry<'a, K, V, A>, } #[stable(feature = "debug_hash_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V> { +impl Debug for VacantEntry<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("VacantEntry").field(self.key()).finish() } @@ -1844,15 +1975,20 @@ impl Debug for VacantEntry<'_, K, V> { /// /// Contains the occupied entry, and the value that was not inserted. #[unstable(feature = "map_try_insert", issue = "82766")] -pub struct OccupiedError<'a, K: 'a, V: 'a> { +pub struct OccupiedError< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// The entry in the map that was already occupied. - pub entry: OccupiedEntry<'a, K, V>, + pub entry: OccupiedEntry<'a, K, V, A>, /// The value which was not inserted, because the entry was already occupied. pub value: V, } #[unstable(feature = "map_try_insert", issue = "82766")] -impl Debug for OccupiedError<'_, K, V> { +impl Debug for OccupiedError<'_, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedError") .field("key", self.entry.key()) @@ -1863,7 +1999,7 @@ impl Debug for OccupiedError<'_, K, V> { } #[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { +impl<'a, K: Debug, V: Debug, A: Allocator> fmt::Display for OccupiedError<'a, K, V, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, @@ -1876,10 +2012,10 @@ impl<'a, K: Debug, V: Debug> fmt::Display for OccupiedError<'a, K, V> { } #[unstable(feature = "map_try_insert", issue = "82766")] -impl<'a, K: Debug, V: Debug> Error for OccupiedError<'a, K, V> {} +impl<'a, K: Debug, V: Debug, A: Allocator> Error for OccupiedError<'a, K, V, A> {} #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a HashMap { +impl<'a, K, V, S, A: Allocator> IntoIterator for &'a HashMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>; @@ -1891,7 +2027,7 @@ impl<'a, K, V, S> IntoIterator for &'a HashMap { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V, S> IntoIterator for &'a mut HashMap { +impl<'a, K, V, S, A: Allocator> IntoIterator for &'a mut HashMap { type Item = (&'a K, &'a mut V); type IntoIter = IterMut<'a, K, V>; @@ -1903,9 +2039,9 @@ impl<'a, K, V, S> IntoIterator for &'a mut HashMap { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashMap { +impl IntoIterator for HashMap { type Item = (K, V); - type IntoIter = IntoIter; + type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each key-value /// pair out of the map in arbitrary order. The map cannot be used after @@ -1927,7 +2063,7 @@ impl IntoIterator for HashMap { /// ``` #[inline] #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { + fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } } @@ -2015,7 +2151,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = (K, V); #[inline] @@ -2040,17 +2176,17 @@ impl Iterator for IntoIter { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { +impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} +impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { +impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.iter()).finish() } @@ -2169,7 +2305,7 @@ impl fmt::Debug for ValuesMut<'_, K, V> { } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoKeys { +impl Iterator for IntoKeys { type Item = K; #[inline] @@ -2194,24 +2330,24 @@ impl Iterator for IntoKeys { } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoKeys { +impl ExactSizeIterator for IntoKeys { #[inline] fn len(&self) -> usize { self.inner.len() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoKeys {} +impl FusedIterator for IntoKeys {} #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoKeys { +impl fmt::Debug for IntoKeys { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl Iterator for IntoValues { +impl Iterator for IntoValues { type Item = V; #[inline] @@ -2236,24 +2372,24 @@ impl Iterator for IntoValues { } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl ExactSizeIterator for IntoValues { +impl ExactSizeIterator for IntoValues { #[inline] fn len(&self) -> usize { self.inner.len() } } #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl FusedIterator for IntoValues {} +impl FusedIterator for IntoValues {} #[stable(feature = "map_into_keys_values", since = "1.54.0")] -impl fmt::Debug for IntoValues { +impl fmt::Debug for IntoValues { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish() } } #[stable(feature = "drain", since = "1.6.0")] -impl<'a, K, V> Iterator for Drain<'a, K, V> { +impl<'a, K, V, A: Allocator> Iterator for Drain<'a, K, V, A> { type Item = (K, V); #[inline] @@ -2274,17 +2410,17 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { } } #[stable(feature = "drain", since = "1.6.0")] -impl ExactSizeIterator for Drain<'_, K, V> { +impl ExactSizeIterator for Drain<'_, K, V, A> { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K, V> {} +impl FusedIterator for Drain<'_, K, V, A> {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K, V> +impl fmt::Debug for Drain<'_, K, V, A> where K: fmt::Debug, V: fmt::Debug, @@ -2295,7 +2431,7 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, V, F> +impl Iterator for ExtractIf<'_, K, V, F, A> where F: FnMut(&K, &mut V) -> bool, { @@ -2312,10 +2448,13 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +impl FusedIterator for ExtractIf<'_, K, V, F, A> where + F: FnMut(&K, &mut V) -> bool +{ +} #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, V, F> +impl fmt::Debug for ExtractIf<'_, K, V, F, A> where K: fmt::Debug, V: fmt::Debug, @@ -2325,7 +2464,7 @@ where } } -impl<'a, K, V> Entry<'a, K, V> { +impl<'a, K, V, A: Allocator> Entry<'a, K, V, A> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. /// @@ -2473,7 +2612,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// ``` #[inline] #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { entry.insert(value); @@ -2510,7 +2649,7 @@ impl<'a, K, V: Default> Entry<'a, K, V> { } } -impl<'a, K, V> OccupiedEntry<'a, K, V> { +impl<'a, K, V, A: Allocator> OccupiedEntry<'a, K, V, A> { /// Gets a reference to the key in the entry. /// /// # Examples @@ -2682,7 +2821,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { } } -impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { +impl<'a, K: 'a, V: 'a, A: Allocator> VacantEntry<'a, K, V, A> { /// Gets a reference to the key that would be used when inserting a value /// through the `VacantEntry`. /// @@ -2760,7 +2899,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// ``` #[inline] #[stable(feature = "entry_insert", since = "1.83.0")] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> { + pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { let base = self.base.insert_entry(value); OccupiedEntry { base } } @@ -2786,10 +2925,11 @@ where /// Inserts all new key-values from the iterator and replaces values with existing /// keys with new values returned from the iterator. #[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for HashMap +impl Extend<(K, V)> for HashMap where K: Eq + Hash, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: T) { @@ -2808,11 +2948,12 @@ where } #[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap +impl<'a, K, V, S, A> Extend<(&'a K, &'a V)> for HashMap where K: Eq + Hash + Copy, V: Copy, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: T) { @@ -2831,7 +2972,9 @@ where } #[inline] -fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K, V> { +fn map_entry<'a, K: 'a, V: 'a, A: Allocator>( + raw: base::RustcEntry<'a, K, V, A>, +) -> Entry<'a, K, V, A> { match raw { base::RustcEntry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), base::RustcEntry::Vacant(base) => Entry::Vacant(VacantEntry { base }), diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index 6795da80aacb..02a4a0d9c815 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -4,6 +4,7 @@ mod tests; use hashbrown::hash_set as base; use super::map::map_try_reserve_error; +use crate::alloc::{Allocator, Global}; use crate::borrow::Borrow; use crate::collections::TryReserveError; use crate::fmt; @@ -122,8 +123,12 @@ use crate::ops::{BitAnd, BitOr, BitXor, Sub}; /// ``` #[cfg_attr(not(test), rustc_diagnostic_item = "HashSet")] #[stable(feature = "rust1", since = "1.0.0")] -pub struct HashSet { - base: base::HashSet, +pub struct HashSet< + T, + S = RandomState, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::HashSet, } impl HashSet { @@ -166,7 +171,141 @@ impl HashSet { } } +impl HashSet { + /// Creates an empty `HashSet` in the provided allocator. + /// + /// The hash set is initially created with a capacity of 0, so it will not allocate until it + /// is first inserted into. + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn new_in(alloc: A) -> HashSet { + HashSet::with_hasher_in(Default::default(), alloc) + } + + /// Creates an empty `HashSet` with at least the specified capacity. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet = HashSet::with_capacity(10); + /// assert!(set.capacity() >= 10); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_in(capacity: usize, alloc: A) -> HashSet { + HashSet::with_capacity_and_hasher_in(capacity, Default::default(), alloc) + } +} + impl HashSet { + /// Creates a new empty hash set which will use the given hasher to hash + /// keys. + /// + /// The hash set is also created with the default initial capacity. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_hasher(s); + /// set.insert(2); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] + pub const fn with_hasher(hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_hasher(hasher) } + } + + /// Creates an empty `HashSet` with at least the specified capacity, using + /// `hasher` to hash the keys. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// use std::hash::RandomState; + /// + /// let s = RandomState::new(); + /// let mut set = HashSet::with_capacity_and_hasher(10, s); + /// set.insert(1); + /// ``` + #[inline] + #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] + pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } + } +} + +impl HashSet { + /// Creates a new empty hash set which will use the given hasher to hash + /// keys and will allocate memory using the provided allocator. + /// + /// The hash set is also created with the default initial capacity. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_hasher_in(hasher: S, alloc: A) -> HashSet { + HashSet { base: base::HashSet::with_hasher_in(hasher, alloc) } + } + + /// Creates an empty `HashSet` with at least the specified capacity, using + /// `hasher` to hash the keys and `alloc` to allocate memory. + /// + /// The hash set will be able to hold at least `capacity` elements without + /// reallocating. This method is allowed to allocate for more elements than + /// `capacity`. If `capacity` is zero, the hash set will not allocate. + /// + /// Warning: `hasher` is normally randomly generated, and + /// is designed to allow `HashSet`s to be resistant to attacks that + /// cause many collisions and very poor performance. Setting it + /// manually using this function can expose a DoS attack vector. + /// + /// The `hash_builder` passed should implement the [`BuildHasher`] trait for + /// the `HashSet` to be useful, see its documentation for details. + #[inline] + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn with_capacity_and_hasher_in(capacity: usize, hasher: S, alloc: A) -> HashSet { + HashSet { base: base::HashSet::with_capacity_and_hasher_in(capacity, hasher, alloc) } + } + /// Returns the number of elements the set can hold without reallocating. /// /// # Examples @@ -272,7 +411,7 @@ impl HashSet { #[inline] #[rustc_lint_query_instability] #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain<'_, T> { + pub fn drain(&mut self) -> Drain<'_, T, A> { Drain { base: self.base.drain() } } @@ -309,7 +448,7 @@ impl HashSet { #[inline] #[rustc_lint_query_instability] #[stable(feature = "hash_extract_if", since = "1.88.0")] - pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F> + pub fn extract_if(&mut self, pred: F) -> ExtractIf<'_, T, F, A> where F: FnMut(&T) -> bool, { @@ -362,67 +501,6 @@ impl HashSet { self.base.clear() } - /// Creates a new empty hash set which will use the given hasher to hash - /// keys. - /// - /// The hash set is also created with the default initial capacity. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_hasher(s); - /// set.insert(2); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - #[rustc_const_stable(feature = "const_collections_with_hasher", since = "1.85.0")] - pub const fn with_hasher(hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_hasher(hasher) } - } - - /// Creates an empty `HashSet` with at least the specified capacity, using - /// `hasher` to hash the keys. - /// - /// The hash set will be able to hold at least `capacity` elements without - /// reallocating. This method is allowed to allocate for more elements than - /// `capacity`. If `capacity` is zero, the hash set will not allocate. - /// - /// Warning: `hasher` is normally randomly generated, and - /// is designed to allow `HashSet`s to be resistant to attacks that - /// cause many collisions and very poor performance. Setting it - /// manually using this function can expose a DoS attack vector. - /// - /// The `hash_builder` passed should implement the [`BuildHasher`] trait for - /// the `HashSet` to be useful, see its documentation for details. - /// - /// # Examples - /// - /// ``` - /// use std::collections::HashSet; - /// use std::hash::RandomState; - /// - /// let s = RandomState::new(); - /// let mut set = HashSet::with_capacity_and_hasher(10, s); - /// set.insert(1); - /// ``` - #[inline] - #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] - pub fn with_capacity_and_hasher(capacity: usize, hasher: S) -> HashSet { - HashSet { base: base::HashSet::with_capacity_and_hasher(capacity, hasher) } - } - /// Returns a reference to the set's [`BuildHasher`]. /// /// # Examples @@ -442,10 +520,11 @@ impl HashSet { } } -impl HashSet +impl HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { /// Reserves capacity for at least `additional` more elements to be inserted /// in the `HashSet`. The collection may reserve more space to speculatively @@ -569,7 +648,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S> { + pub fn difference<'a>(&'a self, other: &'a HashSet) -> Difference<'a, T, S, A> { Difference { iter: self.iter(), other } } @@ -599,8 +678,8 @@ where #[stable(feature = "rust1", since = "1.0.0")] pub fn symmetric_difference<'a>( &'a self, - other: &'a HashSet, - ) -> SymmetricDifference<'a, T, S> { + other: &'a HashSet, + ) -> SymmetricDifference<'a, T, S, A> { SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } } @@ -631,7 +710,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S> { + pub fn intersection<'a>(&'a self, other: &'a HashSet) -> Intersection<'a, T, S, A> { if self.len() <= other.len() { Intersection { iter: self.iter(), other } } else { @@ -660,7 +739,7 @@ where #[inline] #[rustc_lint_query_instability] #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S> { + pub fn union<'a>(&'a self, other: &'a HashSet) -> Union<'a, T, S, A> { if self.len() >= other.len() { Union { iter: self.iter().chain(other.difference(self)) } } else { @@ -812,7 +891,7 @@ where /// ``` #[inline] #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn entry(&mut self, value: T) -> Entry<'_, T, S> { + pub fn entry(&mut self, value: T) -> Entry<'_, T, S, A> { map_entry(self.base.entry(value)) } @@ -834,7 +913,7 @@ where /// assert_eq!(a.is_disjoint(&b), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &HashSet) -> bool { + pub fn is_disjoint(&self, other: &HashSet) -> bool { if self.len() <= other.len() { self.iter().all(|v| !other.contains(v)) } else { @@ -860,7 +939,7 @@ where /// assert_eq!(set.is_subset(&sup), false); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &HashSet) -> bool { + pub fn is_subset(&self, other: &HashSet) -> bool { if self.len() <= other.len() { self.iter().all(|v| other.contains(v)) } else { false } } @@ -886,7 +965,7 @@ where /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &HashSet) -> bool { + pub fn is_superset(&self, other: &HashSet) -> bool { other.is_subset(self) } @@ -995,7 +1074,7 @@ where } #[inline] -fn map_entry<'a, K: 'a, V: 'a>(raw: base::Entry<'a, K, V>) -> Entry<'a, K, V> { +fn map_entry<'a, K: 'a, V: 'a, A: Allocator>(raw: base::Entry<'a, K, V, A>) -> Entry<'a, K, V, A> { match raw { base::Entry::Occupied(base) => Entry::Occupied(OccupiedEntry { base }), base::Entry::Vacant(base) => Entry::Vacant(VacantEntry { base }), @@ -1003,10 +1082,11 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::Entry<'a, K, V>) -> Entry<'a, K, V> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for HashSet +impl Clone for HashSet where T: Clone, S: Clone, + A: Allocator + Clone, { #[inline] fn clone(&self) -> Self { @@ -1024,12 +1104,13 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for HashSet +impl PartialEq for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { - fn eq(&self, other: &HashSet) -> bool { + fn eq(&self, other: &HashSet) -> bool { if self.len() != other.len() { return false; } @@ -1039,17 +1120,19 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for HashSet +impl Eq for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for HashSet +impl fmt::Debug for HashSet where T: fmt::Debug, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_set().entries(self.iter()).finish() @@ -1107,10 +1190,11 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Extend for HashSet +impl Extend for HashSet where T: Eq + Hash, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: I) { @@ -1129,10 +1213,11 @@ where } #[stable(feature = "hash_extend_copy", since = "1.4.0")] -impl<'a, T, S> Extend<&'a T> for HashSet +impl<'a, T, S, A> Extend<&'a T> for HashSet where T: 'a + Eq + Hash + Copy, S: BuildHasher, + A: Allocator, { #[inline] fn extend>(&mut self, iter: I) { @@ -1341,8 +1426,11 @@ impl Default for Iter<'_, K> { /// let mut iter = a.into_iter(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - base: base::IntoIter, +pub struct IntoIter< + K, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::IntoIter, } #[stable(feature = "default_iters_hash", since = "1.83.0")] @@ -1371,8 +1459,12 @@ impl Default for IntoIter { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "hashset_drain_ty")] -pub struct Drain<'a, K: 'a> { - base: base::Drain<'a, K>, +pub struct Drain< + 'a, + K: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::Drain<'a, K, A>, } /// A draining, filtering iterator over the items of a `HashSet`. @@ -1393,8 +1485,13 @@ pub struct Drain<'a, K: 'a> { #[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>, +pub struct ExtractIf< + 'a, + K, + F, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::ExtractIf<'a, K, F, A>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1417,11 +1514,16 @@ pub struct ExtractIf<'a, K, F> { #[must_use = "this returns the intersection as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection<'a, T: 'a, S: 'a> { +pub struct Intersection< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { // iterator of the first set iter: Iter<'a, T>, // the second set - other: &'a HashSet, + other: &'a HashSet, } /// A lazy iterator producing elements in the difference of `HashSet`s. @@ -1444,11 +1546,16 @@ pub struct Intersection<'a, T: 'a, S: 'a> { #[must_use = "this returns the difference as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference<'a, T: 'a, S: 'a> { +pub struct Difference< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { // iterator of the first set iter: Iter<'a, T>, // the second set - other: &'a HashSet, + other: &'a HashSet, } /// A lazy iterator producing elements in the symmetric difference of `HashSet`s. @@ -1471,8 +1578,13 @@ pub struct Difference<'a, T: 'a, S: 'a> { #[must_use = "this returns the difference as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, +pub struct SymmetricDifference< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: Chain, Difference<'a, T, S, A>>, } /// A lazy iterator producing elements in the union of `HashSet`s. @@ -1495,12 +1607,17 @@ pub struct SymmetricDifference<'a, T: 'a, S: 'a> { #[must_use = "this returns the union as an iterator, \ without modifying either input set"] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a, S: 'a> { - iter: Chain, Difference<'a, T, S>>, +pub struct Union< + 'a, + T: 'a, + S: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + iter: Chain, Difference<'a, T, S, A>>, } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> IntoIterator for &'a HashSet { +impl<'a, T, S, A: Allocator> IntoIterator for &'a HashSet { type Item = &'a T; type IntoIter = Iter<'a, T>; @@ -1512,9 +1629,9 @@ impl<'a, T, S> IntoIterator for &'a HashSet { } #[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for HashSet { +impl IntoIterator for HashSet { type Item = T; - type IntoIter = IntoIter; + type IntoIter = IntoIter; /// Creates a consuming iterator, that is, one that moves each value out /// of the set in arbitrary order. The set cannot be used after calling @@ -1538,7 +1655,7 @@ impl IntoIterator for HashSet { /// ``` #[inline] #[rustc_lint_query_instability] - fn into_iter(self) -> IntoIter { + fn into_iter(self) -> IntoIter { IntoIter { base: self.base.into_iter() } } } @@ -1593,7 +1710,7 @@ impl fmt::Debug for Iter<'_, K> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { +impl Iterator for IntoIter { type Item = K; #[inline] @@ -1618,24 +1735,24 @@ impl Iterator for IntoIter { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { +impl ExactSizeIterator for IntoIter { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} +impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for IntoIter { +impl fmt::Debug for IntoIter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.base, f) } } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K> Iterator for Drain<'a, K> { +impl<'a, K, A: Allocator> Iterator for Drain<'a, K, A> { type Item = K; #[inline] @@ -1656,24 +1773,24 @@ impl<'a, K> Iterator for Drain<'a, K> { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Drain<'_, K> { +impl ExactSizeIterator for Drain<'_, K, A> { #[inline] fn len(&self) -> usize { self.base.len() } } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Drain<'_, K> {} +impl FusedIterator for Drain<'_, K, A> {} #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Drain<'_, K> { +impl fmt::Debug for Drain<'_, K, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.base, f) } } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl Iterator for ExtractIf<'_, K, F> +impl Iterator for ExtractIf<'_, K, F, A> where F: FnMut(&K) -> bool, { @@ -1690,10 +1807,10 @@ where } #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl FusedIterator for ExtractIf<'_, K, F> where F: FnMut(&K) -> bool {} +impl FusedIterator for ExtractIf<'_, K, F, A> where F: FnMut(&K) -> bool {} #[stable(feature = "hash_extract_if", since = "1.88.0")] -impl fmt::Debug for ExtractIf<'_, K, F> +impl fmt::Debug for ExtractIf<'_, K, F, A> where K: fmt::Debug, { @@ -1703,7 +1820,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Intersection<'_, T, S> { +impl Clone for Intersection<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Intersection { iter: self.iter.clone(), ..*self } @@ -1711,10 +1828,11 @@ impl Clone for Intersection<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Intersection<'a, T, S> +impl<'a, T, S, A> Iterator for Intersection<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1745,10 +1863,11 @@ where } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Intersection<'_, T, S> +impl fmt::Debug for Intersection<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1756,15 +1875,16 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Intersection<'_, T, S> +impl FusedIterator for Intersection<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Difference<'_, T, S> { +impl Clone for Difference<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Difference { iter: self.iter.clone(), ..*self } @@ -1772,10 +1892,11 @@ impl Clone for Difference<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Difference<'a, T, S> +impl<'a, T, S, A> Iterator for Difference<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1806,18 +1927,20 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Difference<'_, T, S> +impl FusedIterator for Difference<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Difference<'_, T, S> +impl fmt::Debug for Difference<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1825,7 +1948,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for SymmetricDifference<'_, T, S> { +impl Clone for SymmetricDifference<'_, T, S, A> { #[inline] fn clone(&self) -> Self { SymmetricDifference { iter: self.iter.clone() } @@ -1833,10 +1956,11 @@ impl Clone for SymmetricDifference<'_, T, S> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> +impl<'a, T, S, A> Iterator for SymmetricDifference<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1859,18 +1983,20 @@ where } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for SymmetricDifference<'_, T, S> +impl FusedIterator for SymmetricDifference<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for SymmetricDifference<'_, T, S> +impl fmt::Debug for SymmetricDifference<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1878,7 +2004,7 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Union<'_, T, S> { +impl Clone for Union<'_, T, S, A> { #[inline] fn clone(&self) -> Self { Union { iter: self.iter.clone() } @@ -1886,7 +2012,7 @@ impl Clone for Union<'_, T, S> { } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Union<'_, T, S> +impl FusedIterator for Union<'_, T, S, A> where T: Eq + Hash, S: BuildHasher, @@ -1894,10 +2020,11 @@ where } #[stable(feature = "std_debug", since = "1.16.0")] -impl fmt::Debug for Union<'_, T, S> +impl fmt::Debug for Union<'_, T, S, A> where T: fmt::Debug + Eq + Hash, S: BuildHasher, + A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() @@ -1905,10 +2032,11 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T, S> Iterator for Union<'a, T, S> +impl<'a, T, S, A> Iterator for Union<'a, T, S, A> where T: Eq + Hash, S: BuildHasher, + A: Allocator, { type Item = &'a T; @@ -1973,7 +2101,12 @@ where /// assert_eq!(vec, ["a", "b", "c", "d", "e"]); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub enum Entry<'a, T, S> { +pub enum Entry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { /// An occupied entry. /// /// # Examples @@ -1990,7 +2123,7 @@ pub enum Entry<'a, T, S> { /// Entry::Occupied(_) => { } /// } /// ``` - Occupied(OccupiedEntry<'a, T, S>), + Occupied(OccupiedEntry<'a, T, S, A>), /// A vacant entry. /// @@ -2008,11 +2141,11 @@ pub enum Entry<'a, T, S> { /// Entry::Vacant(_) => { } /// } /// ``` - Vacant(VacantEntry<'a, T, S>), + Vacant(VacantEntry<'a, T, S, A>), } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for Entry<'_, T, S> { +impl fmt::Debug for Entry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Entry::Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), @@ -2060,12 +2193,17 @@ impl fmt::Debug for Entry<'_, T, S> { /// assert_eq!(set.len(), 2); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct OccupiedEntry<'a, T, S> { - base: base::OccupiedEntry<'a, T, S>, +pub struct OccupiedEntry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::OccupiedEntry<'a, T, S, A>, } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for OccupiedEntry<'_, T, S> { +impl fmt::Debug for OccupiedEntry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OccupiedEntry").field("value", self.get()).finish() } @@ -2100,18 +2238,23 @@ impl fmt::Debug for OccupiedEntry<'_, T, S> { /// assert!(set.contains("b") && set.len() == 2); /// ``` #[unstable(feature = "hash_set_entry", issue = "60896")] -pub struct VacantEntry<'a, T, S> { - base: base::VacantEntry<'a, T, S>, +pub struct VacantEntry< + 'a, + T, + S, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + base: base::VacantEntry<'a, T, S, A>, } #[unstable(feature = "hash_set_entry", issue = "60896")] -impl fmt::Debug for VacantEntry<'_, T, S> { +impl fmt::Debug for VacantEntry<'_, T, S, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("VacantEntry").field(self.get()).finish() } } -impl<'a, T, S> Entry<'a, T, S> { +impl<'a, T, S, A: Allocator> Entry<'a, T, S, A> { /// Sets the value of the entry, and returns an OccupiedEntry. /// /// # Examples @@ -2128,7 +2271,7 @@ impl<'a, T, S> Entry<'a, T, S> { /// ``` #[inline] #[unstable(feature = "hash_set_entry", issue = "60896")] - pub fn insert(self) -> OccupiedEntry<'a, T, S> + pub fn insert(self) -> OccupiedEntry<'a, T, S, A> where T: Hash, S: BuildHasher, @@ -2198,7 +2341,7 @@ impl<'a, T, S> Entry<'a, T, S> { } } -impl OccupiedEntry<'_, T, S> { +impl OccupiedEntry<'_, T, S, A> { /// Gets a reference to the value in the entry. /// /// # Examples @@ -2255,7 +2398,7 @@ impl OccupiedEntry<'_, T, S> { } } -impl<'a, T, S> VacantEntry<'a, T, S> { +impl<'a, T, S, A: Allocator> VacantEntry<'a, T, S, A> { /// Gets a reference to the value that would be used when inserting /// through the `VacantEntry`. /// @@ -2325,7 +2468,7 @@ impl<'a, T, S> VacantEntry<'a, T, S> { } #[inline] - fn insert_entry(self) -> OccupiedEntry<'a, T, S> + fn insert_entry(self) -> OccupiedEntry<'a, T, S, A> where T: Hash, S: BuildHasher, diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 4edd35b310bd..a24baad61501 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -3446,30 +3446,48 @@ impl DirBuilder { } fn create_dir_all(&self, path: &Path) -> io::Result<()> { - if path == Path::new("") { + // if path's parent is None, it is "/" path, which should + // return Ok immediately + if path == Path::new("") || path.parent() == None { return Ok(()); } - match self.inner.mkdir(path) { - Ok(()) => return Ok(()), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - Err(_) if path.is_dir() => return Ok(()), - Err(e) => return Err(e), - } - match path.parent() { - Some(p) => self.create_dir_all(p)?, - None => { - return Err(io::const_error!( - io::ErrorKind::Uncategorized, - "failed to create whole tree", - )); + let ancestors = path.ancestors(); + let mut uncreated_dirs = 0; + + for ancestor in ancestors { + // for relative paths like "foo/bar", the parent of + // "foo" will be "" which there's no need to invoke + // a mkdir syscall on + if ancestor == Path::new("") || ancestor.parent() == None { + break; + } + + match self.inner.mkdir(ancestor) { + Ok(()) => break, + Err(e) if e.kind() == io::ErrorKind::NotFound => uncreated_dirs += 1, + // we check if the err is AlreadyExists for two reasons + // - in case the path exists as a *file* + // - and to avoid calls to .is_dir() in case of other errs + // (i.e. PermissionDenied) + Err(e) if e.kind() == io::ErrorKind::AlreadyExists && ancestor.is_dir() => break, + Err(e) => return Err(e), } } - match self.inner.mkdir(path) { - Ok(()) => Ok(()), - Err(_) if path.is_dir() => Ok(()), - Err(e) => Err(e), + + // collect only the uncreated directories w/o letting the vec resize + let mut uncreated_dirs_vec = Vec::with_capacity(uncreated_dirs); + uncreated_dirs_vec.extend(ancestors.take(uncreated_dirs)); + + for uncreated_dir in uncreated_dirs_vec.iter().rev() { + if let Err(e) = self.inner.mkdir(uncreated_dir) { + if e.kind() != io::ErrorKind::AlreadyExists || !uncreated_dir.is_dir() { + return Err(e); + } + } } + + Ok(()) } } diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 528eb185df08..898ed0f7469c 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -187,7 +187,7 @@ pub struct SimpleMessage { /// Err(FAIL) /// } /// ``` -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] #[unstable(feature = "io_const_error", issue = "133448")] #[allow_internal_unstable(hint_must_use, io_const_error_internals)] pub macro const_error($kind:expr, $message:expr $(,)?) { diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 7c7ef7b2f701..e4a30b80e3df 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -37,7 +37,8 @@ fn connect_error() { e.kind() == ErrorKind::ConnectionRefused || e.kind() == ErrorKind::InvalidInput || e.kind() == ErrorKind::AddrInUse - || e.kind() == ErrorKind::AddrNotAvailable, + || e.kind() == ErrorKind::AddrNotAvailable + || e.kind() == ErrorKind::NetworkUnreachable, "bad error: {} {:?}", e, e.kind() diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 2acac6f086e4..8a39fe073bf9 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -77,7 +77,11 @@ impl BorrowedFd<'_> { /// # Safety /// /// The resource pointed to by `fd` must remain open for the duration of - /// the returned `BorrowedFd`, and it must not have the value `-1`. + /// the returned `BorrowedFd`. + /// + /// # Panics + /// + /// Panics if the raw file descriptor has the value `-1`. #[inline] #[track_caller] #[rustc_const_stable(feature = "io_safety", since = "1.63.0")] @@ -177,6 +181,10 @@ impl FromRawFd for OwnedFd { /// [ownership][io-safety]. The resource must not require any cleanup other than `close`. /// /// [io-safety]: io#io-safety + /// + /// # Panics + /// + /// Panics if the raw file descriptor has the value `-1`. #[inline] #[track_caller] unsafe fn from_raw_fd(fd: RawFd) -> Self { diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 6d4090ee31cf..708ebaec362e 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -92,9 +92,138 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::io::{self, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::*; +#[allow(unused_imports)] // not used on all targets +use crate::sys::cvt; // Tests for this module #[cfg(test)] mod tests; + +#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] +pub trait StdioExt: crate::sealed::Sealed { + /// Redirects the stdio file descriptor to point to the file description underpinning `fd`. + /// + /// Rust std::io write buffers (if any) are flushed, but other runtimes + /// (e.g. C stdio) or libraries that acquire a clone of the file descriptor + /// will not be aware of this change. + /// + /// # Platform-specific behavior + /// + /// This is [currently] implemented using + /// + /// - `fd_renumber` on wasip1 + /// - `dup2` on most unixes + /// + /// [currently]: crate::io#platform-specific-behavior + /// + /// ``` + /// #![feature(stdio_swap)] + /// use std::io::{self, Read, Write}; + /// use std::os::unix::io::StdioExt; + /// + /// fn main() -> io::Result<()> { + /// let (reader, mut writer) = io::pipe()?; + /// let mut stdin = io::stdin(); + /// stdin.set_fd(reader)?; + /// writer.write_all(b"Hello, world!")?; + /// let mut buffer = vec![0; 13]; + /// assert_eq!(stdin.read(&mut buffer)?, 13); + /// assert_eq!(&buffer, b"Hello, world!"); + /// Ok(()) + /// } + /// ``` + fn set_fd>(&mut self, fd: T) -> io::Result<()>; + + /// Redirects the stdio file descriptor and returns a new `OwnedFd` + /// backed by the previous file description. + /// + /// See [`set_fd()`] for details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn replace_fd>(&mut self, replace_with: T) -> io::Result; + + /// Redirects the stdio file descriptor to the null device (`/dev/null`) + /// and returns a new `OwnedFd` backed by the previous file description. + /// + /// Programs that communicate structured data via stdio can use this early in `main()` to + /// extract the fds, treat them as other IO types (`File`, `UnixStream`, etc), + /// apply custom buffering or avoid interference from stdio use later in the program. + /// + /// See [`set_fd()`] for additional details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn take_fd(&mut self) -> io::Result; +} + +macro io_ext_impl($stdio_ty:ty, $stdio_lock_ty:ty, $writer:literal) { + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + self.lock().set_fd(fd) + } + + fn take_fd(&mut self) -> io::Result { + self.lock().take_fd() + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + self.lock().replace_fd(replace_with) + } + } + + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_lock_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + #[cfg($writer)] + self.flush()?; + replace_stdio_fd(self.as_fd(), fd.into()) + } + + fn take_fd(&mut self) -> io::Result { + let null = null_fd()?; + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(null)?; + Ok(cloned) + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(replace_with)?; + Ok(cloned) + } + } +} + +io_ext_impl!(Stdout, StdoutLock<'_>, true); +io_ext_impl!(Stdin, StdinLock<'_>, false); +io_ext_impl!(Stderr, StderrLock<'_>, true); + +fn null_fd() -> io::Result { + let null_dev = crate::fs::OpenOptions::new().read(true).write(true).open("/dev/null")?; + Ok(null_dev.into()) +} + +/// Replaces the underlying file descriptor with the one from `other`. +/// Does not set CLOEXEC. +fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { + cfg_select! { + all(target_os = "wasi", target_env = "p1") => { + cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ()) + } + not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )) => { + cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ()) + } + _ => { + let _ = (this, other); + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index beddc328582c..658026a8020f 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -212,7 +212,7 @@ impl fmt::Display for PanicHookInfo<'_> { #[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")] #[allow_internal_unstable(libstd_sys_internals, const_format_args, panic_internals, rt)] #[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_2015_macro")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro panic_2015 { () => ({ $crate::rt::begin_panic("explicit panic") diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 74cacd6caff7..8061068e447a 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -6,13 +6,13 @@ use crate::fs::TryLockError; use crate::hash::Hash; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::path::{Path, PathBuf}; -pub use crate::sys::fs::common::Dir; +pub use crate::sys::fs::common::{Dir, remove_dir_all}; use crate::sys::pal::{helpers, unsupported}; use crate::sys::time::SystemTime; const FILE_PERMISSIONS_MASK: u64 = r_efi::protocols::file::READ_ONLY; -pub struct File(!); +pub struct File(uefi_fs::File); #[derive(Clone)] pub struct FileAttr { @@ -244,9 +244,11 @@ impl OpenOptions { pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; + if create_new { + self.create(true); + } } - #[expect(dead_code)] const fn is_mode_valid(&self) -> bool { // Valid Combinations: Read, Read/Write, Read/Write/Create self.mode == file::MODE_READ @@ -256,100 +258,142 @@ impl OpenOptions { } impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + if !opts.is_mode_valid() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "Invalid open options")); + } + + if opts.create_new && exists(path)? { + return Err(io::const_error!(io::ErrorKind::AlreadyExists, "File already exists")); + } + + let f = uefi_fs::File::from_path(path, opts.mode, 0).map(Self)?; + + if opts.truncate { + f.truncate(0)?; + } + + if opts.append { + f.seek(io::SeekFrom::End(0))?; + } + + Ok(f) } pub fn file_attr(&self) -> io::Result { - self.0 + self.0.file_info().map(FileAttr::from_uefi) } pub fn fsync(&self) -> io::Result<()> { - self.0 + self.datasync() } pub fn datasync(&self) -> io::Result<()> { - self.0 + self.0.flush() } pub fn lock(&self) -> io::Result<()> { - self.0 + unsupported() } pub fn lock_shared(&self) -> io::Result<()> { - self.0 + unsupported() } pub fn try_lock(&self) -> Result<(), TryLockError> { - self.0 + unsupported().map_err(TryLockError::Error) } pub fn try_lock_shared(&self) -> Result<(), TryLockError> { - self.0 + unsupported().map_err(TryLockError::Error) } pub fn unlock(&self) -> io::Result<()> { - self.0 + unsupported() } - pub fn truncate(&self, _size: u64) -> io::Result<()> { - self.0 + pub fn truncate(&self, size: u64) -> io::Result<()> { + let mut file_info = self.0.file_info()?; + + unsafe { (*file_info.as_mut_ptr()).file_size = size }; + + self.0.set_file_info(file_info) } - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - self.0 + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) } - pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + crate::io::default_read_vectored(|b| self.read(b), bufs) } pub fn is_read_vectored(&self) -> bool { - self.0 + false } - pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> { - self.0 + pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> { + crate::io::default_read_buf(|buf| self.read(buf), cursor) } - pub fn write(&self, _buf: &[u8]) -> io::Result { - self.0 + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) } - pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result { - self.0 + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { + crate::io::default_write_vectored(|b| self.write(b), bufs) } pub fn is_write_vectored(&self) -> bool { - self.0 + false } + // Write::flush is only meant for buffered writers. So should be noop for unbuffered files. pub fn flush(&self) -> io::Result<()> { - self.0 + Ok(()) } - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - self.0 + pub fn seek(&self, pos: SeekFrom) -> io::Result { + const NEG_OFF_ERR: io::Error = + io::const_error!(io::ErrorKind::InvalidInput, "cannot seek to negative offset."); + + let off = match pos { + SeekFrom::Start(p) => p, + SeekFrom::End(p) => { + // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file. + if p == 0 { + 0xFFFFFFFFFFFFFFFF + } else { + self.file_attr()?.size().checked_add_signed(p).ok_or(NEG_OFF_ERR)? + } + } + SeekFrom::Current(p) => self.tell()?.checked_add_signed(p).ok_or(NEG_OFF_ERR)?, + }; + + self.0.set_position(off).map(|_| off) } pub fn size(&self) -> Option> { - self.0 + match self.file_attr() { + Ok(x) => Some(Ok(x.size())), + Err(e) => Some(Err(e)), + } } pub fn tell(&self) -> io::Result { - self.0 + self.0.position() } pub fn duplicate(&self) -> io::Result { - self.0 + unsupported() } - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - self.0 + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + set_perm_inner(&self.0, perm) } - pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { - self.0 + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + set_times_inner(&self.0, times) } } @@ -364,8 +408,10 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut b = f.debug_struct("File"); + b.field("path", &self.0.path()); + b.finish() } } @@ -435,14 +481,7 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> { pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; - let mut file_info = f.file_info()?; - - unsafe { - (*file_info.as_mut_ptr()).attribute = - ((*file_info.as_ptr()).attribute & !FILE_PERMISSIONS_MASK) | perm.to_attr() - }; - - f.set_file_info(file_info) + set_perm_inner(&f, perm) } pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { @@ -452,21 +491,7 @@ pub fn set_times(p: &Path, times: FileTimes) -> io::Result<()> { pub fn set_times_nofollow(p: &Path, times: FileTimes) -> io::Result<()> { let f = uefi_fs::File::from_path(p, file::MODE_READ | file::MODE_WRITE, 0)?; - let mut file_info = f.file_info()?; - - if let Some(x) = times.accessed { - unsafe { - (*file_info.as_mut_ptr()).last_access_time = uefi_fs::systemtime_to_uefi(x); - } - } - - if let Some(x) = times.modified { - unsafe { - (*file_info.as_mut_ptr()).modification_time = uefi_fs::systemtime_to_uefi(x); - } - } - - f.set_file_info(file_info) + set_times_inner(&f, times) } pub fn rmdir(p: &Path) -> io::Result<()> { @@ -481,10 +506,6 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } } -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() -} - pub fn exists(path: &Path) -> io::Result { let f = uefi_fs::File::from_path(path, r_efi::protocols::file::MODE_READ, 0); match f { @@ -524,6 +545,35 @@ pub fn copy(_from: &Path, _to: &Path) -> io::Result { unsupported() } +fn set_perm_inner(f: &uefi_fs::File, perm: FilePermissions) -> io::Result<()> { + let mut file_info = f.file_info()?; + + unsafe { + (*file_info.as_mut_ptr()).attribute = + ((*file_info.as_ptr()).attribute & !FILE_PERMISSIONS_MASK) | perm.to_attr() + }; + + f.set_file_info(file_info) +} + +fn set_times_inner(f: &uefi_fs::File, times: FileTimes) -> io::Result<()> { + let mut file_info = f.file_info()?; + + if let Some(x) = times.accessed { + unsafe { + (*file_info.as_mut_ptr()).last_access_time = uefi_fs::systemtime_to_uefi(x); + } + } + + if let Some(x) = times.modified { + unsafe { + (*file_info.as_mut_ptr()).modification_time = uefi_fs::systemtime_to_uefi(x); + } + } + + f.set_file_info(file_info) +} + mod uefi_fs { use r_efi::protocols::{device_path, file, simple_file_system}; @@ -633,6 +683,19 @@ mod uefi_fs { Ok(p) } + pub(crate) fn read(&self, buf: &mut [u8]) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = buf.len(); + + let r = unsafe { ((*file_ptr).read)(file_ptr, &mut buf_size, buf.as_mut_ptr().cast()) }; + + if buf_size == 0 && r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(buf_size) + } + } + pub(crate) fn read_dir_entry(&self) -> io::Result>> { let file_ptr = self.protocol.as_ptr(); let mut buf_size = 0; @@ -659,6 +722,25 @@ mod uefi_fs { } } + pub(crate) fn write(&self, buf: &[u8]) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut buf_size = buf.len(); + + let r = unsafe { + ((*file_ptr).write)( + file_ptr, + &mut buf_size, + buf.as_ptr().cast::().cast_mut(), + ) + }; + + if buf_size == 0 && r.is_error() { + Err(io::Error::from_raw_os_error(r.as_usize())) + } else { + Ok(buf_size) + } + } + pub(crate) fn file_info(&self) -> io::Result> { let file_ptr = self.protocol.as_ptr(); let mut info_id = file::INFO_ID; @@ -701,6 +783,20 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + pub(crate) fn position(&self) -> io::Result { + let file_ptr = self.protocol.as_ptr(); + let mut pos = 0; + + let r = unsafe { ((*file_ptr).get_position)(file_ptr, &mut pos) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(pos) } + } + + pub(crate) fn set_position(&self, pos: u64) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let r = unsafe { ((*file_ptr).set_position)(file_ptr, pos) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + pub(crate) fn delete(self) -> io::Result<()> { let file_ptr = self.protocol.as_ptr(); let r = unsafe { ((*file_ptr).delete)(file_ptr) }; @@ -711,6 +807,12 @@ mod uefi_fs { if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } } + pub(crate) fn flush(&self) -> io::Result<()> { + let file_ptr = self.protocol.as_ptr(); + let r = unsafe { ((*file_ptr).flush)(file_ptr) }; + if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) } + } + pub(crate) fn path(&self) -> &Path { &self.path } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 327b0eb7468a..bb2ee6ae10ed 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -2296,7 +2296,7 @@ fn open_to_and_set_permissions( _reader_metadata: &crate::fs::Metadata, ) -> io::Result<(crate::fs::File, crate::fs::Metadata)> { use crate::fs::OpenOptions; - let writer = OpenOptions::new().open(to)?; + let writer = OpenOptions::new().write(true).create(true).truncate(true).open(to)?; let writer_metadata = writer.metadata()?; Ok((writer, writer_metadata)) } diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 38b373be56c9..4dad81685a94 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -47,7 +47,7 @@ pub use lazy::Storage as LazyStorage; )] #[allow_internal_unsafe] #[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro thread_local_inner { // NOTE: we cannot import `LocalKey`, `LazyStorage` or `EagerStorage` with a `use` because that // can shadow user provided type or type alias with a matching name. Please update the shadowing @@ -110,7 +110,7 @@ pub macro thread_local_inner { }}, } -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub(crate) macro local_pointer { () => {}, ($vis:vis static $name:ident; $($rest:tt)*) => { diff --git a/library/std/src/sys/thread_local/no_threads.rs b/library/std/src/sys/thread_local/no_threads.rs index 936d464be9f1..27a589a4a76a 100644 --- a/library/std/src/sys/thread_local/no_threads.rs +++ b/library/std/src/sys/thread_local/no_threads.rs @@ -9,7 +9,7 @@ use crate::ptr; #[allow_internal_unstable(thread_local_internals)] #[allow_internal_unsafe] #[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro thread_local_inner { // used to generate the `LocalKey` value for const-initialized thread locals (@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{ @@ -119,7 +119,7 @@ impl LazyStorage { // SAFETY: the target doesn't have threads. unsafe impl Sync for LazyStorage {} -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub(crate) macro local_pointer { () => {}, ($vis:vis static $name:ident; $($rest:tt)*) => { diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 07b93a2cbbc3..3f06c9bd1d77 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -12,7 +12,7 @@ use crate::ptr::{self, NonNull}; #[allow_internal_unstable(thread_local_internals)] #[allow_internal_unsafe] #[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro thread_local_inner { // NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user // provided type or type alias with a matching name. Please update the shadowing test in @@ -261,7 +261,7 @@ unsafe extern "C" fn destroy_value(ptr: *mut u8) }); } -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub(crate) macro local_pointer { () => {}, ($vis:vis static $name:ident; $($rest:tt)*) => { diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 06e4b252fc8f..1318d8dc2780 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -140,7 +140,7 @@ impl fmt::Debug for LocalKey { #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] #[unstable(feature = "thread_local_internals", issue = "none")] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] pub macro thread_local_process_attrs { // Parse `cfg_attr` to figure out whether it's a `rustc_align_static`. diff --git a/library/std_detect/Cargo.toml b/library/std_detect/Cargo.toml index 2739bb592300..a6785bb20555 100644 --- a/library/std_detect/Cargo.toml +++ b/library/std_detect/Cargo.toml @@ -7,11 +7,6 @@ authors = [ "Gonzalo Brito Gadeschi ", ] description = "`std::detect` - Rust's standard library run-time CPU feature detection." -homepage = "https://github.com/rust-lang/stdarch" -repository = "https://github.com/rust-lang/stdarch" -readme = "README.md" -keywords = ["std", "run-time", "feature", "detection"] -categories = ["hardware-support"] license = "MIT OR Apache-2.0" edition = "2024" @@ -25,10 +20,7 @@ core = { version = "1.0.0", package = 'rustc-std-workspace-core' } alloc = { version = "1.0.0", package = 'rustc-std-workspace-alloc' } [target.'cfg(not(windows))'.dependencies] -libc = { version = "0.2.0", optional = true, default-features = false } +libc = { version = "0.2.0", default-features = false } [features] -default = [] -std_detect_file_io = [ "libc" ] -std_detect_dlsym_getauxval = [ "libc" ] -std_detect_env_override = [ "libc" ] +std_detect_env_override = [] diff --git a/library/std_detect/README.md b/library/std_detect/README.md index 177848dec104..895f3426d049 100644 --- a/library/std_detect/README.md +++ b/library/std_detect/README.md @@ -21,32 +21,6 @@ run-time feature detection support than the one offered by Rust's standard library. We intend to make `std_detect` more flexible and configurable in this regard to better serve the needs of `#[no_std]` targets. -# Features - -* `std_detect_dlsym_getauxval` (enabled by default, requires `libc`): Enable to -use `libc::dlsym` to query whether [`getauxval`] is linked into the binary. When -this is not the case, this feature allows other fallback methods to perform -run-time feature detection. When this feature is disabled, `std_detect` assumes -that [`getauxval`] is linked to the binary. If that is not the case the behavior -is undefined. - - Note: This feature is ignored on `*-linux-{gnu,musl,ohos}*` and `*-android*` targets - because we can safely assume `getauxval` is linked to the binary. - * `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) - have glibc requirements higher than [glibc 2.16 that added `getauxval`](https://sourceware.org/legacy-ml/libc-announce/2012/msg00000.html). - * `*-linux-musl*` targets ([at least since Rust 1.15](https://github.com/rust-lang/rust/blob/1.15.0/src/ci/docker/x86_64-musl/build-musl.sh#L15)) - use musl newer than [musl 1.1.0 that added `getauxval`](https://git.musl-libc.org/cgit/musl/tree/WHATSNEW?h=v1.1.0#n1197) - * `*-linux-ohos*` targets use a [fork of musl 1.2](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/native-lib/musl.md) - * `*-android*` targets ([since Rust 1.68](https://blog.rust-lang.org/2023/01/09/android-ndk-update-r25.html)) - have the minimum supported API level higher than [Android 4.3 (API level 18) that added `getauxval`](https://github.com/aosp-mirror/platform_bionic/blob/d3ebc2f7c49a9893b114124d4a6b315f3a328764/libc/include/sys/auxv.h#L49). - -* `std_detect_file_io` (enabled by default, requires `std`): Enable to perform run-time feature -detection using file APIs (e.g. `/proc/self/auxv`, etc.) if other more performant -methods fail. This feature requires `libstd` as a dependency, preventing the -crate from working on applications in which `std` is not available. - -[`getauxval`]: https://man7.org/linux/man-pages/man3/getauxval.3.html - # Platform support * All `x86`/`x86_64` targets are supported on all platforms by querying the diff --git a/library/std_detect/src/detect/mod.rs b/library/std_detect/src/detect/mod.rs index ae6fb2ab3727..c888dd34d9db 100644 --- a/library/std_detect/src/detect/mod.rs +++ b/library/std_detect/src/detect/mod.rs @@ -47,21 +47,21 @@ cfg_select! { #[path = "os/x86.rs"] mod os; } - all(any(target_os = "linux", target_os = "android"), feature = "libc") => { + any(target_os = "linux", target_os = "android") => { #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] #[path = "os/riscv.rs"] mod riscv; #[path = "os/linux/mod.rs"] mod os; } - all(target_os = "freebsd", feature = "libc") => { + target_os = "freebsd" => { #[cfg(target_arch = "aarch64")] #[path = "os/aarch64.rs"] mod aarch64; #[path = "os/freebsd/mod.rs"] mod os; } - all(target_os = "openbsd", feature = "libc") => { + target_os = "openbsd" => { #[allow(dead_code)] // we don't use code that calls the mrs instruction. #[cfg(target_arch = "aarch64")] #[path = "os/aarch64.rs"] @@ -73,7 +73,7 @@ cfg_select! { #[path = "os/windows/aarch64.rs"] mod os; } - all(target_vendor = "apple", target_arch = "aarch64", feature = "libc") => { + all(target_vendor = "apple", target_arch = "aarch64") => { #[path = "os/darwin/aarch64.rs"] mod os; } diff --git a/library/std_detect/src/detect/os/linux/aarch64/tests.rs b/library/std_detect/src/detect/os/linux/aarch64/tests.rs index a3562f2fd936..4d7c9a419d38 100644 --- a/library/std_detect/src/detect/os/linux/aarch64/tests.rs +++ b/library/std_detect/src/detect/os/linux/aarch64/tests.rs @@ -1,77 +1,70 @@ +use super::auxvec::auxv_from_file; use super::*; - -#[cfg(feature = "std_detect_file_io")] -mod auxv_from_file { - use super::auxvec::auxv_from_file; - use super::*; - // The baseline hwcaps used in the (artificial) auxv test files. - fn baseline_hwcaps() -> AtHwcap { - AtHwcap { - fp: true, - asimd: true, - aes: true, - pmull: true, - sha1: true, - sha2: true, - crc32: true, - atomics: true, - fphp: true, - asimdhp: true, - asimdrdm: true, - lrcpc: true, - dcpop: true, - asimddp: true, - ssbs: true, - ..AtHwcap::default() - } - } - - #[test] - fn linux_empty_hwcap2_aarch64() { - let file = concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv" - ); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!(AtHwcap::from(v), baseline_hwcaps()); - } - #[test] - fn linux_no_hwcap2_aarch64() { - let file = concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv" - ); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!(AtHwcap::from(v), baseline_hwcaps()); - } - #[test] - fn linux_hwcap2_aarch64() { - let file = - concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-hwcap2-aarch64.auxv"); - println!("file: {file}"); - let v = auxv_from_file(file).unwrap(); - println!("HWCAP : 0x{:0x}", v.hwcap); - println!("HWCAP2: 0x{:0x}", v.hwcap2); - assert_eq!( - AtHwcap::from(v), - AtHwcap { - // Some other HWCAP bits. - paca: true, - pacg: true, - // HWCAP2-only bits. - dcpodp: true, - frint: true, - rng: true, - bti: true, - mte: true, - ..baseline_hwcaps() - } - ); +// The baseline hwcaps used in the (artificial) auxv test files. +fn baseline_hwcaps() -> AtHwcap { + AtHwcap { + fp: true, + asimd: true, + aes: true, + pmull: true, + sha1: true, + sha2: true, + crc32: true, + atomics: true, + fphp: true, + asimdhp: true, + asimdrdm: true, + lrcpc: true, + dcpop: true, + asimddp: true, + ssbs: true, + ..AtHwcap::default() } } + +#[test] +fn linux_empty_hwcap2_aarch64() { + let file = concat!( + env!("CARGO_MANIFEST_DIR"), + "/src/detect/test_data/linux-empty-hwcap2-aarch64.auxv" + ); + println!("file: {file}"); + let v = auxv_from_file(file).unwrap(); + println!("HWCAP : 0x{:0x}", v.hwcap); + println!("HWCAP2: 0x{:0x}", v.hwcap2); + assert_eq!(AtHwcap::from(v), baseline_hwcaps()); +} +#[test] +fn linux_no_hwcap2_aarch64() { + let file = + concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-no-hwcap2-aarch64.auxv"); + println!("file: {file}"); + let v = auxv_from_file(file).unwrap(); + println!("HWCAP : 0x{:0x}", v.hwcap); + println!("HWCAP2: 0x{:0x}", v.hwcap2); + assert_eq!(AtHwcap::from(v), baseline_hwcaps()); +} +#[test] +fn linux_hwcap2_aarch64() { + let file = + concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-hwcap2-aarch64.auxv"); + println!("file: {file}"); + let v = auxv_from_file(file).unwrap(); + println!("HWCAP : 0x{:0x}", v.hwcap); + println!("HWCAP2: 0x{:0x}", v.hwcap2); + assert_eq!( + AtHwcap::from(v), + AtHwcap { + // Some other HWCAP bits. + paca: true, + pacg: true, + // HWCAP2-only bits. + dcpodp: true, + frint: true, + rng: true, + bti: true, + mte: true, + ..baseline_hwcaps() + } + ); +} diff --git a/library/std_detect/src/detect/os/linux/auxvec.rs b/library/std_detect/src/detect/os/linux/auxvec.rs index 75e01bdc4499..c0bbc7d4efa8 100644 --- a/library/std_detect/src/detect/os/linux/auxvec.rs +++ b/library/std_detect/src/detect/os/linux/auxvec.rs @@ -44,20 +44,16 @@ pub(crate) struct AuxVec { /// /// There is no perfect way of reading the auxiliary vector. /// -/// - If the `std_detect_dlsym_getauxval` cargo feature is enabled, this will use -/// `getauxval` if its linked to the binary, and otherwise proceed to a fallback implementation. -/// When `std_detect_dlsym_getauxval` is disabled, this will assume that `getauxval` is -/// linked to the binary - if that is not the case the behavior is undefined. -/// - Otherwise, if the `std_detect_file_io` cargo feature is enabled, it will +/// - If [`getauxval`] is linked to the binary we use it, and otherwise it will /// try to read `/proc/self/auxv`. /// - If that fails, this function returns an error. /// /// Note that run-time feature detection is not invoked for features that can /// be detected at compile-time. /// -/// Note: The `std_detect_dlsym_getauxval` cargo feature is ignored on -/// `*-linux-{gnu,musl,ohos}*` and `*-android*` targets because we can safely assume `getauxval` -/// is linked to the binary. +/// Note: We always directly use `getauxval` on `*-linux-{gnu,musl,ohos}*` and +/// `*-android*` targets rather than `dlsym` it because we can safely assume +/// `getauxval` is linked to the binary. /// - `*-linux-gnu*` targets ([since Rust 1.64](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html)) /// have glibc requirements higher than [glibc 2.16 that added `getauxval`](https://sourceware.org/legacy-ml/libc-announce/2012/msg00000.html). /// - `*-linux-musl*` targets ([at least since Rust 1.15](https://github.com/rust-lang/rust/blob/1.15.0/src/ci/docker/x86_64-musl/build-musl.sh#L15)) @@ -71,6 +67,7 @@ pub(crate) struct AuxVec { /// /// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h /// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/ +/// [`getauxval`]: https://man7.org/linux/man-pages/man3/getauxval.3.html pub(crate) fn auxv() -> Result { // Try to call a getauxval function. if let Ok(hwcap) = getauxval(AT_HWCAP) { @@ -115,16 +112,9 @@ pub(crate) fn auxv() -> Result { let _ = hwcap; } - #[cfg(feature = "std_detect_file_io")] - { - // If calling getauxval fails, try to read the auxiliary vector from - // its file: - auxv_from_file("/proc/self/auxv").map_err(|_| ()) - } - #[cfg(not(feature = "std_detect_file_io"))] - { - Err(()) - } + // If calling getauxval fails, try to read the auxiliary vector from + // its file: + auxv_from_file("/proc/self/auxv").map_err(|_| ()) } /// Tries to read the `key` from the auxiliary vector by calling the @@ -132,14 +122,16 @@ pub(crate) fn auxv() -> Result { fn getauxval(key: usize) -> Result { type F = unsafe extern "C" fn(libc::c_ulong) -> libc::c_ulong; cfg_select! { - all( - feature = "std_detect_dlsym_getauxval", - not(all( + any( + all( target_os = "linux", any(target_env = "gnu", target_env = "musl", target_env = "ohos"), - )), - not(target_os = "android"), + ), + target_os = "android", ) => { + let ffi_getauxval: F = libc::getauxval; + } + _ => { let ffi_getauxval: F = unsafe { let ptr = libc::dlsym(libc::RTLD_DEFAULT, c"getauxval".as_ptr()); if ptr.is_null() { @@ -148,23 +140,18 @@ fn getauxval(key: usize) -> Result { core::mem::transmute(ptr) }; } - _ => { - let ffi_getauxval: F = libc::getauxval; - } } Ok(unsafe { ffi_getauxval(key as libc::c_ulong) as usize }) } /// Tries to read the auxiliary vector from the `file`. If this fails, this /// function returns `Err`. -#[cfg(feature = "std_detect_file_io")] pub(super) fn auxv_from_file(file: &str) -> Result { let file = super::read_file(file)?; auxv_from_file_bytes(&file) } /// Read auxiliary vector from a slice of bytes. -#[cfg(feature = "std_detect_file_io")] pub(super) fn auxv_from_file_bytes(bytes: &[u8]) -> Result { // See . // @@ -181,7 +168,6 @@ pub(super) fn auxv_from_file_bytes(bytes: &[u8]) -> Result Result { // Targets with only AT_HWCAP: #[cfg(any( diff --git a/library/std_detect/src/detect/os/linux/auxvec/tests.rs b/library/std_detect/src/detect/os/linux/auxvec/tests.rs index 631a3e5e9ef1..88f0d6d49337 100644 --- a/library/std_detect/src/detect/os/linux/auxvec/tests.rs +++ b/library/std_detect/src/detect/os/linux/auxvec/tests.rs @@ -41,7 +41,6 @@ fn auxv_dump() { } } -#[cfg(feature = "std_detect_file_io")] cfg_select! { target_arch = "arm" => { // The tests below can be executed under qemu, where we do not have access to the test @@ -86,7 +85,6 @@ cfg_select! { } #[test] -#[cfg(feature = "std_detect_file_io")] fn auxv_dump_procfs() { if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") { println!("{:?}", auxvec); @@ -103,7 +101,6 @@ fn auxv_dump_procfs() { target_arch = "s390x", ))] #[test] -#[cfg(feature = "std_detect_file_io")] fn auxv_crate_procfs() { if let Ok(procfs_auxv) = auxv_from_file("/proc/self/auxv") { assert_eq!(auxv().unwrap(), procfs_auxv); diff --git a/library/std_detect/src/detect/os/linux/mod.rs b/library/std_detect/src/detect/os/linux/mod.rs index 5273c16c0893..aec94f963f5c 100644 --- a/library/std_detect/src/detect/os/linux/mod.rs +++ b/library/std_detect/src/detect/os/linux/mod.rs @@ -1,11 +1,9 @@ //! Run-time feature detection on Linux -//! -#[cfg(feature = "std_detect_file_io")] + use alloc::vec::Vec; mod auxvec; -#[cfg(feature = "std_detect_file_io")] fn read_file(orig_path: &str) -> Result, alloc::string::String> { use alloc::format; diff --git a/library/std_detect/src/lib.rs b/library/std_detect/src/lib.rs index 73e2f5dd9644..5e1d21bbfd17 100644 --- a/library/std_detect/src/lib.rs +++ b/library/std_detect/src/lib.rs @@ -27,8 +27,7 @@ extern crate std; // rust-lang/rust#83888: removing `extern crate` gives an error that `vec_spare> -#[cfg_attr(feature = "std_detect_file_io", allow(unused_extern_crates))] -#[cfg(feature = "std_detect_file_io")] +#[allow(unused_extern_crates)] extern crate alloc; #[doc(hidden)] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index eec8c461b6db..a18868082916 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -20,7 +20,7 @@ test = { path = "../test", public = true } # Forward features to the `std` crate as necessary [features] -default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] +default = ["panic-unwind"] backtrace = ["std/backtrace"] backtrace-trace-only = ["std/backtrace-trace-only"] compiler-builtins-c = ["std/compiler-builtins-c"] @@ -32,7 +32,5 @@ system-llvm-libunwind = ["std/system-llvm-libunwind"] optimize_for_size = ["std/optimize_for_size"] panic-unwind = ["std/panic-unwind"] profiler = ["dep:profiler_builtins"] -std_detect_file_io = ["std/std_detect_file_io"] -std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] windows_raw_dylib = ["std/windows_raw_dylib"] llvm_enzyme = ["std/llvm_enzyme"] diff --git a/package.json b/package.json index ef74853b77a5..0cba589d23f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.23.0", + "browser-ui-test": "^0.23.1", "es-check": "^9.4.4", "eslint": "^8.57.1", "typescript": "^5.8.3" diff --git a/src/bootstrap/download-ci-gcc-stamp b/src/bootstrap/download-ci-gcc-stamp index bbe26afc9526..ec8d0fa3135d 100644 --- a/src/bootstrap/download-ci-gcc-stamp +++ b/src/bootstrap/download-ci-gcc-stamp @@ -1,4 +1,4 @@ Change this file to make users of the `download-ci-gcc` configuration download a new version of GCC from CI, even if the GCC submodule hasn’t changed. -Last change is for: https://github.com/rust-lang/rust/pull/138051 +Last change is for: https://github.com/rust-lang/rust/pull/150873 diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index df82b7baa9f9..f983c59f9bf8 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -4,7 +4,8 @@ use std::fs; use std::path::{Path, PathBuf}; use crate::core::build_steps::compile::{ - add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make, + ArtifactKeepMode, add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, + std_crates_for_run_make, }; use crate::core::build_steps::tool; use crate::core::build_steps::tool::{ @@ -111,8 +112,7 @@ impl Step for Std { builder.config.free_args.clone(), &check_stamp, vec![], - true, - false, + ArtifactKeepMode::OnlyRmeta, ); drop(_guard); @@ -148,7 +148,14 @@ impl Step for Std { build_compiler, target, ); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + run_cargo( + builder, + cargo, + builder.config.free_args.clone(), + &stamp, + vec![], + ArtifactKeepMode::OnlyRmeta, + ); check_stamp } @@ -368,7 +375,14 @@ impl Step for Rustc { let stamp = build_stamp::librustc_stamp(builder, build_compiler, target).with_prefix("check"); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + run_cargo( + builder, + cargo, + builder.config.free_args.clone(), + &stamp, + vec![], + ArtifactKeepMode::OnlyRmeta, + ); stamp } @@ -568,7 +582,14 @@ impl Step for CraneliftCodegenBackend { ) .with_prefix("check"); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + run_cargo( + builder, + cargo, + builder.config.free_args.clone(), + &stamp, + vec![], + ArtifactKeepMode::OnlyRmeta, + ); } fn metadata(&self) -> Option { @@ -639,7 +660,14 @@ impl Step for GccCodegenBackend { ) .with_prefix("check"); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + run_cargo( + builder, + cargo, + builder.config.free_args.clone(), + &stamp, + vec![], + ArtifactKeepMode::OnlyRmeta, + ); } fn metadata(&self) -> Option { @@ -777,7 +805,14 @@ fn run_tool_check_step( .with_prefix(&format!("{display_name}-check")); let _guard = builder.msg(builder.kind, display_name, mode, build_compiler, target); - run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false); + run_cargo( + builder, + cargo, + builder.config.free_args.clone(), + &stamp, + vec![], + ArtifactKeepMode::OnlyRmeta, + ); } tool_check_step!(Rustdoc { @@ -793,8 +828,7 @@ tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustcPrivate tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustcPrivate, - enable_features: ["stack-cache"], - default_features: false, + enable_features: ["check_only"], }); tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustcPrivate }); tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustcPrivate }); diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index 085c2d952e30..9e5bfd2e60e9 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -15,7 +15,7 @@ use build_helper::exit; -use super::compile::{run_cargo, rustc_cargo, std_cargo}; +use super::compile::{ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check}; @@ -214,8 +214,7 @@ impl Step for Std { lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &build_stamp::libstd_stamp(builder, build_compiler, target), vec![], - true, - false, + ArtifactKeepMode::OnlyRmeta, ); } @@ -309,8 +308,7 @@ impl Step for Rustc { lint_args(builder, &self.config, IGNORED_RULES_FOR_STD_AND_RUSTC), &build_stamp::librustc_stamp(builder, build_compiler, target), vec![], - true, - false, + ArtifactKeepMode::OnlyRmeta, ); } @@ -380,7 +378,7 @@ impl Step for CodegenGcc { .with_prefix("rustc_codegen_gcc-check"); let args = lint_args(builder, &self.config, &[]); - run_cargo(builder, cargo, args.clone(), &stamp, vec![], true, false); + run_cargo(builder, cargo, args.clone(), &stamp, vec![], ArtifactKeepMode::OnlyRmeta); // Same but we disable the features enabled by default. let mut cargo = prepare_tool_cargo( @@ -396,7 +394,7 @@ impl Step for CodegenGcc { self.build_compiler.configure_cargo(&mut cargo); println!("Now running clippy on `rustc_codegen_gcc` with `--no-default-features`"); cargo.arg("--no-default-features"); - run_cargo(builder, cargo, args, &stamp, vec![], true, false); + run_cargo(builder, cargo, args, &stamp, vec![], ArtifactKeepMode::OnlyRmeta); } fn metadata(&self) -> Option { @@ -478,8 +476,7 @@ macro_rules! lint_any { lint_args(builder, &self.config, &[]), &stamp, vec![], - true, - false, + ArtifactKeepMode::OnlyRmeta ); } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 762aeea9f451..2808c29cc6c0 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -19,7 +19,7 @@ use serde_derive::Deserialize; #[cfg(feature = "tracing")] use tracing::span; -use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair, add_cg_gcc_cargo_flags}; +use crate::core::build_steps::gcc::{Gcc, GccOutput, GccTargetPair}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; use crate::core::build_steps::{dist, llvm}; use crate::core::builder; @@ -296,8 +296,11 @@ impl Step for Std { vec![], &stamp, target_deps, - self.is_for_mir_opt_tests, // is_check - false, + if self.is_for_mir_opt_tests { + ArtifactKeepMode::OnlyRmeta + } else { + ArtifactKeepMode::OnlyRlib + }, ); builder.ensure(StdLink::from_std( @@ -1167,14 +1170,28 @@ impl Step for Rustc { target, ); let stamp = build_stamp::librustc_stamp(builder, build_compiler, target); + run_cargo( builder, cargo, vec![], &stamp, vec![], - false, - true, // Only ship rustc_driver.so and .rmeta files, not all intermediate .rlib files. + ArtifactKeepMode::Custom(Box::new(|filename| { + if filename.contains("jemalloc_sys") + || filename.contains("rustc_public_bridge") + || filename.contains("rustc_public") + { + // jemalloc_sys and rustc_public_bridge are not linked into librustc_driver.so, + // so we need to distribute them as rlib to be able to use them. + filename.ends_with(".rlib") + } else { + // Distribute the rest of the rustc crates as rmeta files only to reduce + // the tarball sizes by about 50%. The object files are linked into + // librustc_driver.so, so it is still possible to link against them. + filename.ends_with(".rmeta") + } + })), ); let target_root_dir = stamp.path().parent().unwrap(); @@ -1569,21 +1586,29 @@ impl Step for RustcLink { } /// Set of `libgccjit` dylibs that can be used by `cg_gcc` to compile code for a set of targets. +/// `libgccjit` requires a separate build for each `(host, target)` pair. +/// So if you are on linux-x64 and build for linux-aarch64, you will need at least: +/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros) +/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code) #[derive(Clone)] pub struct GccDylibSet { dylibs: BTreeMap, - host_pair: GccTargetPair, } impl GccDylibSet { - /// Returns the libgccjit.so dylib that corresponds to a host target on which `cg_gcc` will be - /// executed, and which will target the host. So e.g. if `cg_gcc` will be executed on - /// x86_64-unknown-linux-gnu, the host dylib will be for compilation pair - /// `(x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu)`. - fn host_dylib(&self) -> &GccOutput { - self.dylibs.get(&self.host_pair).unwrap_or_else(|| { - panic!("libgccjit.so was not built for host target {}", self.host_pair) - }) + /// Build a set of libgccjit dylibs that will be executed on `host` and will generate code for + /// each specified target. + pub fn build( + builder: &Builder<'_>, + host: TargetSelection, + targets: Vec, + ) -> Self { + let dylibs = targets + .iter() + .map(|t| GccTargetPair::for_target_pair(host, *t)) + .map(|target_pair| (target_pair, builder.ensure(Gcc { target_pair }))) + .collect(); + Self { dylibs } } /// Install the libgccjit dylibs to the corresponding target directories of the given compiler. @@ -1626,39 +1651,34 @@ impl GccDylibSet { /// Output of the `compile::GccCodegenBackend` step. /// -/// It contains paths to all built libgccjit libraries on which this backend depends here. +/// It contains a build stamp with the path to the built cg_gcc dylib. #[derive(Clone)] pub struct GccCodegenBackendOutput { stamp: BuildStamp, - dylib_set: GccDylibSet, +} + +impl GccCodegenBackendOutput { + pub fn stamp(&self) -> &BuildStamp { + &self.stamp + } } /// Builds the GCC codegen backend (`cg_gcc`). -/// The `cg_gcc` backend uses `libgccjit`, which requires a separate build for each -/// `host -> target` pair. So if you are on linux-x64 and build for linux-aarch64, -/// you will need at least: -/// - linux-x64 -> linux-x64 libgccjit (for building host code like proc macros) -/// - linux-x64 -> linux-aarch64 libgccjit (for the aarch64 target code) -/// -/// We model this by having a single cg_gcc for a given host target, which contains one -/// libgccjit per (host, target) pair. -/// Note that the host target is taken from `self.compilers.target_compiler.host`. +/// Note that this **does not** build libgccjit, which is a dependency of cg_gcc. +/// That has to be built separately, because a separate copy of libgccjit is required +/// for each (host, target) compilation pair. +/// cg_gcc goes to great lengths to ensure that it does not *directly* link to libgccjit, +/// so we respect that here and allow building cg_gcc without building libgccjit itself. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GccCodegenBackend { compilers: RustcPrivateCompilers, - targets: Vec, + target: TargetSelection, } impl GccCodegenBackend { - /// Build `cg_gcc` that will run on host `H` (`compilers.target_compiler.host`) and will be - /// able to produce code target pairs (`H`, `T`) for all `T` from `targets`. - pub fn for_targets( - compilers: RustcPrivateCompilers, - mut targets: Vec, - ) -> Self { - // Sort targets to improve step cache hits - targets.sort(); - Self { compilers, targets } + /// Build `cg_gcc` that will run on the given host target. + pub fn for_target(compilers: RustcPrivateCompilers, target: TargetSelection) -> Self { + Self { compilers, target } } } @@ -1672,10 +1692,8 @@ impl Step for GccCodegenBackend { } fn make_run(run: RunConfig<'_>) { - // By default, build cg_gcc that will only be able to compile native code for the given - // host target. let compilers = RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target); - run.builder.ensure(GccCodegenBackend { compilers, targets: vec![run.target] }); + run.builder.ensure(GccCodegenBackend::for_target(compilers, run.target)); } fn run(self, builder: &Builder<'_>) -> Self::Output { @@ -1689,19 +1707,7 @@ impl Step for GccCodegenBackend { &CodegenBackendKind::Gcc, ); - let dylib_set = GccDylibSet { - dylibs: self - .targets - .iter() - .map(|&target| { - let target_pair = GccTargetPair::for_target_pair(host, target); - (target_pair, builder.ensure(Gcc { target_pair })) - }) - .collect(), - host_pair: GccTargetPair::for_native_build(host), - }; - - if builder.config.keep_stage.contains(&build_compiler.stage) { + if builder.config.keep_stage.contains(&build_compiler.stage) && stamp.path().exists() { trace!("`keep-stage` requested"); builder.info( "WARNING: Using a potentially old codegen backend. \ @@ -1709,7 +1715,7 @@ impl Step for GccCodegenBackend { ); // Codegen backends are linked separately from this step today, so we don't do // anything here. - return GccCodegenBackendOutput { stamp, dylib_set }; + return GccCodegenBackendOutput { stamp }; } let mut cargo = builder::Cargo::new( @@ -1723,15 +1729,12 @@ impl Step for GccCodegenBackend { cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml")); rustc_cargo_env(builder, &mut cargo, host); - add_cg_gcc_cargo_flags(&mut cargo, dylib_set.host_dylib()); - let _guard = builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, host); - let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); GccCodegenBackendOutput { stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()), - dylib_set, } } @@ -1804,7 +1807,7 @@ impl Step for CraneliftCodegenBackend { build_compiler, target, ); - let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); + let files = run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); write_codegen_backend_stamp(stamp, files, builder.config.dry_run()) } @@ -2457,12 +2460,18 @@ impl Step for Assemble { // GCC dylibs built below by taking a look at the current stage and whether // cg_gcc is used as the default codegen backend. + // First, the easy part: build cg_gcc let compilers = prepare_compilers(); + let cg_gcc = builder + .ensure(GccCodegenBackend::for_target(compilers, target_compiler.host)); + copy_codegen_backends_to_sysroot(builder, cg_gcc.stamp, target_compiler); + + // Then, the hard part: prepare all required libgccjit dylibs. // The left side of the target pairs below is implied. It has to match the - // host target on which cg_gcc will run, which is the host target of + // host target on which libgccjit will be used, which is the host target of // `target_compiler`. We only pass the right side of the target pairs to - // the `GccCodegenBackend` constructor. + // the `GccDylibSet` constructor. let mut targets = HashSet::new(); // Add all host targets, so that we are able to build host code in this // bootstrap invocation using cg_gcc. @@ -2477,14 +2486,16 @@ impl Step for Assemble { // host code (e.g. proc macros) using cg_gcc. targets.insert(compilers.target_compiler().host); - let output = builder.ensure(GccCodegenBackend::for_targets( - compilers, + // Now build all the required libgccjit dylibs + let dylib_set = GccDylibSet::build( + builder, + compilers.target_compiler().host, targets.into_iter().collect(), - )); - copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler); - // Also copy all requires libgccjit dylibs to the corresponding - // library sysroots, so that they are available for the codegen backend. - output.dylib_set.install_to(builder, target_compiler); + ); + + // And then copy all the dylibs to the corresponding + // library sysroots, so that they are available for cg_gcc. + dylib_set.install_to(builder, target_compiler); } CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue, } @@ -2626,14 +2637,26 @@ pub fn add_to_sysroot( } } +/// Specifies which rlib/rmeta artifacts outputted by Cargo should be put into the resulting +/// build stamp, and thus be included in dist archives and copied into sysroots by default. +/// Note that some kinds of artifacts are copied automatically (e.g. native libraries). +pub enum ArtifactKeepMode { + /// Only keep .rlib files, ignore .rmeta files + OnlyRlib, + /// Only keep .rmeta files, ignore .rlib files + OnlyRmeta, + /// Custom logic for keeping an artifact + /// It receives the filename of an artifact, and returns true if it should be kept. + Custom(Box bool>), +} + pub fn run_cargo( builder: &Builder<'_>, cargo: Cargo, tail_args: Vec, stamp: &BuildStamp, additional_target_deps: Vec<(PathBuf, DependencyType)>, - is_check: bool, - rlib_only_metadata: bool, + artifact_keep_mode: ArtifactKeepMode, ) -> Vec { // `target_root_dir` looks like $dir/$target/release let target_root_dir = stamp.path().parent().unwrap(); @@ -2667,36 +2690,20 @@ pub fn run_cargo( }; for filename in filenames_vec { // Skip files like executables - let mut keep = false; - if filename.ends_with(".lib") + let keep = if filename.ends_with(".lib") || filename.ends_with(".a") || is_debug_info(&filename) || is_dylib(Path::new(&*filename)) { // Always keep native libraries, rust dylibs and debuginfo - keep = true; - } - if is_check && filename.ends_with(".rmeta") { - // During check builds we need to keep crate metadata - keep = true; - } else if rlib_only_metadata { - if filename.contains("jemalloc_sys") - || filename.contains("rustc_public_bridge") - || filename.contains("rustc_public") - { - // jemalloc_sys and rustc_public_bridge are not linked into librustc_driver.so, - // so we need to distribute them as rlib to be able to use them. - keep |= filename.ends_with(".rlib"); - } else { - // Distribute the rest of the rustc crates as rmeta files only to reduce - // the tarball sizes by about 50%. The object files are linked into - // librustc_driver.so, so it is still possible to link against them. - keep |= filename.ends_with(".rmeta"); - } + true } else { - // In all other cases keep all rlibs - keep |= filename.ends_with(".rlib"); - } + match &artifact_keep_mode { + ArtifactKeepMode::OnlyRlib => filename.ends_with(".rlib"), + ArtifactKeepMode::OnlyRmeta => filename.ends_with(".rmeta"), + ArtifactKeepMode::Custom(func) => func(&filename), + } + }; if !keep { continue; diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 0e7051cc22df..f47b0c0b0072 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -1652,23 +1652,7 @@ impl Step for CraneliftCodegenBackend { return None; } - // Get the relative path of where the codegen backend should be stored. - let backends_dst = builder.sysroot_codegen_backends(compilers.target_compiler()); - let backends_rel = backends_dst - .strip_prefix(builder.sysroot(compilers.target_compiler())) - .unwrap() - .strip_prefix(builder.sysroot_libdir_relative(compilers.target_compiler())) - .unwrap(); - // Don't use custom libdir here because ^lib/ will be resolved again with installer - let backends_dst = PathBuf::from("lib").join(backends_rel); - - let codegen_backend_dylib = get_codegen_backend_file(&stamp); - tarball.add_renamed_file( - &codegen_backend_dylib, - &backends_dst, - &normalize_codegen_backend_name(builder, &codegen_backend_dylib), - FileType::NativeLibrary, - ); + add_codegen_backend_to_tarball(builder, &tarball, compilers.target_compiler(), &stamp); Some(tarball.generate()) } @@ -1681,6 +1665,113 @@ impl Step for CraneliftCodegenBackend { } } +/// Builds a dist component containing the GCC codegen backend. +/// Note that for this backend to work, it must have a set of libgccjit dylibs available +/// at runtime. +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct GccCodegenBackend { + pub compilers: RustcPrivateCompilers, + pub target: TargetSelection, +} + +impl Step for GccCodegenBackend { + type Output = Option; + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.alias("rustc_codegen_gcc") + } + + fn is_default_step(builder: &Builder<'_>) -> bool { + // We only want to build the gcc backend in `x dist` if the backend was enabled + // in rust.codegen-backends. + // Sadly, we don't have access to the actual target for which we're disting clif here.. + // So we just use the host target. + builder + .config + .enabled_codegen_backends(builder.host_target) + .contains(&CodegenBackendKind::Gcc) + } + + fn make_run(run: RunConfig<'_>) { + run.builder.ensure(GccCodegenBackend { + compilers: RustcPrivateCompilers::new(run.builder, run.builder.top_stage, run.target), + target: run.target, + }); + } + + fn run(self, builder: &Builder<'_>) -> Option { + // This prevents rustc_codegen_gcc from being built for "dist" + // or "install" on the stable/beta channels. It is not yet stable and + // should not be included. + if !builder.build.unstable_features() { + return None; + } + + let target = self.target; + if target != "x86_64-unknown-linux-gnu" { + builder + .info(&format!("target `{target}` not supported by rustc_codegen_gcc. skipping")); + return None; + } + + let mut tarball = Tarball::new(builder, "rustc-codegen-gcc", &target.triple); + tarball.set_overlay(OverlayKind::RustcCodegenGcc); + tarball.is_preview(true); + tarball.add_legal_and_readme_to("share/doc/rustc_codegen_gcc"); + + let compilers = self.compilers; + let backend = builder.ensure(compile::GccCodegenBackend::for_target(compilers, target)); + + if builder.config.dry_run() { + return None; + } + + add_codegen_backend_to_tarball( + builder, + &tarball, + compilers.target_compiler(), + backend.stamp(), + ); + + Some(tarball.generate()) + } + + fn metadata(&self) -> Option { + Some( + StepMetadata::dist("rustc_codegen_gcc", self.target) + .built_by(self.compilers.build_compiler()), + ) + } +} + +/// Add a codegen backend built for `compiler`, with its artifacts stored in `stamp`, to the given +/// `tarball` at the correct place. +fn add_codegen_backend_to_tarball( + builder: &Builder<'_>, + tarball: &Tarball<'_>, + compiler: Compiler, + stamp: &BuildStamp, +) { + // Get the relative path of where the codegen backend should be stored. + let backends_dst = builder.sysroot_codegen_backends(compiler); + let backends_rel = backends_dst + .strip_prefix(builder.sysroot(compiler)) + .unwrap() + .strip_prefix(builder.sysroot_libdir_relative(compiler)) + .unwrap(); + // Don't use custom libdir here because ^lib/ will be resolved again with installer + let backends_dst = PathBuf::from("lib").join(backends_rel); + + let codegen_backend_dylib = get_codegen_backend_file(stamp); + tarball.add_renamed_file( + &codegen_backend_dylib, + &backends_dst, + &normalize_codegen_backend_name(builder, &codegen_backend_dylib), + FileType::NativeLibrary, + ); +} + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Rustfmt { pub compilers: RustcPrivateCompilers, diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index b26df1586e71..f3a1c6b0e3dd 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -14,7 +14,7 @@ use std::{env, fs, iter}; use build_helper::exit; -use crate::core::build_steps::compile::{Std, run_cargo}; +use crate::core::build_steps::compile::{ArtifactKeepMode, Std, run_cargo}; use crate::core::build_steps::doc::{DocumentationFormat, prepare_doc_compiler}; use crate::core::build_steps::gcc::{Gcc, GccTargetPair, add_cg_gcc_cargo_flags}; use crate::core::build_steps::llvm::get_llvm_version; @@ -2265,6 +2265,8 @@ Please disable assertions with `rust.debug-assertions = false`. cmd.arg("--with-std-remap-debuginfo"); } + cmd.arg("--jobs").arg(builder.jobs().to_string()); + let mut llvm_components_passed = false; let mut copts_passed = false; if builder.config.llvm_enabled(test_compiler.host) { @@ -2585,7 +2587,8 @@ impl BookTest { let stamp = BuildStamp::new(&builder.cargo_out(test_compiler, mode, target)) .with_prefix(PathBuf::from(dep).file_name().and_then(|v| v.to_str()).unwrap()); - let output_paths = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false); + let output_paths = + run_cargo(builder, cargo, vec![], &stamp, vec![], ArtifactKeepMode::OnlyRlib); let directories = output_paths .into_iter() .filter_map(|p| p.parent().map(ToOwned::to_owned)) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 9e1a65bca79d..4a04b97c549a 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -967,6 +967,7 @@ impl<'a> Builder<'a> { dist::Mingw, dist::Rustc, dist::CraneliftCodegenBackend, + dist::GccCodegenBackend, dist::Std, dist::RustcDev, dist::Analysis, diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 43efdcd7db17..074404b4cdf3 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -396,7 +396,7 @@ impl Config { "; self.download_file(&format!("{base}/{gcc_sha}/{filename}"), &tarball, help_on_error); } - self.unpack(&tarball, root_dir, "gcc"); + self.unpack(&tarball, root_dir, "gcc-dev"); } } diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 079afb7a0054..7db06dcc3e9a 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -25,6 +25,7 @@ pub(crate) enum OverlayKind { Rustfmt, RustAnalyzer, RustcCodegenCranelift, + RustcCodegenGcc, LlvmBitcodeLinker, } @@ -66,6 +67,11 @@ impl OverlayKind { "compiler/rustc_codegen_cranelift/LICENSE-APACHE", "compiler/rustc_codegen_cranelift/LICENSE-MIT", ], + OverlayKind::RustcCodegenGcc => &[ + "compiler/rustc_codegen_gcc/Readme.md", + "compiler/rustc_codegen_gcc/LICENSE-APACHE", + "compiler/rustc_codegen_gcc/LICENSE-MIT", + ], OverlayKind::LlvmBitcodeLinker => &[ "COPYRIGHT", "LICENSE-APACHE", @@ -93,6 +99,7 @@ impl OverlayKind { .rust_analyzer_info .version(builder, &builder.release_num("rust-analyzer/crates/rust-analyzer")), OverlayKind::RustcCodegenCranelift => builder.rust_version(), + OverlayKind::RustcCodegenGcc => builder.rust_version(), OverlayKind::LlvmBitcodeLinker => builder.rust_version(), } } diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh index 9579e0d79cb0..f77a0d562f03 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/dist.sh @@ -7,7 +7,9 @@ python3 ../x.py build --set rust.debug=true opt-dist ./build/$HOSTS/stage1-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \ --host $HOSTS --target $HOSTS \ --include-default-paths \ - build-manifest bootstrap + build-manifest \ + bootstrap \ + rustc_codegen_gcc # Use GCC for building GCC components, as it seems to behave badly when built with Clang # Only build GCC on full builds, not try builds diff --git a/src/ci/run.sh b/src/ci/run.sh index 425ad38a6655..b486f0525f40 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -187,9 +187,8 @@ else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp" fi - # Download GCC from CI on test builders (temporarily disabled because the CI gcc component - # was renamed). - RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=false" + # Download GCC from CI on test builders + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true" # download-rustc seems to be broken on CI after the stage0 redesign # Disable it until these issues are debugged and resolved diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index d2b4e791df0e..c43c1f680acf 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -22,6 +22,7 @@ The following external projects are managed using some form of a `subtree`: * [rustfmt](https://github.com/rust-lang/rustfmt) * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) * [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) +* [rustc_codegen_gcc](https://github.com/rust-lang/rustc_codegen_gcc) * [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide) * [compiler-builtins](https://github.com/rust-lang/compiler-builtins) * [stdarch](https://github.com/rust-lang/stdarch) @@ -40,6 +41,7 @@ implement a new tool feature or test, that should happen in one collective rustc * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) * `rustfmt` * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) + * `rustc_codegen_gcc` ([sync guide](https://github.com/rust-lang/rustc_codegen_gcc/blob/master/doc/subtree.md)) * Using the [josh](#synchronizing-a-josh-subtree) tool * `miri` * `rust-analyzer` diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index 3d776677d23e..5d1201bbf426 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -29,14 +29,15 @@ You may prefer the `-softfloat` target when writing a kernel or interfacing with pre-compiled binaries that use the soft-float ABI. When using the hardfloat targets, the minimum floating-point features assumed -are those of the `fp-armv8`, which excludes NEON SIMD support. If your +are [`FEAT_AdvSIMD`][feat-advsimd], which means NEON SIMD support. If your processor supports a different set of floating-point features than the default -expectations of `fp-armv8`, then these should also be enabled or disabled as -needed with `-C target-feature=(+/-)`. It is also possible to tell Rust (or +expectations of `FEAT_AdvSIMD`, then these should also be enabled or disabled +as needed with `-C target-feature=(+/-)`. It is also possible to tell Rust (or LLVM) that you have a specific model of Arm processor, using the [`-Ctarget-cpu`][target-cpu] option. Doing so may change the default set of target-features enabled. +[feat-advsimd]: https://developer.arm.com/documentation/109697/2025_12/Feature-descriptions/The-Armv8-0-architecture-extension?lang=en [target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu [target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature diff --git a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md index 0eb7e1d84bd0..2d9fc85dad33 100644 --- a/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md +++ b/src/doc/rustc/src/platform-support/nvptx64-nvidia-cuda.md @@ -7,7 +7,6 @@ platform. ## Target maintainers -[@RDambrosio016](https://github.com/RDambrosio016) [@kjetilkjeka](https://github.com/kjetilkjeka) ## Requirements @@ -49,6 +48,39 @@ $ rustup component add llvm-tools --toolchain nightly $ rustup component add llvm-bitcode-linker --toolchain nightly ``` +## Target specific restrictions + +The PTX instruction set architecture has special requirements regarding what is +and isn't allowed. In order to avoid producing invalid PTX or generating undefined +behavior by LLVM, some Rust language features are disallowed when compiling for this target. + +### Static initializers must be acyclic + +A static's initializer must not form a cycle with itself or another static's +initializer. Therefore, the compiler will reject not only the self-referencing static `A`, +but all of the following statics. + +```Rust +struct Foo(&'static Foo); + +static A: Foo = Foo(&A); //~ ERROR static initializer forms a cycle involving `A` + +static B0: Foo = Foo(&B1); //~ ERROR static initializer forms a cycle involving `B0` +static B1: Foo = Foo(&B0); + +static C0: Foo = Foo(&C1); //~ ERROR static initializer forms a cycle involving `C0` +static C1: Foo = Foo(&C2); +static C2: Foo = Foo(&C0); +``` + +Initializers that are acyclic are allowed: + +```Rust +struct Bar(&'static u32); + +static BAR: Bar = Bar(&INT); // is allowed +static INT: u32 = 42u32; // also allowed +``` -Copyright 2014-2025 The Rust Project Developers +Copyright (c) The Rust Project Contributors Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index c2abbac37535..238465210ee2 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -10,7 +10,7 @@ clap = { version = "4.4", features = ["derive"] } indoc = "1.0" itertools = "0.12" opener = "0.7" -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" walkdir = "2.3" [package.metadata.rust-analyzer] diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index 84b65d3185e3..0235b130b6c0 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -1,10 +1,10 @@ use super::ALLOW_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; +use rustc_ast::attr::AttributeExt; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, LintContext}; -use rustc_ast::attr::AttributeExt; // Separate each crate's features. pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) { @@ -15,12 +15,7 @@ pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) { { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, ALLOW_ATTRIBUTES, path_span, "#[allow] attribute found", |diag| { - diag.span_suggestion( - path_span, - "replace it with", - "expect", - Applicability::MachineApplicable, - ); + diag.span_suggestion(path_span, "replace it with", "expect", Applicability::MachineApplicable); }); } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 366f5873a1aa..42c321df61c1 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -16,7 +16,7 @@ mod utils; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv, MsrvStack}; -use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind, AttrItemKind}; +use rustc_ast::{self as ast, AttrArgs, AttrItemKind, AttrKind, Attribute, MetaItemInner, MetaItemKind}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -604,7 +604,9 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::ignore) && match &attr.kind { - AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })), + AttrKind::Normal(normal_attr) => { + !matches!(normal_attr.item.args, AttrItemKind::Unparsed(AttrArgs::Eq { .. })) + }, AttrKind::DocComment(..) => true, } { diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index b854a3070bef..1a9abd88a46a 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -2,7 +2,7 @@ use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; -use rustc_ast::{AttrArgs, AttrKind, AttrItemKind}; +use rustc_ast::{AttrArgs, AttrItemKind, AttrKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs index f31b67f470f9..165941a859f7 100644 --- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::Sugg; use clippy_utils::sym; use clippy_utils::ty::{implements_trait, is_copy}; @@ -130,22 +131,24 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())]; - if let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) { - let sugg = if bool_value ^ eq_macro { - !sugg.maybe_paren() - } else if ty::Bool == *non_lit_ty.kind() { - sugg - } else { - !!sugg.maybe_paren() - }; - suggestions.push((non_lit_expr.span, sugg.to_string())); + let mut applicability = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_context(cx, non_lit_expr, macro_call.span.ctxt(), "..", &mut applicability); + let sugg = if bool_value ^ eq_macro { + !sugg.maybe_paren() + } else if ty::Bool == *non_lit_ty.kind() { + sugg + } else { + !!sugg.maybe_paren() + }; + let non_lit_expr_span = + walk_span_to_context(non_lit_expr.span, macro_call.span.ctxt()).unwrap_or(non_lit_expr.span); + suggestions.push((non_lit_expr_span, sugg.to_string())); - diag.multipart_suggestion( - format!("replace it with `{non_eq_mac}!(..)`"), - suggestions, - Applicability::MachineApplicable, - ); - } + diag.multipart_suggestion( + format!("replace it with `{non_eq_mac}!(..)`"), + suggestions, + applicability, + ); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index a04a56d72bc0..0bd459d8b021 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -15,6 +15,7 @@ use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol, SyntaxContext}; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -356,7 +357,7 @@ impl SuggestContext<'_, '_, '_> { if app != Applicability::MachineApplicable { return None; } - self.output.push_str(&(!snip).to_string()); + let _cannot_fail = write!(&mut self.output, "{}", &(!snip)); } }, True | False | Not(_) => { diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 60371dcd7715..08d92adbacef 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -132,7 +132,7 @@ declare_clippy_lint! { /// Because this can be caused purely by the dependencies /// themselves, it's not always possible to fix this issue. /// In those cases, you can allow that specific crate using - /// the `allowed_duplicate_crates` configuration option. + /// the `allowed-duplicate-crates` configuration option. /// /// ### Example /// ```toml diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index ec543d02c9dd..88d07be0d4d4 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::attr::data_structures::CfgEntry; +use rustc_ast::{AttrItemKind, EarlyParsedAttribute}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_ast::AttrItemKind; -use rustc_ast::EarlyParsedAttribute; use rustc_span::sym; -use rustc_ast::attr::data_structures::CfgEntry; declare_clippy_lint! { /// ### What it does @@ -40,7 +39,7 @@ impl EarlyLintPass for CfgNotTest { unreachable!() }; - if contains_not_test(&cfg, false) { + if contains_not_test(cfg, false) { span_lint_and_then( cx, CFG_NOT_TEST, @@ -58,11 +57,9 @@ impl EarlyLintPass for CfgNotTest { fn contains_not_test(cfg: &CfgEntry, not: bool) -> bool { match cfg { - CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| { - contains_not_test(item, not) - }), + CfgEntry::All(subs, _) | CfgEntry::Any(subs, _) => subs.iter().any(|item| contains_not_test(item, not)), CfgEntry::Not(sub, _) => contains_not_test(sub, !not), CfgEntry::NameValue { name: sym::test, .. } => not, - _ => false + _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 9b3822f9d8f0..8303897d1294 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; @@ -80,7 +80,8 @@ impl LateLintPass<'_> for CheckedConversions { && self.msrv.meets(cx, msrvs::TRY_FROM) { let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); + let (snippet, _) = + snippet_with_context(cx, cv.expr_to_cast.span, item.span.ctxt(), "_", &mut applicability); span_lint_and_sugg( cx, CHECKED_CONVERSIONS, diff --git a/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs index 2bd5e2cbfb1a..316d800a70c9 100644 --- a/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs +++ b/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -1,15 +1,16 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::fulfill_or_allowed; use rustc_hir::{self as hir, HirId}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::{Span, sym}; +use rustc_span::sym; use super::DERIVE_ORD_XOR_PARTIAL_ORD; /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - span: Span, + item: &hir::Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>, adt_hir_id: HirId, @@ -19,6 +20,8 @@ pub(super) fn check<'tcx>( && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() && let Some(def_id) = &trait_ref.trait_def_id() && *def_id == ord_trait_def_id + && let item_hir_id = cx.tcx.local_def_id_to_hir_id(item.owner_id) + && !fulfill_or_allowed(cx, DERIVE_ORD_XOR_PARTIAL_ORD, [adt_hir_id]) { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { @@ -39,7 +42,7 @@ pub(super) fn check<'tcx>( "you are deriving `Ord` but have implemented `PartialOrd` explicitly" }; - span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, adt_hir_id, span, mess, |diag| { + span_lint_hir_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, item_hir_id, item.span, mess, |diag| { if let Some(local_def_id) = impl_id.as_local() { let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); diff --git a/src/tools/clippy/clippy_lints/src/derive/mod.rs b/src/tools/clippy/clippy_lints/src/derive/mod.rs index eafe7c4bb9f2..86614201c406 100644 --- a/src/tools/clippy/clippy_lints/src/derive/mod.rs +++ b/src/tools/clippy/clippy_lints/src/derive/mod.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); - derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, adt_hir_id, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item, trait_ref, ty, adt_hir_id, is_automatically_derived); if is_automatically_derived { unsafe_derive_deserialize::check(cx, item, trait_ref, ty, adt_hir_id); diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs index f8e9e870f629..6c800f47b68a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrKind, AttrStyle, Attribute, AttrItemKind}; +use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index b11b2f8392c1..2b41275ee3a4 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -869,10 +869,12 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ }), true, ); - let mut doc = fragments.iter().fold(String::new(), |mut acc, fragment| { - add_doc_fragment(&mut acc, fragment); - acc - }); + + let mut doc = String::with_capacity(fragments.iter().map(|frag| frag.doc.as_str().len() + 1).sum()); + + for fragment in &fragments { + add_doc_fragment(&mut doc, fragment); + } doc.pop(); if doc.trim().is_empty() { diff --git a/src/tools/clippy/clippy_lints/src/double_parens.rs b/src/tools/clippy/clippy_lints/src/double_parens.rs index 8defbeeaa5f2..351d29d87432 100644 --- a/src/tools/clippy/clippy_lints/src/double_parens.rs +++ b/src/tools/clippy/clippy_lints/src/double_parens.rs @@ -114,6 +114,8 @@ fn check_source(cx: &EarlyContext<'_>, inner: &Expr) -> bool { && inner.starts_with('(') && inner.ends_with(')') && outer_after_inner.trim_start().starts_with(')') + // Don't lint macro repetition patterns like `($($result),*)` where parens are necessary + && !inner.trim_start_matches('(').trim_start().starts_with("$(") { true } else { diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 7b6f8729cb75..516f9e3aa60c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -1,9 +1,13 @@ +use std::borrow::Cow; + use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::{ - SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, peel_blocks, peel_blocks_with_stmt, sym, + SpanlessEq, eq_expr_value, higher, is_in_const_context, is_integer_literal, is_integer_literal_untyped, + peel_blocks, peel_blocks_with_stmt, sym, }; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; @@ -238,10 +242,21 @@ fn check_subtraction( if eq_expr_value(cx, left, big_expr) && eq_expr_value(cx, right, little_expr) { // This part of the condition is voluntarily split from the one before to ensure that // if `snippet_opt` fails, it won't try the next conditions. - if (!is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST)) - && let Some(big_expr_sugg) = Sugg::hir_opt(cx, big_expr).map(Sugg::maybe_paren) - && let Some(little_expr_sugg) = Sugg::hir_opt(cx, little_expr) - { + if !is_in_const_context(cx) || msrv.meets(cx, msrvs::SATURATING_SUB_CONST) { + let mut applicability = Applicability::MachineApplicable; + let big_expr_sugg = (if is_integer_literal_untyped(big_expr) { + let get_snippet = |span: Span| { + let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); + let big_expr_ty = cx.typeck_results().expr_ty(big_expr); + Cow::Owned(format!("{snippet}_{big_expr_ty}")) + }; + Sugg::hir_from_snippet(cx, big_expr, get_snippet) + } else { + Sugg::hir_with_applicability(cx, big_expr, "..", &mut applicability) + }) + .maybe_paren(); + let little_expr_sugg = Sugg::hir_with_applicability(cx, little_expr, "..", &mut applicability); + let sugg = format!( "{}{big_expr_sugg}.saturating_sub({little_expr_sugg}){}", if is_composited { "{ " } else { "" }, @@ -254,7 +269,7 @@ fn check_subtraction( "manual arithmetic check found", "replace it with", sugg, - Applicability::MachineApplicable, + applicability, ); } } else if eq_expr_value(cx, left, little_expr) diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index e0149a23fccf..8d538ff1acba 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -3,14 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::{is_in_const_context, is_in_test, sym}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, HirId, RustcVersion, StabilityLevel, StableSince, find_attr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::{CrateNum, DefId}; use rustc_span::{ExpnKind, Span}; -use rustc_hir::attrs::AttributeKind; -use rustc_hir::find_attr; declare_clippy_lint! { /// ### What it does @@ -270,6 +269,9 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv { /// attribute. fn is_under_cfg_attribute(cx: &LateContext<'_>, hir_id: HirId) -> bool { cx.tcx.hir_parent_id_iter(hir_id).any(|id| { - find_attr!(cx.tcx.hir_attrs(id), AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace) + find_attr!( + cx.tcx.hir_attrs(id), + AttributeKind::CfgTrace(..) | AttributeKind::CfgAttrTrace + ) }) } diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs index f59c7615d745..14928a1be13b 100644 --- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs +++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs @@ -101,7 +101,21 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { InherentImplLintScope::Crate => Criterion::Crate, }; let is_test = is_cfg_test(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id); - match type_map.entry((impl_ty, criterion, is_test)) { + let predicates = { + // Gets the predicates (bounds) for the given impl block, + // sorted for consistent comparison to allow distinguishing between impl blocks + // with different generic bounds. + let mut predicates = cx + .tcx + .predicates_of(impl_id) + .predicates + .iter() + .map(|(clause, _)| *clause) + .collect::>(); + predicates.sort_by_key(|c| format!("{c:?}")); + predicates + }; + match type_map.entry((impl_ty, predicates, criterion, is_test)) { Entry::Vacant(e) => { // Store the id for the first impl block of this type. The span is retrieved lazily. e.insert(IdOrSpan::Id(impl_id)); @@ -152,15 +166,12 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl { fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { let id = cx.tcx.local_def_id_to_hir_id(id); if let Node::Item(&Item { - kind: ItemKind::Impl(impl_item), + kind: ItemKind::Impl(_), span, .. }) = cx.tcx.hir_node(id) { - (!span.from_expansion() - && impl_item.generics.params.is_empty() - && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])) - .then_some(span) + (!span.from_expansion() && !fulfill_or_allowed(cx, MULTIPLE_INHERENT_IMPL, [id])).then_some(span) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 5c37747b8c9b..54599499515e 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -1,9 +1,8 @@ -use rustc_ast::AttrItemKind; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrKind, Attribute, LitKind}; +use rustc_ast::{AttrArgs, AttrItemKind, AttrKind, Attribute, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 261b03abba17..32c23a2d0362 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -94,6 +94,15 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { }) && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { + // libtest might generate a large array containing the test cases, and no span will be associated + // to it. In this case it is better not to complain. + // + // Note that this condition is not checked explicitly by a unit test. Do not remove it without + // ensuring that stays fixed. + if expr.span.is_dummy() { + return; + } + span_lint_and_then( cx, LARGE_STACK_ARRAYS, diff --git a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs index 39b2391c98ec..7fb8e51377a2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/loops/for_kv_map.rs @@ -1,16 +1,22 @@ use super::FOR_KV_MAP; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet_with_applicability, walk_span_to_context}; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::sym; +use rustc_span::{Span, sym}; /// Checks for the `FOR_KV_MAP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + arg: &'tcx Expr<'_>, + body: &'tcx Expr<'_>, + span: Span, +) { let pat_span = pat.span; if let PatKind::Tuple(pat, _) = pat.kind @@ -34,21 +40,25 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) { + if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) + && let Some(arg_span) = walk_span_to_context(arg_span, span.ctxt()) + { span_lint_and_then( cx, FOR_KV_MAP, arg_span, format!("you seem to want to iterate on a map's {kind}s"), |diag| { - let map = sugg::Sugg::hir(cx, arg, "map"); + let mut applicability = Applicability::MachineApplicable; + let map = sugg::Sugg::hir_with_context(cx, arg, span.ctxt(), "map", &mut applicability); + let pat = snippet_with_applicability(cx, new_pat_span, kind, &mut applicability); diag.multipart_suggestion( "use the corresponding method", vec![ - (pat_span, snippet(cx, new_pat_span, kind).into_owned()), + (pat_span, pat.to_string()), (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_paren())), ], - Applicability::MachineApplicable, + applicability, ); }, ); diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index ddc783069385..83574cab6b67 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -943,7 +943,7 @@ impl Loops { explicit_counter_loop::check(cx, pat, arg, body, expr, label); } self.check_for_loop_arg(cx, pat, arg); - for_kv_map::check(cx, pat, arg, body); + for_kv_map::check(cx, pat, arg, body, span); mut_range_bound::check(cx, arg, body); single_element_loop::check(cx, pat, arg, body, expr); same_item_push::check(cx, pat, arg, body, expr, self.msrv); diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index a037af3433c3..e7b9b1cd3881 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -4,8 +4,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{snippet, snippet_with_context}; -use clippy_utils::sym; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; +use clippy_utils::{contains_return, sym}; use rustc_errors::Applicability; use rustc_hir::{ Block, Closure, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, @@ -82,7 +82,7 @@ pub(super) fn check_iterator_reduction<'tcx>( ) { let closure_body = cx.tcx.hir_body(closure.body).value; let body_ty = cx.typeck_results().expr_ty(closure_body); - if body_ty.is_never() { + if body_ty.is_never() && !contains_return(closure_body) { span_lint_and_then( cx, NEVER_LOOP, diff --git a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs index 25057b4aeaa2..1c20a8f81efb 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ignore_case_cmp.rs @@ -1,7 +1,7 @@ use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sym; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -111,14 +111,12 @@ impl LateLintPass<'_> for ManualIgnoreCaseCmp { "manual case-insensitive ASCII comparison", |diag| { let mut app = Applicability::MachineApplicable; + let (left_snip, _) = snippet_with_context(cx, left_span, expr.span.ctxt(), "..", &mut app); + let (right_snip, _) = snippet_with_context(cx, right_span, expr.span.ctxt(), "..", &mut app); diag.span_suggestion_verbose( expr.span, "consider using `.eq_ignore_ascii_case()` instead", - format!( - "{neg}{}.eq_ignore_ascii_case({deref}{})", - snippet_with_applicability(cx, left_span, "_", &mut app), - snippet_with_applicability(cx, right_span, "_", &mut app) - ), + format!("{neg}{left_snip}.eq_ignore_ascii_case({deref}{right_snip})"), app, ); }, diff --git a/src/tools/clippy/clippy_lints/src/manual_ilog2.rs b/src/tools/clippy/clippy_lints/src/manual_ilog2.rs index 1c61db530606..4b411a60f3bf 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ilog2.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ilog2.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{is_from_proc_macro, sym}; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; @@ -102,7 +102,7 @@ impl LateLintPass<'_> for ManualIlog2 { fn emit(cx: &LateContext<'_>, recv: &Expr<'_>, full_expr: &Expr<'_>) { let mut app = Applicability::MachineApplicable; - let recv = snippet_with_applicability(cx, recv.span, "_", &mut app); + let (recv, _) = snippet_with_context(cx, recv.span, full_expr.span.ctxt(), "_", &mut app); span_lint_and_sugg( cx, MANUAL_ILOG2, 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 c35c3d1f62e6..1fc8bb9acce2 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 @@ -135,7 +135,7 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok } else { Applicability::MachineApplicable }; - let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); + let scrut = Sugg::hir_with_context(cx, scrutinee, expr.span.ctxt(), "..", &mut app).maybe_paren(); let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty); 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 795355f25f9e..12fe44ef2f13 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 @@ -46,6 +46,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: let cast = if input_ty == output_ty { "" } else { ".map(|x| x as _)" }; let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); span_lint_and_then( cx, MATCH_AS_REF, @@ -59,7 +60,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: "use `Option::as_ref()`", format!( "{}.as_ref(){cast}", - Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + Sugg::hir_with_context(cx, ex, ctxt, "_", &mut applicability).maybe_paren(), ), applicability, ); @@ -69,7 +70,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: format!("use `Option::{method}()` directly"), format!( "{}.{method}(){cast}", - Sugg::hir_with_applicability(cx, ex, "_", &mut applicability).maybe_paren(), + Sugg::hir_with_context(cx, ex, ctxt, "_", &mut applicability).maybe_paren(), ), applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index a2c8741f4f74..3e76231b6ef1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -16,6 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] && arms .iter() .all(|arm| arm.pat.walk_short(|p| !matches!(p.kind, PatKind::Binding(..)))) + && arms.len() == 2 { span_lint_and_then( cx, @@ -23,59 +24,58 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] expr.span, "`match` on a boolean expression", move |diag| { - if arms.len() == 2 { - let mut app = Applicability::MachineApplicable; - let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { - let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app); - if let PatExprKind::Lit { lit, .. } = arm_bool.kind { - match &lit.node { - LitKind::Bool(true) => Some(test), - LitKind::Bool(false) => Some(!test), - _ => None, - } - .map(|test| { - if let Some(guard) = &arms[0] - .guard - .map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app)) - { - test.and(guard) - } else { - test - } - }) - } else { - None + let mut app = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + let test = Sugg::hir_with_context(cx, scrutinee, ctxt, "_", &mut app); + if let PatExprKind::Lit { lit, .. } = arm_bool.kind { + match &lit.node { + LitKind::Bool(true) => Some(test), + LitKind::Bool(false) => Some(!test), + _ => None, } + .map(|test| { + if let Some(guard) = &arms[0] + .guard + .map(|g| Sugg::hir_with_context(cx, g, ctxt, "_", &mut app)) + { + test.and(guard) + } else { + test + } + }) } else { None + } + } else { + None + }; + + if let Some(test_sugg) = test_sugg { + let ctxt = expr.span.ctxt(); + let (true_expr, false_expr) = (arms[0].body, arms[1].body); + let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { + (false, false) => Some(format!( + "if {} {} else {}", + test_sugg, + expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) + )), + (false, true) => Some(format!( + "if {} {}", + test_sugg, + expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) + )), + (true, false) => Some(format!( + "if {} {}", + !test_sugg, + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) + )), + (true, true) => None, }; - if let Some(test_sugg) = test_sugg { - let ctxt = expr.span.ctxt(); - let (true_expr, false_expr) = (arms[0].body, arms[1].body); - let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { - (false, false) => Some(format!( - "if {} {} else {}", - test_sugg, - expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), - expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) - )), - (false, true) => Some(format!( - "if {} {}", - test_sugg, - expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) - )), - (true, false) => Some(format!( - "if {} {}", - !test_sugg, - expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) - )), - (true, true) => None, - }; - - if let Some(sugg) = sugg { - diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app); - } + if let Some(sugg) = sugg { + diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app); } } }, 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 bc3783750e5c..bf0c0c4aec3c 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 @@ -1,7 +1,6 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::walk_span_to_context; use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; @@ -25,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { .. }) = higher::WhileLet::hir(expr) { - find_method_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false); + find_method_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false, let_span); find_if_let_true(cx, let_pat, let_expr, let_span); } } @@ -39,7 +38,7 @@ pub(super) fn check_if_let<'tcx>( let_span: Span, ) { find_if_let_true(cx, pat, scrutinee, let_span); - find_method_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else); + find_method_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else, let_span); } /// Looks for: @@ -182,6 +181,7 @@ fn find_method_sugg_for_if_let<'tcx>( let_expr: &'tcx Expr<'_>, keyword: &'static str, has_else: bool, + let_span: Span, ) { // also look inside refs // if we have &None for example, peel it so we can detect "if let None = x" @@ -239,15 +239,9 @@ fn find_method_sugg_for_if_let<'tcx>( let expr_span = expr.span; let ctxt = expr.span.ctxt(); - // if/while let ... = ... { ... } - // ^^^ - let Some(res_span) = walk_span_to_context(result_expr.span.source_callsite(), ctxt) else { - return; - }; - // if/while let ... = ... { ... } // ^^^^^^^^^^^^^^^^^^^^^^ - let span = expr_span.until(res_span.shrink_to_hi()); + let span = expr_span.until(let_span.shrink_to_hi()); let mut app = if needs_drop { Applicability::MaybeIncorrect @@ -273,13 +267,14 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op if let Ok(arms) = arms.try_into() // TODO: use `slice::as_array` once stabilized && let Some((good_method, maybe_guard)) = found_good_method(cx, arms) { - let span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span.to(op.span)); + let expr_span = is_expn_of(expr.span, sym::matches).unwrap_or(expr.span); + let result_expr = match &op.kind { ExprKind::AddrOf(_, _, borrowed) => borrowed, _ => op, }; let mut app = Applicability::MachineApplicable; - let receiver_sugg = Sugg::hir_with_applicability(cx, result_expr, "_", &mut app).maybe_paren(); + let receiver_sugg = Sugg::hir_with_context(cx, result_expr, expr_span.ctxt(), "_", &mut app).maybe_paren(); let mut sugg = format!("{receiver_sugg}.{good_method}"); if let Some(guard) = maybe_guard { @@ -302,14 +297,14 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op return; } - let guard = Sugg::hir(cx, guard, ".."); + let guard = Sugg::hir_with_context(cx, guard, expr_span.ctxt(), "..", &mut app); let _ = write!(sugg, " && {}", guard.maybe_paren()); } span_lint_and_sugg( cx, REDUNDANT_PATTERN_MATCHING, - span, + expr_span, format!("redundant pattern matching, consider using `{good_method}`"), "try", sugg, diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs index 834456ff6668..8135c67d0d78 100644 --- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs +++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs @@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{is_assert_macro, root_macro_call}; use clippy_utils::res::MaybeResPath; use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context}; -use rustc_hir::{Expr, HirId}; -use rustc_lint::{LateContext, LintContext}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::find_attr; +use rustc_hir::{Expr, HirId, find_attr}; +use rustc_lint::{LateContext, LintContext}; use super::CONST_IS_EMPTY; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 16db8663941e..366bfaed73d4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -2,7 +2,7 @@ use super::ITER_KV_MAP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::{pat_is_wild, sym}; use rustc_hir::{Body, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; @@ -58,6 +58,8 @@ pub(super) fn check<'tcx>( applicability, ); } else { + let (body_snippet, _) = + snippet_with_context(cx, body_expr.span, expr.span.ctxt(), "..", &mut applicability); span_lint_and_sugg( cx, ITER_KV_MAP, @@ -65,9 +67,8 @@ pub(super) fn check<'tcx>( format!("iterating on a map's {replacement_kind}s"), "try", format!( - "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {})", + "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {body_snippet})", annotation.prefix_str(), - snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability) ), applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 9dae6fbb48dd..c3f031edff2e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -1,10 +1,10 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_blocks, strip_pat_refs}; use rustc_ast::ast; use rustc_data_structures::packed::Pu128; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_hir::def::{DefKind, Res}; @@ -59,6 +59,34 @@ struct Replacement { method_name: &'static str, has_args: bool, has_generic_return: bool, + is_short_circuiting: bool, +} + +impl Replacement { + fn default_applicability(&self) -> Applicability { + if self.is_short_circuiting { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + } + } + + fn maybe_add_note(&self, diag: &mut Diag<'_, ()>) { + if self.is_short_circuiting { + diag.note(format!( + "the `{}` method is short circuiting and may change the program semantics if the iterator has side effects", + self.method_name + )); + } + } + + fn maybe_turbofish(&self, ty: Ty<'_>) -> String { + if self.has_generic_return { + format!("::<{ty}>") + } else { + String::new() + } + } } fn check_fold_with_op( @@ -86,32 +114,30 @@ fn check_fold_with_op( && left_expr.res_local_id() == Some(first_arg_id) && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) { - let mut applicability = Applicability::MachineApplicable; - - let turbofish = if replacement.has_generic_return { - format!("::<{}>", cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()) - } else { - String::new() - }; - - let sugg = if replacement.has_args { - format!( - "{method}{turbofish}(|{second_arg_ident}| {r})", - method = replacement.method_name, - r = snippet_with_applicability(cx, right_expr.span, "EXPR", &mut applicability), - ) - } else { - format!("{method}{turbofish}()", method = replacement.method_name) - }; - - span_lint_and_sugg( + let span = fold_span.with_hi(expr.span.hi()); + span_lint_and_then( cx, UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), + span, "this `.fold` can be written more succinctly using another method", - "try", - sugg, - applicability, + |diag| { + let mut applicability = replacement.default_applicability(); + let turbofish = + replacement.maybe_turbofish(cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()); + let (r_snippet, _) = + snippet_with_context(cx, right_expr.span, expr.span.ctxt(), "EXPR", &mut applicability); + let sugg = if replacement.has_args { + format!( + "{method}{turbofish}(|{second_arg_ident}| {r_snippet})", + method = replacement.method_name, + ) + } else { + format!("{method}{turbofish}()", method = replacement.method_name) + }; + + diag.span_suggestion(span, "try", sugg, applicability); + replacement.maybe_add_note(diag); + }, ); return true; } @@ -131,22 +157,25 @@ fn check_fold_with_method( // Check if the function belongs to the operator && cx.tcx.is_diagnostic_item(method, fn_did) { - let applicability = Applicability::MachineApplicable; - - let turbofish = if replacement.has_generic_return { - format!("::<{}>", cx.typeck_results().expr_ty(expr)) - } else { - String::new() - }; - - span_lint_and_sugg( + let span = fold_span.with_hi(expr.span.hi()); + span_lint_and_then( cx, UNNECESSARY_FOLD, - fold_span.with_hi(expr.span.hi()), + span, "this `.fold` can be written more succinctly using another method", - "try", - format!("{method}{turbofish}()", method = replacement.method_name), - applicability, + |diag| { + diag.span_suggestion( + span, + "try", + format!( + "{method}{turbofish}()", + method = replacement.method_name, + turbofish = replacement.maybe_turbofish(cx.typeck_results().expr_ty(expr)) + ), + replacement.default_applicability(), + ); + replacement.maybe_add_note(diag); + }, ); } } @@ -171,6 +200,7 @@ pub(super) fn check<'tcx>( method_name: "any", has_args: true, has_generic_return: false, + is_short_circuiting: true, }; check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, replacement); }, @@ -179,6 +209,7 @@ pub(super) fn check<'tcx>( method_name: "all", has_args: true, has_generic_return: false, + is_short_circuiting: true, }; check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, replacement); }, @@ -187,6 +218,7 @@ pub(super) fn check<'tcx>( method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), + is_short_circuiting: false, }; if !check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, replacement) { check_fold_with_method(cx, expr, acc, fold_span, sym::add, replacement); @@ -197,6 +229,7 @@ pub(super) fn check<'tcx>( method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), + is_short_circuiting: false, }; if !check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, replacement) { check_fold_with_method(cx, expr, acc, fold_span, sym::mul, replacement); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a6a39cb6ab30..74e8dbc15a6c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,7 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_context}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; @@ -131,8 +131,10 @@ fn check_addr_of_expr( && (*referent_ty != receiver_ty || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) || is_cow_into_owned(cx, method_name, method_parent_id)) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { + let mut applicability = Applicability::MachineApplicable; + let (receiver_snippet, _) = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut applicability); + if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { span_lint_and_sugg( cx, @@ -145,7 +147,7 @@ fn check_addr_of_expr( "", width = n_target_refs - n_receiver_refs ), - Applicability::MachineApplicable, + applicability, ); return true; } @@ -165,8 +167,8 @@ fn check_addr_of_expr( parent.span, format!("unnecessary use of `{method_name}`"), "use", - receiver_snippet.to_owned(), - Applicability::MachineApplicable, + receiver_snippet.to_string(), + applicability, ); } else { span_lint_and_sugg( @@ -176,7 +178,7 @@ fn check_addr_of_expr( format!("unnecessary use of `{method_name}`"), "remove this", String::new(), - Applicability::MachineApplicable, + applicability, ); } return true; @@ -191,7 +193,7 @@ fn check_addr_of_expr( format!("unnecessary use of `{method_name}`"), "use", format!("{receiver_snippet}.as_ref()"), - Applicability::MachineApplicable, + applicability, ); return true; } @@ -409,8 +411,10 @@ fn check_other_call_arg<'tcx>( None } && can_change_type(cx, maybe_arg, receiver_ty) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) { + let mut applicability = Applicability::MachineApplicable; + let (receiver_snippet, _) = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut applicability); + span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, @@ -418,7 +422,7 @@ fn check_other_call_arg<'tcx>( format!("unnecessary use of `{method_name}`"), "use", format!("{:&>n_refs$}{receiver_snippet}", ""), - Applicability::MachineApplicable, + applicability, ); return true; } diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index eeea6dfd5f4b..5dd38cf059c2 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -82,7 +82,8 @@ impl LateLintPass<'_> for ImportRename { && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { - if rename.trim() == name.as_str() { + let trimmed_rename = rename.trim(); + if trimmed_rename == "_" || trimmed_rename == name.as_str() { None } else { Some(import.trim()) diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 741f38f97560..5b6b4f112455 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -31,7 +31,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub MULTIPLE_BOUND_LOCATIONS, - suspicious, + style, "defining generic bounds in multiple locations" } diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 2fef8404f824..ad44d65b4d66 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -143,7 +143,7 @@ fn check_expr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, ty_ascription: &T && new.ident.name == sym::new { let mut applicability = Applicability::MaybeIncorrect; - let arg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability); + let arg = Sugg::hir_with_context(cx, arg, expr.span.ctxt(), "_", &mut applicability); let mut suggs = vec![(expr.span, format!("std::sync::atomic::{atomic_name}::new({arg})"))]; match ty_ascription { TypeAscriptionKind::Required(ty_ascription) => { diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 854e927aa2f7..6b5db9dcf3e2 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call, @@ -171,8 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) { let mut applicability = Applicability::MachineApplicable; - let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); - let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); + let cond = Sugg::hir_with_context(cx, cond, e.span.ctxt(), "..", &mut applicability); + let (lhs, _) = snippet_with_context(cx, lhs_a.span, e.span.ctxt(), "..", &mut applicability); let mut sugg = if a == b { format!("{cond}; {lhs} = {a:?};") } else { diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index d03188f1d39b..55a5a16c0099 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -56,8 +56,20 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind - && let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind { + check_expr(cx, expr, stmt.span); + } + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + if let Some(expr) = block.expr { + check_expr(cx, expr, expr.span); + } + } +} + +fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>, outer_span: Span) { + if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. @@ -76,69 +88,74 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}` // and suggesting `for … in … { unsafe { } }` is a little ugly. && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..)) + { + let mut applicability = Applicability::MachineApplicable; + + // If any closure parameter has an explicit type specified, applying the lint would necessarily + // remove that specification, possibly breaking type inference + if fn_decl + .inputs + .iter() + .any(|input| matches!(input.kind, TyKind::Infer(..))) { - let mut applicability = Applicability::MachineApplicable; + applicability = Applicability::MaybeIncorrect; + } - // If any closure parameter has an explicit type specified, applying the lint would necessarily - // remove that specification, possibly breaking type inference - if fn_decl - .inputs - .iter() - .any(|input| matches!(input.kind, TyKind::Infer(..))) - { - applicability = Applicability::MaybeIncorrect; - } + let mut ret_collector = RetCollector::default(); + ret_collector.visit_expr(body.value); - let mut ret_collector = RetCollector::default(); - ret_collector.visit_expr(body.value); + // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. + if ret_collector.ret_in_loop { + return; + } - // Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`. - if ret_collector.ret_in_loop { - return; - } + let ret_suggs = if ret_collector.spans.is_empty() { + None + } else { + applicability = Applicability::MaybeIncorrect; + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), + ) + }; - let ret_suggs = if ret_collector.spans.is_empty() { - None + let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability); + let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability); + let (body_value_sugg, is_macro_call) = + snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability); + + let sugg = format!( + "for {} in {} {}", + body_param_sugg, + for_each_rev_sugg, + if is_macro_call { + format!("{{ {body_value_sugg}; }}") } else { - applicability = Applicability::MaybeIncorrect; - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ) - }; - - let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability); - let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability); - let (body_value_sugg, is_macro_call) = - snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability); - - let sugg = format!( - "for {} in {} {}", - body_param_sugg, - for_each_rev_sugg, - if is_macro_call { - format!("{{ {body_value_sugg}; }}") - } else { - match body.value.kind { - ExprKind::Block(block, _) if is_let_desugar(block) => { - format!("{{ {body_value_sugg} }}") - }, - ExprKind::Block(_, _) => body_value_sugg.to_string(), - _ => format!("{{ {body_value_sugg}; }}"), - } + match body.value.kind { + ExprKind::Block(block, _) if is_let_desugar(block) => { + format!("{{ {body_value_sugg} }}") + }, + ExprKind::Block(_, _) => body_value_sugg.to_string(), + _ => format!("{{ {body_value_sugg}; }}"), } - ); + } + ); - span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| { - diag.span_suggestion(stmt.span, "try", sugg, applicability); + span_lint_and_then( + cx, + NEEDLESS_FOR_EACH, + outer_span, + "needless use of `for_each`", + |diag| { + diag.span_suggestion(outer_span, "try", sugg, applicability); if let Some(ret_suggs) = ret_suggs { diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability); } - }); - } + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs index 67493d54b552..e151c06c6c20 100644 --- a/src/tools/clippy/clippy_lints/src/new_without_default.rs +++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs @@ -1,16 +1,15 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::return_ty; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::sugg::DiagExt; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::HirIdSet; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{Attribute, HirIdSet}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocKind; use rustc_session::impl_lint_pass; use rustc_span::sym; -use rustc_hir::Attribute; -use rustc_hir::attrs::AttributeKind; declare_clippy_lint! { /// ### What it does @@ -59,6 +58,7 @@ pub struct NewWithoutDefault { impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { + #[expect(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let hir::ItemKind::Impl(hir::Impl { of_trait: None, @@ -139,16 +139,34 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { sugg.push_str(&snippet_with_applicability(cx.sess(), *attr_span, "_", &mut app)); sugg.push('\n'); } - } sugg }; let generics_sugg = snippet_with_applicability(cx, generics.span, "", &mut app); let where_clause_sugg = if generics.has_where_clause_predicates { - format!( - "\n{}\n", - snippet_with_applicability(cx, generics.where_clause_span, "", &mut app) - ) + let where_clause_sugg = + snippet_with_applicability(cx, generics.where_clause_span, "", &mut app).to_string(); + let mut where_clause_sugg = reindent_multiline(&where_clause_sugg, true, Some(4)); + if impl_item.generics.has_where_clause_predicates { + if !where_clause_sugg.ends_with(',') { + where_clause_sugg.push(','); + } + + let additional_where_preds = + snippet_with_applicability(cx, impl_item.generics.where_clause_span, "", &mut app); + let ident = indent_of(cx, generics.where_clause_span).unwrap_or(0); + // Remove the leading `where ` keyword + let additional_where_preds = additional_where_preds.trim_start_matches("where").trim_start(); + where_clause_sugg.push('\n'); + where_clause_sugg.extend(std::iter::repeat_n(' ', ident)); + where_clause_sugg.push_str(additional_where_preds); + } + format!("\n{where_clause_sugg}\n") + } else if impl_item.generics.has_where_clause_predicates { + let where_clause_sugg = + snippet_with_applicability(cx, impl_item.generics.where_clause_span, "", &mut app); + let where_clause_sugg = reindent_multiline(&where_clause_sugg, true, Some(4)); + format!("\n{}\n", where_clause_sugg.trim_start()) } else { String::new() }; diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 05358de5b348..39097833a6c5 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeQPath; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; @@ -94,51 +94,37 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) return; } - let arg_snip = snippet(cx, arg_span, ".."); - let expr_snip; - let eq_impl; - if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() { - expr_snip = format!("*{arg_snip}"); - eq_impl = with_deref; + let mut applicability = Applicability::MachineApplicable; + let (arg_snip, _) = snippet_with_context(cx, arg_span, expr.span.ctxt(), "..", &mut applicability); + let (expr_snip, eq_impl) = if with_deref.is_implemented() && !arg_ty.peel_refs().is_str() { + (format!("*{arg_snip}"), with_deref) } else { - expr_snip = arg_snip.to_string(); - eq_impl = without_deref; - } + (arg_snip.to_string(), without_deref) + }; - let span; - let hint; - if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) { - span = expr.span; - hint = expr_snip; + let (span, hint) = if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) { + (expr.span, expr_snip) } else { - span = expr.span.to(other.span); + let span = expr.span.to(other.span); let cmp_span = if other.span < expr.span { other.span.between(expr.span) } else { expr.span.between(other.span) }; - if eq_impl.ty_eq_other { - hint = format!( - "{expr_snip}{}{}", - snippet(cx, cmp_span, ".."), - snippet(cx, other.span, "..") - ); - } else { - hint = format!( - "{}{}{expr_snip}", - snippet(cx, other.span, ".."), - snippet(cx, cmp_span, "..") - ); - } - } - diag.span_suggestion( - span, - "try", - hint, - Applicability::MachineApplicable, // snippet - ); + let (cmp_snippet, _) = snippet_with_context(cx, cmp_span, expr.span.ctxt(), "..", &mut applicability); + let (other_snippet, _) = + snippet_with_context(cx, other.span, expr.span.ctxt(), "..", &mut applicability); + + if eq_impl.ty_eq_other { + (span, format!("{expr_snip}{cmp_snippet}{other_snippet}")) + } else { + (span, format!("{other_snippet}{cmp_snippet}{expr_snip}")) + } + }; + + diag.span_suggestion(span, "try", hint, applicability); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs index 98aa47421537..5ed923d719bc 100644 --- a/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/operators/manual_div_ceil.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeQPath}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::{SpanlessEq, sym}; use rustc_ast::{BinOpKind, LitIntType, LitKind, UnOp}; @@ -7,7 +8,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self}; +use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Spanned; use super::MANUAL_DIV_CEIL; @@ -16,59 +17,84 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & let mut applicability = Applicability::MachineApplicable; if op == BinOpKind::Div - && check_int_ty_and_feature(cx, lhs) - && check_int_ty_and_feature(cx, rhs) - && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = lhs.kind + && check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(lhs)) + && check_int_ty_and_feature(cx, cx.typeck_results().expr_ty(rhs)) && msrv.meets(cx, msrvs::DIV_CEIL) { - // (x + (y - 1)) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, rhs) - { - build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); - return; - } + match lhs.kind { + ExprKind::Binary(inner_op, inner_lhs, inner_rhs) => { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) + { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + return; + } - // ((y - 1) + x) / y - if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Add - && sub_op.node == BinOpKind::Sub - && check_literal(sub_rhs) - && check_eq_expr(cx, sub_lhs, rhs) - { - build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); - return; - } + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, rhs) + { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + return; + } - // (x + y - 1) / y - if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind - && inner_op.node == BinOpKind::Sub - && add_op.node == BinOpKind::Add - && check_literal(inner_rhs) - && check_eq_expr(cx, add_rhs, rhs) - { - build_suggestion(cx, expr, add_lhs, rhs, &mut applicability); - } + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, rhs) + { + build_suggestion(cx, expr, add_lhs, rhs, &mut applicability); + } - // (x + (Y - 1)) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) { - build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); - } + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, rhs) { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + } - // ((Y - 1) + x) / Y - if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) { - build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); - } + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, rhs) { + build_suggestion(cx, expr, inner_rhs, rhs, &mut applicability); + } - // (x - (-Y - 1)) / Y - if inner_op.node == BinOpKind::Sub - && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind - && differ_by_one(abs_div_rhs, inner_rhs) - { - build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); + } + }, + ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) => { + // x.next_multiple_of(Y) / Y + if method.ident.name == sym::next_multiple_of + && check_int_ty(cx.typeck_results().expr_ty(receiver)) + && check_eq_expr(cx, next_multiple_of_arg, rhs) + { + build_suggestion(cx, expr, receiver, rhs, &mut applicability); + } + }, + ExprKind::Call(callee, [receiver, next_multiple_of_arg]) => { + // int_type::next_multiple_of(x, Y) / Y + if let Some(impl_ty_binder) = callee + .ty_rel_def_if_named(cx, sym::next_multiple_of) + .assoc_fn_parent(cx) + .opt_impl_ty(cx) + && check_int_ty(impl_ty_binder.skip_binder()) + && check_eq_expr(cx, next_multiple_of_arg, rhs) + { + build_suggestion(cx, expr, receiver, rhs, &mut applicability); + } + }, + _ => (), } } } @@ -91,8 +117,11 @@ fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool { } } -fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - let expr_ty = cx.typeck_results().expr_ty(expr); +fn check_int_ty(expr_ty: Ty<'_>) -> bool { + matches!(expr_ty.peel_refs().kind(), ty::Int(_) | ty::Uint(_)) +} + +fn check_int_ty_and_feature(cx: &LateContext<'_>, expr_ty: Ty<'_>) -> bool { match expr_ty.peel_refs().kind() { ty::Uint(_) => true, ty::Int(_) => cx.tcx.features().enabled(sym::int_roundings), diff --git a/src/tools/clippy/clippy_lints/src/operators/manual_is_multiple_of.rs b/src/tools/clippy/clippy_lints/src/operators/manual_is_multiple_of.rs index 0b9bd4fb6d32..291d81097b51 100644 --- a/src/tools/clippy/clippy_lints/src/operators/manual_is_multiple_of.rs +++ b/src/tools/clippy/clippy_lints/src/operators/manual_is_multiple_of.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( { let mut app = Applicability::MachineApplicable; let divisor = deref_sugg( - Sugg::hir_with_applicability(cx, operand_right, "_", &mut app), + Sugg::hir_with_context(cx, operand_right, expr.span.ctxt(), "_", &mut app), cx.typeck_results().expr_ty_adjusted(operand_right), ); span_lint_and_sugg( @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( format!( "{}{}.is_multiple_of({divisor})", if op == BinOpKind::Eq { "" } else { "!" }, - Sugg::hir_with_applicability(cx, operand_left, "_", &mut app).maybe_paren() + Sugg::hir_with_context(cx, operand_left, expr.span.ctxt(), "_", &mut app).maybe_paren() ), app, ); diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 59d31f782bc3..e5fb3c0fa431 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -5,7 +5,7 @@ use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::usage::local_used_after_expr; @@ -147,7 +147,8 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { && !span_contains_cfg(cx, els.span) { let mut applicability = Applicability::MaybeIncorrect; - let init_expr_str = Sugg::hir_with_applicability(cx, init_expr, "..", &mut applicability).maybe_paren(); + let init_expr_str = + Sugg::hir_with_context(cx, init_expr, stmt.span.ctxt(), "..", &mut applicability).maybe_paren(); // Take care when binding is `ref` let sugg = if let PatKind::Binding( BindingMode(ByRef::Yes(_, ref_mutability), binding_mutability), @@ -295,7 +296,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex && (is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block)) { let mut applicability = Applicability::MachineApplicable; - let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); + let receiver_str = snippet_with_context(cx, caller.span, expr.span.ctxt(), "..", &mut applicability).0; let by_ref = !cx.type_is_copy_modulo_regions(caller_ty) && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { 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 92d1b112198f..e4906eb0c777 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 @@ -98,7 +98,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && snippet.starts_with(suggested_type.starts_with()) && snippet.ends_with(suggested_type.ends_with()) { - let mut applicability = Applicability::MachineApplicable; + let mut applicability = Applicability::MaybeIncorrect; 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); diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 1d0efa46a14c..c0be724bcdee 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::{MaybeDef, MaybeQPath}; -use clippy_utils::source::{snippet, snippet_with_applicability}; +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; @@ -273,6 +273,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { let string_expression = &expressions[0].0; let snippet_app = snippet_with_applicability(cx, string_expression.span, "..", &mut applicability); + let (right_snip, _) = snippet_with_context(cx, right.span, e.span.ctxt(), "..", &mut applicability); span_lint_and_sugg( cx, @@ -280,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling a slice of `as_bytes()` with `from_utf8` should be not necessary", "try", - format!("Some(&{snippet_app}[{}])", snippet(cx, right.span, "..")), + format!("Some(&{snippet_app}[{right_snip}])"), applicability, ); } @@ -404,7 +405,8 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { "`to_string()` called on a `&str`", |diag| { let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability); + let (snippet, _) = + snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut applicability); diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability); }, ); diff --git a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs index 58d692db5029..0d50bd547652 100644 --- a/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs +++ b/src/tools/clippy/clippy_lints/src/strlen_on_c_strings.rs @@ -14,8 +14,8 @@ declare_clippy_lint! { /// and suggest calling `as_bytes().len()` or `to_bytes().len()` respectively instead. /// /// ### Why is this bad? - /// This avoids calling an unsafe `libc` function. - /// Currently, it also avoids calculating the length. + /// libc::strlen is an unsafe function, which we don't need to call + /// if all we want to know is the length of the c-string. /// /// ### Example /// ```rust, ignore diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs index 31e770f421e1..3f435f255d91 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_integer_const; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{ConstBlock, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::symbol::sym; @@ -42,5 +42,23 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t return true; } + // Catching: + // `std::mem::transmute({ 0 as *const u64 })` and similar const blocks + if let ExprKind::Block(block, _) = arg.kind + && block.stmts.is_empty() + && let Some(inner) = block.expr + { + // Run again with the inner expression + return check(cx, expr, inner, to_ty); + } + + // Catching: + // `std::mem::transmute(const { u64::MIN as *const u64 });` + if let ExprKind::ConstBlock(ConstBlock { body, .. }) = arg.kind { + // Strip out the const and run again + let block = cx.tcx.hir_body(body).value; + return check(cx, expr, block, to_ty); + } + false } diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index c06313d1a4c4..423301edfe83 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -456,13 +456,25 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { let mut prefix = String::new(); - for adj in cx.typeck_results().expr_adjustments(expr) { + + let adjustments = cx.typeck_results().expr_adjustments(expr); + + let [.., last] = adjustments else { return prefix }; + let target = last.target; + + for adj in adjustments { match adj.kind { Adjust::Deref(_) => prefix = format!("*{prefix}"), Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) => prefix = format!("&mut {prefix}"), Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)) => prefix = format!("&{prefix}"), _ => {}, } + + // Stop once we reach the final target type. + // This prevents over-adjusting (e.g. suggesting &**y instead of *y). + if adj.target == target { + break; + } } prefix } diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 455f76edc904..acea701b2e83 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -321,9 +321,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { }, ConstArgKind::Struct(..) => chain!(self, "let ConstArgKind::Struct(..) = {const_arg}.kind"), ConstArgKind::TupleCall(..) => chain!(self, "let ConstArgKind::TupleCall(..) = {const_arg}.kind"), - ConstArgKind::Tup(..) => chain!(self, "let ConstArgKind::Tup(..) = {const_arg}.kind"), ConstArgKind::Infer(..) => chain!(self, "let ConstArgKind::Infer(..) = {const_arg}.kind"), ConstArgKind::Error(..) => chain!(self, "let ConstArgKind::Error(..) = {const_arg}.kind"), + ConstArgKind::Tup(..) => chain!(self, "let ConstArgKind::Tup(..) = {const_arg}.kind"), + ConstArgKind::Literal(..) => chain!(self, "let ConstArgKind::Literal(..) = {const_arg}.kind") } } diff --git a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs index fda65bc84eda..f56b6f31550b 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lint_without_lint_pass.rs @@ -250,8 +250,8 @@ pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item< if let hir::Attribute::Unparsed(attr_kind) = &attr // Identify attribute && let [tool_name, attr_name] = &attr_kind.path.segments[..] - && tool_name.name == sym::clippy - && attr_name.name == sym::version + && tool_name == &sym::clippy + && attr_name == &sym::version && let Some(version) = attr.value_str() { Some(version) diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 01257c1a3059..ecd36b157571 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-12-25 +nightly-2026-01-08 ``` @@ -30,7 +30,7 @@ Function signatures can change or be removed without replacement without any pri -Copyright 2014-2025 The Rust Project Developers +Copyright (c) The Rust Project Contributors Licensed under the Apache License, Version 2.0 <[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license 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 618719286e8f..cf8716398efb 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -976,7 +976,9 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool { l.style == r.style && match (&l.kind, &r.kind) { (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, - (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args), + (Normal(l), Normal(r)) => { + eq_path(&l.item.path, &r.item.path) && eq_attr_item_kind(&l.item.args, &r.item.args) + }, _ => false, } } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 87fdd755908b..32f6cb4fd5e9 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -23,7 +23,9 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( if let [clippy, segment2] = &*attr.path() && *clippy == sym::clippy { - let path_span = attr.path_span().expect("Clippy attributes are unparsed and have a span"); + let path_span = attr + .path_span() + .expect("Clippy attributes are unparsed and have a span"); let new_name = match *segment2 { sym::cyclomatic_complexity => Some("cognitive_complexity"), sym::author @@ -48,7 +50,7 @@ pub fn get_builtin_attr<'a, A: AttributeExt + 'a>( .with_span_suggestion( path_span, "consider using", - format!("clippy::{}", new_name), + format!("clippy::{new_name}"), Applicability::MachineApplicable, ) .emit(); diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 46b87fd5df96..538f1fd2628c 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -1140,9 +1140,13 @@ pub fn const_item_rhs_to_expr<'tcx>(tcx: TyCtxt<'tcx>, ct_rhs: ConstItemRhs<'tcx ConstItemRhs::Body(body_id) => Some(tcx.hir_body(body_id).value), ConstItemRhs::TypeConst(const_arg) => match const_arg.kind { ConstArgKind::Anon(anon) => Some(tcx.hir_body(anon.body).value), - ConstArgKind::Struct(..) | ConstArgKind::TupleCall(..) | ConstArgKind::Tup(..) | ConstArgKind::Path(_) | ConstArgKind::Error(..) | ConstArgKind::Infer(..) => { - None - }, + ConstArgKind::Struct(..) + | ConstArgKind::Tup(..) + | ConstArgKind::Literal(..) + | ConstArgKind::TupleCall(..) + | 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 57c896c97172..0bb641568879 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -666,6 +666,9 @@ impl HirEqInterExpr<'_, '_, '_> { } match (&left.kind, &right.kind) { + (ConstArgKind::Tup(l_t), ConstArgKind::Tup(r_t)) => { + l_t.len() == r_t.len() && l_t.iter().zip(*r_t).all(|(l_c, r_c)| self.eq_const_arg(*l_c, *r_c)) + }, (ConstArgKind::Path(l_p), ConstArgKind::Path(r_p)) => self.eq_qpath(l_p, r_p), (ConstArgKind::Anon(l_an), ConstArgKind::Anon(r_an)) => self.eq_body(l_an.body, r_an.body), (ConstArgKind::Infer(..), ConstArgKind::Infer(..)) => true, @@ -675,28 +678,24 @@ impl HirEqInterExpr<'_, '_, '_> { .iter() .zip(*inits_b) .all(|(init_a, init_b)| self.eq_const_arg(init_a.expr, init_b.expr)) - } + }, (ConstArgKind::TupleCall(path_a, args_a), ConstArgKind::TupleCall(path_b, args_b)) => { self.eq_qpath(path_a, path_b) && args_a .iter() .zip(*args_b) .all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b)) - } - (ConstArgKind::Tup(args_a), ConstArgKind::Tup(args_b)) => { - args_a - .iter() - .zip(*args_b) - .all(|(arg_a, arg_b)| self.eq_const_arg(arg_a, arg_b)) - } + }, + (ConstArgKind::Literal(kind_l), ConstArgKind::Literal(kind_r)) => kind_l == kind_r, // Use explicit match for now since ConstArg is undergoing flux. ( ConstArgKind::Path(..) + | ConstArgKind::Tup(..) | ConstArgKind::Anon(..) | ConstArgKind::TupleCall(..) - | ConstArgKind::Tup(..) | ConstArgKind::Infer(..) | ConstArgKind::Struct(..) + | ConstArgKind::Literal(..) | ConstArgKind::Error(..), _, ) => false, @@ -1557,6 +1556,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { fn hash_const_arg(&mut self, const_arg: &ConstArg<'_>) { match &const_arg.kind { + ConstArgKind::Tup(tup) => { + for arg in *tup { + self.hash_const_arg(*arg); + } + }, ConstArgKind::Path(path) => self.hash_qpath(path), ConstArgKind::Anon(anon) => self.hash_body(anon.body), ConstArgKind::Struct(path, inits) => { @@ -1571,12 +1575,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_const_arg(arg); } }, - ConstArgKind::Tup(args) => { - for arg in *args { - self.hash_const_arg(arg); - } - }, ConstArgKind::Infer(..) | ConstArgKind::Error(..) => {}, + ConstArgKind::Literal(lit) => lit.hash(&mut self.s), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 38e1542cd758..2a620e917228 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -90,13 +90,13 @@ use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; use rustc_abi::Integer; use rustc_ast::ast::{self, LitKind, RangeLimits}; -use rustc_ast::join_path_syms; +use rustc_ast::{LitIntType, join_path_syms}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::indexmap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::{AttributeKind, CfgEntry}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -121,7 +121,6 @@ use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; -use rustc_hir::attrs::CfgEntry; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; @@ -1386,6 +1385,17 @@ pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool { false } +/// Checks whether the given expression is an untyped integer literal. +pub fn is_integer_literal_untyped(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(spanned) = expr.kind + && let LitKind::Int(_, suffix) = spanned.node + { + return suffix == LitIntType::Unsuffixed; + } + + false +} + /// Checks whether the given expression is a constant literal of the given value. pub fn is_float_literal(expr: &Expr<'_>, value: f64) -> bool { if let ExprKind::Lit(spanned) = expr.kind @@ -2403,7 +2413,10 @@ pub fn is_test_function(tcx: TyCtxt<'_>, fn_def_id: LocalDefId) -> bool { /// use [`is_in_cfg_test`] pub fn is_cfg_test(tcx: TyCtxt<'_>, id: HirId) -> bool { if let Some(cfgs) = find_attr!(tcx.hir_attrs(id), AttributeKind::CfgTrace(cfgs) => cfgs) - && cfgs.iter().any(|(cfg, _)| { matches!(cfg, CfgEntry::NameValue { name: sym::test, ..})}) { + && cfgs + .iter() + .any(|(cfg, _)| matches!(cfg, CfgEntry::NameValue { name: sym::test, .. })) + { true } else { false @@ -2423,9 +2436,11 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { /// Checks if the item of any of its parents has `#[cfg(...)]` attribute applied. pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { find_attr!(tcx.get_all_attrs(def_id), AttributeKind::CfgTrace(..)) - || find_attr!(tcx - .hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) - .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), AttributeKind::CfgTrace(..)) + || find_attr!( + tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id)) + .flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)), + AttributeKind::CfgTrace(..) + ) } /// Walks up the HIR tree from the given expression in an attempt to find where the value is diff --git a/src/tools/clippy/clippy_utils/src/res.rs b/src/tools/clippy/clippy_utils/src/res.rs index a3efece7d224..e890a73620a5 100644 --- a/src/tools/clippy/clippy_utils/src/res.rs +++ b/src/tools/clippy/clippy_utils/src/res.rs @@ -301,7 +301,7 @@ impl<'tcx> MaybeQPath<'tcx> for &'tcx PatExpr<'_> { fn opt_qpath(self) -> Option> { match &self.kind { PatExprKind::Path(qpath) => Some((qpath, self.hir_id)), - _ => None, + PatExprKind::Lit { .. } => None, } } } @@ -419,7 +419,7 @@ impl<'a> MaybeResPath<'a> for &PatExpr<'a> { fn opt_res_path(self) -> OptResPath<'a> { match &self.kind { PatExprKind::Path(qpath) => qpath.opt_res_path(), - _ => (None, None), + PatExprKind::Lit { .. } => (None, None), } } } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 2ef2afb45071..3ade38bea8ed 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -127,7 +127,7 @@ impl<'a> Sugg<'a> { /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*` /// function variants of `Sugg`, since these use different snippet functions. - fn hir_from_snippet( + pub fn hir_from_snippet( cx: &LateContext<'_>, expr: &hir::Expr<'_>, mut get_snippet: impl FnMut(Span) -> Cow<'a, str>, diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index a0d2e8673fe6..94591a7ee30a 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -62,7 +62,6 @@ generate! { MsrvStack, Octal, OpenOptions, - Other, PathLookup, Regex, RegexBuilder, @@ -248,6 +247,7 @@ generate! { next_back, next_if, next_if_eq, + next_multiple_of, next_tuple, nth, ok, diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index dbec79e111fb..0755e1d29c69 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-12-25" +channel = "nightly-2026-01-08" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/README.md b/src/tools/clippy/rustc_tools_util/README.md index f47a4c69c2c3..45d2844ad00b 100644 --- a/src/tools/clippy/rustc_tools_util/README.md +++ b/src/tools/clippy/rustc_tools_util/README.md @@ -51,7 +51,7 @@ The changelog for `rustc_tools_util` is available under: -Copyright 2014-2025 The Rust Project Developers +Copyright (c) The Rust Project Contributors Licensed under the Apache License, Version 2.0 or the MIT license diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml index 05ba822874d5..11af5b0a3306 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/clippy.toml @@ -6,5 +6,6 @@ enforced-import-renames = [ { path = "std::clone", rename = "foo" }, { path = "std::thread::sleep", rename = "thread_sleep" }, { path = "std::any::type_name", rename = "ident" }, - { path = "std::sync::Mutex", rename = "StdMutie" } + { path = "std::sync::Mutex", rename = "StdMutie" }, + { path = "std::io::Write", rename = "to_test_rename_as_underscore" } ] diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed index 3e882f496985..c96df2884b8d 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed @@ -15,6 +15,7 @@ use std::{ sync :: Mutex as StdMutie, //~^ missing_enforced_import_renames }; +use std::io::Write as _; fn main() { use std::collections::BTreeMap as Map; diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs index 32255af5117f..662e89157675 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs @@ -15,6 +15,7 @@ use std::{ sync :: Mutex, //~^ missing_enforced_import_renames }; +use std::io::Write as _; fn main() { use std::collections::BTreeMap as OopsWrongRename; diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr index 982b144eb872..139331d17619 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -32,7 +32,7 @@ LL | sync :: Mutex, | ^^^^^^^^^^^^^ help: try: `sync :: Mutex as StdMutie` error: this import should be renamed - --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:20:5 + --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:21:5 | LL | use std::collections::BTreeMap as OopsWrongRename; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `use std::collections::BTreeMap as Map` diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed index ec76abbef05a..cd390ce0db9d 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed @@ -216,3 +216,16 @@ fn main() { assert!(!(b + b)); //~^ bool_assert_comparison } + +fn issue16279() { + macro_rules! is_empty { + ($x:expr) => { + $x.is_empty() + }; + } + + assert!(!is_empty!("a")); + //~^ bool_assert_comparison + assert!(is_empty!("")); + //~^ bool_assert_comparison +} diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs index 40824a23c82e..b2ea5b6ea540 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs @@ -216,3 +216,16 @@ fn main() { assert_eq!(b + b, false); //~^ bool_assert_comparison } + +fn issue16279() { + macro_rules! is_empty { + ($x:expr) => { + $x.is_empty() + }; + } + + assert_eq!(is_empty!("a"), false); + //~^ bool_assert_comparison + assert_eq!(is_empty!(""), true); + //~^ bool_assert_comparison +} diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr index 72aa6303a202..b4e8fcf09bb6 100644 --- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr +++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr @@ -444,5 +444,29 @@ LL - assert_eq!(b + b, false); LL + assert!(!(b + b)); | -error: aborting due to 37 previous errors +error: used `assert_eq!` with a literal bool + --> tests/ui/bool_assert_comparison.rs:227:5 + | +LL | assert_eq!(is_empty!("a"), false); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(is_empty!("a"), false); +LL + assert!(!is_empty!("a")); + | + +error: used `assert_eq!` with a literal bool + --> tests/ui/bool_assert_comparison.rs:229:5 + | +LL | assert_eq!(is_empty!(""), true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: replace it with `assert!(..)` + | +LL - assert_eq!(is_empty!(""), true); +LL + assert!(is_empty!("")); + | + +error: aborting due to 39 previous errors diff --git a/src/tools/clippy/tests/ui/checked_conversions.fixed b/src/tools/clippy/tests/ui/checked_conversions.fixed index 6175275ef047..2309a053146f 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.fixed +++ b/src/tools/clippy/tests/ui/checked_conversions.fixed @@ -1,6 +1,7 @@ #![allow( clippy::cast_lossless, clippy::legacy_numeric_constants, + clippy::no_effect, unused, // Int::max_value will be deprecated in the future deprecated, @@ -105,4 +106,19 @@ fn msrv_1_34() { //~^ checked_conversions } +fn issue16293() { + struct Outer { + inner: u32, + } + let outer = Outer { inner: 42 }; + macro_rules! dot_inner { + ($obj:expr) => { + $obj.inner + }; + } + + i32::try_from(dot_inner!(outer)).is_ok(); + //~^ checked_conversions +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.rs b/src/tools/clippy/tests/ui/checked_conversions.rs index 9ed0e8f660d0..dabb552eba27 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.rs +++ b/src/tools/clippy/tests/ui/checked_conversions.rs @@ -1,6 +1,7 @@ #![allow( clippy::cast_lossless, clippy::legacy_numeric_constants, + clippy::no_effect, unused, // Int::max_value will be deprecated in the future deprecated, @@ -105,4 +106,19 @@ fn msrv_1_34() { //~^ checked_conversions } +fn issue16293() { + struct Outer { + inner: u32, + } + let outer = Outer { inner: 42 }; + macro_rules! dot_inner { + ($obj:expr) => { + $obj.inner + }; + } + + dot_inner!(outer) <= i32::MAX as u32; + //~^ checked_conversions +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/checked_conversions.stderr b/src/tools/clippy/tests/ui/checked_conversions.stderr index 624876dacb26..6018dacace39 100644 --- a/src/tools/clippy/tests/ui/checked_conversions.stderr +++ b/src/tools/clippy/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:15:13 + --> tests/ui/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -8,100 +8,106 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = help: to override `-D warnings` add `#[allow(clippy::checked_conversions)]` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:17:13 + --> tests/ui/checked_conversions.rs:18:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:22:13 + --> tests/ui/checked_conversions.rs:23:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:24:13 + --> tests/ui/checked_conversions.rs:25:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:29:13 + --> tests/ui/checked_conversions.rs:30:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:31:13 + --> tests/ui/checked_conversions.rs:32:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:38:13 + --> tests/ui/checked_conversions.rs:39:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:40:13 + --> tests/ui/checked_conversions.rs:41:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:45:13 + --> tests/ui/checked_conversions.rs:46:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:47:13 + --> tests/ui/checked_conversions.rs:48:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:54:13 + --> tests/ui/checked_conversions.rs:55:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:56:13 + --> tests/ui/checked_conversions.rs:57:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:61:13 + --> tests/ui/checked_conversions.rs:62:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:63:13 + --> tests/ui/checked_conversions.rs:64:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:68:13 + --> tests/ui/checked_conversions.rs:69:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:70:13 + --> tests/ui/checked_conversions.rs:71:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> tests/ui/checked_conversions.rs:104:13 + --> tests/ui/checked_conversions.rs:105:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` -error: aborting due to 17 previous errors +error: checked cast can be simplified + --> tests/ui/checked_conversions.rs:120:5 + | +LL | dot_inner!(outer) <= i32::MAX as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(dot_inner!(outer)).is_ok()` + +error: aborting due to 18 previous errors diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed index 85d0991bef05..4c3b13b30043 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.fixed @@ -83,3 +83,32 @@ fn issue_8103() { let _ = foo1 == foo2; //~^ cmp_owned } + +macro_rules! issue16322_macro_generator { + ($locale:ident) => { + mod $locale { + macro_rules! _make { + ($token:tt) => { + stringify!($token) + }; + } + + pub(crate) use _make; + } + + macro_rules! t { + ($token:tt) => { + crate::$locale::_make!($token) + }; + } + }; +} + +issue16322_macro_generator!(de); + +fn issue16322(item: String) { + if item == t!(frohes_neu_Jahr) { + //~^ cmp_owned + println!("Ja!"); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs index 2393757d76f2..a9d7509feaaf 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.rs @@ -83,3 +83,32 @@ fn issue_8103() { let _ = foo1 == foo2.to_owned(); //~^ cmp_owned } + +macro_rules! issue16322_macro_generator { + ($locale:ident) => { + mod $locale { + macro_rules! _make { + ($token:tt) => { + stringify!($token) + }; + } + + pub(crate) use _make; + } + + macro_rules! t { + ($token:tt) => { + crate::$locale::_make!($token) + }; + } + }; +} + +issue16322_macro_generator!(de); + +fn issue16322(item: String) { + if item == t!(frohes_neu_Jahr).to_string() { + //~^ cmp_owned + println!("Ja!"); + } +} diff --git a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr index dd9ffa70897a..66544ce0c217 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr +++ b/src/tools/clippy/tests/ui/cmp_owned/with_suggestion.stderr @@ -49,5 +49,11 @@ error: this creates an owned instance just for comparison LL | let _ = foo1 == foo2.to_owned(); | ^^^^^^^^^^^^^^^ help: try: `foo2` -error: aborting due to 8 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:110:16 + | +LL | if item == t!(frohes_neu_Jahr).to_string() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)` + +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs index b4bb24b0d2fe..386ab39401c5 100644 --- a/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs +++ b/src/tools/clippy/tests/ui/derive_ord_xor_partial_ord.rs @@ -91,3 +91,17 @@ mod issue15708 { } } } + +mod issue16298 { + #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] + struct Normalized(S); + + impl Eq for Normalized {} + + #[expect(clippy::derive_ord_xor_partial_ord)] + impl Ord for Normalized { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.partial_cmp(other).unwrap() + } + } +} diff --git a/src/tools/clippy/tests/ui/double_parens.fixed b/src/tools/clippy/tests/ui/double_parens.fixed index 024af6840132..ef7838491f8f 100644 --- a/src/tools/clippy/tests/ui/double_parens.fixed +++ b/src/tools/clippy/tests/ui/double_parens.fixed @@ -161,4 +161,20 @@ fn issue15940() { pub struct Person; } +fn issue16224() { + fn test() -> i32 { 42 } + + macro_rules! call { + ($matcher:pat $(=> $result:expr)?) => { + match test() { + $matcher => Result::Ok(($($result),*)), + _ => Result::Err("No match".to_string()), + } + }; + } + + let _: Result<(), String> = call!(_); + let _: Result = call!(_ => 42); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/double_parens.rs b/src/tools/clippy/tests/ui/double_parens.rs index 8a76f2837f35..07eafdf69575 100644 --- a/src/tools/clippy/tests/ui/double_parens.rs +++ b/src/tools/clippy/tests/ui/double_parens.rs @@ -161,4 +161,20 @@ fn issue15940() { pub struct Person; } +fn issue16224() { + fn test() -> i32 { 42 } + + macro_rules! call { + ($matcher:pat $(=> $result:expr)?) => { + match test() { + $matcher => Result::Ok(($($result),*)), + _ => Result::Err("No match".to_string()), + } + }; + } + + let _: Result<(), String> = call!(_); + let _: Result = call!(_ => 42); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/for_kv_map.fixed b/src/tools/clippy/tests/ui/for_kv_map.fixed index 2a68b7443fbf..6ec4cb01ffd1 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.fixed +++ b/src/tools/clippy/tests/ui/for_kv_map.fixed @@ -69,3 +69,20 @@ fn main() { let _v = v; } } + +fn wrongly_unmangled_macros() { + use std::collections::HashMap; + + macro_rules! test_map { + ($val:expr) => { + &*$val + }; + } + + let m: HashMap = HashMap::new(); + let wrapped = Rc::new(m); + for v in test_map!(wrapped).values() { + //~^ for_kv_map + let _v = v; + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.rs b/src/tools/clippy/tests/ui/for_kv_map.rs index 485a97815e3c..19e907ff10a6 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.rs +++ b/src/tools/clippy/tests/ui/for_kv_map.rs @@ -69,3 +69,20 @@ fn main() { let _v = v; } } + +fn wrongly_unmangled_macros() { + use std::collections::HashMap; + + macro_rules! test_map { + ($val:expr) => { + &*$val + }; + } + + let m: HashMap = HashMap::new(); + let wrapped = Rc::new(m); + for (_, v) in test_map!(wrapped) { + //~^ for_kv_map + let _v = v; + } +} diff --git a/src/tools/clippy/tests/ui/for_kv_map.stderr b/src/tools/clippy/tests/ui/for_kv_map.stderr index 0bd474a10682..5436592f2ab6 100644 --- a/src/tools/clippy/tests/ui/for_kv_map.stderr +++ b/src/tools/clippy/tests/ui/for_kv_map.stderr @@ -72,5 +72,17 @@ LL - 'label: for (k, _value) in rm { LL + 'label: for k in rm.keys() { | -error: aborting due to 6 previous errors +error: you seem to want to iterate on a map's values + --> tests/ui/for_kv_map.rs:84:19 + | +LL | for (_, v) in test_map!(wrapped) { + | ^^^^^^^^^^^^^^^^^^ + | +help: use the corresponding method + | +LL - for (_, v) in test_map!(wrapped) { +LL + for v in test_map!(wrapped).values() { + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/impl.rs b/src/tools/clippy/tests/ui/impl.rs index e6044cc50781..75761a34c86e 100644 --- a/src/tools/clippy/tests/ui/impl.rs +++ b/src/tools/clippy/tests/ui/impl.rs @@ -14,6 +14,7 @@ impl MyStruct { } impl<'a> MyStruct { + //~^ multiple_inherent_impl fn lifetimed() {} } @@ -90,10 +91,12 @@ struct Lifetime<'s> { } impl Lifetime<'_> {} -impl Lifetime<'_> {} // false negative +impl Lifetime<'_> {} +//~^ multiple_inherent_impl impl<'a> Lifetime<'a> {} -impl<'a> Lifetime<'a> {} // false negative +impl<'a> Lifetime<'a> {} +//~^ multiple_inherent_impl impl<'b> Lifetime<'b> {} // false negative? @@ -104,6 +107,39 @@ struct Generic { } impl Generic {} -impl Generic {} // false negative +impl Generic {} +//~^ multiple_inherent_impl + +use std::fmt::Debug; + +#[derive(Debug)] +struct GenericWithBounds(T); + +impl GenericWithBounds { + fn make_one(_one: T) -> Self { + todo!() + } +} + +impl GenericWithBounds { + //~^ multiple_inherent_impl + fn make_two(_two: T) -> Self { + todo!() + } +} + +struct MultipleTraitBounds(T); + +impl MultipleTraitBounds { + fn debug_fn() {} +} + +impl MultipleTraitBounds { + fn clone_fn() {} +} + +impl MultipleTraitBounds { + fn debug_clone_fn() {} +} fn main() {} diff --git a/src/tools/clippy/tests/ui/impl.stderr b/src/tools/clippy/tests/ui/impl.stderr index 93d4b3998f90..9c4aaf183d70 100644 --- a/src/tools/clippy/tests/ui/impl.stderr +++ b/src/tools/clippy/tests/ui/impl.stderr @@ -19,7 +19,24 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::multiple_inherent_impl)]` error: multiple implementations of this structure - --> tests/ui/impl.rs:26:5 + --> tests/ui/impl.rs:16:1 + | +LL | / impl<'a> MyStruct { +LL | | +LL | | fn lifetimed() {} +LL | | } + | |_^ + | +note: first implementation here + --> tests/ui/impl.rs:6:1 + | +LL | / impl MyStruct { +LL | | fn first() {} +LL | | } + | |_^ + +error: multiple implementations of this structure + --> tests/ui/impl.rs:27:5 | LL | / impl super::MyStruct { LL | | @@ -37,7 +54,7 @@ LL | | } | |_^ error: multiple implementations of this structure - --> tests/ui/impl.rs:48:1 + --> tests/ui/impl.rs:49:1 | LL | / impl WithArgs { LL | | @@ -47,7 +64,7 @@ LL | | } | |_^ | note: first implementation here - --> tests/ui/impl.rs:45:1 + --> tests/ui/impl.rs:46:1 | LL | / impl WithArgs { LL | | fn f2() {} @@ -55,28 +72,85 @@ LL | | } | |_^ error: multiple implementations of this structure - --> tests/ui/impl.rs:71:1 + --> tests/ui/impl.rs:72:1 | LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ | note: first implementation here - --> tests/ui/impl.rs:68:1 + --> tests/ui/impl.rs:69:1 | LL | impl OneAllowedImpl {} | ^^^^^^^^^^^^^^^^^^^^^^ error: multiple implementations of this structure - --> tests/ui/impl.rs:84:1 + --> tests/ui/impl.rs:85:1 | LL | impl OneExpected {} | ^^^^^^^^^^^^^^^^^^^ | note: first implementation here - --> tests/ui/impl.rs:81:1 + --> tests/ui/impl.rs:82:1 | LL | impl OneExpected {} | ^^^^^^^^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: multiple implementations of this structure + --> tests/ui/impl.rs:94:1 + | +LL | impl Lifetime<'_> {} + | ^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:93:1 + | +LL | impl Lifetime<'_> {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/impl.rs:98:1 + | +LL | impl<'a> Lifetime<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:97:1 + | +LL | impl<'a> Lifetime<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/impl.rs:110:1 + | +LL | impl Generic {} + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: first implementation here + --> tests/ui/impl.rs:109:1 + | +LL | impl Generic {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: multiple implementations of this structure + --> tests/ui/impl.rs:124:1 + | +LL | / impl GenericWithBounds { +LL | | +LL | | fn make_two(_two: T) -> Self { +LL | | todo!() +LL | | } +LL | | } + | |_^ + | +note: first implementation here + --> tests/ui/impl.rs:118:1 + | +LL | / impl GenericWithBounds { +LL | | fn make_one(_one: T) -> Self { +LL | | todo!() +LL | | } +LL | | } + | |_^ + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed index 1aab6c54407e..22e59bbd2705 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -252,3 +252,11 @@ fn arbitrary_expression() { 0 }; } + +fn issue16307() { + let x: u8 = 100; + let y = 100_u8.saturating_sub(x); + //~^ implicit_saturating_sub + + println!("{y}"); +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs index 7ca57a2902db..7fa19f0c8ad2 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -326,3 +326,11 @@ fn arbitrary_expression() { 0 }; } + +fn issue16307() { + let x: u8 = 100; + let y = if x >= 100 { 0 } else { 100 - x }; + //~^ implicit_saturating_sub + + println!("{y}"); +} diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr index 0c225856fd07..2f3d2ba787e8 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.stderr @@ -238,5 +238,11 @@ error: manual arithmetic check found LL | let _ = if a < b * 2 { 0 } else { a - b * 2 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b * 2)` -error: aborting due to 27 previous errors +error: manual arithmetic check found + --> tests/ui/implicit_saturating_sub.rs:332:13 + | +LL | let y = if x >= 100 { 0 } else { 100 - x }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `100_u8.saturating_sub(x)` + +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/iter_kv_map.fixed b/src/tools/clippy/tests/ui/iter_kv_map.fixed index b18dda358877..189d76bc9431 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.fixed +++ b/src/tools/clippy/tests/ui/iter_kv_map.fixed @@ -189,3 +189,9 @@ fn issue14595() { let _ = map.as_ref().values().copied().collect::>(); //~^ iter_kv_map } + +fn issue16340() { + let hm: HashMap<&str, &str> = HashMap::new(); + let _ = hm.keys().map(|key| vec![key]); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.rs b/src/tools/clippy/tests/ui/iter_kv_map.rs index 729e4e8a266c..cfc303447004 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.rs +++ b/src/tools/clippy/tests/ui/iter_kv_map.rs @@ -193,3 +193,9 @@ fn issue14595() { let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); //~^ iter_kv_map } + +fn issue16340() { + let hm: HashMap<&str, &str> = HashMap::new(); + let _ = hm.iter().map(|(key, _)| vec![key]); + //~^ iter_kv_map +} diff --git a/src/tools/clippy/tests/ui/iter_kv_map.stderr b/src/tools/clippy/tests/ui/iter_kv_map.stderr index 8f73541f5033..866e69ea1922 100644 --- a/src/tools/clippy/tests/ui/iter_kv_map.stderr +++ b/src/tools/clippy/tests/ui/iter_kv_map.stderr @@ -269,5 +269,11 @@ error: iterating on a map's values LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` -error: aborting due to 39 previous errors +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:199:13 + | +LL | let _ = hm.iter().map(|(key, _)| vec![key]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hm.keys().map(|key| vec![key])` + +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/manual_clamp.fixed b/src/tools/clippy/tests/ui/manual_clamp.fixed index 2450a4f4c611..b279a413bc17 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.fixed +++ b/src/tools/clippy/tests/ui/manual_clamp.fixed @@ -4,7 +4,8 @@ dead_code, clippy::unnecessary_operation, clippy::no_effect, - clippy::if_same_then_else + clippy::if_same_then_else, + clippy::needless_match )] use std::cmp::{max as cmp_max, min as cmp_min}; diff --git a/src/tools/clippy/tests/ui/manual_clamp.rs b/src/tools/clippy/tests/ui/manual_clamp.rs index ee341d50768f..7fda41cd4c35 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.rs +++ b/src/tools/clippy/tests/ui/manual_clamp.rs @@ -4,7 +4,8 @@ dead_code, clippy::unnecessary_operation, clippy::no_effect, - clippy::if_same_then_else + clippy::if_same_then_else, + clippy::needless_match )] use std::cmp::{max as cmp_max, min as cmp_min}; diff --git a/src/tools/clippy/tests/ui/manual_clamp.stderr b/src/tools/clippy/tests/ui/manual_clamp.stderr index 4a0e4fa51646..775a16418eba 100644 --- a/src/tools/clippy/tests/ui/manual_clamp.stderr +++ b/src/tools/clippy/tests/ui/manual_clamp.stderr @@ -1,5 +1,5 @@ error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:212:5 + --> tests/ui/manual_clamp.rs:213:5 | LL | / if x9 < CONST_MIN { LL | | @@ -15,7 +15,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_clamp)]` error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:230:5 + --> tests/ui/manual_clamp.rs:231:5 | LL | / if x11 > CONST_MAX { LL | | @@ -29,7 +29,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:240:5 + --> tests/ui/manual_clamp.rs:241:5 | LL | / if CONST_MIN > x12 { LL | | @@ -43,7 +43,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:250:5 + --> tests/ui/manual_clamp.rs:251:5 | LL | / if CONST_MAX < x13 { LL | | @@ -57,7 +57,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:363:5 + --> tests/ui/manual_clamp.rs:364:5 | LL | / if CONST_MAX < x35 { LL | | @@ -71,7 +71,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:144:14 + --> tests/ui/manual_clamp.rs:145:14 | LL | let x0 = if CONST_MAX < input { | ______________^ @@ -86,7 +86,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:154:14 + --> tests/ui/manual_clamp.rs:155:14 | LL | let x1 = if input > CONST_MAX { | ______________^ @@ -101,7 +101,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:164:14 + --> tests/ui/manual_clamp.rs:165:14 | LL | let x2 = if input < CONST_MIN { | ______________^ @@ -116,7 +116,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:174:14 + --> tests/ui/manual_clamp.rs:175:14 | LL | let x3 = if CONST_MIN > input { | ______________^ @@ -131,7 +131,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:184:14 + --> tests/ui/manual_clamp.rs:185:14 | LL | let x4 = input.max(CONST_MIN).min(CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -139,7 +139,7 @@ LL | let x4 = input.max(CONST_MIN).min(CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:187:14 + --> tests/ui/manual_clamp.rs:188:14 | LL | let x5 = input.min(CONST_MAX).max(CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -147,7 +147,7 @@ LL | let x5 = input.min(CONST_MAX).max(CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:190:14 + --> tests/ui/manual_clamp.rs:191:14 | LL | let x6 = match input { | ______________^ @@ -161,7 +161,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:197:14 + --> tests/ui/manual_clamp.rs:198:14 | LL | let x7 = match input { | ______________^ @@ -175,7 +175,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:204:14 + --> tests/ui/manual_clamp.rs:205:14 | LL | let x8 = match input { | ______________^ @@ -189,7 +189,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:221:15 + --> tests/ui/manual_clamp.rs:222:15 | LL | let x10 = match input { | _______________^ @@ -203,7 +203,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:259:15 + --> tests/ui/manual_clamp.rs:260:15 | LL | let x14 = if input > CONST_MAX { | _______________^ @@ -218,7 +218,7 @@ LL | | }; = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:270:19 + --> tests/ui/manual_clamp.rs:271:19 | LL | let x15 = if input > CONST_F64_MAX { | ___________________^ @@ -234,7 +234,7 @@ LL | | }; = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:283:19 + --> tests/ui/manual_clamp.rs:284:19 | LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -242,7 +242,7 @@ LL | let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:286:19 + --> tests/ui/manual_clamp.rs:287:19 | LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -250,7 +250,7 @@ LL | let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:289:19 + --> tests/ui/manual_clamp.rs:290:19 | LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -258,7 +258,7 @@ LL | let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:292:19 + --> tests/ui/manual_clamp.rs:293:19 | LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -266,7 +266,7 @@ LL | let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:295:19 + --> tests/ui/manual_clamp.rs:296:19 | LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -274,7 +274,7 @@ LL | let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:298:19 + --> tests/ui/manual_clamp.rs:299:19 | LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -282,7 +282,7 @@ LL | let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:301:19 + --> tests/ui/manual_clamp.rs:302:19 | LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -290,7 +290,7 @@ LL | let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:304:19 + --> tests/ui/manual_clamp.rs:305:19 | LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)` @@ -298,7 +298,7 @@ LL | let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input)); = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:308:19 + --> tests/ui/manual_clamp.rs:309:19 | LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -307,7 +307,7 @@ LL | let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:311:19 + --> tests/ui/manual_clamp.rs:312:19 | LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -316,7 +316,7 @@ LL | let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:314:19 + --> tests/ui/manual_clamp.rs:315:19 | LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -325,7 +325,7 @@ LL | let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:317:19 + --> tests/ui/manual_clamp.rs:318:19 | LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -334,7 +334,7 @@ LL | let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:320:19 + --> tests/ui/manual_clamp.rs:321:19 | LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -343,7 +343,7 @@ LL | let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:323:19 + --> tests/ui/manual_clamp.rs:324:19 | LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -352,7 +352,7 @@ LL | let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:326:19 + --> tests/ui/manual_clamp.rs:327:19 | LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -361,7 +361,7 @@ LL | let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:329:19 + --> tests/ui/manual_clamp.rs:330:19 | LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_F64_MIN, CONST_F64_MAX)` @@ -370,7 +370,7 @@ LL | let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input)); = note: clamp returns NaN if the input is NaN error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:333:5 + --> tests/ui/manual_clamp.rs:334:5 | LL | / if x32 < CONST_MIN { LL | | @@ -384,7 +384,7 @@ LL | | } = note: clamp will panic if max < min error: clamp-like pattern without using clamp function - --> tests/ui/manual_clamp.rs:525:13 + --> tests/ui/manual_clamp.rs:526:13 | LL | let _ = if input > CONST_MAX { | _____________^ diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index cd91be87ec17..8ffd107dd42e 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) { let _ = size.div_ceil(*c); //~^ manual_div_ceil } + +struct MyStruct(u32); +impl MyStruct { + // Method matching name on different type should not trigger lint + fn next_multiple_of(self, y: u32) -> u32 { + self.0.next_multiple_of(y) + } +} + +fn issue_16219() { + let x = 33u32; + + // Lint. + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + + let y = &x; + let _ = y.div_ceil(8); + //~^ manual_div_ceil + + // No lint. + let _ = x.next_multiple_of(8) / 7; + let _ = x.next_multiple_of(7) / 8; + + let z = MyStruct(x); + let _ = z.next_multiple_of(8) / 8; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 9899c7d775c2..859fb5a13c44 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -105,3 +105,32 @@ fn issue_15705(size: u64, c: &u64) { let _ = (size + c - 1) / c; //~^ manual_div_ceil } + +struct MyStruct(u32); +impl MyStruct { + // Method matching name on different type should not trigger lint + fn next_multiple_of(self, y: u32) -> u32 { + self.0.next_multiple_of(y) + } +} + +fn issue_16219() { + let x = 33u32; + + // Lint. + let _ = x.next_multiple_of(8) / 8; + //~^ manual_div_ceil + let _ = u32::next_multiple_of(x, 8) / 8; + //~^ manual_div_ceil + + let y = &x; + let _ = y.next_multiple_of(8) / 8; + //~^ manual_div_ceil + + // No lint. + let _ = x.next_multiple_of(8) / 7; + let _ = x.next_multiple_of(7) / 8; + + let z = MyStruct(x); + let _ = z.next_multiple_of(8) / 8; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 44de3ba99be7..0efc114c7078 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -131,5 +131,23 @@ error: manually reimplementing `div_ceil` LL | let _ = (size + c - 1) / c; | ^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `size.div_ceil(*c)` -error: aborting due to 20 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:121:13 + | +LL | let _ = x.next_multiple_of(8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:123:13 + | +LL | let _ = u32::next_multiple_of(x, 8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:127:13 + | +LL | let _ = y.next_multiple_of(8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)` + +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index f55999c5ad08..d77b8bc28986 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -84,3 +84,32 @@ fn issue_13950() { let _ = y.div_ceil(-7); //~^ manual_div_ceil } + +struct MyStruct(i32); +impl MyStruct { + // Method matching name on different type should not trigger lint + fn next_multiple_of(self, y: i32) -> i32 { + self.0.next_multiple_of(y) + } +} + +fn issue_16219() { + let x = 33i32; + + // Lint. + let _ = x.div_ceil(8); + //~^ manual_div_ceil + let _ = x.div_ceil(8); + //~^ manual_div_ceil + + let y = &x; + let _ = y.div_ceil(8); + //~^ manual_div_ceil + + // No lint. + let _ = x.next_multiple_of(8) / 7; + let _ = x.next_multiple_of(7) / 8; + + let z = MyStruct(x); + let _ = z.next_multiple_of(8) / 8; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 8a895f634cb4..5b9a4d9156a7 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -84,3 +84,32 @@ fn issue_13950() { let _ = (y - 8) / -7; //~^ manual_div_ceil } + +struct MyStruct(i32); +impl MyStruct { + // Method matching name on different type should not trigger lint + fn next_multiple_of(self, y: i32) -> i32 { + self.0.next_multiple_of(y) + } +} + +fn issue_16219() { + let x = 33i32; + + // Lint. + let _ = x.next_multiple_of(8) / 8; + //~^ manual_div_ceil + let _ = i32::next_multiple_of(x, 8) / 8; + //~^ manual_div_ceil + + let y = &x; + let _ = y.next_multiple_of(8) / 8; + //~^ manual_div_ceil + + // No lint. + let _ = x.next_multiple_of(8) / 7; + let _ = x.next_multiple_of(7) / 8; + + let z = MyStruct(x); + let _ = z.next_multiple_of(8) / 8; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index e1160d962996..c5fa15112a87 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -139,5 +139,23 @@ error: manually reimplementing `div_ceil` LL | let _ = (y - 8) / -7; | ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` -error: aborting due to 23 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:100:13 + | +LL | let _ = x.next_multiple_of(8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:102:13 + | +LL | let _ = i32::next_multiple_of(x, 8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:106:13 + | +LL | let _ = y.next_multiple_of(8) / 8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(8)` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed index cd7adc20b127..f0e413aaec0d 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.fixed @@ -160,3 +160,23 @@ fn ref_osstring(a: OsString, b: &OsString) { b.eq_ignore_ascii_case(a); //~^ manual_ignore_case_cmp } + +fn wrongly_unmangled_macros(a: &str, b: &str) -> bool { + struct S<'a> { + inner: &'a str, + } + + let a = S { inner: a }; + let b = S { inner: b }; + + macro_rules! dot_inner { + ($s:expr) => { + $s.inner + }; + } + + dot_inner!(a).eq_ignore_ascii_case(dot_inner!(b)) + //~^ manual_ignore_case_cmp + || dot_inner!(a).eq_ignore_ascii_case("abc") + //~^ manual_ignore_case_cmp +} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs index 85f6719827c9..9802e87cd233 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.rs @@ -160,3 +160,23 @@ fn ref_osstring(a: OsString, b: &OsString) { b.to_ascii_lowercase() == a.to_ascii_lowercase(); //~^ manual_ignore_case_cmp } + +fn wrongly_unmangled_macros(a: &str, b: &str) -> bool { + struct S<'a> { + inner: &'a str, + } + + let a = S { inner: a }; + let b = S { inner: b }; + + macro_rules! dot_inner { + ($s:expr) => { + $s.inner + }; + } + + dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase() + //~^ manual_ignore_case_cmp + || dot_inner!(a).to_ascii_lowercase() == "abc" + //~^ manual_ignore_case_cmp +} diff --git a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr index fa7fadd91076..2f698e076ed3 100644 --- a/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr +++ b/src/tools/clippy/tests/ui/manual_ignore_case_cmp.stderr @@ -588,5 +588,29 @@ LL - b.to_ascii_lowercase() == a.to_ascii_lowercase(); LL + b.eq_ignore_ascii_case(a); | -error: aborting due to 49 previous errors +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:178:5 + | +LL | dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL - dot_inner!(a).to_ascii_lowercase() == dot_inner!(b).to_ascii_lowercase() +LL + dot_inner!(a).eq_ignore_ascii_case(dot_inner!(b)) + | + +error: manual case-insensitive ASCII comparison + --> tests/ui/manual_ignore_case_cmp.rs:180:12 + | +LL | || dot_inner!(a).to_ascii_lowercase() == "abc" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `.eq_ignore_ascii_case()` instead + | +LL - || dot_inner!(a).to_ascii_lowercase() == "abc" +LL + || dot_inner!(a).eq_ignore_ascii_case("abc") + | + +error: aborting due to 51 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ilog2.fixed b/src/tools/clippy/tests/ui/manual_ilog2.fixed index a0f6d9392c30..ea86fc927c7c 100644 --- a/src/tools/clippy/tests/ui/manual_ilog2.fixed +++ b/src/tools/clippy/tests/ui/manual_ilog2.fixed @@ -30,3 +30,20 @@ fn foo(a: u32, b: u64) { external!($a.ilog(2)); with_span!(span; a.ilog(2)); } + +fn wrongly_unmangled_macros() { + struct S { + inner: u32, + } + + let x = S { inner: 42 }; + macro_rules! access { + ($s:expr) => { + $s.inner + }; + } + let log = access!(x).ilog2(); + //~^ manual_ilog2 + let log = access!(x).ilog2(); + //~^ manual_ilog2 +} diff --git a/src/tools/clippy/tests/ui/manual_ilog2.rs b/src/tools/clippy/tests/ui/manual_ilog2.rs index bd4b5d9d3c0d..8cb0e12d7361 100644 --- a/src/tools/clippy/tests/ui/manual_ilog2.rs +++ b/src/tools/clippy/tests/ui/manual_ilog2.rs @@ -30,3 +30,20 @@ fn foo(a: u32, b: u64) { external!($a.ilog(2)); with_span!(span; a.ilog(2)); } + +fn wrongly_unmangled_macros() { + struct S { + inner: u32, + } + + let x = S { inner: 42 }; + macro_rules! access { + ($s:expr) => { + $s.inner + }; + } + let log = 31 - access!(x).leading_zeros(); + //~^ manual_ilog2 + let log = access!(x).ilog(2); + //~^ manual_ilog2 +} diff --git a/src/tools/clippy/tests/ui/manual_ilog2.stderr b/src/tools/clippy/tests/ui/manual_ilog2.stderr index 7c9694f35330..d0ef8378081a 100644 --- a/src/tools/clippy/tests/ui/manual_ilog2.stderr +++ b/src/tools/clippy/tests/ui/manual_ilog2.stderr @@ -19,5 +19,17 @@ error: manually reimplementing `ilog2` LL | 63 - b.leading_zeros(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `b.ilog2()` -error: aborting due to 3 previous errors +error: manually reimplementing `ilog2` + --> tests/ui/manual_ilog2.rs:45:15 + | +LL | let log = 31 - access!(x).leading_zeros(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `access!(x).ilog2()` + +error: manually reimplementing `ilog2` + --> tests/ui/manual_ilog2.rs:47:15 + | +LL | let log = access!(x).ilog(2); + | ^^^^^^^^^^^^^^^^^^ help: try: `access!(x).ilog2()` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/manual_is_multiple_of.fixed b/src/tools/clippy/tests/ui/manual_is_multiple_of.fixed index 03f75e725ed5..82e0684e5e57 100644 --- a/src/tools/clippy/tests/ui/manual_is_multiple_of.fixed +++ b/src/tools/clippy/tests/ui/manual_is_multiple_of.fixed @@ -101,3 +101,19 @@ mod issue15103 { (1..1_000).filter(|&i| i == d(d(i)) && i != d(i)).sum() } } + +fn wrongly_unmangled_macros(a: u32, b: u32) { + struct Wrapper(u32); + let a = Wrapper(a); + let b = Wrapper(b); + macro_rules! dot_0 { + ($x:expr) => { + $x.0 + }; + } + + if dot_0!(a).is_multiple_of(dot_0!(b)) { + //~^ manual_is_multiple_of + todo!() + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_multiple_of.rs b/src/tools/clippy/tests/ui/manual_is_multiple_of.rs index 7b6fa64c843d..82a492e24092 100644 --- a/src/tools/clippy/tests/ui/manual_is_multiple_of.rs +++ b/src/tools/clippy/tests/ui/manual_is_multiple_of.rs @@ -101,3 +101,19 @@ mod issue15103 { (1..1_000).filter(|&i| i == d(d(i)) && i != d(i)).sum() } } + +fn wrongly_unmangled_macros(a: u32, b: u32) { + struct Wrapper(u32); + let a = Wrapper(a); + let b = Wrapper(b); + macro_rules! dot_0 { + ($x:expr) => { + $x.0 + }; + } + + if dot_0!(a) % dot_0!(b) == 0 { + //~^ manual_is_multiple_of + todo!() + } +} diff --git a/src/tools/clippy/tests/ui/manual_is_multiple_of.stderr b/src/tools/clippy/tests/ui/manual_is_multiple_of.stderr index 8523599ec402..3aba869c9111 100644 --- a/src/tools/clippy/tests/ui/manual_is_multiple_of.stderr +++ b/src/tools/clippy/tests/ui/manual_is_multiple_of.stderr @@ -67,5 +67,11 @@ error: manual implementation of `.is_multiple_of()` LL | let d = |n: u32| -> u32 { (1..=n / 2).filter(|i| n % i == 0).sum() }; | ^^^^^^^^^^ help: replace with: `n.is_multiple_of(*i)` -error: aborting due to 11 previous errors +error: manual implementation of `.is_multiple_of()` + --> tests/ui/manual_is_multiple_of.rs:115:8 + | +LL | if dot_0!(a) % dot_0!(b) == 0 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `dot_0!(a).is_multiple_of(dot_0!(b))` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ok_err.fixed b/src/tools/clippy/tests/ui/manual_ok_err.fixed index 9b70ce0df43a..e22f91a0155f 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.fixed +++ b/src/tools/clippy/tests/ui/manual_ok_err.fixed @@ -127,3 +127,13 @@ mod issue15051 { result_with_ref_mut(x).as_mut().ok() } } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Ok::($val) + }; + } + + let _ = test_expr!(42).ok(); +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.rs b/src/tools/clippy/tests/ui/manual_ok_err.rs index dee904638245..c1355f0d4096 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.rs +++ b/src/tools/clippy/tests/ui/manual_ok_err.rs @@ -177,3 +177,17 @@ mod issue15051 { } } } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Ok::($val) + }; + } + + let _ = match test_expr!(42) { + //~^ manual_ok_err + Ok(v) => Some(v), + Err(_) => None, + }; +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.stderr b/src/tools/clippy/tests/ui/manual_ok_err.stderr index 448fbffc0509..0c2ed368eb97 100644 --- a/src/tools/clippy/tests/ui/manual_ok_err.stderr +++ b/src/tools/clippy/tests/ui/manual_ok_err.stderr @@ -141,5 +141,16 @@ LL | | Err(_) => None, LL | | } | |_________^ help: replace with: `result_with_ref_mut(x).as_mut().ok()` -error: aborting due to 12 previous errors +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:188:13 + | +LL | let _ = match test_expr!(42) { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | Err(_) => None, +LL | | }; + | |_____^ help: replace with: `test_expr!(42).ok()` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed index 09a6ed169390..b1b8ffb885f5 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.fixed +++ b/src/tools/clippy/tests/ui/match_as_ref.fixed @@ -90,3 +90,13 @@ fn issue15932() { let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Some($val) + }; + } + + let _: Option<&u32> = test_expr!(42).as_ref(); +} diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs index 347b6d186887..3113167957d4 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.rs +++ b/src/tools/clippy/tests/ui/match_as_ref.rs @@ -114,3 +114,17 @@ fn issue15932() { Some(ref mut v) => Some(v), }; } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Some($val) + }; + } + + let _: Option<&u32> = match test_expr!(42) { + //~^ match_as_ref + None => None, + Some(ref v) => Some(v), + }; +} diff --git a/src/tools/clippy/tests/ui/match_as_ref.stderr b/src/tools/clippy/tests/ui/match_as_ref.stderr index df06e358f296..3eab499fe409 100644 --- a/src/tools/clippy/tests/ui/match_as_ref.stderr +++ b/src/tools/clippy/tests/ui/match_as_ref.stderr @@ -127,5 +127,26 @@ LL - }; LL + let _: Option<&dyn std::fmt::Debug> = Some(0).as_ref().map(|x| x as _); | -error: aborting due to 6 previous errors +error: manual implementation of `Option::as_ref` + --> tests/ui/match_as_ref.rs:125:27 + | +LL | let _: Option<&u32> = match test_expr!(42) { + | ___________________________^ +LL | | +LL | | None => None, +LL | | Some(ref v) => Some(v), +LL | | }; + | |_____^ + | +help: use `Option::as_ref()` directly + | +LL - let _: Option<&u32> = match test_expr!(42) { +LL - +LL - None => None, +LL - Some(ref v) => Some(v), +LL - }; +LL + let _: Option<&u32> = test_expr!(42).as_ref(); + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/match_bool.fixed b/src/tools/clippy/tests/ui/match_bool.fixed index 876ae935afde..3d5d0a0d532c 100644 --- a/src/tools/clippy/tests/ui/match_bool.fixed +++ b/src/tools/clippy/tests/ui/match_bool.fixed @@ -74,4 +74,15 @@ fn issue15351() { } } +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + ($val + 1) > 0 + }; + } + + let x = 5; + if test_expr!(x) { 1 } else { 0 }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs index a134ad8346e2..4db0aedf3260 100644 --- a/src/tools/clippy/tests/ui/match_bool.rs +++ b/src/tools/clippy/tests/ui/match_bool.rs @@ -126,4 +126,19 @@ fn issue15351() { } } +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + ($val + 1) > 0 + }; + } + + let x = 5; + match test_expr!(x) { + //~^ match_bool + true => 1, + false => 0, + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr index c05742e56339..223acd17aead 100644 --- a/src/tools/clippy/tests/ui/match_bool.stderr +++ b/src/tools/clippy/tests/ui/match_bool.stderr @@ -183,5 +183,15 @@ LL + break 'a; LL + } } | -error: aborting due to 13 previous errors +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:137:5 + | +LL | / match test_expr!(x) { +LL | | +LL | | true => 1, +LL | | false => 0, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test_expr!(x) { 1 } else { 0 }` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/mutex_atomic.fixed b/src/tools/clippy/tests/ui/mutex_atomic.fixed index e4218726019f..dc05d8a2c61f 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.fixed +++ b/src/tools/clippy/tests/ui/mutex_atomic.fixed @@ -65,3 +65,15 @@ fn issue13378() { let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); //~^ mutex_integer } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + ($val > 0 && true) + }; + } + + let _ = std::sync::atomic::AtomicBool::new(test_expr!(1)); + //~^ mutex_atomic + // The suggestion should preserve the macro call: `AtomicBool::new(test_expr!(true))` +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 95f2b135903f..33745f8fc5e1 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -65,3 +65,15 @@ fn issue13378() { let (funky_mtx): Mutex = Mutex::new(0); //~^ mutex_integer } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + ($val > 0 && true) + }; + } + + let _ = Mutex::new(test_expr!(1)); + //~^ mutex_atomic + // The suggestion should preserve the macro call: `AtomicBool::new(test_expr!(true))` +} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index 0afc6d541dea..56d94035583c 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -130,5 +130,13 @@ LL - let (funky_mtx): Mutex = Mutex::new(0); LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | -error: aborting due to 14 previous errors +error: using a `Mutex` where an atomic would do + --> tests/ui/mutex_atomic.rs:76:13 + | +LL | let _ = Mutex::new(test_expr!(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::sync::atomic::AtomicBool::new(test_expr!(1))` + | + = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed index 89a3c1474f25..751d3d257000 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -34,7 +34,9 @@ fn main() { println!("true") // This is a const method call } - if y & (0 < 1) { + // Resolved + if y && (0 < 1) { + //~^ needless_bitwise_bool println!("true") // This is a BinOp with no side effects } } diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs index f5aa7a9f3d9e..5d3ff3b2079c 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -34,7 +34,9 @@ fn main() { println!("true") // This is a const method call } + // Resolved if y & (0 < 1) { + //~^ needless_bitwise_bool println!("true") // This is a BinOp with no side effects } } diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr index 9f14646c3e5a..4f64c7136916 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.stderr @@ -7,5 +7,11 @@ LL | if y & !x { = note: `-D clippy::needless-bitwise-bool` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_bitwise_bool)]` -error: aborting due to 1 previous error +error: use of bitwise operator instead of lazy operator between booleans + --> tests/ui/needless_bitwise_bool.rs:38:8 + | +LL | if y & (0 < 1) { + | ^^^^^^^^^^^ help: try: `y && (0 < 1)` + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index d6fab4c51b53..8fd572038140 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -42,3 +42,22 @@ fn issue15063(x: bool, y: bool) { } else { z = x || y; } //~^^^^^ needless_bool_assign } + +fn wrongly_unmangled_macros(must_keep: fn(usize, usize) -> bool, x: usize, y: usize) { + struct Wrapper(T); + let mut skip = Wrapper(false); + + macro_rules! invoke { + ($func:expr, $a:expr, $b:expr) => { + $func($a, $b) + }; + } + macro_rules! dot_0 { + ($w:expr) => { + $w.0 + }; + } + + dot_0!(skip) = !invoke!(must_keep, x, y); + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index c504f61f4dd1..4721ab433b32 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -58,3 +58,26 @@ fn issue15063(x: bool, y: bool) { } //~^^^^^ needless_bool_assign } + +fn wrongly_unmangled_macros(must_keep: fn(usize, usize) -> bool, x: usize, y: usize) { + struct Wrapper(T); + let mut skip = Wrapper(false); + + macro_rules! invoke { + ($func:expr, $a:expr, $b:expr) => { + $func($a, $b) + }; + } + macro_rules! dot_0 { + ($w:expr) => { + $w.0 + }; + } + + if invoke!(must_keep, x, y) { + dot_0!(skip) = false; + } else { + dot_0!(skip) = true; + } + //~^^^^^ needless_bool_assign +} diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index 1d09b8b25a09..34ff782f34a3 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -62,5 +62,15 @@ LL | | z = false; LL | | } | |_____^ help: you can reduce it to: `{ z = x || y; }` -error: aborting due to 5 previous errors +error: this if-then-else expression assigns a bool literal + --> tests/ui/needless_bool_assign.rs:77:5 + | +LL | / if invoke!(must_keep, x, y) { +LL | | dot_0!(skip) = false; +LL | | } else { +LL | | dot_0!(skip) = true; +LL | | } + | |_____^ help: you can reduce it to: `dot_0!(skip) = !invoke!(must_keep, x, y);` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed index a6d64d9afc1a..19b34f42af24 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.fixed @@ -149,3 +149,11 @@ fn issue15256() { for v in vec.iter() { println!("{v}"); } //~^ needless_for_each } + +fn issue16294() { + let vec: Vec = Vec::new(); + for elem in vec.iter() { + //~^ needless_for_each + println!("{elem}"); + } +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs index 7e74d2b428fd..f04e2555a370 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.rs +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.rs @@ -149,3 +149,11 @@ fn issue15256() { vec.iter().for_each(|v| println!("{v}")); //~^ needless_for_each } + +fn issue16294() { + let vec: Vec = Vec::new(); + vec.iter().for_each(|elem| { + //~^ needless_for_each + println!("{elem}"); + }) +} diff --git a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr index 204cfa36b022..121669d15072 100644 --- a/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_for_each_fixable.stderr @@ -154,5 +154,22 @@ error: needless use of `for_each` LL | vec.iter().for_each(|v| println!("{v}")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { println!("{v}"); }` -error: aborting due to 11 previous errors +error: needless use of `for_each` + --> tests/ui/needless_for_each_fixable.rs:155:5 + | +LL | / vec.iter().for_each(|elem| { +LL | | +LL | | println!("{elem}"); +LL | | }) + | |______^ + | +help: try + | +LL ~ for elem in vec.iter() { +LL + +LL + println!("{elem}"); +LL + } + | + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/never_loop_iterator_reduction.rs b/src/tools/clippy/tests/ui/never_loop_iterator_reduction.rs index 6b07b91db29a..27f1766b841d 100644 --- a/src/tools/clippy/tests/ui/never_loop_iterator_reduction.rs +++ b/src/tools/clippy/tests/ui/never_loop_iterator_reduction.rs @@ -1,8 +1,9 @@ //@no-rustfix #![warn(clippy::never_loop)] +#![expect(clippy::needless_return)] fn main() { - // diverging closure: should trigger + // diverging closure with no `return`: should trigger [0, 1].into_iter().for_each(|x| { //~^ never_loop @@ -14,4 +15,10 @@ fn main() { [0, 1].into_iter().for_each(|x| { let _ = x + 1; }); + + // `return` should NOT trigger even though it is diverging + [0, 1].into_iter().for_each(|x| { + println!("x = {x}"); + return; + }); } diff --git a/src/tools/clippy/tests/ui/never_loop_iterator_reduction.stderr b/src/tools/clippy/tests/ui/never_loop_iterator_reduction.stderr index b76ee283146c..92483c85432d 100644 --- a/src/tools/clippy/tests/ui/never_loop_iterator_reduction.stderr +++ b/src/tools/clippy/tests/ui/never_loop_iterator_reduction.stderr @@ -1,5 +1,5 @@ error: this iterator reduction never loops (closure always diverges) - --> tests/ui/never_loop_iterator_reduction.rs:6:5 + --> tests/ui/never_loop_iterator_reduction.rs:7:5 | LL | / [0, 1].into_iter().for_each(|x| { LL | | diff --git a/src/tools/clippy/tests/ui/new_without_default.fixed b/src/tools/clippy/tests/ui/new_without_default.fixed index 9a5e90b48065..f6591820feeb 100644 --- a/src/tools/clippy/tests/ui/new_without_default.fixed +++ b/src/tools/clippy/tests/ui/new_without_default.fixed @@ -409,3 +409,58 @@ mod issue15778 { } } } + +pub mod issue16255 { + use std::fmt::Display; + use std::marker::PhantomData; + + pub struct Foo { + marker: PhantomData, + } + + impl Default for Foo + where + T: Display, + T: Clone, + { + fn default() -> Self { + Self::new() + } + } + + impl Foo + where + T: Display, + { + pub fn new() -> Self + //~^ new_without_default + where + T: Clone, + { + Self { marker: PhantomData } + } + } + + pub struct Bar { + marker: PhantomData, + } + + impl Default for Bar + where + T: Clone, + { + fn default() -> Self { + Self::new() + } + } + + impl Bar { + pub fn new() -> Self + //~^ new_without_default + where + T: Clone, + { + Self { marker: PhantomData } + } + } +} diff --git a/src/tools/clippy/tests/ui/new_without_default.rs b/src/tools/clippy/tests/ui/new_without_default.rs index f7466aa32189..d3447f2e16b2 100644 --- a/src/tools/clippy/tests/ui/new_without_default.rs +++ b/src/tools/clippy/tests/ui/new_without_default.rs @@ -324,3 +324,39 @@ mod issue15778 { } } } + +pub mod issue16255 { + use std::fmt::Display; + use std::marker::PhantomData; + + pub struct Foo { + marker: PhantomData, + } + + impl Foo + where + T: Display, + { + pub fn new() -> Self + //~^ new_without_default + where + T: Clone, + { + Self { marker: PhantomData } + } + } + + pub struct Bar { + marker: PhantomData, + } + + impl Bar { + pub fn new() -> Self + //~^ new_without_default + where + T: Clone, + { + Self { marker: PhantomData } + } + } +} diff --git a/src/tools/clippy/tests/ui/new_without_default.stderr b/src/tools/clippy/tests/ui/new_without_default.stderr index 0593dbb00fb6..6c0f73d13185 100644 --- a/src/tools/clippy/tests/ui/new_without_default.stderr +++ b/src/tools/clippy/tests/ui/new_without_default.stderr @@ -250,5 +250,58 @@ LL + } LL + } | -error: aborting due to 13 previous errors +error: you should consider adding a `Default` implementation for `Foo` + --> tests/ui/new_without_default.rs:340:9 + | +LL | / pub fn new() -> Self +LL | | +LL | | where +LL | | T: Clone, +LL | | { +LL | | Self { marker: PhantomData } +LL | | } + | |_________^ + | +help: try adding this + | +LL ~ impl Default for Foo +LL + where +LL + T: Display, +LL + T: Clone, +LL + { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL + +LL ~ impl Foo + | + +error: you should consider adding a `Default` implementation for `Bar` + --> tests/ui/new_without_default.rs:354:9 + | +LL | / pub fn new() -> Self +LL | | +LL | | where +LL | | T: Clone, +LL | | { +LL | | Self { marker: PhantomData } +LL | | } + | |_________^ + | +help: try adding this + | +LL ~ impl Default for Bar +LL + where +LL + T: Clone, +LL + { +LL + fn default() -> Self { +LL + Self::new() +LL + } +LL + } +LL + +LL ~ impl Bar { + | + +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 2c5ee0245038..b8072932c4ea 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -1,5 +1,5 @@ #![feature(try_blocks)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::no_effect)] use std::sync::MutexGuard; @@ -500,3 +500,18 @@ mod issue14894 { Ok(()) } } + +fn wrongly_unmangled_macros() -> Option { + macro_rules! test_expr { + ($val:expr) => { + Some($val) + }; + } + + let x = test_expr!(42)?; + //~^^^ question_mark + Some(x); + + test_expr!(42)?; + test_expr!(42) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index b9ff9d1565b2..b320dcd4b0bc 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -1,5 +1,5 @@ #![feature(try_blocks)] -#![allow(clippy::unnecessary_wraps)] +#![allow(clippy::unnecessary_wraps, clippy::no_effect)] use std::sync::MutexGuard; @@ -615,3 +615,23 @@ mod issue14894 { Ok(()) } } + +fn wrongly_unmangled_macros() -> Option { + macro_rules! test_expr { + ($val:expr) => { + Some($val) + }; + } + + let Some(x) = test_expr!(42) else { + return None; + }; + //~^^^ question_mark + Some(x); + + if test_expr!(42).is_none() { + //~^ question_mark + return None; + } + test_expr!(42) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 9b2896328e66..d645c8830adc 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -333,5 +333,22 @@ LL | | return Err(reason); LL | | } | |_________^ help: replace it with: `result?;` -error: aborting due to 35 previous errors +error: this `let...else` may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:626:5 + | +LL | / let Some(x) = test_expr!(42) else { +LL | | return None; +LL | | }; + | |______^ help: replace it with: `let x = test_expr!(42)?;` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:632:5 + | +LL | / if test_expr!(42).is_none() { +LL | | +LL | | return None; +LL | | } + | |_____^ help: replace it with: `test_expr!(42)?;` + +error: aborting due to 37 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed index 08903ef7fdda..b44009446640 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.fixed @@ -195,3 +195,16 @@ fn issue16045() { } } } + +fn issue14989() { + macro_rules! x { + () => { + None:: + }; + } + + if x! {}.is_some() {}; + //~^ redundant_pattern_matching + while x! {}.is_some() {} + //~^ redundant_pattern_matching +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs index 95eff3f9ebf9..c13cf993e786 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.rs @@ -231,3 +231,16 @@ fn issue16045() { } } } + +fn issue14989() { + macro_rules! x { + () => { + None:: + }; + } + + if let Some(_) = (x! {}) {}; + //~^ redundant_pattern_matching + while let Some(_) = (x! {}) {} + //~^ redundant_pattern_matching +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr index 6fd0c5a6f859..5c9edfd4c50a 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_option.stderr @@ -236,5 +236,17 @@ error: redundant pattern matching, consider using `is_some()` LL | if let Some(_) = x.await { | -------^^^^^^^---------- help: try: `if x.await.is_some()` -error: aborting due to 33 previous errors +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:242:12 + | +LL | if let Some(_) = (x! {}) {}; + | -------^^^^^^^---------- help: try: `if x! {}.is_some()` + +error: redundant pattern matching, consider using `is_some()` + --> tests/ui/redundant_pattern_matching_option.rs:244:15 + | +LL | while let Some(_) = (x! {}) {} + | ----------^^^^^^^---------- help: try: `while x! {}.is_some()` + +error: aborting due to 35 previous errors diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed index 261d82fc35c8..8754d71aa629 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.fixed @@ -165,3 +165,23 @@ fn issue10803() { // Don't lint let _ = matches!(x, Err(16)); } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Ok::($val) + }; + } + + let _ = test_expr!(42).is_ok(); + + macro_rules! test_guard { + ($val:expr) => { + ($val + 1) > 0 + }; + } + + let x: Result = Ok(42); + let _ = x.is_ok() && test_guard!(42); + //~^ redundant_pattern_matching +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs index 6cae4cc4b6b0..b83b02588fb6 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.rs @@ -205,3 +205,27 @@ fn issue10803() { // Don't lint let _ = matches!(x, Err(16)); } + +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($val:expr) => { + Ok::($val) + }; + } + + let _ = match test_expr!(42) { + //~^ redundant_pattern_matching + Ok(_) => true, + Err(_) => false, + }; + + macro_rules! test_guard { + ($val:expr) => { + ($val + 1) > 0 + }; + } + + let x: Result = Ok(42); + let _ = matches!(x, Ok(_) if test_guard!(42)); + //~^ redundant_pattern_matching +} diff --git a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr index 7e7d27d07a7f..dda203b753c3 100644 --- a/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr +++ b/src/tools/clippy/tests/ui/redundant_pattern_matching_result.stderr @@ -209,5 +209,22 @@ error: redundant pattern matching, consider using `is_err()` LL | let _ = matches!(x, Err(_)); | ^^^^^^^^^^^^^^^^^^^ help: try: `x.is_err()` -error: aborting due to 28 previous errors +error: redundant pattern matching, consider using `is_ok()` + --> tests/ui/redundant_pattern_matching_result.rs:216:13 + | +LL | let _ = match test_expr!(42) { + | _____________^ +LL | | +LL | | Ok(_) => true, +LL | | Err(_) => false, +LL | | }; + | |_____^ help: try: `test_expr!(42).is_ok()` + +error: redundant pattern matching, consider using `is_ok()` + --> tests/ui/redundant_pattern_matching_result.rs:229:13 + | +LL | let _ = matches!(x, Ok(_) if test_guard!(42)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_ok() && test_guard!(42)` + +error: aborting due to 30 previous errors diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.rs b/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.rs new file mode 100644 index 000000000000..33378b386f34 --- /dev/null +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.rs @@ -0,0 +1,12 @@ +//@no-rustfix +#![warn(clippy::single_range_in_vec_init)] + +use std::ops::Range; + +fn issue16306(v: &[i32]) { + fn takes_range_slice(_: &[Range]) {} + + let len = v.len(); + takes_range_slice(&[0..len as i64]); + //~^ single_range_in_vec_init +} diff --git a/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.stderr b/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.stderr new file mode 100644 index 000000000000..b10af21ed21c --- /dev/null +++ b/src/tools/clippy/tests/ui/single_range_in_vec_init_unfixable.stderr @@ -0,0 +1,16 @@ +error: an array of `Range` that is only one element + --> tests/ui/single_range_in_vec_init_unfixable.rs:10:24 + | +LL | takes_range_slice(&[0..len as i64]); + | ^^^^^^^^^^^^^^^ + | + = note: `-D clippy::single-range-in-vec-init` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::single_range_in_vec_init)]` +help: if you wanted a `Vec` that contains the entire range, try + | +LL - takes_range_slice(&[0..len as i64]); +LL + takes_range_slice(&(0..len as i64).collect::>()); + | + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/str_to_string.fixed b/src/tools/clippy/tests/ui/str_to_string.fixed index 2941c4dbd33d..8713c4f9bc86 100644 --- a/src/tools/clippy/tests/ui/str_to_string.fixed +++ b/src/tools/clippy/tests/ui/str_to_string.fixed @@ -8,3 +8,17 @@ fn main() { msg.to_owned(); //~^ str_to_string } + +fn issue16271(key: &[u8]) { + macro_rules! t { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + } + }; + } + + let _value = t!(str::from_utf8(key)).to_owned(); + //~^ str_to_string +} diff --git a/src/tools/clippy/tests/ui/str_to_string.rs b/src/tools/clippy/tests/ui/str_to_string.rs index 4c4d2bb18062..b81759e1037b 100644 --- a/src/tools/clippy/tests/ui/str_to_string.rs +++ b/src/tools/clippy/tests/ui/str_to_string.rs @@ -8,3 +8,17 @@ fn main() { msg.to_string(); //~^ str_to_string } + +fn issue16271(key: &[u8]) { + macro_rules! t { + ($e:expr) => { + match $e { + Ok(e) => e, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + } + }; + } + + let _value = t!(str::from_utf8(key)).to_string(); + //~^ str_to_string +} diff --git a/src/tools/clippy/tests/ui/str_to_string.stderr b/src/tools/clippy/tests/ui/str_to_string.stderr index cb7b6b48843a..c0a38c8ebe46 100644 --- a/src/tools/clippy/tests/ui/str_to_string.stderr +++ b/src/tools/clippy/tests/ui/str_to_string.stderr @@ -13,5 +13,11 @@ error: `to_string()` called on a `&str` LL | msg.to_string(); | ^^^^^^^^^^^^^^^ help: try: `msg.to_owned()` -error: aborting due to 2 previous errors +error: `to_string()` called on a `&str` + --> tests/ui/str_to_string.rs:22:18 + | +LL | let _value = t!(str::from_utf8(key)).to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` + +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed index 193217114d88..98fa3a4fcf70 100644 --- a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed +++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.fixed @@ -1,6 +1,16 @@ #![warn(clippy::string_from_utf8_as_bytes)] +macro_rules! test_range { + ($start:expr, $end:expr) => { + $start..$end + }; +} + fn main() { let _ = Some(&"Hello World!"[6..11]); //~^ string_from_utf8_as_bytes + + let s = "Hello World!"; + let _ = Some(&s[test_range!(6, 11)]); + //~^ string_from_utf8_as_bytes } diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs index 49beb19ee40f..6354d5376ad6 100644 --- a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs +++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.rs @@ -1,6 +1,16 @@ #![warn(clippy::string_from_utf8_as_bytes)] +macro_rules! test_range { + ($start:expr, $end:expr) => { + $start..$end + }; +} + fn main() { let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); //~^ string_from_utf8_as_bytes + + let s = "Hello World!"; + let _ = std::str::from_utf8(&s.as_bytes()[test_range!(6, 11)]); + //~^ string_from_utf8_as_bytes } diff --git a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr index 99c8d8ae4eab..bba9ec0caf19 100644 --- a/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr +++ b/src/tools/clippy/tests/ui/string_from_utf8_as_bytes.stderr @@ -1,5 +1,5 @@ error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary - --> tests/ui/string_from_utf8_as_bytes.rs:4:13 + --> tests/ui/string_from_utf8_as_bytes.rs:10:13 | LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&"Hello World!"[6..11])` @@ -7,5 +7,11 @@ LL | let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]); = note: `-D clippy::string-from-utf8-as-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::string_from_utf8_as_bytes)]` -error: aborting due to 1 previous error +error: calling a slice of `as_bytes()` with `from_utf8` should be not necessary + --> tests/ui/string_from_utf8_as_bytes.rs:14:13 + | +LL | let _ = std::str::from_utf8(&s.as_bytes()[test_range!(6, 11)]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(&s[test_range!(6, 11)])` + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/transmuting_null.rs b/src/tools/clippy/tests/ui/transmuting_null.rs index 0d3b26673452..00aa35dff803 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.rs +++ b/src/tools/clippy/tests/ui/transmuting_null.rs @@ -37,8 +37,19 @@ fn transmute_const_int() { } } +fn transumute_single_expr_blocks() { + unsafe { + let _: &u64 = std::mem::transmute({ 0 as *const u64 }); + //~^ transmuting_null + + let _: &u64 = std::mem::transmute(const { u64::MIN as *const u64 }); + //~^ transmuting_null + } +} + fn main() { one_liners(); transmute_const(); transmute_const_int(); + transumute_single_expr_blocks(); } diff --git a/src/tools/clippy/tests/ui/transmuting_null.stderr b/src/tools/clippy/tests/ui/transmuting_null.stderr index ed7c3396a243..e1de391813bd 100644 --- a/src/tools/clippy/tests/ui/transmuting_null.stderr +++ b/src/tools/clippy/tests/ui/transmuting_null.stderr @@ -25,5 +25,17 @@ error: transmuting a known null pointer into a reference LL | let _: &u64 = std::mem::transmute(u64::MIN as *const u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: transmuting a known null pointer into a reference + --> tests/ui/transmuting_null.rs:42:23 + | +LL | let _: &u64 = std::mem::transmute({ 0 as *const u64 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmuting a known null pointer into a reference + --> tests/ui/transmuting_null.rs:45:23 + | +LL | let _: &u64 = std::mem::transmute(const { u64::MIN as *const u64 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed index c3eeafbc39cd..d51359349cb9 100644 --- a/src/tools/clippy/tests/ui/unnecessary_fold.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed @@ -178,4 +178,15 @@ fn issue10000() { } } +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($e:expr) => { + ($e + 1) > 2 + }; + } + + let _ = (0..3).any(|x| test_expr!(x)); + //~^ unnecessary_fold +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs index 6ab41a942625..c6eb7157ab12 100644 --- a/src/tools/clippy/tests/ui/unnecessary_fold.rs +++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs @@ -178,4 +178,15 @@ fn issue10000() { } } +fn wrongly_unmangled_macros() { + macro_rules! test_expr { + ($e:expr) => { + ($e + 1) > 2 + }; + } + + let _ = (0..3).fold(false, |acc: bool, x| acc || test_expr!(x)); + //~^ unnecessary_fold +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr index bb8aa7e18d34..560427a681a9 100644 --- a/src/tools/clippy/tests/ui/unnecessary_fold.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr @@ -4,6 +4,7 @@ error: this `.fold` can be written more succinctly using another method LL | let _ = (0..3).fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` | + = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects = note: `-D clippy::unnecessary-fold` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fold)]` @@ -21,6 +22,8 @@ error: this `.fold` can be written more succinctly using another method | LL | let _ = (0..3).fold(true, |acc, x| acc && x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)` + | + = note: the `all` method is short circuiting and may change the program semantics if the iterator has side effects error: this `.fold` can be written more succinctly using another method --> tests/ui/unnecessary_fold.rs:24:25 @@ -63,12 +66,16 @@ error: this `.fold` can be written more succinctly using another method | LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + | + = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects error: this `.fold` can be written more succinctly using another method --> tests/ui/unnecessary_fold.rs:110:10 | LL | .fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + | + = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects error: this `.fold` can be written more succinctly using another method --> tests/ui/unnecessary_fold.rs:123:33 @@ -196,5 +203,13 @@ error: this `.fold` can be written more succinctly using another method LL | (0..3).fold(1, |acc, x| acc * x) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` -error: aborting due to 32 previous errors +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:188:20 + | +LL | let _ = (0..3).fold(false, |acc: bool, x| acc || test_expr!(x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| test_expr!(x))` + | + = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects + +error: aborting due to 33 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index 316eac0b58b7..590359bc1ad2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -681,3 +681,12 @@ fn issue14833() { let mut s = HashSet::<&String>::new(); s.remove(&"hello".to_owned()); } + +#[allow(clippy::redundant_clone)] +fn issue16351() { + fn take(_: impl AsRef) {} + + let dot = "."; + take(&format!("ouch{dot}")); + //~^ unnecessary_to_owned +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index f2dbd1db3c9f..d1e3e6497c0a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -681,3 +681,12 @@ fn issue14833() { let mut s = HashSet::<&String>::new(); s.remove(&"hello".to_owned()); } + +#[allow(clippy::redundant_clone)] +fn issue16351() { + fn take(_: impl AsRef) {} + + let dot = "."; + take(format!("ouch{dot}").to_string()); + //~^ unnecessary_to_owned +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr index 6c52be839301..50e3d5eb2195 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr @@ -550,5 +550,11 @@ error: unnecessary use of `to_vec` LL | s.remove(&(&["b"]).to_vec()); | ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()` -error: aborting due to 82 previous errors +error: unnecessary use of `to_string` + --> tests/ui/unnecessary_to_owned.rs:690:10 + | +LL | take(format!("ouch{dot}").to_string()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `&format!("ouch{dot}")` + +error: aborting due to 83 previous errors diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index adf5e58d9a1a..4832e922fa8e 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -1,4 +1,5 @@ #![deny(clippy::useless_conversion)] +#![allow(clippy::into_iter_on_ref)] #![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -131,6 +132,15 @@ fn main() { dont_lint_into_iter_on_copy_iter(); dont_lint_into_iter_on_static_copy_iter(); + { + // triggers the IntoIterator trait + fn consume(_: impl IntoIterator) {} + + // Should suggest `*items` instead of `&**items` + let items = &&[1, 2, 3]; + consume(*items); //~ useless_conversion + } + let _: String = "foo".into(); let _: String = From::from("foo"); let _ = String::from("foo"); diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index d95fe49e2e2b..6ef1f93a5606 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -1,4 +1,5 @@ #![deny(clippy::useless_conversion)] +#![allow(clippy::into_iter_on_ref)] #![allow(clippy::needless_ifs, clippy::unnecessary_wraps, unused)] // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] @@ -131,6 +132,15 @@ fn main() { dont_lint_into_iter_on_copy_iter(); dont_lint_into_iter_on_static_copy_iter(); + { + // triggers the IntoIterator trait + fn consume(_: impl IntoIterator) {} + + // Should suggest `*items` instead of `&**items` + let items = &&[1, 2, 3]; + consume(items.into_iter()); //~ useless_conversion + } + let _: String = "foo".into(); let _: String = From::from("foo"); let _ = String::from("foo"); diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 052c664f6f2e..d28b7a5cbfb6 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:9:13 + --> tests/ui/useless_conversion.rs:10:13 | LL | let _ = T::from(val); | ^^^^^^^^^^^^ help: consider removing `T::from()`: `val` @@ -11,115 +11,132 @@ LL | #![deny(clippy::useless_conversion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:11:5 + --> tests/ui/useless_conversion.rs:12:5 | LL | val.into() | ^^^^^^^^^^ help: consider removing `.into()`: `val` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:24:22 + --> tests/ui/useless_conversion.rs:25:22 | LL | let _: i32 = 0i32.into(); | ^^^^^^^^^^^ help: consider removing `.into()`: `0i32` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:55:22 + --> tests/ui/useless_conversion.rs:56:22 | LL | if Some("ok") == lines.into_iter().next() {} | ^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `lines` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:61:21 + --> tests/ui/useless_conversion.rs:62:21 | LL | let mut lines = text.lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:68:22 + --> tests/ui/useless_conversion.rs:69:22 | LL | if Some("ok") == text.lines().into_iter().next() {} | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `text.lines()` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:75:13 + --> tests/ui/useless_conversion.rs:76:13 | LL | let _ = NUMBERS.into_iter().next(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:81:17 + --> tests/ui/useless_conversion.rs:82:17 | LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:141:17 + | +LL | consume(items.into_iter()); + | ^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:137:28 + | +LL | fn consume(_: impl IntoIterator) {} + | ^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - consume(items.into_iter()); +LL + consume(*items); + | + error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:144:21 + --> tests/ui/useless_conversion.rs:154:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:146:21 + --> tests/ui/useless_conversion.rs:156:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:148:13 + --> tests/ui/useless_conversion.rs:158:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:150:13 + --> tests/ui/useless_conversion.rs:160:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:152:13 + --> tests/ui/useless_conversion.rs:162:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> tests/ui/useless_conversion.rs:154:13 + --> tests/ui/useless_conversion.rs:164:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:156:21 + --> tests/ui/useless_conversion.rs:166:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:162:13 + --> tests/ui/useless_conversion.rs:172:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:169:23 + --> tests/ui/useless_conversion.rs:179:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:172:13 + --> tests/ui/useless_conversion.rs:182:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> tests/ui/useless_conversion.rs:175:13 + --> tests/ui/useless_conversion.rs:185:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:208:7 + --> tests/ui/useless_conversion.rs:218:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -127,13 +144,13 @@ LL | b(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:208:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:210:7 + --> tests/ui/useless_conversion.rs:220:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -141,13 +158,13 @@ LL | c(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:199:18 + --> tests/ui/useless_conversion.rs:209:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:212:7 + --> tests/ui/useless_conversion.rs:222:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -155,13 +172,13 @@ LL | d(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:202:12 + --> tests/ui/useless_conversion.rs:212:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:216:7 + --> tests/ui/useless_conversion.rs:226:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^------------------------ @@ -169,13 +186,13 @@ LL | b(vec![1, 2].into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:208:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:218:7 + --> tests/ui/useless_conversion.rs:228:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^------------------------------------ @@ -183,13 +200,13 @@ LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:198:13 + --> tests/ui/useless_conversion.rs:208:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:265:24 + --> tests/ui/useless_conversion.rs:275:24 | LL | foo2::([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -197,13 +214,13 @@ LL | foo2::([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:244:12 + --> tests/ui/useless_conversion.rs:254:12 | LL | I: IntoIterator + Helper, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:274:14 + --> tests/ui/useless_conversion.rs:284:14 | LL | foo3([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -211,13 +228,13 @@ LL | foo3([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:253:12 + --> tests/ui/useless_conversion.rs:263:12 | LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:284:16 + --> tests/ui/useless_conversion.rs:294:16 | LL | S1.foo([1, 2].into_iter()); | ^^^^^^------------ @@ -225,13 +242,13 @@ LL | S1.foo([1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:281:27 + --> tests/ui/useless_conversion.rs:291:27 | LL | pub fn foo(&self, _: I) {} | ^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:304:44 + --> tests/ui/useless_conversion.rs:314:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); | ^^------------ @@ -239,67 +256,67 @@ LL | v0.into_iter().interleave_shortest(v1.into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:291:20 + --> tests/ui/useless_conversion.rs:301:20 | LL | J: IntoIterator, | ^^^^^^^^^^^^ error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:332:58 + --> tests/ui/useless_conversion.rs:342:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:335:58 + --> tests/ui/useless_conversion.rs:345:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:338:58 + --> tests/ui/useless_conversion.rs:348:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(From::from); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:341:58 + --> tests/ui/useless_conversion.rs:351:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:345:31 + --> tests/ui/useless_conversion.rs:355:31 | LL | let _: ControlFlow<()> = c.map_break(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:349:31 + --> tests/ui/useless_conversion.rs:359:31 | LL | let _: ControlFlow<()> = c.map_continue(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `u32` - --> tests/ui/useless_conversion.rs:363:41 + --> tests/ui/useless_conversion.rs:373:41 | LL | let _: Vec = [1u32].into_iter().map(Into::into).collect(); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:374:18 + --> tests/ui/useless_conversion.rs:384:18 | LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:390:29 + --> tests/ui/useless_conversion.rs:400:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:389:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -310,13 +327,13 @@ LL + takes_into_iter(&self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:398:29 + --> tests/ui/useless_conversion.rs:408:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:389:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,13 +344,13 @@ LL + takes_into_iter(&mut self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:407:29 + --> tests/ui/useless_conversion.rs:417:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:389:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,13 +361,13 @@ LL + takes_into_iter(*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:416:29 + --> tests/ui/useless_conversion.rs:426:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:389:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -361,13 +378,13 @@ LL + takes_into_iter(&*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:425:29 + --> tests/ui/useless_conversion.rs:435:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:379:32 + --> tests/ui/useless_conversion.rs:389:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -378,22 +395,22 @@ LL + takes_into_iter(&mut *self.my_field); | error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:440:5 + --> tests/ui/useless_conversion.rs:450:5 | LL | R.into_iter().for_each(|_x| {}); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:442:13 + --> tests/ui/useless_conversion.rs:452:13 | LL | let _ = R.into_iter().map(|_x| 0); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` error: useless conversion to the same type: `std::slice::Iter<'_, i32>` - --> tests/ui/useless_conversion.rs:453:14 + --> tests/ui/useless_conversion.rs:463:14 | LL | for _ in mac!(iter [1, 2]).into_iter() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])` -error: aborting due to 44 previous errors +error: aborting due to 45 previous errors diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 5f637205fa65..09dec7675e7e 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -63,7 +63,6 @@ users_on_vacation = [ "Alexendoo", "y21", "blyxyas", - "samueltardieu", ] [assign.owners] diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 450fde3bbc38..a2f3a8f00dac 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -715,6 +715,11 @@ pub struct Config { pub override_codegen_backend: Option, /// Whether to ignore `//@ ignore-backends`. pub bypass_ignore_backends: bool, + + /// Number of parallel jobs configured for the build. + /// + /// This is forwarded from bootstrap's `jobs` configuration. + pub jobs: u32, } impl Config { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 2dc878fa2a20..21d1940d5673 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -225,6 +225,7 @@ impl ConfigBuilder { "--nightly-branch=", "--git-merge-commit-email=", "--minicore-path=", + "--jobs=0", ]; let mut args: Vec = args.iter().map(ToString::to_string).collect(); diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index c219a8887f8f..82e8a0da6b60 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -218,7 +218,8 @@ fn parse_config(args: Vec) -> Config { "the codegen backend to use instead of the default one", "CODEGEN BACKEND [NAME | PATH]", ) - .optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives"); + .optflag("", "bypass-ignore-backends", "ignore `//@ ignore-backends` directives") + .reqopt("", "jobs", "number of parallel jobs bootstrap was configured with", "JOBS"); let (argv0, args_) = args.split_first().unwrap(); if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { @@ -363,6 +364,11 @@ fn parse_config(args: Vec) -> Config { let build_test_suite_root = opt_path(matches, "build-test-suite-root"); assert!(build_test_suite_root.starts_with(&build_root)); + let jobs = match matches.opt_str("jobs") { + Some(jobs) => jobs.parse::().expect("expected `--jobs` to be an `u32`"), + None => panic!("`--jobs` is required"), + }; + Config { bless: matches.opt_present("bless"), fail_fast: matches.opt_present("fail-fast") @@ -481,6 +487,8 @@ fn parse_config(args: Vec) -> Config { default_codegen_backend, override_codegen_backend, bypass_ignore_backends: matches.opt_present("bypass-ignore-backends"), + + jobs, } } diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index ba3a12347367..4eb8f91fe894 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -249,6 +249,9 @@ impl TestCx<'_> { cmd.env("__STD_REMAP_DEBUGINFO_ENABLED", "1"); } + // Used for `run_make_support::env::jobs`. + cmd.env("__BOOTSTRAP_JOBS", self.config.jobs.to_string()); + // We don't want RUSTFLAGS set from the outside to interfere with // compiler flags set in the test cases: cmd.env_remove("RUSTFLAGS"); diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index bacc8061d209..f02918ae683b 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -139,5 +139,6 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { default_codegen_backend: CodegenBackend::Llvm, override_codegen_backend: None, bypass_ignore_backends: Default::default(), + jobs: Default::default(), } } diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml index 6e1ab84ed18d..ab99eb8ea3b7 100644 --- a/src/tools/lint-docs/Cargo.toml +++ b/src/tools/lint-docs/Cargo.toml @@ -7,7 +7,7 @@ description = "A script to extract the lint documentation for the rustc book." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustc-literal-escaper = "0.0.5" +rustc-literal-escaper = "0.0.7" serde_json = "1.0.57" tempfile = "3.1.0" walkdir = "2.3.1" diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index f86933029341..694a5922b799 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -249,6 +249,15 @@ impl FileDescription for io::Stdin { finish.call(ecx, result) } + fn destroy<'tcx>( + self, + _self_id: FdId, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } + fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.is_terminal() } @@ -279,6 +288,15 @@ impl FileDescription for io::Stdout { finish.call(ecx, result) } + fn destroy<'tcx>( + self, + _self_id: FdId, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } + fn is_tty(&self, communicate_allowed: bool) -> bool { communicate_allowed && self.is_terminal() } @@ -289,6 +307,15 @@ impl FileDescription for io::Stderr { "stderr" } + fn destroy<'tcx>( + self, + _self_id: FdId, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } + fn write<'tcx>( self: FileDescriptionRef, _communicate_allowed: bool, @@ -436,6 +463,15 @@ impl FileDescription for NullOutput { // We just don't write anything, but report to the user that we did. finish.call(ecx, Ok(len)) } + + fn destroy<'tcx>( + self, + _self_id: FdId, + _communicate_allowed: bool, + _ecx: &mut MiriInterpCx<'tcx>, + ) -> InterpResult<'tcx, io::Result<()>> { + interp_ok(Ok(())) + } } /// Internal type of a file-descriptor - this is what [`FdTable`] expects diff --git a/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs deleted file mode 100644 index 7911133f548f..000000000000 --- a/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ignore-target: windows # No libc IO on Windows -//@compile-flags: -Zmiri-disable-isolation - -// FIXME: standard handles cannot be closed (https://github.com/rust-lang/rust/issues/40032) - -fn main() { - unsafe { - libc::close(1); //~ ERROR: cannot close stdout - } -} diff --git a/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr b/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr deleted file mode 100644 index 84973ac020de..000000000000 --- a/src/tools/miri/tests/fail-dep/libc/fs/close_stdout.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error: unsupported operation: cannot close stdout - --> tests/fail-dep/libc/fs/close_stdout.rs:LL:CC - | -LL | libc::close(1); - | ^^^^^^^^^^^^^^ unsupported operation occurred here - | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support - -note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace - -error: aborting due to 1 previous error - diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 99685d6d976b..8c860b5db7ba 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -48,6 +48,7 @@ fn main() { test_nofollow_not_symlink(); #[cfg(target_os = "macos")] test_ioctl(); + test_close_stdout(); } fn test_file_open_unix_allow_two_args() { @@ -579,3 +580,11 @@ fn test_ioctl() { assert_eq!(libc::ioctl(fd, libc::FIOCLEX), 0); } } + +fn test_close_stdout() { + // This is std library UB, but that's not relevant since we're + // only interacting with libc here. + unsafe { + libc::close(1); + } +} diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index 5515dbf1940e..b52dab001ef5 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -450,12 +450,14 @@ fn main() -> anyhow::Result<()> { "rust-docs-json", "rust-analyzer", "rustc-src", + "rustc-src-gpl", "extended", "clippy", "miri", "rustfmt", "generate-copyright", "bootstrap", + "rustc_codegen_gcc", ] { build_args.extend(["--skip".to_string(), target.to_string()]); } diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index bfe8f54937f6..48b0d83e8d82 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -387,7 +387,7 @@ fn batch_copy(buf: &[u8], which: u8, dst: &Mutex) { if n > 0 { t!(dst.write_all(buf)); // Marking buf finished - t!(dst.write_all(&[which, 0, 0, 0, 0,])); + t!(dst.write_all(&create_header(which, 0))); } } diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs index 507d51471df0..65b6d3db85d5 100644 --- a/src/tools/run-make-support/src/env.rs +++ b/src/tools/run-make-support/src/env.rs @@ -49,3 +49,17 @@ pub fn set_current_dir>(dir: P) { std::env::set_current_dir(dir.as_ref()) .expect(&format!("could not set current directory to \"{}\"", dir.as_ref().display())); } + +/// Number of parallel jobs bootstrap was configured with. +/// +/// This may fallback to [`std::thread::available_parallelism`] when no explicit jobs count has been +/// configured. Refer to bootstrap's jobs fallback logic. +#[track_caller] +pub fn jobs() -> u32 { + std::env::var_os("__BOOTSTRAP_JOBS") + .expect("`__BOOTSTRAP_JOBS` must be set by `compiletest`") + .to_str() + .expect("`__BOOTSTRAP_JOBS` must be a valid string") + .parse::() + .expect("`__BOOTSTRAP_JOBS` must be a valid `u32`") +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index de5974fff1ad..d34ec9bbc182 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -61,7 +61,7 @@ impl Expander { pub(super) fn hygiene_for_range(&self, db: &dyn DefDatabase, range: TextRange) -> HygieneId { match self.span_map.as_ref() { hir_expand::span_map::SpanMapRef::ExpansionSpanMap(span_map) => { - HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semitransparent(db)) + HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semiopaque(db)) } hir_expand::span_map::SpanMapRef::RealSpanMap(_) => HygieneId::ROOT, } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index d3774ded39dc..4ae4271b92f5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2546,7 +2546,7 @@ impl<'db> ExprCollector<'db> { // Therefore, if we got to the rib of its declaration, give up its hygiene // and use its parent expansion. - hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(self.db)); + hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(self.db)); hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| { let expansion = self.db.lookup_intern_macro_call(expansion.into()); (parent_ctx.parent(self.db), expansion.def) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index f643ed31ad53..2ac0f90fb209 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -936,7 +936,7 @@ fn handle_macro_def_scope( // A macro is allowed to refer to variables from before its declaration. // Therefore, if we got to the rib of its declaration, give up its hygiene // and use its parent expansion. - *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semitransparent(db)); + *hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(db)); *hygiene_info = parent_ctx.outer_expn(db).map(|expansion| { let expansion = db.lookup_intern_macro_call(expansion.into()); (parent_ctx.parent(db), expansion.def) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index d2df9a1ff6b2..d10e122a5deb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -100,7 +100,8 @@ impl DeclarativeMacroExpander { { match &*value { "transparent" => ControlFlow::Break(Transparency::Transparent), - "semitransparent" => ControlFlow::Break(Transparency::SemiTransparent), + // "semitransparent" is for old rustc versions. + "semiopaque" | "semitransparent" => ControlFlow::Break(Transparency::SemiOpaque), "opaque" => ControlFlow::Break(Transparency::Opaque), _ => ControlFlow::Continue(()), } @@ -140,7 +141,7 @@ impl DeclarativeMacroExpander { )), }, transparency(ast::AnyHasAttrs::from(macro_rules)) - .unwrap_or(Transparency::SemiTransparent), + .unwrap_or(Transparency::SemiOpaque), ), ast::Macro::MacroDef(macro_def) => ( match macro_def.body() { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs index bd6f7e4f2b77..ce7650d07711 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs @@ -54,7 +54,7 @@ pub fn span_with_mixed_site_ctxt( expn_id: MacroCallId, edition: Edition, ) -> Span { - span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent, edition) + span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiOpaque, edition) } fn span_with_ctxt_from_mark( @@ -82,7 +82,7 @@ pub(super) fn apply_mark( } let call_site_ctxt = db.lookup_intern_macro_call(call_id.into()).ctxt; - let mut call_site_ctxt = if transparency == Transparency::SemiTransparent { + let mut call_site_ctxt = if transparency == Transparency::SemiOpaque { call_site_ctxt.normalize_to_macros_2_0(db) } else { call_site_ctxt.normalize_to_macro_rules(db) @@ -117,16 +117,16 @@ fn apply_mark_internal( let call_id = Some(call_id); let mut opaque = ctxt.opaque(db); - let mut opaque_and_semitransparent = ctxt.opaque_and_semitransparent(db); + let mut opaque_and_semiopaque = ctxt.opaque_and_semiopaque(db); if transparency >= Transparency::Opaque { let parent = opaque; opaque = SyntaxContext::new(db, call_id, transparency, edition, parent, identity, identity); } - if transparency >= Transparency::SemiTransparent { - let parent = opaque_and_semitransparent; - opaque_and_semitransparent = + if transparency >= Transparency::SemiOpaque { + let parent = opaque_and_semiopaque; + opaque_and_semiopaque = SyntaxContext::new(db, call_id, transparency, edition, parent, |_| opaque, identity); } @@ -138,6 +138,6 @@ fn apply_mark_internal( edition, parent, |_| opaque, - |_| opaque_and_semitransparent, + |_| opaque_and_semiopaque, ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs index 385c98ef8778..b49190237776 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs @@ -429,7 +429,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE), rustc_attr!( rustc_macro_transparency, Normal, - template!(NameValueStr: "transparent|semitransparent|opaque"), ErrorFollowing, + template!(NameValueStr: "transparent|semiopaque|opaque"), ErrorFollowing, "used internally for testing macro hygiene", ), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 51e449fe5085..1712c28aa8ab 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -401,8 +401,8 @@ pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> O result_mark = Some(mark); iter.next(); } - // Then find the last semi-transparent mark from the end if it exists. - while let Some((mark, Transparency::SemiTransparent)) = iter.next() { + // Then find the last semi-opaque mark from the end if it exists. + while let Some((mark, Transparency::SemiOpaque)) = iter.next() { result_mark = Some(mark); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 6367521841ab..a9a5e96f75cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3708,7 +3708,7 @@ fn main() { } #[test] -fn macro_semitransparent_hygiene() { +fn macro_semiopaque_hygiene() { check_types( r#" macro_rules! m { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index bf123e13f94d..6ba7a42c1946 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1808,5 +1808,5 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H }; let span_map = db.expansion_span_map(macro_file); let ctx = span_map.span_at(name.value.text_range().start()).ctx; - HygieneId::new(ctx.opaque_and_semitransparent(db)) + HygieneId::new(ctx.opaque_and_semiopaque(db)) } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 3fadca29d118..781e58a1204e 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -442,7 +442,6 @@ define_symbols! { rustc_skip_array_during_method_dispatch, rustc_skip_during_method_dispatch, rustc_force_inline, - semitransparent, shl_assign, shl, shr_assign, diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 92bf892ea529..9904c562a740 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -46,7 +46,7 @@ const _: () = { edition: Edition, parent: SyntaxContext, opaque: SyntaxContext, - opaque_and_semitransparent: SyntaxContext, + opaque_and_semiopaque: SyntaxContext, } impl PartialEq for SyntaxContextData { @@ -214,7 +214,7 @@ const _: () = { edition: T2, parent: T3, opaque: impl FnOnce(SyntaxContext) -> SyntaxContext, - opaque_and_semitransparent: impl FnOnce(SyntaxContext) -> SyntaxContext, + opaque_and_semiopaque: impl FnOnce(SyntaxContext) -> SyntaxContext, ) -> Self where Db: ?Sized + salsa::Database, @@ -241,7 +241,7 @@ const _: () = { edition: zalsa_::interned::Lookup::into_owned(data.2), parent: zalsa_::interned::Lookup::into_owned(data.3), opaque: opaque(zalsa_::FromId::from_id(id)), - opaque_and_semitransparent: opaque_and_semitransparent( + opaque_and_semiopaque: opaque_and_semiopaque( zalsa_::FromId::from_id(id), ), }, @@ -301,7 +301,7 @@ const _: () = { } } - /// This context, but with all transparent and semi-transparent expansions filtered away. + /// This context, but with all transparent and semi-opaque expansions filtered away. pub fn opaque(self, db: &'db Db) -> SyntaxContext where Db: ?Sized + zalsa_::Database, @@ -317,7 +317,7 @@ const _: () = { } /// This context, but with all transparent expansions filtered away. - pub fn opaque_and_semitransparent(self, db: &'db Db) -> SyntaxContext + pub fn opaque_and_semiopaque(self, db: &'db Db) -> SyntaxContext where Db: ?Sized + zalsa_::Database, { @@ -325,7 +325,7 @@ const _: () = { Some(id) => { let zalsa = db.zalsa(); let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); - fields.opaque_and_semitransparent + fields.opaque_and_semiopaque } None => self, } @@ -405,7 +405,7 @@ impl<'db> SyntaxContext { #[inline] pub fn normalize_to_macro_rules(self, db: &'db dyn salsa::Database) -> SyntaxContext { - self.opaque_and_semitransparent(db) + self.opaque_and_semiopaque(db) } pub fn is_opaque(self, db: &'db dyn salsa::Database) -> bool { @@ -476,13 +476,13 @@ pub enum Transparency { /// Identifier produced by a transparent expansion is always resolved at call-site. /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this. Transparent, - /// Identifier produced by a semi-transparent expansion may be resolved + /// Identifier produced by a semi-opaque expansion may be resolved /// either at call-site or at definition-site. /// If it's a local variable, label or `$crate` then it's resolved at def-site. /// Otherwise it's resolved at call-site. /// `macro_rules` macros behave like this, built-in macros currently behave like this too, /// but that's an implementation detail. - SemiTransparent, + SemiOpaque, /// Identifier produced by an opaque expansion is always resolved at definition-site. /// Def-site spans in procedural macros, identifiers from `macro` by default use this. Opaque, diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 01274a9835f4..c3429356d9e5 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -546,11 +546,11 @@ pub mod ptr { // endregion:non_null // region:addr_of - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] pub macro addr_of($place:expr) { &raw const $place } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] pub macro addr_of_mut($place:expr) { &raw mut $place } diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index a45af7fcf158..1c81a485608a 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -21,13 +21,18 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; -use std::{fmt, fs, io}; +use std::{env, fmt, fs, io}; + +use build_helper::ci::CiEnv; use crate::CiInfo; use crate::diagnostics::TidyCtx; mod rustdoc_js; +#[cfg(test)] +mod tests; + const MIN_PY_REV: (u32, u32) = (3, 9); const MIN_PY_REV_STR: &str = "≥3.9"; @@ -43,6 +48,7 @@ const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"]; const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"]; const SPELLCHECK_DIRS: &[&str] = &["compiler", "library", "src/bootstrap", "src/librustdoc"]; +const SPELLCHECK_VER: &str = "1.38.1"; pub fn check( root_path: &Path, @@ -115,6 +121,7 @@ fn check_impl( .collect(), None => vec![], }; + lint_args.retain(|ck| ck.is_non_if_installed_or_matches(root_path, outdir)); if lint_args.iter().any(|ck| ck.auto) { crate::files_modified_batch_filter(ci_info, &mut lint_args, |ck, path| { ck.is_non_auto_or_matches(path) @@ -421,21 +428,11 @@ fn py_runner( /// Create a virtuaenv at a given path if it doesn't already exist, or validate /// the install if it does. Returns the path to that venv's python executable. fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result { - let mut should_create = true; - let dst_reqs_path = venv_path.join("requirements.txt"); let mut py_path = venv_path.to_owned(); py_path.extend(REL_PY_PATH); - if let Ok(req) = fs::read_to_string(&dst_reqs_path) { - if req == fs::read_to_string(src_reqs_path)? { - // found existing environment - should_create = false; - } else { - eprintln!("requirements.txt file mismatch, recreating environment"); - } - } - - if should_create { + if !has_py_tools(venv_path, src_reqs_path)? { + let dst_reqs_path = venv_path.join("requirements.txt"); eprintln!("removing old virtual environment"); if venv_path.is_dir() { fs::remove_dir_all(venv_path).unwrap_or_else(|_| { @@ -450,6 +447,18 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result Result { + let dst_reqs_path = venv_path.join("requirements.txt"); + if let Ok(req) = fs::read_to_string(&dst_reqs_path) { + if req == fs::read_to_string(src_reqs_path)? { + return Ok(true); + } + eprintln!("requirements.txt file mismatch"); + } + + Ok(false) +} + /// Attempt to create a virtualenv at this path. Cycles through all expected /// valid python versions to find one that is installed. fn create_venv_at_path(path: &Path) -> Result<(), Error> { @@ -591,23 +600,26 @@ fn install_requirements( Ok(()) } +/// Returns `Ok` if shellcheck is installed, `Err` otherwise. +fn has_shellcheck() -> Result<(), Error> { + match Command::new("shellcheck").arg("--version").status() { + Ok(_) => Ok(()), + Err(e) if e.kind() == io::ErrorKind::NotFound => Err(Error::MissingReq( + "shellcheck", + "shell file checks", + Some( + "see \ + for installation instructions" + .to_owned(), + ), + )), + Err(e) => Err(e.into()), + } +} + /// Check that shellcheck is installed then run it at the given path fn shellcheck_runner(args: &[&OsStr]) -> Result<(), Error> { - match Command::new("shellcheck").arg("--version").status() { - Ok(_) => (), - Err(e) if e.kind() == io::ErrorKind::NotFound => { - return Err(Error::MissingReq( - "shellcheck", - "shell file checks", - Some( - "see \ - for installation instructions" - .to_owned(), - ), - )); - } - Err(e) => return Err(e.into()), - } + has_shellcheck()?; let status = Command::new("shellcheck").args(args).status()?; if status.success() { Ok(()) } else { Err(Error::FailedCheck("shellcheck")) } @@ -621,7 +633,7 @@ fn spellcheck_runner( args: &[&str], ) -> Result<(), Error> { let bin_path = - crate::ensure_version_or_cargo_install(outdir, cargo, "typos-cli", "typos", "1.38.1")?; + ensure_version_or_cargo_install(outdir, cargo, "typos-cli", "typos", SPELLCHECK_VER)?; match Command::new(bin_path).current_dir(src_root).args(args).status() { Ok(status) => { if status.success() { @@ -675,6 +687,83 @@ fn find_with_extension( Ok(output) } +/// Check if the given executable is installed and the version is expected. +fn ensure_version(build_dir: &Path, bin_name: &str, version: &str) -> Result { + let bin_path = build_dir.join("misc-tools").join("bin").join(bin_name); + + match Command::new(&bin_path).arg("--version").output() { + Ok(output) => { + let Some(v) = str::from_utf8(&output.stdout).unwrap().trim().split_whitespace().last() + else { + return Err(Error::Generic("version check failed".to_string())); + }; + + if v != version { + return Err(Error::Version { program: "", required: "", installed: v.to_string() }); + } + Ok(bin_path) + } + Err(e) => Err(Error::Io(e)), + } +} + +/// If the given executable is installed with the given version, use that, +/// otherwise install via cargo. +fn ensure_version_or_cargo_install( + build_dir: &Path, + cargo: &Path, + pkg_name: &str, + bin_name: &str, + version: &str, +) -> Result { + if let Ok(bin_path) = ensure_version(build_dir, bin_name, version) { + return Ok(bin_path); + } + + eprintln!("building external tool {bin_name} from package {pkg_name}@{version}"); + + let tool_root_dir = build_dir.join("misc-tools"); + let tool_bin_dir = tool_root_dir.join("bin"); + let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); + + // use --force to ensure that if the required version is bumped, we update it. + // use --target-dir to ensure we have a build cache so repeated invocations aren't slow. + // modify PATH so that cargo doesn't print a warning telling the user to modify the path. + let mut cmd = Command::new(cargo); + cmd.args(["install", "--locked", "--force", "--quiet"]) + .arg("--root") + .arg(&tool_root_dir) + .arg("--target-dir") + .arg(tool_root_dir.join("target")) + .arg(format!("{pkg_name}@{version}")) + .env( + "PATH", + env::join_paths( + env::split_paths(&env::var("PATH").unwrap()) + .chain(std::iter::once(tool_bin_dir.clone())), + ) + .expect("build dir contains invalid char"), + ); + + // On CI, we set opt-level flag for quicker installation. + // Since lower opt-level decreases the tool's performance, + // we don't set this option on local. + if CiEnv::is_ci() { + cmd.env("RUSTFLAGS", "-Copt-level=0"); + } + + let cargo_exit_code = cmd.spawn()?.wait()?; + if !cargo_exit_code.success() { + return Err(Error::Generic("cargo install failed".to_string())); + } + assert!( + matches!(bin_path.try_exists(), Ok(true)), + "cargo install did not produce the expected binary" + ); + eprintln!("finished building tool {bin_name}"); + Ok(bin_path) +} + #[derive(Debug)] enum Error { Io(io::Error), @@ -723,7 +812,7 @@ impl From for Error { } } -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum ExtraCheckParseError { #[allow(dead_code, reason = "shown through Debug")] UnknownKind(String), @@ -736,10 +825,16 @@ enum ExtraCheckParseError { Empty, /// `auto` specified without lang part. AutoRequiresLang, + /// `if-installed` specified without lang part. + IfInstalledRequiresLang, } +#[derive(PartialEq, Debug)] struct ExtraCheckArg { + /// Only run the check if files to check have been modified. auto: bool, + /// Only run the check if the requisite software is already installed. + if_installed: bool, lang: ExtraCheckLang, /// None = run all extra checks for the given lang kind: Option, @@ -750,6 +845,58 @@ impl ExtraCheckArg { self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) } + fn is_non_if_installed_or_matches(&self, root_path: &Path, build_dir: &Path) -> bool { + if !self.if_installed { + return true; + } + + match self.lang { + ExtraCheckLang::Spellcheck => { + match ensure_version(build_dir, "typos", SPELLCHECK_VER) { + Ok(_) => true, + Err(Error::Version { installed, .. }) => { + eprintln!( + "warning: the tool `typos` is detected, but version {installed} doesn't match with the expected version {SPELLCHECK_VER}" + ); + false + } + _ => false, + } + } + ExtraCheckLang::Shell => has_shellcheck().is_ok(), + ExtraCheckLang::Js => { + match self.kind { + Some(ExtraCheckKind::Lint) => { + // If Lint is enabled, check both eslint and es-check. + rustdoc_js::has_tool(build_dir, "eslint") + && rustdoc_js::has_tool(build_dir, "es-check") + } + Some(ExtraCheckKind::Typecheck) => { + // If Typecheck is enabled, check tsc. + rustdoc_js::has_tool(build_dir, "tsc") + } + None => { + // No kind means it will check both Lint and Typecheck. + rustdoc_js::has_tool(build_dir, "eslint") + && rustdoc_js::has_tool(build_dir, "es-check") + && rustdoc_js::has_tool(build_dir, "tsc") + } + Some(_) => unreachable!("js shouldn't have other type of ExtraCheckKind"), + } + } + ExtraCheckLang::Py | ExtraCheckLang::Cpp => { + let venv_path = build_dir.join("venv"); + let mut reqs_path = root_path.to_owned(); + reqs_path.extend(PIP_REQ_PATH); + let Ok(v) = has_py_tools(&venv_path, &reqs_path) else { + return false; + }; + + v + } + } + } + /// Returns `false` if this is an auto arg and the passed filename does not trigger the auto rule fn is_non_auto_or_matches(&self, filepath: &str) -> bool { if !self.auto { @@ -792,22 +939,44 @@ impl FromStr for ExtraCheckArg { fn from_str(s: &str) -> Result { let mut auto = false; + let mut if_installed = false; let mut parts = s.split(':'); - let Some(mut first) = parts.next() else { - return Err(ExtraCheckParseError::Empty); + let mut first = match parts.next() { + Some("") | None => return Err(ExtraCheckParseError::Empty), + Some(part) => part, }; - if first == "auto" { - let Some(part) = parts.next() else { - return Err(ExtraCheckParseError::AutoRequiresLang); - }; - auto = true; - first = part; + + // The loop allows users to specify `auto` and `if-installed` in any order. + // Both auto:if-installed: and if-installed:auto: are valid. + loop { + match (first, auto, if_installed) { + ("auto", false, _) => { + let Some(part) = parts.next() else { + return Err(ExtraCheckParseError::AutoRequiresLang); + }; + auto = true; + first = part; + } + ("if-installed", _, false) => { + let Some(part) = parts.next() else { + return Err(ExtraCheckParseError::IfInstalledRequiresLang); + }; + if_installed = true; + first = part; + } + _ => break, + } } let second = parts.next(); if parts.next().is_some() { return Err(ExtraCheckParseError::TooManyParts); } - let arg = Self { auto, lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }; + let arg = Self { + auto, + if_installed, + lang: first.parse()?, + kind: second.map(|s| s.parse()).transpose()?, + }; if !arg.has_supported_kind() { return Err(ExtraCheckParseError::UnsupportedKindForLang); } @@ -816,7 +985,7 @@ impl FromStr for ExtraCheckArg { } } -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, Debug)] enum ExtraCheckLang { Py, Shell, @@ -840,7 +1009,7 @@ impl FromStr for ExtraCheckLang { } } -#[derive(PartialEq, Copy, Clone)] +#[derive(PartialEq, Copy, Clone, Debug)] enum ExtraCheckKind { Lint, Fmt, diff --git a/src/tools/tidy/src/extra_checks/rustdoc_js.rs b/src/tools/tidy/src/extra_checks/rustdoc_js.rs index 944d8a44112f..6ad5a6665071 100644 --- a/src/tools/tidy/src/extra_checks/rustdoc_js.rs +++ b/src/tools/tidy/src/extra_checks/rustdoc_js.rs @@ -21,6 +21,11 @@ fn spawn_cmd(cmd: &mut Command) -> Result { }) } +pub(super) fn has_tool(outdir: &Path, name: &str) -> bool { + let bin_path = node_module_bin(outdir, name); + Command::new(bin_path).arg("--version").status().is_ok() +} + /// install all js dependencies from package.json. pub(super) fn npm_install(root_path: &Path, outdir: &Path, npm: &Path) -> Result<(), super::Error> { npm::install(root_path, outdir, npm)?; diff --git a/src/tools/tidy/src/extra_checks/tests.rs b/src/tools/tidy/src/extra_checks/tests.rs new file mode 100644 index 000000000000..5d274069a80c --- /dev/null +++ b/src/tools/tidy/src/extra_checks/tests.rs @@ -0,0 +1,86 @@ +use std::str::FromStr; + +use crate::extra_checks::{ExtraCheckArg, ExtraCheckKind, ExtraCheckLang, ExtraCheckParseError}; + +#[test] +fn test_extra_check_arg_from_str_ok() { + let test_cases = [ + ( + "auto:if-installed:spellcheck", + ExtraCheckArg { + auto: true, + if_installed: true, + lang: ExtraCheckLang::Spellcheck, + kind: None, + }, + ), + ( + "if-installed:auto:spellcheck", + ExtraCheckArg { + auto: true, + if_installed: true, + lang: ExtraCheckLang::Spellcheck, + kind: None, + }, + ), + ( + "auto:spellcheck", + ExtraCheckArg { + auto: true, + if_installed: false, + lang: ExtraCheckLang::Spellcheck, + kind: None, + }, + ), + ( + "if-installed:spellcheck", + ExtraCheckArg { + auto: false, + if_installed: true, + lang: ExtraCheckLang::Spellcheck, + kind: None, + }, + ), + ( + "spellcheck", + ExtraCheckArg { + auto: false, + if_installed: false, + lang: ExtraCheckLang::Spellcheck, + kind: None, + }, + ), + ( + "js:lint", + ExtraCheckArg { + auto: false, + if_installed: false, + lang: ExtraCheckLang::Js, + kind: Some(ExtraCheckKind::Lint), + }, + ), + ]; + + for (s, expected) in test_cases { + assert_eq!(ExtraCheckArg::from_str(s), Ok(expected)); + } +} + +#[test] +fn test_extra_check_arg_from_str_err() { + let test_cases = [ + ("some:spellcheck", ExtraCheckParseError::UnknownLang("some".to_string())), + ("spellcheck:some", ExtraCheckParseError::UnknownKind("some".to_string())), + ("spellcheck:lint", ExtraCheckParseError::UnsupportedKindForLang), + ("auto:spellcheck:some", ExtraCheckParseError::UnknownKind("some".to_string())), + ("auto:js:lint:some", ExtraCheckParseError::TooManyParts), + ("some", ExtraCheckParseError::UnknownLang("some".to_string())), + ("auto", ExtraCheckParseError::AutoRequiresLang), + ("if-installed", ExtraCheckParseError::IfInstalledRequiresLang), + ("", ExtraCheckParseError::Empty), + ]; + + for (s, expected) in test_cases { + assert_eq!(ExtraCheckArg::from_str(s), Err(expected)); + } +} diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 45187a0b6450..b3e17b73237d 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -379,7 +379,6 @@ ui/borrowck/issue-88434-minimal-example.rs ui/borrowck/issue-88434-removal-index-should-be-less.rs ui/borrowck/issue-91206.rs ui/borrowck/issue-92015.rs -ui/borrowck/issue-92157.rs ui/borrowck/issue-93078.rs ui/borrowck/issue-93093.rs ui/borrowck/issue-95079-missing-move-in-nested-closure.rs diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 756f9790e04a..425f43e42b7f 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -4,9 +4,7 @@ //! to be used by tools. use std::ffi::OsStr; -use std::path::{Path, PathBuf}; use std::process::Command; -use std::{env, io}; use build_helper::ci::CiEnv; use build_helper::git::{GitConfig, get_closest_upstream_commit}; @@ -158,77 +156,6 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { !v.is_empty() } -/// If the given executable is installed with the given version, use that, -/// otherwise install via cargo. -pub fn ensure_version_or_cargo_install( - build_dir: &Path, - cargo: &Path, - pkg_name: &str, - bin_name: &str, - version: &str, -) -> io::Result { - let tool_root_dir = build_dir.join("misc-tools"); - let tool_bin_dir = tool_root_dir.join("bin"); - let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); - - // ignore the process exit code here and instead just let the version number check fail. - // we also importantly don't return if the program wasn't installed, - // instead we want to continue to the fallback. - 'ck: { - // FIXME: rewrite as if-let chain once this crate is 2024 edition. - let Ok(output) = Command::new(&bin_path).arg("--version").output() else { - break 'ck; - }; - let Ok(s) = str::from_utf8(&output.stdout) else { - break 'ck; - }; - let Some(v) = s.trim().split_whitespace().last() else { - break 'ck; - }; - if v == version { - return Ok(bin_path); - } - } - - eprintln!("building external tool {bin_name} from package {pkg_name}@{version}"); - // use --force to ensure that if the required version is bumped, we update it. - // use --target-dir to ensure we have a build cache so repeated invocations aren't slow. - // modify PATH so that cargo doesn't print a warning telling the user to modify the path. - let mut cmd = Command::new(cargo); - cmd.args(["install", "--locked", "--force", "--quiet"]) - .arg("--root") - .arg(&tool_root_dir) - .arg("--target-dir") - .arg(tool_root_dir.join("target")) - .arg(format!("{pkg_name}@{version}")) - .env( - "PATH", - env::join_paths( - env::split_paths(&env::var("PATH").unwrap()) - .chain(std::iter::once(tool_bin_dir.clone())), - ) - .expect("build dir contains invalid char"), - ); - - // On CI, we set opt-level flag for quicker installation. - // Since lower opt-level decreases the tool's performance, - // we don't set this option on local. - if CiEnv::is_ci() { - cmd.env("RUSTFLAGS", "-Copt-level=0"); - } - - let cargo_exit_code = cmd.spawn()?.wait()?; - if !cargo_exit_code.success() { - return Err(io::Error::other("cargo install failed")); - } - assert!( - matches!(bin_path.try_exists(), Ok(true)), - "cargo install did not produce the expected binary" - ); - eprintln!("finished building tool {bin_name}"); - Ok(bin_path) -} - pub mod alphabetical; pub mod bins; pub mod debug_artifacts; diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 59cd9d80b07a..a74d980cfcb7 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -2,11 +2,22 @@ use std::collections::HashSet; use std::path::Path; +use std::sync::LazyLock; use toml::Value; use crate::diagnostics::TidyCtx; +static SUBMODULES: LazyLock> = LazyLock::new(|| { + // WORKSPACES doesn't list all submodules but it's contains the main at least + crate::deps::WORKSPACES + .iter() + .map(|ws| ws.submodules.iter()) + .flatten() + .map(|p| Path::new(p)) + .collect() +}); + pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut check = tidy_ctx.start_check("triagebot"); let triagebot_path = path.join("triagebot.toml"); @@ -39,8 +50,13 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { if !full_path.exists() { // The full-path doesn't exists, maybe it's a glob, let's add it to the glob set builder // to be checked against all the file and directories in the repository. - builder.add(globset::Glob::new(&format!("{clean_path}*")).unwrap()); + let trimmed_path = clean_path.trim_end_matches('/'); + builder.add(globset::Glob::new(&format!("{trimmed_path}{{,/*}}")).unwrap()); glob_entries.push(clean_path.to_string()); + } else if is_in_submodule(Path::new(clean_path)) { + check.error(format!( + "triagebot.toml [mentions.*] '{clean_path}' cannot match inside a submodule" + )); } } @@ -49,8 +65,18 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { let mut found = HashSet::new(); let mut matches = Vec::new(); + let cloned_path = path.to_path_buf(); + // Walk the entire repository and match any entry against the remaining paths - for entry in ignore::WalkBuilder::new(path).build().flatten() { + for entry in ignore::WalkBuilder::new(&path) + .filter_entry(move |entry| { + // Ignore entries inside submodules as triagebot cannot detect them + let entry_path = entry.path().strip_prefix(&cloned_path).unwrap(); + is_not_in_submodule(entry_path) + }) + .build() + .flatten() + { // Strip the prefix as mentions entries are always relative to the repo let entry_path = entry.path().strip_prefix(path).unwrap(); @@ -126,3 +152,11 @@ pub fn check(path: &Path, tidy_ctx: TidyCtx) { } } } + +fn is_not_in_submodule(path: &Path) -> bool { + SUBMODULES.contains(&path) || !SUBMODULES.iter().any(|p| path.starts_with(*p)) +} + +fn is_in_submodule(path: &Path) -> bool { + !SUBMODULES.contains(&path) && SUBMODULES.iter().any(|p| path.starts_with(*p)) +} diff --git a/tests/assembly-llvm/rust-abi-arg-attr.rs b/tests/assembly-llvm/rust-abi-arg-attr.rs index 4f3673ccfc3b..2d13feb021ba 100644 --- a/tests/assembly-llvm/rust-abi-arg-attr.rs +++ b/tests/assembly-llvm/rust-abi-arg-attr.rs @@ -1,3 +1,4 @@ +//@ add-minicore //@ assembly-output: emit-asm //@ revisions: riscv64 riscv64-zbb loongarch64 //@ compile-flags: -C opt-level=3 @@ -14,45 +15,8 @@ #![no_std] #![no_core] // FIXME: Migrate these code after PR #130693 is landed. - -#[lang = "pointee_sized"] -pub trait PointeeSized {} - -#[lang = "meta_sized"] -pub trait MetaSized: PointeeSized {} - -#[lang = "sized"] -pub trait Sized: MetaSized {} - -#[lang = "copy"] -trait Copy {} - -impl Copy for i8 {} -impl Copy for u32 {} -impl Copy for i32 {} - -#[lang = "neg"] -trait Neg { - type Output; - - fn neg(self) -> Self::Output; -} - -impl Neg for i8 { - type Output = i8; - - fn neg(self) -> Self::Output { - -self - } -} - -#[lang = "Ordering"] -#[repr(i8)] -enum Ordering { - Less = -1, - Equal = 0, - Greater = 1, -} +extern crate minicore; +use minicore::*; #[rustc_intrinsic] fn three_way_compare(lhs: T, rhs: T) -> Ordering; diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index 288a5b50dd5e..95b217c13031 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -210,8 +210,16 @@ impl Neg for isize { } } +impl Neg for i8 { + type Output = i8; + + fn neg(self) -> i8 { + loop {} + } +} + #[lang = "sync"] -trait Sync {} +pub trait Sync {} impl_marker_trait!( Sync => [ char, bool, @@ -280,6 +288,16 @@ pub enum c_void { __variant2, } +#[lang = "Ordering"] +#[repr(i8)] +pub enum Ordering { + Less = -1, + Equal = 0, + Greater = 1, +} + +impl Copy for Ordering {} + #[lang = "const_param_ty"] #[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] pub trait ConstParamTy_ {} diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index 5b2ff41ef052..73dc08dc2b57 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -1,21 +1,48 @@ -//@ only-loongarch64-unknown-linux-gnu +//@ ignore-powerpc64 (handles dso_local differently) +//@ ignore-apple (handles dso_local differently) -//@ revisions: DEFAULT DIRECT INDIRECT +//@ revisions: DEFAULT PIE DIRECT INDIRECT //@ [DEFAULT] compile-flags: -C relocation-model=static -//@ [DIRECT] compile-flags: -C relocation-model=static -Z direct-access-external-data=yes +//@ [PIE] compile-flags: -C relocation-model=pie +//@ [DIRECT] compile-flags: -C relocation-model=pie -Z direct-access-external-data=yes //@ [INDIRECT] compile-flags: -C relocation-model=static -Z direct-access-external-data=no #![crate_type = "rlib"] +#![feature(linkage)] -// DEFAULT: @VAR = external {{.*}} global i32 -// DIRECT: @VAR = external dso_local {{.*}} global i32 -// INDIRECT: @VAR = external {{.*}} global i32 +unsafe extern "C" { + // CHECK: @VAR = external + // DEFAULT-SAME: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i32 + safe static VAR: i32; -extern "C" { - static VAR: i32; + // When "linkage" is used, we generate an indirection global. + // Check dso_local is still applied to the actual global. + // CHECK: @EXTERNAL = external + // DEFAULT-SAME: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i8 + #[linkage = "external"] + safe static EXTERNAL: *const u32; + + // CHECK: @WEAK = extern_weak + // DEFAULT-SAME: dso_local + // PIE-NOT: dso_local + // DIRECT-SAME: dso_local + // INDIRECT-NOT: dso_local + // CHECK-SAME: global i8 + #[linkage = "extern_weak"] + safe static WEAK: *const u32; } #[no_mangle] -pub fn get() -> i32 { - unsafe { VAR } +pub fn refer() { + core::hint::black_box(VAR); + core::hint::black_box(EXTERNAL); + core::hint::black_box(WEAK); } diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index a1fe278c6520..40527446e5dd 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,36 +1,36 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] -| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] -| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send -| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] -| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] -| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send -| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] -| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] -| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send -| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] -| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 0: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send +| 2: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] +| 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send +| 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] +| 10: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send +| 12: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] +| 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send +| 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] +| 20: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send +| 22: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^c_0), max_universe: U0, var_kinds: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] +| 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 27: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, var_kinds: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send +| 28: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] +| 29: user_ty: Canonical { value: Ty(*mut [i32]), max_universe: U0, var_kinds: [] }, span: $DIR/address_of.rs:37:12: 37:22, inferred_ty: *mut [i32] | fn address_of_reborrow() -> () { let mut _0: (); diff --git a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir index b9d26c67538e..aa7d75242b40 100644 --- a/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/basic_assignment.main.SimplifyCfg-initial.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> -| 1: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, variables: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> +| 0: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> +| 1: user_ty: Canonical { value: Ty(std::option::Option>), max_universe: U0, var_kinds: [] }, span: $DIR/basic_assignment.rs:38:17: 38:33, inferred_ty: std::option::Option> | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/issue_101867.main.built.after.mir b/tests/mir-opt/building/issue_101867.main.built.after.mir index fa35658a16d4..83281dea44db 100644 --- a/tests/mir-opt/building/issue_101867.main.built.after.mir +++ b/tests/mir-opt/building/issue_101867.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option -| 1: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, variables: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option +| 0: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option +| 1: user_ty: Canonical { value: Ty(std::option::Option), max_universe: U0, var_kinds: [] }, span: $DIR/issue_101867.rs:5:12: 5:22, inferred_ty: std::option::Option | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir index 5cf182c21c31..0c73bd8ac654 100644 --- a/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir +++ b/tests/mir-opt/building/receiver_ptr_mutability.main.built.after.mir @@ -1,10 +1,10 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:16:14: 16:23, inferred_ty: *mut Test -| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, variables: [] }, span: $DIR/receiver_ptr_mutability.rs:16:14: 16:23, inferred_ty: *mut Test -| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:20:18: 20:31, inferred_ty: &&&&*mut Test -| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, variables: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:20:18: 20:31, inferred_ty: &&&&*mut Test +| 0: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, var_kinds: [] }, span: $DIR/receiver_ptr_mutability.rs:16:14: 16:23, inferred_ty: *mut Test +| 1: user_ty: Canonical { value: Ty(*mut Test), max_universe: U0, var_kinds: [] }, span: $DIR/receiver_ptr_mutability.rs:16:14: 16:23, inferred_ty: *mut Test +| 2: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, var_kinds: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:20:18: 20:31, inferred_ty: &&&&*mut Test +| 3: user_ty: Canonical { value: Ty(&&&&*mut Test), max_universe: U0, var_kinds: [Region(U0), Region(U0), Region(U0), Region(U0)] }, span: $DIR/receiver_ptr_mutability.rs:20:18: 20:31, inferred_ty: &&&&*mut Test | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir index bbf504d311f6..4b6aa46129a4 100644 --- a/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_else.built.after.mir @@ -1,8 +1,8 @@ // MIR for `let_else` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:36:20: 36:45, inferred_ty: (u32, u64, &char) -| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:36:20: 36:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:36:20: 36:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:36:20: 36:45, inferred_ty: (u32, u64, &char) | fn let_else() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir index 7bf2551e99f2..3814980b4306 100644 --- a/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_else_bindless.built.after.mir @@ -1,8 +1,8 @@ // MIR for `let_else_bindless` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:41:20: 41:45, inferred_ty: (u32, u64, &char) -| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:41:20: 41:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:41:20: 41:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:41:20: 41:45, inferred_ty: (u32, u64, &char) | fn let_else_bindless() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir index 0cf681d8ab2c..dd05ef37de3c 100644 --- a/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_init.built.after.mir @@ -1,8 +1,8 @@ // MIR for `let_init` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:26:20: 26:45, inferred_ty: (u32, u64, &char) -| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:26:20: 26:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:26:20: 26:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:26:20: 26:45, inferred_ty: (u32, u64, &char) | fn let_init() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir index 968813c826e4..d949e1945339 100644 --- a/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_init_bindless.built.after.mir @@ -1,8 +1,8 @@ // MIR for `let_init_bindless` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:31:20: 31:45, inferred_ty: (u32, u64, &char) -| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:31:20: 31:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:31:20: 31:45, inferred_ty: (u32, u64, &char) +| 1: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:31:20: 31:45, inferred_ty: (u32, u64, &char) | fn let_init_bindless() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir index b6fdc4ff46dc..22bf17bd7898 100644 --- a/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_uninit.built.after.mir @@ -1,7 +1,7 @@ // MIR for `let_uninit` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:16:20: 16:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:16:20: 16:45, inferred_ty: (u32, u64, &char) | fn let_uninit() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir b/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir index 472dbfb63043..aad2de0e7d6e 100644 --- a/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.let_uninit_bindless.built.after.mir @@ -1,7 +1,7 @@ // MIR for `let_uninit_bindless` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:21:20: 21:45, inferred_ty: (u32, u64, &char) +| 0: user_ty: Canonical { value: Ty((u32, u64, &'static char)), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:21:20: 21:45, inferred_ty: (u32, u64, &char) | fn let_uninit_bindless() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir index ff4b0bf7600d..8ec5028250b0 100644 --- a/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const.built.after.mir @@ -1,8 +1,8 @@ // MIR for `match_assoc_const` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 -| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 +| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:55:9: 55:44, inferred_ty: u32 | fn match_assoc_const() -> () { let mut _0: (); diff --git a/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir index 4cc433f475f6..61e5d9b459d0 100644 --- a/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir +++ b/tests/mir-opt/building/user_type_annotations.match_assoc_const_range.built.after.mir @@ -1,10 +1,10 @@ // MIR for `match_assoc_const_range` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 -| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 -| 2: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 -| 3: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 +| 0: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 +| 1: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:63:11: 63:46, inferred_ty: u32 +| 2: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 +| 3: user_ty: Canonical { value: TypeOf(DefId(0:11 ~ user_type_annotations[ee8e]::MyTrait::FOO), UserArgs { args: [MyStruct, 'static], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/user_type_annotations.rs:64:9: 64:44, inferred_ty: u32 | fn match_assoc_const_range() -> () { let mut _0: (); diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff index 408ff60712e1..1b75a2bcba8b 100644 --- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff @@ -26,12 +26,12 @@ scope 4 { debug _x => _8; } - scope 18 (inlined foo) { + scope 19 (inlined foo) { let mut _27: *const [()]; } } - scope 16 (inlined slice_from_raw_parts::<()>) { - scope 17 (inlined std::ptr::from_raw_parts::<[()], ()>) { + scope 17 (inlined slice_from_raw_parts::<()>) { + scope 18 (inlined std::ptr::from_raw_parts::<[()], ()>) { } } } @@ -49,19 +49,21 @@ scope 7 { let _21: std::ptr::NonNull<[u8]>; scope 8 { - scope 11 (inlined NonNull::<[u8]>::as_mut_ptr) { - scope 12 (inlined NonNull::<[u8]>::as_non_null_ptr) { - scope 13 (inlined NonNull::<[u8]>::cast::) { + scope 12 (inlined NonNull::<[u8]>::as_mut_ptr) { + scope 13 (inlined NonNull::<[u8]>::as_non_null_ptr) { + scope 14 (inlined NonNull::<[u8]>::cast::) { let mut _25: *mut [u8]; - scope 14 (inlined NonNull::<[u8]>::as_ptr) { + scope 15 (inlined NonNull::<[u8]>::as_ptr) { } } } - scope 15 (inlined NonNull::::as_ptr) { + scope 16 (inlined NonNull::::as_ptr) { } } } scope 10 (inlined ::allocate) { + scope 11 (inlined std::alloc::Global::alloc_impl) { + } } } scope 9 (inlined #[track_caller] Layout::from_size_align_unchecked) { @@ -192,8 +194,8 @@ + _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}; StorageDead(_24); StorageLive(_19); -- _19 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], copy _18, const false) -> [return: bb7, unwind unreachable]; -+ _19 = std::alloc::Global::alloc_impl(const alloc::alloc::exchange_malloc::promoted[0], const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; +- _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable]; ++ _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable]; } bb7: { diff --git a/tests/mir-opt/issue_72181_1.main.built.after.mir b/tests/mir-opt/issue_72181_1.main.built.after.mir index 79eaf9668330..398a4bcb3ab3 100644 --- a/tests/mir-opt/issue_72181_1.main.built.after.mir +++ b/tests/mir-opt/issue_72181_1.main.built.after.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void -| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, variables: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 0: user_ty: Canonical { value: Ty(Void), max_universe: U0, var_kinds: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void +| 1: user_ty: Canonical { value: Ty(Void), max_universe: U0, var_kinds: [] }, span: $DIR/issue_72181_1.rs:17:12: 17:16, inferred_ty: Void | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir index 48a399eb39ce..9e48cf038bf2 100644 --- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir index 48a399eb39ce..9e48cf038bf2 100644 --- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir +++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir @@ -1,8 +1,8 @@ // MIR for `main` after built | User Type Annotations -| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} -| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:13:16: 13:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} +| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[d56d]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[d56d]::main::{constant#1}), args: [] }], user_self_ty: None }), max_universe: U0, var_kinds: [] }, span: $DIR/issue_99325.rs:14:16: 14:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">} | fn main() -> () { let mut _0: (); diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff index 8777ac426d78..3cf28f4b60af 100644 --- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff +++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-abort.diff @@ -48,8 +48,8 @@ let _29: &str; scope 7 (inlined String::as_str) { let _30: &[u8]; - let mut _31: &std::vec::Vec; scope 8 (inlined Vec::::as_slice) { + let _31: *const [u8]; let mut _32: *const u8; let mut _33: usize; scope 9 (inlined Vec::::as_ptr) { @@ -71,160 +71,86 @@ } } } - scope 18 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - let _35: (); - let mut _36: *mut (); - let _37: *const [u8]; - scope 19 (inlined ub_checks::check_language_ub) { - scope 20 (inlined ub_checks::check_language_ub::runtime) { - } - } - scope 21 (inlined std::mem::size_of::) { - } - scope 22 (inlined std::mem::align_of::) { - } - scope 23 (inlined slice_from_raw_parts::) { - scope 24 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - } - } - } } - scope 25 (inlined from_utf8_unchecked) { + scope 18 (inlined from_utf8_unchecked) { } } - scope 26 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 19 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 27 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 28 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 20 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 21 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 29 (inlined core::str::traits::::eq) { - let mut _38: &&[u8]; - let _39: &[u8]; - let mut _40: &&[u8]; - let _41: &[u8]; - scope 30 (inlined core::str::::as_bytes) { + scope 22 (inlined core::str::traits::::eq) { + let mut _35: &&[u8]; + let _36: &[u8]; + let mut _37: &&[u8]; + let _38: &[u8]; + scope 23 (inlined core::str::::as_bytes) { } - scope 31 (inlined core::str::::as_bytes) { + scope 24 (inlined core::str::::as_bytes) { } - scope 32 (inlined std::cmp::impls::::eq) { - scope 33 (inlined core::slice::cmp::::eq) { - scope 34 (inlined <[u8] as core::slice::cmp::SlicePartialEq>::equal) { - let mut _42: bool; - let mut _43: usize; - let mut _44: usize; - let _45: usize; - let mut _46: i32; - let mut _47: *const u8; - let mut _48: *const u8; - scope 35 { - scope 37 (inlined core::slice::::as_ptr) { - let mut _50: *const [u8]; - } - scope 38 (inlined core::slice::::as_ptr) { - let mut _51: *const [u8]; - } - } - scope 36 (inlined std::mem::size_of_val::<[u8]>) { - let mut _49: *const [u8]; - } - } + scope 25 (inlined std::cmp::impls::::eq) { + scope 26 (inlined core::slice::cmp::::eq) { } } } } } - scope 39 (inlined std::cmp::impls:: for &String>::eq) { - let mut _52: &std::string::String; - let mut _53: &str; - scope 40 (inlined >::eq) { - scope 41 (inlined #[track_caller] >::index) { - let _54: &str; - scope 42 (inlined String::as_str) { - let _55: &[u8]; - let mut _56: &std::vec::Vec; - scope 43 (inlined Vec::::as_slice) { - let mut _57: *const u8; - let mut _58: usize; - scope 44 (inlined Vec::::as_ptr) { - scope 45 (inlined alloc::raw_vec::RawVec::::ptr) { - scope 46 (inlined alloc::raw_vec::RawVecInner::ptr::) { - scope 47 (inlined alloc::raw_vec::RawVecInner::non_null::) { - let mut _59: std::ptr::NonNull; - scope 48 (inlined Unique::::cast::) { - scope 49 (inlined NonNull::::cast::) { - scope 50 (inlined NonNull::::as_ptr) { + scope 27 (inlined std::cmp::impls:: for &String>::eq) { + let mut _39: &std::string::String; + let mut _40: &str; + scope 28 (inlined >::eq) { + scope 29 (inlined #[track_caller] >::index) { + let _41: &str; + scope 30 (inlined String::as_str) { + let _42: &[u8]; + scope 31 (inlined Vec::::as_slice) { + let _43: *const [u8]; + let mut _44: *const u8; + let mut _45: usize; + scope 32 (inlined Vec::::as_ptr) { + scope 33 (inlined alloc::raw_vec::RawVec::::ptr) { + scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _46: std::ptr::NonNull; + scope 36 (inlined Unique::::cast::) { + scope 37 (inlined NonNull::::cast::) { + scope 38 (inlined NonNull::::as_ptr) { } } } - scope 51 (inlined Unique::::as_non_null_ptr) { + scope 39 (inlined Unique::::as_non_null_ptr) { } } - scope 52 (inlined NonNull::::as_ptr) { + scope 40 (inlined NonNull::::as_ptr) { } } } } - scope 53 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - let _60: (); - let mut _61: *mut (); - let _62: *const [u8]; - scope 54 (inlined ub_checks::check_language_ub) { - scope 55 (inlined ub_checks::check_language_ub::runtime) { - } - } - scope 56 (inlined std::mem::size_of::) { - } - scope 57 (inlined std::mem::align_of::) { - } - scope 58 (inlined slice_from_raw_parts::) { - scope 59 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - } - } - } } - scope 60 (inlined from_utf8_unchecked) { + scope 41 (inlined from_utf8_unchecked) { } } - scope 61 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 62 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 63 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 64 (inlined core::str::traits::::eq) { - let mut _63: &&[u8]; - let _64: &[u8]; - let mut _65: &&[u8]; - let _66: &[u8]; - scope 65 (inlined core::str::::as_bytes) { + scope 45 (inlined core::str::traits::::eq) { + let mut _47: &&[u8]; + let _48: &[u8]; + let mut _49: &&[u8]; + let _50: &[u8]; + scope 46 (inlined core::str::::as_bytes) { } - scope 66 (inlined core::str::::as_bytes) { + scope 47 (inlined core::str::::as_bytes) { } - scope 67 (inlined std::cmp::impls::::eq) { - scope 68 (inlined core::slice::cmp::::eq) { - scope 69 (inlined <[u8] as core::slice::cmp::SlicePartialEq>::equal) { - let mut _67: bool; - let mut _68: usize; - let mut _69: usize; - let _70: usize; - let mut _71: i32; - let mut _72: *const u8; - let mut _73: *const u8; - scope 70 { - scope 72 (inlined core::slice::::as_ptr) { - let mut _75: *const [u8]; - } - scope 73 (inlined core::slice::::as_ptr) { - let mut _76: *const [u8]; - } - } - scope 71 (inlined std::mem::size_of_val::<[u8]>) { - let mut _74: *const [u8]; - } - } + scope 48 (inlined std::cmp::impls::::eq) { + scope 49 (inlined core::slice::cmp::::eq) { } } } @@ -253,7 +179,7 @@ bb3: { _1 = chained_conditions::BacktraceStyle::Off; - goto -> bb18; -+ goto -> bb37; ++ goto -> bb23; } bb4: { @@ -272,15 +198,27 @@ _27 = copy (*_8); _28 = copy (*_10); StorageLive(_29); - StorageLive(_35); StorageLive(_30); - StorageLive(_34); + StorageLive(_31); StorageLive(_32); + StorageLive(_34); _34 = copy ((((((*_27).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _32 = copy _34 as *const u8 (Transmute); + StorageDead(_34); StorageLive(_33); _33 = copy (((*_27).0: std::vec::Vec).1: usize); - switchInt(UbChecks) -> [0: bb21, otherwise: bb19]; + _31 = *const [u8] from (copy _32, move _33); + StorageDead(_33); + StorageDead(_32); + _30 = &(*_31); + StorageDead(_31); + _29 = copy _30 as &str (Transmute); + StorageDead(_30); + StorageLive(_36); + StorageLive(_38); + _36 = copy _29 as &[u8] (Transmute); + _38 = copy _28 as &[u8] (Transmute); + _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb19, unwind unreachable]; } bb5: { @@ -311,27 +249,39 @@ StorageLive(_17); _20 = const chained_conditions::promoted[0]; _17 = &(*_20); - StorageLive(_52); - StorageLive(_53); - _52 = copy (*_15); - _53 = copy (*_17); - StorageLive(_54); - StorageLive(_60); - StorageLive(_55); - StorageLive(_59); - StorageLive(_57); - _59 = copy ((((((*_52).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - _57 = copy _59 as *const u8 (Transmute); - StorageLive(_58); - _58 = copy (((*_52).0: std::vec::Vec).1: usize); - switchInt(UbChecks) -> [0: bb29, otherwise: bb27]; + StorageLive(_39); + StorageLive(_40); + _39 = copy (*_15); + _40 = copy (*_17); + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); + StorageLive(_44); + StorageLive(_46); + _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _44 = copy _46 as *const u8 (Transmute); + StorageDead(_46); + StorageLive(_45); + _45 = copy (((*_39).0: std::vec::Vec).1: usize); + _43 = *const [u8] from (copy _44, move _45); + StorageDead(_45); + StorageDead(_44); + _42 = &(*_43); + StorageDead(_43); + _41 = copy _42 as &str (Transmute); + StorageDead(_42); + StorageLive(_48); + StorageLive(_50); + _48 = copy _41 as &[u8] (Transmute); + _50 = copy _40 as &[u8] (Transmute); + _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb20, unwind unreachable]; } bb7: { StorageDead(_5); StorageDead(_6); - goto -> bb18; -+ goto -> bb39; ++ goto -> bb21; } bb8: { @@ -354,14 +304,14 @@ StorageDead(_13); _1 = chained_conditions::BacktraceStyle::Short; - goto -> bb18; -+ goto -> bb37; ++ goto -> bb23; } bb10: { StorageDead(_12); StorageDead(_13); - goto -> bb18; -+ goto -> bb39; ++ goto -> bb21; } bb11: { @@ -406,223 +356,39 @@ } bb19: { - StorageLive(_36); - _36 = copy _34 as *mut () (Transmute); - _35 = std::slice::from_raw_parts::precondition_check(move _36, const ::SIZE, const ::ALIGN, copy _33) -> [return: bb20, unwind unreachable]; - } - - bb20: { + StorageDead(_38); StorageDead(_36); - goto -> bb21; - } - - bb21: { - StorageLive(_37); - _37 = *const [u8] from (copy _32, copy _33); - _30 = &(*_37); - StorageDead(_37); - StorageDead(_33); - StorageDead(_32); - StorageDead(_34); - _29 = copy _30 as &str (Transmute); - StorageDead(_30); - StorageLive(_39); - StorageLive(_41); - _39 = copy _29 as &[u8] (Transmute); - _41 = copy _28 as &[u8] (Transmute); - StorageLive(_45); - StorageLive(_50); - StorageLive(_51); - StorageLive(_42); - StorageLive(_43); - _43 = PtrMetadata(copy _39); - StorageLive(_44); - _44 = PtrMetadata(copy _41); - _42 = Ne(move _43, move _44); - switchInt(move _42) -> [0: bb24, otherwise: bb23]; - } - - bb22: { - StorageDead(_51); - StorageDead(_50); - StorageDead(_45); - StorageDead(_41); - StorageDead(_39); - StorageDead(_35); StorageDead(_29); StorageDead(_28); StorageDead(_27); switchInt(move _7) -> [0: bb6, otherwise: bb5]; } - bb23: { - StorageDead(_44); - StorageDead(_43); - _7 = const false; - StorageDead(_42); -- goto -> bb22; -+ goto -> bb35; - } - - bb24: { - StorageDead(_44); - StorageDead(_43); - StorageDead(_42); - StorageLive(_49); - _49 = &raw const (*_39); - _45 = std::intrinsics::size_of_val::<[u8]>(move _49) -> [return: bb26, unwind unreachable]; - } - - bb25: { + bb20: { + StorageDead(_50); StorageDead(_48); - StorageDead(_47); - _7 = Eq(move _46, const 0_i32); - StorageDead(_46); - goto -> bb22; - } - - bb26: { - StorageDead(_49); - StorageLive(_46); - StorageLive(_47); - _50 = &raw const (*_39); - _47 = copy _50 as *const u8 (PtrToPtr); - StorageLive(_48); - _51 = &raw const (*_41); - _48 = copy _51 as *const u8 (PtrToPtr); - _46 = compare_bytes(move _47, move _48, move _45) -> [return: bb25, unwind unreachable]; - } - - bb27: { - StorageLive(_61); - _61 = copy _59 as *mut () (Transmute); - _60 = std::slice::from_raw_parts::precondition_check(move _61, const ::SIZE, const ::ALIGN, copy _58) -> [return: bb28, unwind unreachable]; - } - - bb28: { - StorageDead(_61); - goto -> bb29; - } - - bb29: { - StorageLive(_62); - _62 = *const [u8] from (copy _57, copy _58); - _55 = &(*_62); - StorageDead(_62); - StorageDead(_58); - StorageDead(_57); - StorageDead(_59); - _54 = copy _55 as &str (Transmute); - StorageDead(_55); - StorageLive(_64); - StorageLive(_66); - _64 = copy _54 as &[u8] (Transmute); - _66 = copy _53 as &[u8] (Transmute); - StorageLive(_70); - StorageLive(_75); - StorageLive(_76); - StorageLive(_67); - StorageLive(_68); - _68 = PtrMetadata(copy _64); - StorageLive(_69); - _69 = PtrMetadata(copy _66); - _67 = Ne(move _68, move _69); - switchInt(move _67) -> [0: bb32, otherwise: bb31]; - } - - bb30: { - StorageDead(_76); - StorageDead(_75); - StorageDead(_70); - StorageDead(_66); - StorageDead(_64); - StorageDead(_60); - StorageDead(_54); - StorageDead(_53); - StorageDead(_52); + StorageDead(_41); + StorageDead(_40); + StorageDead(_39); switchInt(move _14) -> [0: bb9, otherwise: bb8]; - } - - bb31: { - StorageDead(_69); - StorageDead(_68); - _14 = const false; - StorageDead(_67); -- goto -> bb30; -+ goto -> bb36; - } - - bb32: { - StorageDead(_69); - StorageDead(_68); - StorageDead(_67); - StorageLive(_74); - _74 = &raw const (*_64); - _70 = std::intrinsics::size_of_val::<[u8]>(move _74) -> [return: bb34, unwind unreachable]; - } - - bb33: { - StorageDead(_73); - StorageDead(_72); - _14 = Eq(move _71, const 0_i32); - StorageDead(_71); - goto -> bb30; - } - - bb34: { - StorageDead(_74); - StorageLive(_71); - StorageLive(_72); - _75 = &raw const (*_64); - _72 = copy _75 as *const u8 (PtrToPtr); - StorageLive(_73); - _76 = &raw const (*_66); - _73 = copy _76 as *const u8 (PtrToPtr); - _71 = compare_bytes(move _72, move _73, move _70) -> [return: bb33, unwind unreachable]; + } + -+ bb35: { -+ StorageDead(_51); -+ StorageDead(_50); -+ StorageDead(_45); -+ StorageDead(_41); -+ StorageDead(_39); -+ StorageDead(_35); -+ StorageDead(_29); -+ StorageDead(_28); -+ StorageDead(_27); -+ goto -> bb6; -+ } -+ -+ bb36: { -+ StorageDead(_76); -+ StorageDead(_75); -+ StorageDead(_70); -+ StorageDead(_66); -+ StorageDead(_64); -+ StorageDead(_60); -+ StorageDead(_54); -+ StorageDead(_53); -+ StorageDead(_52); -+ goto -> bb9; -+ } -+ -+ bb37: { ++ bb21: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb38, otherwise: bb15]; ++ switchInt(move _24) -> [1: bb22, otherwise: bb15]; + } + -+ bb38: { -+ goto -> bb17; -+ } -+ -+ bb39: { -+ _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb40, otherwise: bb15]; -+ } -+ -+ bb40: { ++ bb22: { + goto -> bb15; ++ } ++ ++ bb23: { ++ _24 = discriminant(_2); ++ switchInt(move _24) -> [1: bb24, otherwise: bb15]; ++ } ++ ++ bb24: { ++ goto -> bb17; } } diff --git a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff index 822d33c89391..2f0d83f92792 100644 --- a/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff +++ b/tests/mir-opt/jump_threading.chained_conditions.JumpThreading.panic-unwind.diff @@ -48,8 +48,8 @@ let _29: &str; scope 7 (inlined String::as_str) { let _30: &[u8]; - let mut _31: &std::vec::Vec; scope 8 (inlined Vec::::as_slice) { + let _31: *const [u8]; let mut _32: *const u8; let mut _33: usize; scope 9 (inlined Vec::::as_ptr) { @@ -71,160 +71,86 @@ } } } - scope 18 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - let _35: (); - let mut _36: *mut (); - let _37: *const [u8]; - scope 19 (inlined ub_checks::check_language_ub) { - scope 20 (inlined ub_checks::check_language_ub::runtime) { - } - } - scope 21 (inlined std::mem::size_of::) { - } - scope 22 (inlined std::mem::align_of::) { - } - scope 23 (inlined slice_from_raw_parts::) { - scope 24 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - } - } - } } - scope 25 (inlined from_utf8_unchecked) { + scope 18 (inlined from_utf8_unchecked) { } } - scope 26 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 19 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 27 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 28 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 20 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 21 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 29 (inlined core::str::traits::::eq) { - let mut _38: &&[u8]; - let _39: &[u8]; - let mut _40: &&[u8]; - let _41: &[u8]; - scope 30 (inlined core::str::::as_bytes) { + scope 22 (inlined core::str::traits::::eq) { + let mut _35: &&[u8]; + let _36: &[u8]; + let mut _37: &&[u8]; + let _38: &[u8]; + scope 23 (inlined core::str::::as_bytes) { } - scope 31 (inlined core::str::::as_bytes) { + scope 24 (inlined core::str::::as_bytes) { } - scope 32 (inlined std::cmp::impls::::eq) { - scope 33 (inlined core::slice::cmp::::eq) { - scope 34 (inlined <[u8] as core::slice::cmp::SlicePartialEq>::equal) { - let mut _42: bool; - let mut _43: usize; - let mut _44: usize; - let _45: usize; - let mut _46: i32; - let mut _47: *const u8; - let mut _48: *const u8; - scope 35 { - scope 37 (inlined core::slice::::as_ptr) { - let mut _50: *const [u8]; - } - scope 38 (inlined core::slice::::as_ptr) { - let mut _51: *const [u8]; - } - } - scope 36 (inlined std::mem::size_of_val::<[u8]>) { - let mut _49: *const [u8]; - } - } + scope 25 (inlined std::cmp::impls::::eq) { + scope 26 (inlined core::slice::cmp::::eq) { } } } } } - scope 39 (inlined std::cmp::impls:: for &String>::eq) { - let mut _52: &std::string::String; - let mut _53: &str; - scope 40 (inlined >::eq) { - scope 41 (inlined #[track_caller] >::index) { - let _54: &str; - scope 42 (inlined String::as_str) { - let _55: &[u8]; - let mut _56: &std::vec::Vec; - scope 43 (inlined Vec::::as_slice) { - let mut _57: *const u8; - let mut _58: usize; - scope 44 (inlined Vec::::as_ptr) { - scope 45 (inlined alloc::raw_vec::RawVec::::ptr) { - scope 46 (inlined alloc::raw_vec::RawVecInner::ptr::) { - scope 47 (inlined alloc::raw_vec::RawVecInner::non_null::) { - let mut _59: std::ptr::NonNull; - scope 48 (inlined Unique::::cast::) { - scope 49 (inlined NonNull::::cast::) { - scope 50 (inlined NonNull::::as_ptr) { + scope 27 (inlined std::cmp::impls:: for &String>::eq) { + let mut _39: &std::string::String; + let mut _40: &str; + scope 28 (inlined >::eq) { + scope 29 (inlined #[track_caller] >::index) { + let _41: &str; + scope 30 (inlined String::as_str) { + let _42: &[u8]; + scope 31 (inlined Vec::::as_slice) { + let _43: *const [u8]; + let mut _44: *const u8; + let mut _45: usize; + scope 32 (inlined Vec::::as_ptr) { + scope 33 (inlined alloc::raw_vec::RawVec::::ptr) { + scope 34 (inlined alloc::raw_vec::RawVecInner::ptr::) { + scope 35 (inlined alloc::raw_vec::RawVecInner::non_null::) { + let mut _46: std::ptr::NonNull; + scope 36 (inlined Unique::::cast::) { + scope 37 (inlined NonNull::::cast::) { + scope 38 (inlined NonNull::::as_ptr) { } } } - scope 51 (inlined Unique::::as_non_null_ptr) { + scope 39 (inlined Unique::::as_non_null_ptr) { } } - scope 52 (inlined NonNull::::as_ptr) { + scope 40 (inlined NonNull::::as_ptr) { } } } } - scope 53 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - let _60: (); - let mut _61: *mut (); - let _62: *const [u8]; - scope 54 (inlined ub_checks::check_language_ub) { - scope 55 (inlined ub_checks::check_language_ub::runtime) { - } - } - scope 56 (inlined std::mem::size_of::) { - } - scope 57 (inlined std::mem::align_of::) { - } - scope 58 (inlined slice_from_raw_parts::) { - scope 59 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - } - } - } } - scope 60 (inlined from_utf8_unchecked) { + scope 41 (inlined from_utf8_unchecked) { } } - scope 61 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 42 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 62 (inlined #[track_caller] core::str::traits:: for str>::index) { - scope 63 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { + scope 43 (inlined #[track_caller] core::str::traits:: for str>::index) { + scope 44 (inlined #[track_caller] core::str::traits:: for RangeFull>::index) { } } - scope 64 (inlined core::str::traits::::eq) { - let mut _63: &&[u8]; - let _64: &[u8]; - let mut _65: &&[u8]; - let _66: &[u8]; - scope 65 (inlined core::str::::as_bytes) { + scope 45 (inlined core::str::traits::::eq) { + let mut _47: &&[u8]; + let _48: &[u8]; + let mut _49: &&[u8]; + let _50: &[u8]; + scope 46 (inlined core::str::::as_bytes) { } - scope 66 (inlined core::str::::as_bytes) { + scope 47 (inlined core::str::::as_bytes) { } - scope 67 (inlined std::cmp::impls::::eq) { - scope 68 (inlined core::slice::cmp::::eq) { - scope 69 (inlined <[u8] as core::slice::cmp::SlicePartialEq>::equal) { - let mut _67: bool; - let mut _68: usize; - let mut _69: usize; - let _70: usize; - let mut _71: i32; - let mut _72: *const u8; - let mut _73: *const u8; - scope 70 { - scope 72 (inlined core::slice::::as_ptr) { - let mut _75: *const [u8]; - } - scope 73 (inlined core::slice::::as_ptr) { - let mut _76: *const [u8]; - } - } - scope 71 (inlined std::mem::size_of_val::<[u8]>) { - let mut _74: *const [u8]; - } - } + scope 48 (inlined std::cmp::impls::::eq) { + scope 49 (inlined core::slice::cmp::::eq) { } } } @@ -253,7 +179,7 @@ bb3: { _1 = chained_conditions::BacktraceStyle::Off; - goto -> bb19; -+ goto -> bb41; ++ goto -> bb27; } bb4: { @@ -272,15 +198,27 @@ _27 = copy (*_8); _28 = copy (*_10); StorageLive(_29); - StorageLive(_35); StorageLive(_30); - StorageLive(_34); + StorageLive(_31); StorageLive(_32); + StorageLive(_34); _34 = copy ((((((*_27).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _32 = copy _34 as *const u8 (Transmute); + StorageDead(_34); StorageLive(_33); _33 = copy (((*_27).0: std::vec::Vec).1: usize); - switchInt(UbChecks) -> [0: bb25, otherwise: bb23]; + _31 = *const [u8] from (copy _32, move _33); + StorageDead(_33); + StorageDead(_32); + _30 = &(*_31); + StorageDead(_31); + _29 = copy _30 as &str (Transmute); + StorageDead(_30); + StorageLive(_36); + StorageLive(_38); + _36 = copy _29 as &[u8] (Transmute); + _38 = copy _28 as &[u8] (Transmute); + _7 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _36, move _38) -> [return: bb23, unwind: bb22]; } bb5: { @@ -311,27 +249,39 @@ StorageLive(_17); _20 = const chained_conditions::promoted[0]; _17 = &(*_20); - StorageLive(_52); - StorageLive(_53); - _52 = copy (*_15); - _53 = copy (*_17); - StorageLive(_54); - StorageLive(_60); - StorageLive(_55); - StorageLive(_59); - StorageLive(_57); - _59 = copy ((((((*_52).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); - _57 = copy _59 as *const u8 (Transmute); - StorageLive(_58); - _58 = copy (((*_52).0: std::vec::Vec).1: usize); - switchInt(UbChecks) -> [0: bb33, otherwise: bb31]; + StorageLive(_39); + StorageLive(_40); + _39 = copy (*_15); + _40 = copy (*_17); + StorageLive(_41); + StorageLive(_42); + StorageLive(_43); + StorageLive(_44); + StorageLive(_46); + _46 = copy ((((((*_39).0: std::vec::Vec).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); + _44 = copy _46 as *const u8 (Transmute); + StorageDead(_46); + StorageLive(_45); + _45 = copy (((*_39).0: std::vec::Vec).1: usize); + _43 = *const [u8] from (copy _44, move _45); + StorageDead(_45); + StorageDead(_44); + _42 = &(*_43); + StorageDead(_43); + _41 = copy _42 as &str (Transmute); + StorageDead(_42); + StorageLive(_48); + StorageLive(_50); + _48 = copy _41 as &[u8] (Transmute); + _50 = copy _40 as &[u8] (Transmute); + _14 = <[u8] as core::slice::cmp::SlicePartialEq>::equal(move _48, move _50) -> [return: bb24, unwind: bb22]; } bb7: { StorageDead(_5); StorageDead(_6); - goto -> bb19; -+ goto -> bb43; ++ goto -> bb25; } bb8: { @@ -354,14 +304,14 @@ StorageDead(_13); _1 = chained_conditions::BacktraceStyle::Short; - goto -> bb19; -+ goto -> bb41; ++ goto -> bb27; } bb10: { StorageDead(_12); StorageDead(_13); - goto -> bb19; -+ goto -> bb43; ++ goto -> bb25; } bb11: { @@ -423,223 +373,39 @@ } bb23: { - StorageLive(_36); - _36 = copy _34 as *mut () (Transmute); - _35 = std::slice::from_raw_parts::precondition_check(move _36, const ::SIZE, const ::ALIGN, copy _33) -> [return: bb24, unwind unreachable]; - } - - bb24: { + StorageDead(_38); StorageDead(_36); - goto -> bb25; - } - - bb25: { - StorageLive(_37); - _37 = *const [u8] from (copy _32, copy _33); - _30 = &(*_37); - StorageDead(_37); - StorageDead(_33); - StorageDead(_32); - StorageDead(_34); - _29 = copy _30 as &str (Transmute); - StorageDead(_30); - StorageLive(_39); - StorageLive(_41); - _39 = copy _29 as &[u8] (Transmute); - _41 = copy _28 as &[u8] (Transmute); - StorageLive(_45); - StorageLive(_50); - StorageLive(_51); - StorageLive(_42); - StorageLive(_43); - _43 = PtrMetadata(copy _39); - StorageLive(_44); - _44 = PtrMetadata(copy _41); - _42 = Ne(move _43, move _44); - switchInt(move _42) -> [0: bb28, otherwise: bb27]; - } - - bb26: { - StorageDead(_51); - StorageDead(_50); - StorageDead(_45); - StorageDead(_41); - StorageDead(_39); - StorageDead(_35); StorageDead(_29); StorageDead(_28); StorageDead(_27); switchInt(move _7) -> [0: bb6, otherwise: bb5]; } - bb27: { - StorageDead(_44); - StorageDead(_43); - _7 = const false; - StorageDead(_42); -- goto -> bb26; -+ goto -> bb39; - } - - bb28: { - StorageDead(_44); - StorageDead(_43); - StorageDead(_42); - StorageLive(_49); - _49 = &raw const (*_39); - _45 = std::intrinsics::size_of_val::<[u8]>(move _49) -> [return: bb30, unwind unreachable]; - } - - bb29: { + bb24: { + StorageDead(_50); StorageDead(_48); - StorageDead(_47); - _7 = Eq(move _46, const 0_i32); - StorageDead(_46); - goto -> bb26; - } - - bb30: { - StorageDead(_49); - StorageLive(_46); - StorageLive(_47); - _50 = &raw const (*_39); - _47 = copy _50 as *const u8 (PtrToPtr); - StorageLive(_48); - _51 = &raw const (*_41); - _48 = copy _51 as *const u8 (PtrToPtr); - _46 = compare_bytes(move _47, move _48, move _45) -> [return: bb29, unwind unreachable]; - } - - bb31: { - StorageLive(_61); - _61 = copy _59 as *mut () (Transmute); - _60 = std::slice::from_raw_parts::precondition_check(move _61, const ::SIZE, const ::ALIGN, copy _58) -> [return: bb32, unwind unreachable]; - } - - bb32: { - StorageDead(_61); - goto -> bb33; - } - - bb33: { - StorageLive(_62); - _62 = *const [u8] from (copy _57, copy _58); - _55 = &(*_62); - StorageDead(_62); - StorageDead(_58); - StorageDead(_57); - StorageDead(_59); - _54 = copy _55 as &str (Transmute); - StorageDead(_55); - StorageLive(_64); - StorageLive(_66); - _64 = copy _54 as &[u8] (Transmute); - _66 = copy _53 as &[u8] (Transmute); - StorageLive(_70); - StorageLive(_75); - StorageLive(_76); - StorageLive(_67); - StorageLive(_68); - _68 = PtrMetadata(copy _64); - StorageLive(_69); - _69 = PtrMetadata(copy _66); - _67 = Ne(move _68, move _69); - switchInt(move _67) -> [0: bb36, otherwise: bb35]; - } - - bb34: { - StorageDead(_76); - StorageDead(_75); - StorageDead(_70); - StorageDead(_66); - StorageDead(_64); - StorageDead(_60); - StorageDead(_54); - StorageDead(_53); - StorageDead(_52); + StorageDead(_41); + StorageDead(_40); + StorageDead(_39); switchInt(move _14) -> [0: bb9, otherwise: bb8]; - } - - bb35: { - StorageDead(_69); - StorageDead(_68); - _14 = const false; - StorageDead(_67); -- goto -> bb34; -+ goto -> bb40; - } - - bb36: { - StorageDead(_69); - StorageDead(_68); - StorageDead(_67); - StorageLive(_74); - _74 = &raw const (*_64); - _70 = std::intrinsics::size_of_val::<[u8]>(move _74) -> [return: bb38, unwind unreachable]; - } - - bb37: { - StorageDead(_73); - StorageDead(_72); - _14 = Eq(move _71, const 0_i32); - StorageDead(_71); - goto -> bb34; - } - - bb38: { - StorageDead(_74); - StorageLive(_71); - StorageLive(_72); - _75 = &raw const (*_64); - _72 = copy _75 as *const u8 (PtrToPtr); - StorageLive(_73); - _76 = &raw const (*_66); - _73 = copy _76 as *const u8 (PtrToPtr); - _71 = compare_bytes(move _72, move _73, move _70) -> [return: bb37, unwind unreachable]; + } + -+ bb39: { -+ StorageDead(_51); -+ StorageDead(_50); -+ StorageDead(_45); -+ StorageDead(_41); -+ StorageDead(_39); -+ StorageDead(_35); -+ StorageDead(_29); -+ StorageDead(_28); -+ StorageDead(_27); -+ goto -> bb6; -+ } -+ -+ bb40: { -+ StorageDead(_76); -+ StorageDead(_75); -+ StorageDead(_70); -+ StorageDead(_66); -+ StorageDead(_64); -+ StorageDead(_60); -+ StorageDead(_54); -+ StorageDead(_53); -+ StorageDead(_52); -+ goto -> bb9; -+ } -+ -+ bb41: { ++ bb25: { + _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb42, otherwise: bb16]; ++ switchInt(move _24) -> [1: bb26, otherwise: bb16]; + } + -+ bb42: { -+ goto -> bb18; -+ } -+ -+ bb43: { -+ _24 = discriminant(_2); -+ switchInt(move _24) -> [1: bb44, otherwise: bb16]; -+ } -+ -+ bb44: { ++ bb26: { + goto -> bb16; ++ } ++ ++ bb27: { ++ _24 = discriminant(_2); ++ switchInt(move _24) -> [1: bb28, otherwise: bb16]; ++ } ++ ++ bb28: { ++ goto -> bb18; } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index 791d6b71a6f7..013361d1d2fb 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -25,17 +25,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { } } scope 18 (inlined ::deallocate) { - let mut _9: *mut u8; - scope 19 (inlined Layout::size) { - } - scope 20 (inlined NonNull::::as_ptr) { - } - scope 21 (inlined std::alloc::dealloc) { - let mut _10: usize; - scope 22 (inlined Layout::size) { - } - scope 23 (inlined Layout::align) { - scope 24 (inlined std::ptr::Alignment::as_usize) { + scope 19 (inlined std::alloc::Global::deallocate_impl) { + scope 20 (inlined std::alloc::Global::deallocate_impl_runtime) { + let mut _9: *mut u8; + scope 21 (inlined Layout::size) { + } + scope 22 (inlined NonNull::::as_ptr) { + } + scope 23 (inlined std::alloc::dealloc) { + let mut _10: usize; + scope 24 (inlined Layout::size) { + } + scope 25 (inlined Layout::align) { + scope 26 (inlined std::ptr::Alignment::as_usize) { + } + } } } } diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff index 1dbe9394e709..b45a0f4a9bdd 100644 --- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff @@ -9,33 +9,33 @@ let mut _4: *mut [u8]; let mut _5: std::ptr::NonNull<[u8]>; let mut _6: std::result::Result, std::alloc::AllocError>; - let mut _7: &std::alloc::Global; - let mut _8: std::alloc::Layout; + let mut _7: std::alloc::Layout; scope 1 { debug layout => _1; - let mut _9: &std::alloc::Global; scope 2 { debug ptr => _3; } scope 5 (inlined ::allocate) { - } - scope 6 (inlined #[track_caller] Result::, std::alloc::AllocError>::unwrap) { - let mut _12: isize; - let _13: std::alloc::AllocError; - let mut _14: !; - let mut _15: &dyn std::fmt::Debug; - let _16: &std::alloc::AllocError; - scope 7 { + scope 6 (inlined std::alloc::Global::alloc_impl) { } + } + scope 7 (inlined #[track_caller] Result::, std::alloc::AllocError>::unwrap) { + let mut _10: isize; + let _11: std::alloc::AllocError; + let mut _12: !; + let mut _13: &dyn std::fmt::Debug; + let _14: &std::alloc::AllocError; scope 8 { } + scope 9 { + } } - scope 9 (inlined NonNull::<[u8]>::as_ptr) { + scope 10 (inlined NonNull::<[u8]>::as_ptr) { } } scope 3 (inlined #[track_caller] Option::::unwrap) { - let mut _10: isize; - let mut _11: !; + let mut _8: isize; + let mut _9: !; scope 4 { } } @@ -46,10 +46,10 @@ StorageLive(_2); - _2 = Option::::None; + _2 = const Option::::None; - StorageLive(_10); -- _10 = discriminant(_2); -- switchInt(move _10) -> [0: bb2, 1: bb3, otherwise: bb1]; -+ _10 = const 0_isize; + StorageLive(_8); +- _8 = discriminant(_2); +- switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1]; ++ _8 = const 0_isize; + switchInt(const 0_isize) -> [0: bb2, 1: bb3, otherwise: bb1]; } @@ -58,48 +58,44 @@ } bb2: { - _11 = option::unwrap_failed() -> unwind unreachable; + _9 = option::unwrap_failed() -> unwind unreachable; } bb3: { - _1 = move ((_2 as Some).0: std::alloc::Layout); + _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; - StorageDead(_10); + StorageDead(_8); StorageDead(_2); StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); StorageLive(_7); - _9 = const main::promoted[0]; - _7 = copy _9; - StorageLive(_8); -- _8 = copy _1; -- _6 = std::alloc::Global::alloc_impl(move _7, move _8, const false) -> [return: bb4, unwind unreachable]; -+ _8 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; -+ _6 = std::alloc::Global::alloc_impl(copy _9, const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable]; +- _7 = copy _1; +- _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable]; ++ _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}; ++ _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable]; } bb4: { - StorageDead(_8); StorageDead(_7); - StorageLive(_12); - StorageLive(_16); - _12 = discriminant(_6); - switchInt(move _12) -> [0: bb6, 1: bb5, otherwise: bb1]; + StorageLive(_10); + StorageLive(_14); + _10 = discriminant(_6); + switchInt(move _10) -> [0: bb6, 1: bb5, otherwise: bb1]; } bb5: { - StorageLive(_15); - _16 = &_13; - _15 = copy _16 as &dyn std::fmt::Debug (PointerCoercion(Unsize, Implicit)); - _14 = result::unwrap_failed(const "called `Result::unwrap()` on an `Err` value", move _15) -> unwind unreachable; + StorageLive(_13); + _14 = &_11; + _13 = copy _14 as &dyn std::fmt::Debug (PointerCoercion(Unsize, Implicit)); + _12 = result::unwrap_failed(const "called `Result::unwrap()` on an `Err` value", move _13) -> unwind unreachable; } bb6: { _5 = move ((_6 as Ok).0: std::ptr::NonNull<[u8]>); - StorageDead(_16); - StorageDead(_12); + StorageDead(_14); + StorageDead(_10); StorageDead(_6); _4 = copy _5 as *mut [u8] (Transmute); StorageDead(_5); diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir index 2eee8a97db0d..8308ecbad716 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-abort.mir @@ -9,6 +9,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug self => _1; let mut _3: *const u8; let mut _4: usize; + let _5: *const [u8]; scope 3 (inlined Vec::::as_ptr) { debug self => _1; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { @@ -29,43 +30,23 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } } - scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - debug data => _3; - debug len => _4; - let _5: *const [u8]; - scope 13 (inlined core::ub_checks::check_language_ub) { - scope 14 (inlined core::ub_checks::check_language_ub::runtime) { - } - } - scope 15 (inlined std::mem::size_of::) { - } - scope 16 (inlined std::mem::align_of::) { - } - scope 17 (inlined slice_from_raw_parts::) { - debug data => _3; - debug len => _4; - scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _3; - } - } - } } } bb0: { - StorageLive(_2); + StorageLive(_5); StorageLive(_3); + StorageLive(_2); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_2); StorageLive(_4); _4 = copy ((*_1).1: usize); - StorageLive(_5); - _5 = *const [u8] from (copy _3, copy _4); - _0 = &(*_5); - StorageDead(_5); + _5 = *const [u8] from (copy _3, move _4); StorageDead(_4); StorageDead(_3); - StorageDead(_2); + _0 = &(*_5); + StorageDead(_5); return; } } diff --git a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir index 2eee8a97db0d..8308ecbad716 100644 --- a/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/vec_deref.vec_deref_to_slice.PreCodegen.after.panic-unwind.mir @@ -9,6 +9,7 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { debug self => _1; let mut _3: *const u8; let mut _4: usize; + let _5: *const [u8]; scope 3 (inlined Vec::::as_ptr) { debug self => _1; scope 4 (inlined alloc::raw_vec::RawVec::::ptr) { @@ -29,43 +30,23 @@ fn vec_deref_to_slice(_1: &Vec) -> &[u8] { } } } - scope 12 (inlined #[track_caller] std::slice::from_raw_parts::<'_, u8>) { - debug data => _3; - debug len => _4; - let _5: *const [u8]; - scope 13 (inlined core::ub_checks::check_language_ub) { - scope 14 (inlined core::ub_checks::check_language_ub::runtime) { - } - } - scope 15 (inlined std::mem::size_of::) { - } - scope 16 (inlined std::mem::align_of::) { - } - scope 17 (inlined slice_from_raw_parts::) { - debug data => _3; - debug len => _4; - scope 18 (inlined std::ptr::from_raw_parts::<[u8], u8>) { - debug data_pointer => _3; - } - } - } } } bb0: { - StorageLive(_2); + StorageLive(_5); StorageLive(_3); + StorageLive(_2); _2 = copy (((((*_1).0: alloc::raw_vec::RawVec).0: alloc::raw_vec::RawVecInner).0: std::ptr::Unique).0: std::ptr::NonNull); _3 = copy _2 as *const u8 (Transmute); + StorageDead(_2); StorageLive(_4); _4 = copy ((*_1).1: usize); - StorageLive(_5); - _5 = *const [u8] from (copy _3, copy _4); - _0 = &(*_5); - StorageDead(_5); + _5 = *const [u8] from (copy _3, move _4); StorageDead(_4); StorageDead(_3); - StorageDead(_2); + _0 = &(*_5); + StorageDead(_5); return; } } diff --git a/tests/run-make/compiletest-self-test/jobs/rmake.rs b/tests/run-make/compiletest-self-test/jobs/rmake.rs new file mode 100644 index 000000000000..d21b44ada1c3 --- /dev/null +++ b/tests/run-make/compiletest-self-test/jobs/rmake.rs @@ -0,0 +1,5 @@ +//! Very basic smoke test to make sure `run_make_support::env::jobs` at least does not panic. + +fn main() { + println!("{}", run_make_support::env::jobs()); +} diff --git a/tests/rustdoc-gui/anchor-navigable.goml b/tests/rustdoc-gui/anchor-navigable.goml index 61d7c89d434f..db7fc3bbf182 100644 --- a/tests/rustdoc-gui/anchor-navigable.goml +++ b/tests/rustdoc-gui/anchor-navigable.goml @@ -7,5 +7,5 @@ go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" // We check that ".item-info" is bigger than its content. move-cursor-to: ".impl" -assert-property: (".impl > a.anchor", {"offsetWidth": "8"}) +assert-property: (".impl > a.anchor", {"offsetWidth": "16"}) assert-css: (".impl > a.anchor", {"left": "-8px"}) diff --git a/tests/rustdoc-gui/anchors.goml b/tests/rustdoc-gui/anchors.goml index 0e8c834a7a77..eaec73f50e97 100644 --- a/tests/rustdoc-gui/anchors.goml +++ b/tests/rustdoc-gui/anchors.goml @@ -14,9 +14,9 @@ define-function: ( assert-css: ("#toggle-all-docs", {"color": |main_color|}) assert-css: (".main-heading h1 span", {"color": |main_heading_type_color|}) assert-css: ( - ".rightside a.src", - {"color": |src_link_color|, "text-decoration": "none"}, - ALL, + ".rightside a.src", + {"color": |src_link_color|, "text-decoration": "none"}, + ALL, ) compare-elements-css: ( ".rightside a.src", @@ -31,25 +31,39 @@ define-function: ( move-cursor-to: ".main-heading a.src" assert-css: ( - ".main-heading a.src", - {"color": |src_link_color|, "text-decoration": "underline"}, + ".main-heading a.src", + {"color": |src_link_color|, "text-decoration": "underline"}, ) move-cursor-to: ".impl-items .rightside a.src" assert-css: ( - ".impl-items .rightside a.src", - {"color": |src_link_color|, "text-decoration": "none"}, + ".impl-items .rightside a.src", + {"color": |src_link_color|, "text-decoration": "none"}, ) move-cursor-to: ".impl-items a.rightside.src" assert-css: ( - ".impl-items a.rightside.src", - {"color": |src_link_color|, "text-decoration": "none"}, + ".impl-items a.rightside.src", + {"color": |src_link_color|, "text-decoration": "none"}, ) + // Now we ensure that the `§` anchor is "reachable" for users on trait methods. + // To ensure the anchor is not hovered, we move the cursor to another item. + move-cursor-to: "#search-button" + // By default, the anchor is not displayed. + wait-for-css: ("#method\.vroum .anchor", {"display": "none"}) + // Once we move the cursor to the method, the anchor should appear. + move-cursor-to: "#method\.vroum .code-header" + assert-css-false: ("#method\.vroum .anchor", {"display": "none"}) + // Now we move the cursor to the anchor to check there is no gap between the method and the + // anchor, making the anchor disappear and preventing users to click on it. + // To make it work, we need to first move it between the method and the anchor. + store-position: ("#method\.vroum .code-header", {"x": method_x, "y": method_y}) + move-cursor-to: (|method_x| - 2, |method_y|) + // Anchor should still be displayed. + assert-css-false: ("#method\.vroum .anchor", {"display": "none"}) + go-to: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html" // Since we changed page, we need to set the theme again. - set-local-storage: {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"} - // We reload the page so the local storage settings are being used. - reload: + call-function: ("switch-theme", {"theme": |theme|}) assert-css: ("#top-doc-prose-title", {"color": |title_color|}) diff --git a/tests/rustdoc-gui/copy-code.goml b/tests/rustdoc-gui/copy-code.goml index 14421ab746f5..eb003ef5e1ca 100644 --- a/tests/rustdoc-gui/copy-code.goml +++ b/tests/rustdoc-gui/copy-code.goml @@ -1,5 +1,6 @@ // Checks that the "copy code" button is not triggering JS error and its display // isn't broken. +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html" define-function: ( @@ -47,15 +48,15 @@ assert: |copy_height| > 0 && |copy_width| > 0 // First we ensure that the clipboard is empty. assert-clipboard: "" + // We make the line numbers appear. -click: "rustdoc-toolbar .settings-menu" -wait-for-css: ("#settings", {"display": "block"}) -// We make the line numbers appear. +call-function: ("open-settings-menu", {}) click: "#line-numbers" wait-for-local-storage: {"rustdoc-line-numbers": "true" } + // We close the settings menu. -click: "rustdoc-toolbar .settings-menu" -wait-for-css-false: ("#settings", {"display": "block"}) +call-function: ("close-settings-menu", {}) + // We ensure that there are actually line numbers generated in the DOM. assert-text: (".example-wrap pre.rust code span[data-nosnippet]", "1") // We make the copy button appear. diff --git a/tests/rustdoc-gui/font-serif-change.goml b/tests/rustdoc-gui/font-serif-change.goml index 1e9f21c35416..32e95cdc6ee1 100644 --- a/tests/rustdoc-gui/font-serif-change.goml +++ b/tests/rustdoc-gui/font-serif-change.goml @@ -1,4 +1,5 @@ // Ensures that the font serif change is working as expected. +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" // By default, it should be the serif fonts. @@ -8,8 +9,7 @@ assert-css: ("body", {"font-family": |serif_font|}) assert-css: ("p code", {"font-family": |serif_code_font|}) // We now switch to the sans serif font -click: "main .settings-menu" -wait-for: "#sans-serif-fonts" +call-function: ("open-settings-menu", {}) click: "#sans-serif-fonts" store-value: (font, '"Fira Sans", sans-serif') @@ -23,8 +23,7 @@ assert-css: ("body", {"font-family": |font|}) assert-css: ("p code", {"font-family": |code_font|}) // We switch back to the serif font -click: "main .settings-menu" -wait-for: "#sans-serif-fonts" +call-function: ("open-settings-menu", {}) click: "#sans-serif-fonts" assert-css: ("body", {"font-family": |serif_font|}) diff --git a/tests/rustdoc-gui/notable-trait.goml b/tests/rustdoc-gui/notable-trait.goml index 6bd4661ac8f4..839988021fce 100644 --- a/tests/rustdoc-gui/notable-trait.goml +++ b/tests/rustdoc-gui/notable-trait.goml @@ -82,15 +82,15 @@ call-function: ("check-notable-tooltip-position", { "i_x": 528, }) +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html" +// This is needed to ensure that the text color is computed. +show-text: true + // Now check the colors. define-function: ( "check-colors", [theme, header_color, content_color, type_color, trait_color, link_color], block { - go-to: "file://" + |DOC_PATH| + "/test_docs/struct.NotableStructWithLongName.html" - // This is needed to ensure that the text color is computed. - show-text: true - call-function: ("switch-theme", {"theme": |theme|}) assert-css: ( @@ -251,7 +251,6 @@ reload: assert-count: ("//*[@class='tooltip popover']", 0) click: "//*[@id='method.create_an_iterator_from_read']//*[@class='tooltip']" assert-count: ("//*[@class='tooltip popover']", 1) -click: "rustdoc-toolbar .settings-menu a" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) assert-count: ("//*[@class='tooltip popover']", 0) assert-false: "#method\.create_an_iterator_from_read .tooltip:focus" diff --git a/tests/rustdoc-gui/search-filter.goml b/tests/rustdoc-gui/search-filter.goml index d92d522c119d..6bd7eaa92063 100644 --- a/tests/rustdoc-gui/search-filter.goml +++ b/tests/rustdoc-gui/search-filter.goml @@ -70,9 +70,7 @@ assert-css: ("#crate-search", { }) // We now check the dark theme. -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" -click: "#theme-dark" +call-function: ("switch-theme", {"theme": "dark"}) wait-for-css: ("#crate-search", { "border": "1px solid #e0e0e0", "color": "#ddd", @@ -80,7 +78,7 @@ wait-for-css: ("#crate-search", { }) // And finally we check the ayu theme. -click: "#theme-ayu" +call-function: ("switch-theme", {"theme": "ayu"}) wait-for-css: ("#crate-search", { "border": "1px solid #5c6773", "color": "#c5c5c5", diff --git a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml index 342bd726694e..dbf80ae34912 100644 --- a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml +++ b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml @@ -1,6 +1,8 @@ // This test ensures that the "Auto-hide item contents for large items" setting is working as // expected. +include: "utils.goml" + // We need to disable this check because `trait.impl/test_docs/trait.Iterator.js` doesn't exist. fail-on-request-error: false @@ -9,8 +11,7 @@ define-function: ( [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-large-items": |storage_value|} - click: "rustdoc-toolbar .settings-menu" - wait-for: "#settings" + call-function: ("open-settings-menu", {}) assert-property: ("#auto-hide-large-items", {"checked": |setting_attribute_value|}) assert-attribute: (".item-decl .type-contents-toggle", {"open": |toggle_attribute_value|}) } diff --git a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml index 02d4ce8855fd..89c1a8b0e98e 100644 --- a/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml +++ b/tests/rustdoc-gui/setting-auto-hide-item-methods-docs.goml @@ -1,13 +1,14 @@ // This test ensures that the "Auto-hide item methods' documentation" setting is working as // expected. +include: "utils.goml" + define-function: ( "check-setting", [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-method-docs": |storage_value|} - click: "rustdoc-toolbar .settings-menu" - wait-for: "#settings" + call-function: ("open-settings-menu", {}) assert-property: ("#auto-hide-method-docs", {"checked": |setting_attribute_value|}) assert-attribute: (".toggle.method-toggle", {"open": |toggle_attribute_value|}) } diff --git a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml index 4af1e829b31c..81c26bfb7f3a 100644 --- a/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml +++ b/tests/rustdoc-gui/setting-auto-hide-trait-implementations.goml @@ -1,12 +1,13 @@ // Checks that the setting "auto hide trait implementations" is working as expected. +include: "utils.goml" + define-function: ( "check-setting", [storage_value, setting_attribute_value, toggle_attribute_value], block { assert-local-storage: {"rustdoc-auto-hide-trait-implementations": |storage_value|} - click: "rustdoc-toolbar .settings-menu" - wait-for: "#settings" + call-function: ("open-settings-menu", {}) assert-property: ("#auto-hide-trait-implementations", {"checked": |setting_attribute_value|}) assert-attribute: ("#trait-implementations-list > details", {"open": |toggle_attribute_value|}, ALL) } diff --git a/tests/rustdoc-gui/setting-go-to-only-result.goml b/tests/rustdoc-gui/setting-go-to-only-result.goml index 5a9c81e0b836..72c1e2bf59ca 100644 --- a/tests/rustdoc-gui/setting-go-to-only-result.goml +++ b/tests/rustdoc-gui/setting-go-to-only-result.goml @@ -1,12 +1,14 @@ -// Checks that the setting "Directly go to item in search if there is only one result " is working as expected. +// Checks that the setting "Directly go to item in search if there is only one result " is working +// as expected. + +include: "utils.goml" define-function: ( "check-setting", [storage_value, setting_attribute_value], block { assert-local-storage: {"rustdoc-go-to-only-result": |storage_value|} - click: "rustdoc-toolbar .settings-menu" - wait-for: "#settings" + call-function: ("open-settings-menu", {}) assert-property: ("#go-to-only-result", {"checked": |setting_attribute_value|}) } ) @@ -25,8 +27,7 @@ wait-for: "#search" assert-document-property: ({"URL": "/lib2/index.html"}, CONTAINS) // Now we change its value. -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) click: "#go-to-only-result" assert-local-storage: {"rustdoc-go-to-only-result": "true"} diff --git a/tests/rustdoc-gui/settings.goml b/tests/rustdoc-gui/settings.goml index 7a163103b5ab..85994be468d3 100644 --- a/tests/rustdoc-gui/settings.goml +++ b/tests/rustdoc-gui/settings.goml @@ -1,20 +1,18 @@ // This test ensures that the settings menu display is working as expected and that // the settings page is also rendered as expected. +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" show-text: true // needed when we check for colors below. // First, we check that the settings page doesn't exist. assert-false: "#settings" -// We now click on the settings button. -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) assert-css: ("#settings", {"display": "block"}) // Store the line margin to compare with the settings.html later. store-css: (".setting-line", {"margin": setting_line_margin}) // Let's close it by clicking on the same button. -click: "rustdoc-toolbar .settings-menu" -wait-for-css: ("#settings", {"display": "none"}) +call-function: ("close-settings-menu", {}) // Let's check that pressing "ESCAPE" is closing it. click: "rustdoc-toolbar .settings-menu" @@ -28,8 +26,7 @@ write: "test" // To be SURE that the search will be run. press-key: 'Enter' wait-for: "#alternative-display #search" -click: "rustdoc-toolbar .settings-menu" -wait-for-css: ("#settings", {"display": "block"}) +call-function: ("open-settings-menu", {}) // Ensure that the search is still displayed. wait-for: "#alternative-display #search" assert: "#main-content.hidden" @@ -41,8 +38,7 @@ set-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false" // We reload the page so the local storage settings are being used. reload: -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) // We check that the "Use system theme" is disabled. assert-property: ("#theme-system-preference", {"checked": "false"}) diff --git a/tests/rustdoc-gui/sidebar-mobile.goml b/tests/rustdoc-gui/sidebar-mobile.goml index 1fa5521baac9..3183650b555a 100644 --- a/tests/rustdoc-gui/sidebar-mobile.goml +++ b/tests/rustdoc-gui/sidebar-mobile.goml @@ -65,8 +65,7 @@ define-function: ( "check-colors", [theme, color, background], block { - call-function: ("switch-theme-mobile", {"theme": |theme|}) - reload: + call-function: ("switch-theme", {"theme": |theme|}) // Open the sidebar menu. click: ".sidebar-menu-toggle" @@ -86,13 +85,3 @@ call-function: ("check-colors", { "color": "#c5c5c5", "background": "#14191f", }) -call-function: ("check-colors", { - "theme": "dark", - "color": "#ddd", - "background": "#505050", -}) -call-function: ("check-colors", { - "theme": "light", - "color": "black", - "background": "#F5F5F5", -}) diff --git a/tests/rustdoc-gui/sidebar-resize-close-popover.goml b/tests/rustdoc-gui/sidebar-resize-close-popover.goml index d3fea9b0f400..ac8f7e9c65fa 100644 --- a/tests/rustdoc-gui/sidebar-resize-close-popover.goml +++ b/tests/rustdoc-gui/sidebar-resize-close-popover.goml @@ -1,9 +1,9 @@ // Checks sidebar resizing close the Settings popover +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" assert-property: (".sidebar", {"clientWidth": "199"}) show-text: true -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) assert-css: ("#settings", {"display": "block"}) // normal resizing drag-and-drop: ((205, 100), (185, 100)) @@ -12,8 +12,7 @@ assert-css: ("#settings", {"display": "none"}) // Now same thing, but for source code go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) assert-css: ("#settings", {"display": "block"}) assert-property: (".sidebar", {"clientWidth": "49"}) drag-and-drop: ((52, 100), (185, 100)) diff --git a/tests/rustdoc-gui/sidebar-resize-setting.goml b/tests/rustdoc-gui/sidebar-resize-setting.goml index a4572c670f81..132f5f387b29 100644 --- a/tests/rustdoc-gui/sidebar-resize-setting.goml +++ b/tests/rustdoc-gui/sidebar-resize-setting.goml @@ -1,11 +1,11 @@ // Checks sidebar resizing stays synced with the setting +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" assert-property: (".sidebar", {"clientWidth": "199"}) show-text: true // Verify that the "hide" option is unchecked -click: "rustdoc-toolbar .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) assert-css: ("#settings", {"display": "block"}) assert-property: ("#hide-sidebar", {"checked": "false"}) press-key: "Escape" diff --git a/tests/rustdoc-gui/source-code-wrapping.goml b/tests/rustdoc-gui/source-code-wrapping.goml index c1fc2835c89a..6dc56672da80 100644 --- a/tests/rustdoc-gui/source-code-wrapping.goml +++ b/tests/rustdoc-gui/source-code-wrapping.goml @@ -1,4 +1,5 @@ // Checks that the interactions with the source code pages are working as expected. +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html" show-text: true set-window-size: (1000, 1000) @@ -13,8 +14,7 @@ define-function: ( ) store-size: (".rust code", {"width": width, "height": height}) -click: "main .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) call-function: ("click-code-wrapping", {"expected": "true"}) wait-for-size-false: (".rust code", {"width": |width|, "height": |height|}) store-size: (".rust code", {"width": new_width, "height": new_height}) @@ -28,8 +28,7 @@ assert-size: (".rust code", {"width": |width|, "height": |height|}) // Now let's check in docs code examples. go-to: "file://" + |DOC_PATH| + "/test_docs/trait_bounds/index.html" -click: "main .settings-menu" -wait-for: "#settings" +call-function: ("open-settings-menu", {}) store-property: (".example-wrap .rust code", {"scrollWidth": rust_width, "scrollHeight": rust_height}) store-property: (".example-wrap .language-text code", {"scrollWidth": txt_width, "scrollHeight": txt_height}) diff --git a/tests/rustdoc-gui/src/staged_api/lib.rs b/tests/rustdoc-gui/src/staged_api/lib.rs index 9b5ad1c5ff32..7304d2f02ab1 100644 --- a/tests/rustdoc-gui/src/staged_api/lib.rs +++ b/tests/rustdoc-gui/src/staged_api/lib.rs @@ -4,6 +4,10 @@ #![stable(feature = "some_feature", since = "1.3.5")] #![doc(rust_logo)] +pub trait X { + fn vroum(); +} + #[stable(feature = "some_feature", since = "1.3.5")] pub struct Foo {} @@ -13,3 +17,7 @@ impl Foo { #[stable(feature = "some_other_feature", since = "1.3.6")] pub fn yo() {} } + +impl X for Foo { + fn vroum() {} +} diff --git a/tests/rustdoc-gui/theme-change.goml b/tests/rustdoc-gui/theme-change.goml index 3860596e3433..14b32baac57e 100644 --- a/tests/rustdoc-gui/theme-change.goml +++ b/tests/rustdoc-gui/theme-change.goml @@ -7,8 +7,7 @@ store-value: (background_light, "white") store-value: (background_dark, "#353535") store-value: (background_ayu, "#0f1419") -click: "rustdoc-toolbar .settings-menu" -wait-for: "#theme-ayu" +call-function: ("open-settings-menu", {}) click: "#theme-ayu" // should be the ayu theme so let's check the color. wait-for-css: ("body", { "background-color": |background_ayu| }) @@ -22,10 +21,6 @@ click: "#theme-dark" wait-for-css: ("body", { "background-color": |background_dark| }) assert-local-storage: { "rustdoc-theme": "dark" } -set-local-storage: { - "rustdoc-preferred-light-theme": "light", - "rustdoc-preferred-dark-theme": "light", -} go-to: "file://" + |DOC_PATH| + "/settings.html" wait-for: "#settings" @@ -75,8 +70,7 @@ store-value: (background_dark, "#353535") store-value: (background_ayu, "#0f1419") store-value: (background_custom_theme, "red") -click: "rustdoc-toolbar .settings-menu" -wait-for: "#theme-ayu" +call-function: ("open-settings-menu", {}) click: "#theme-ayu" // should be the ayu theme so let's check the color. wait-for-css: ("body", { "background-color": |background_ayu| }) diff --git a/tests/rustdoc-gui/theme-defaults.goml b/tests/rustdoc-gui/theme-defaults.goml index 12c17166e874..b7ba64a4adaa 100644 --- a/tests/rustdoc-gui/theme-defaults.goml +++ b/tests/rustdoc-gui/theme-defaults.goml @@ -1,7 +1,7 @@ // Ensure that the theme picker always starts with the actual defaults. +include: "utils.goml" go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -click: "rustdoc-toolbar .settings-menu" -wait-for: "#theme-system-preference" +call-function: ("open-settings-menu", {}) assert: "#theme-system-preference:checked" assert: "#preferred-light-theme-light:checked" assert: "#preferred-dark-theme-dark:checked" @@ -16,8 +16,7 @@ set-local-storage: { "rustdoc-theme": "ayu" } go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" -click: "rustdoc-toolbar .settings-menu" -wait-for: "#theme-system-preference" +call-function: ("open-settings-menu", {}) assert: "#theme-system-preference:checked" assert: "#preferred-light-theme-light:checked" assert-false: "#preferred-dark-theme-dark:checked" diff --git a/tests/rustdoc-gui/utils.goml b/tests/rustdoc-gui/utils.goml index a40f9fc6fdd7..c0625ead2f1a 100644 --- a/tests/rustdoc-gui/utils.goml +++ b/tests/rustdoc-gui/utils.goml @@ -1,11 +1,35 @@ // This file contains code to be re-used by other tests. +define-function: ( + "click-settings-button", + [], + block { + store-count: ("rustdoc-topbar", __topbar_count) + store-value: (__topbar_display, "none") + // The `rustdoc-topbar` element is created with JS when the window size is below a given + // size, however if the window size gets bigger again, the element isn't deleted, just + // hidden, so we need to check for both that it exists and is visible. + if: (|__topbar_count| != 0, block { + store-css: ("rustdoc-topbar", {"display": __topbar_display}) + }) + + // Open the settings menu. + if: (|__topbar_display| != "none", block { + // In mobile mode, this is instead the "topbar" the settings button is located into. + click: "rustdoc-topbar .settings-menu" + }) + else: block { + // We're not in mobile mode so clicking on the "normal" sidebar. + click: "rustdoc-toolbar .settings-menu" + } + } +) + define-function: ( "open-settings-menu", [], block { - // Open the settings menu. - click: "rustdoc-toolbar .settings-menu" + call-function: ("click-settings-button", {}) // Wait for the popover to appear... wait-for-css: ("#settings", {"display": "block"}) } @@ -15,7 +39,8 @@ define-function: ( "close-settings-menu", [], block { - click: "rustdoc-toolbar .settings-menu" + call-function: ("click-settings-button", {}) + // Wait for the popover to disappear... wait-for-css-false: ("#settings", {"display": "block"}) } ) @@ -34,24 +59,6 @@ define-function: ( }, ) -// FIXME: To be removed once `browser-ui-test` has conditions. -define-function: ( - "switch-theme-mobile", - [theme], - block { - // Open the settings menu. - click: "rustdoc-topbar .settings-menu" - // Wait for the popover to appear... - wait-for-css: ("#settings", {"display": "block"}) - // Change the setting. - click: "#theme-"+ |theme| - click: "rustdoc-topbar .settings-menu" - wait-for-css-false: ("#settings", {"display": "block"}) - // Ensure that the local storage was correctly updated. - assert-local-storage: {"rustdoc-theme": |theme|} - }, -) - define-function: ( "perform-search", [query], diff --git a/tests/rustdoc-html/intra-doc/deprecated.rs b/tests/rustdoc-html/intra-doc/deprecated.rs new file mode 100644 index 000000000000..6f8639593a2d --- /dev/null +++ b/tests/rustdoc-html/intra-doc/deprecated.rs @@ -0,0 +1,12 @@ +//@ has deprecated/struct.A.html '//a[@href="{{channel}}/core/ops/range/struct.Range.html#structfield.start"]' 'start' +//@ has deprecated/struct.B1.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' +//@ has deprecated/struct.B2.html '//a[@href="{{channel}}/std/io/error/enum.ErrorKind.html#variant.NotFound"]' 'not_found' + +#[deprecated = "[start][std::ops::Range::start]"] +pub struct A; + +#[deprecated(since = "0.0.0", note = "[not_found][std::io::ErrorKind::NotFound]")] +pub struct B1; + +#[deprecated(note = "[not_found][std::io::ErrorKind::NotFound]", since = "0.0.0")] +pub struct B2; diff --git a/tests/rustdoc-html/macro/macro_pub_in_module.rs b/tests/rustdoc-html/macro/macro_pub_in_module.rs index 2dce73c2cf26..a4b39e20d126 100644 --- a/tests/rustdoc-html/macro/macro_pub_in_module.rs +++ b/tests/rustdoc-html/macro/macro_pub_in_module.rs @@ -33,7 +33,7 @@ pub mod inner { // Make sure the logic is not affected by re-exports. mod unrenamed { //@ !has krate/macro.unrenamed.html - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] pub macro unrenamed() {} } //@ has krate/inner/macro.unrenamed.html diff --git a/tests/rustdoc-ui/intra-doc/deprecated.rs b/tests/rustdoc-ui/intra-doc/deprecated.rs new file mode 100644 index 000000000000..37c27dcde598 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated.rs @@ -0,0 +1,10 @@ +#![deny(rustdoc::broken_intra_doc_links)] + +#[deprecated = "[broken cross-reference](TypeAlias::hoge)"] //~ ERROR +pub struct A; + +#[deprecated(since = "0.0.0", note = "[broken cross-reference](TypeAlias::hoge)")] //~ ERROR +pub struct B1; + +#[deprecated(note = "[broken cross-reference](TypeAlias::hoge)", since = "0.0.0")] //~ ERROR +pub struct B2; diff --git a/tests/rustdoc-ui/intra-doc/deprecated.stderr b/tests/rustdoc-ui/intra-doc/deprecated.stderr new file mode 100644 index 000000000000..9bd64544eef8 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/deprecated.stderr @@ -0,0 +1,43 @@ +error: unresolved link to `TypeAlias::hoge` + --> $DIR/deprecated.rs:3:1 + | +LL | #[deprecated = "[broken cross-reference](TypeAlias::hoge)"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + [broken cross-reference](TypeAlias::hoge) + ^^^^^^^^^^^^^^^ + = note: no item named `TypeAlias` in scope +note: the lint level is defined here + --> $DIR/deprecated.rs:1:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `TypeAlias::hoge` + --> $DIR/deprecated.rs:6:1 + | +LL | #[deprecated(since = "0.0.0", note = "[broken cross-reference](TypeAlias::hoge)")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + [broken cross-reference](TypeAlias::hoge) + ^^^^^^^^^^^^^^^ + = note: no item named `TypeAlias` in scope + +error: unresolved link to `TypeAlias::hoge` + --> $DIR/deprecated.rs:9:1 + | +LL | #[deprecated(note = "[broken cross-reference](TypeAlias::hoge)", since = "0.0.0")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the link appears in this line: + + [broken cross-reference](TypeAlias::hoge) + ^^^^^^^^^^^^^^^ + = note: no item named `TypeAlias` in scope + +error: aborting due to 3 previous errors + diff --git a/tests/ui/abi/issues/issue-22565-rust-call.stderr b/tests/ui/abi/issues/issue-22565-rust-call.stderr index 0fd3285cd3a5..3e296bdaea41 100644 --- a/tests/ui/abi/issues/issue-22565-rust-call.stderr +++ b/tests/ui/abi/issues/issue-22565-rust-call.stderr @@ -2,7 +2,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup --> $DIR/issue-22565-rust-call.rs:3:1 | LL | extern "rust-call" fn b(_i: i32) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32` error: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/issue-22565-rust-call.rs:17:5 @@ -32,7 +32,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup --> $DIR/issue-22565-rust-call.rs:27:7 | LL | b(10); - | ^^ the trait `Tuple` is not implemented for `i32` + | ^^ the trait `std::marker::Tuple` is not implemented for `i32` error: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/issue-22565-rust-call.rs:29:5 diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7027328bc27b..fd6f34fb45e2 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -223,8 +223,8 @@ help: try changing it to one of the following valid forms of the attribute | LL | #[rustc_macro_transparency = "opaque"] | ++++++++++ -LL | #[rustc_macro_transparency = "semitransparent"] - | +++++++++++++++++++ +LL | #[rustc_macro_transparency = "semiopaque"] + | ++++++++++++++ LL | #[rustc_macro_transparency = "transparent"] | +++++++++++++++ diff --git a/tests/ui/binop/can-have-side-effects-consider-operands.rs b/tests/ui/binop/can-have-side-effects-consider-operands.rs new file mode 100644 index 000000000000..34eeb11c6add --- /dev/null +++ b/tests/ui/binop/can-have-side-effects-consider-operands.rs @@ -0,0 +1,23 @@ +//@ check-pass + +#![allow(unused)] + +// Test if `Expr::can_have_side_effects` considers operands of binary operators. + +fn drop_repeat_in_arm_body() { + // Built-in lint `dropping_copy_types` relies on `Expr::can_have_side_effects` + // (See rust-clippy#9482 and rust#113231) + + match () { + () => drop(5 % 3), // No side effects + //~^ WARNING calls to `std::mem::drop` with a value that implements `Copy` does nothing + } + match () { + () => drop(5 % calls_are_considered_side_effects()), // Definitely has side effects + } +} +fn calls_are_considered_side_effects() -> i32 { + 3 +} + +fn main() {} diff --git a/tests/ui/binop/can-have-side-effects-consider-operands.stderr b/tests/ui/binop/can-have-side-effects-consider-operands.stderr new file mode 100644 index 000000000000..ef3fc6b4be7e --- /dev/null +++ b/tests/ui/binop/can-have-side-effects-consider-operands.stderr @@ -0,0 +1,13 @@ +warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/can-have-side-effects-consider-operands.rs:12:15 + | +LL | () => drop(5 % 3), // No side effects + | ^^^^^-----^ + | | + | argument has type `i32` + | + = note: use `let _ = ...` to ignore the expression or result + = note: `#[warn(dropping_copy_types)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/block-result/block-must-not-have-result-while.stderr b/tests/ui/block-result/block-must-not-have-result-while.stderr index 0b9941ea96ef..d8e854c9b9ba 100644 --- a/tests/ui/block-result/block-must-not-have-result-while.stderr +++ b/tests/ui/block-result/block-must-not-have-result-while.stderr @@ -9,12 +9,8 @@ LL | while true { error[E0308]: mismatched types --> $DIR/block-must-not-have-result-while.rs:5:9 | -LL | / while true { -LL | | true - | | ^^^^ expected `()`, found `bool` -LL | | -LL | | } - | |_____- expected this to be `()` +LL | true + | ^^^^ expected `()`, found `bool` error: aborting due to 1 previous error; 1 warning emitted diff --git a/tests/ui/borrowck/issue-92157.rs b/tests/ui/borrowck/issue-92157.rs deleted file mode 100644 index 3dbcb4ad8b7f..000000000000 --- a/tests/ui/borrowck/issue-92157.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ add-minicore -#![feature(no_core)] -#![feature(lang_items)] - -#![no_core] - -#[cfg(target_os = "linux")] -#[link(name = "c")] -extern "C" {} - -#[lang = "start"] -fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { - //~^ ERROR lang item `start` function has wrong type [E0308] - 40+2 -} - -extern crate minicore; -use minicore::*; - -fn main() {} diff --git a/tests/ui/borrowck/issue-92157.stderr b/tests/ui/borrowck/issue-92157.stderr deleted file mode 100644 index 248d700ab4b9..000000000000 --- a/tests/ui/borrowck/issue-92157.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0308]: lang item `start` function has wrong type - --> $DIR/issue-92157.rs:12:1 - | -LL | fn start(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters - | - = note: expected signature `fn(fn() -> T, isize, *const *const u8, u8) -> _` - found signature `fn(fn() -> T, isize, *const *const u8) -> _` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/invalid-attributes-on-const-params-78957.stderr b/tests/ui/const-generics/invalid-attributes-on-const-params-78957.stderr index f8010b4ea687..5fc7e7fef75d 100644 --- a/tests/ui/const-generics/invalid-attributes-on-const-params-78957.stderr +++ b/tests/ui/const-generics/invalid-attributes-on-const-params-78957.stderr @@ -1,4 +1,4 @@ -error: `#[inline]` attribute cannot be used on function params +error: `#[inline]` attribute cannot be used on const parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:6:16 | LL | pub struct Foo<#[inline] const N: usize>; @@ -6,7 +6,7 @@ LL | pub struct Foo<#[inline] const N: usize>; | = help: `#[inline]` can only be applied to functions -error: `#[inline]` attribute cannot be used on function params +error: `#[inline]` attribute cannot be used on lifetime parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:14:17 | LL | pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); @@ -14,7 +14,7 @@ LL | pub struct Foo2<#[inline] 'a>(PhantomData<&'a ()>); | = help: `#[inline]` can only be applied to functions -error: `#[inline]` attribute cannot be used on function params +error: `#[inline]` attribute cannot be used on type parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:22:17 | LL | pub struct Foo3<#[inline] T>(PhantomData); @@ -40,7 +40,7 @@ error[E0517]: attribute should be applied to a struct, enum, or union LL | pub struct Baz3<#[repr(C)] T>(PhantomData); | ^ - not a struct, enum, or union -error: `#[cold]` attribute cannot be used on function params +error: `#[cold]` attribute cannot be used on const parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:8:16 | LL | pub struct Bar<#[cold] const N: usize>; @@ -54,7 +54,7 @@ note: the lint level is defined here LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ -error: `#[cold]` attribute cannot be used on function params +error: `#[cold]` attribute cannot be used on lifetime parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:16:17 | LL | pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); @@ -63,7 +63,7 @@ LL | pub struct Bar2<#[cold] 'a>(PhantomData<&'a ()>); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[cold]` can only be applied to functions -error: `#[cold]` attribute cannot be used on function params +error: `#[cold]` attribute cannot be used on type parameters --> $DIR/invalid-attributes-on-const-params-78957.rs:24:17 | LL | pub struct Bar3<#[cold] T>(PhantomData); diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs index ffb763da325c..16479ba3f64f 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs @@ -12,7 +12,7 @@ fn foo>() {} trait Trait { #[type_const] - const ASSOC: usize; + const ASSOC: u32; } fn bar() { diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs new file mode 100644 index 000000000000..5f0d6c924bd1 --- /dev/null +++ b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.rs @@ -0,0 +1,26 @@ +#![feature(min_generic_const_args, adt_const_params, unsized_const_params)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const ASSOC: usize; +} + +fn takes_tuple() {} +fn takes_nested_tuple() {} + +fn generic_caller() { + takes_tuple::<{ (N, N2) }>(); + //~^ ERROR the constant `N` is not of type `u32` + takes_tuple::<{ (N, T::ASSOC) }>(); + //~^ ERROR the constant `N` is not of type `u32` + //~| ERROR the constant `::ASSOC` is not of type `u32` + + takes_nested_tuple::<{ (N, (N, N2)) }>(); + //~^ ERROR the constant `N` is not of type `u32` + takes_nested_tuple::<{ (N, (N, T::ASSOC)) }>(); + //~^ ERROR the constant `N` is not of type `u32` + //~| ERROR the constant `::ASSOC` is not of type `u32` +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.stderr b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.stderr new file mode 100644 index 000000000000..9d80ebeaa680 --- /dev/null +++ b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_fail.stderr @@ -0,0 +1,38 @@ +error: the constant `N` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:13:21 + | +LL | takes_tuple::<{ (N, N2) }>(); + | ^^^^^^^ expected `u32`, found `usize` + +error: the constant `N` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:15:21 + | +LL | takes_tuple::<{ (N, T::ASSOC) }>(); + | ^^^^^^^^^^^^^ expected `u32`, found `usize` + +error: the constant `::ASSOC` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:15:21 + | +LL | takes_tuple::<{ (N, T::ASSOC) }>(); + | ^^^^^^^^^^^^^ expected `u32`, found `usize` + +error: the constant `N` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:19:28 + | +LL | takes_nested_tuple::<{ (N, (N, N2)) }>(); + | ^^^^^^^^^^^^ expected `u32`, found `usize` + +error: the constant `N` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:21:28 + | +LL | takes_nested_tuple::<{ (N, (N, T::ASSOC)) }>(); + | ^^^^^^^^^^^^^^^^^^ expected `u32`, found `usize` + +error: the constant `::ASSOC` is not of type `u32` + --> $DIR/adt_expr_arg_tuple_expr_fail.rs:21:28 + | +LL | takes_nested_tuple::<{ (N, (N, T::ASSOC)) }>(); + | ^^^^^^^^^^^^^^^^^^ expected `u32`, found `usize` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_simple.rs b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_simple.rs index 60c4c6e952cf..3fde431e27e2 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_simple.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_tuple_expr_simple.rs @@ -5,7 +5,7 @@ trait Trait { #[type_const] - const ASSOC: usize; + const ASSOC: u32; } fn takes_tuple() {} diff --git a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs index eafc8382966b..496a424bac65 100644 --- a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs +++ b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.rs @@ -1,17 +1,38 @@ -//@ check-pass -// FIXME(mgca): This should error - #![feature(min_generic_const_args, adt_const_params)] #![expect(incomplete_features)] #[derive(Eq, PartialEq, std::marker::ConstParamTy)] -struct Foo { field: T } +struct S1 { + f1: T, + f2: isize, +} -fn accepts>() {} +#[derive(Eq, PartialEq, std::marker::ConstParamTy)] +struct S2(T, isize); + +#[derive(Eq, PartialEq, std::marker::ConstParamTy)] +enum En { + Var1(bool, T), + Var2 { field: i64 }, +} + +fn accepts_1>() {} +fn accepts_2>() {} +fn accepts_3>() {} fn bar() { - // `N` is not of type `u8` but we don't actually check this anywhere yet - accepts::<{ Foo:: { field: N }}>(); + accepts_1::<{ S1:: { f1: N, f2: N } }>(); + //~^ ERROR the constant `N` is not of type `u8` + //~| ERROR the constant `N` is not of type `isize` + accepts_2::<{ S2::(N, N) }>(); + //~^ ERROR the constant `N` is not of type `u8` + //~| ERROR the constant `N` is not of type `isize` + accepts_3::<{ En::Var1::(N, N) }>(); + //~^ ERROR the constant `N` is not of type `u8` + accepts_3::<{ En::Var2:: { field: N } }>(); + //~^ ERROR the constant `N` is not of type `i64` + accepts_3::<{ En::Var2:: { field: const { false } } }>(); + //~^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr new file mode 100644 index 000000000000..81f7c5366b46 --- /dev/null +++ b/tests/ui/const-generics/mgca/adt_expr_fields_type_check.stderr @@ -0,0 +1,45 @@ +error: the constant `N` is not of type `u8` + --> $DIR/adt_expr_fields_type_check.rs:24:19 + | +LL | accepts_1::<{ S1:: { f1: N, f2: N } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `bool` + +error: the constant `N` is not of type `isize` + --> $DIR/adt_expr_fields_type_check.rs:24:19 + | +LL | accepts_1::<{ S1:: { f1: N, f2: N } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `isize`, found `bool` + +error: the constant `N` is not of type `u8` + --> $DIR/adt_expr_fields_type_check.rs:27:19 + | +LL | accepts_2::<{ S2::(N, N) }>(); + | ^^^^^^^^^^^^^^ expected `u8`, found `bool` + +error: the constant `N` is not of type `isize` + --> $DIR/adt_expr_fields_type_check.rs:27:19 + | +LL | accepts_2::<{ S2::(N, N) }>(); + | ^^^^^^^^^^^^^^ expected `isize`, found `bool` + +error: the constant `N` is not of type `u8` + --> $DIR/adt_expr_fields_type_check.rs:30:19 + | +LL | accepts_3::<{ En::Var1::(N, N) }>(); + | ^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `bool` + +error: the constant `N` is not of type `i64` + --> $DIR/adt_expr_fields_type_check.rs:32:19 + | +LL | accepts_3::<{ En::Var2:: { field: N } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i64`, found `bool` + +error[E0308]: mismatched types + --> $DIR/adt_expr_fields_type_check.rs:34:51 + | +LL | accepts_3::<{ En::Var2:: { field: const { false } } }>(); + | ^^^^^ expected `i64`, found `bool` + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs b/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs index eb8f04b8b24b..8131a5b72343 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs +++ b/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.rs @@ -1,8 +1,4 @@ -// We allow for literals to implicitly be anon consts still regardless -// of whether a const block is placed around them or not -// -// However, we don't allow so for const arguments in field init positions. -// This is just harder to implement so we did not do so. +//@ check-pass #![feature(min_generic_const_args, adt_const_params)] #![expect(incomplete_features)] @@ -27,7 +23,7 @@ fn struct_expr() { fn takes_n() {} takes_n::<{ ADT { field: 1 } }>(); - //~^ ERROR: complex const arguments must be placed inside of a `const` block + takes_n::<{ ADT { field: const { 1 } } }>(); } diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.stderr b/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.stderr deleted file mode 100644 index 02647fd808cc..000000000000 --- a/tests/ui/const-generics/mgca/explicit_anon_consts_literals_hack.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts_literals_hack.rs:29:30 - | -LL | takes_n::<{ ADT { field: 1 } }>(); - | ^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs index 484d6f14a82a..819323d9cbec 100644 --- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs +++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.rs @@ -9,7 +9,7 @@ struct Foo; trait Trait { #[type_const] - const ASSOC: usize; + const ASSOC: u32; } fn foo() {} @@ -27,7 +27,7 @@ fn baz() { fn main() {} fn test_ice_missing_bound() { - foo::<{Option::Some::{0: ::ASSOC}}>(); + foo::<{ Option::Some:: { 0: ::ASSOC } }>(); //~^ ERROR the trait bound `T: Trait` is not satisfied //~| ERROR the constant `Option::::Some(_)` is not of type `Foo` } diff --git a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr index bd2162468944..0184aebf157a 100644 --- a/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr +++ b/tests/ui/const-generics/mgca/printing_valtrees_supports_non_values.stderr @@ -25,8 +25,8 @@ LL | fn foo() {} error[E0277]: the trait bound `T: Trait` is not satisfied --> $DIR/printing_valtrees_supports_non_values.rs:30:5 | -LL | foo::<{Option::Some::{0: ::ASSOC}}>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` +LL | foo::<{ Option::Some:: { 0: ::ASSOC } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `T` | help: consider restricting type parameter `T` with trait `Trait` | @@ -34,10 +34,10 @@ LL | fn test_ice_missing_bound() { | +++++++ error: the constant `Option::::Some(_)` is not of type `Foo` - --> $DIR/printing_valtrees_supports_non_values.rs:30:12 + --> $DIR/printing_valtrees_supports_non_values.rs:30:13 | -LL | foo::<{Option::Some::{0: ::ASSOC}}>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` +LL | foo::<{ Option::Some:: { 0: ::ASSOC } }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Foo`, found `Option` | note: required by a const generic parameter in `foo` --> $DIR/printing_valtrees_supports_non_values.rs:15:8 diff --git a/tests/ui/const-generics/mgca/type_const-array-return.rs b/tests/ui/const-generics/mgca/type_const-array-return.rs new file mode 100644 index 000000000000..5375e4fded6d --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-array-return.rs @@ -0,0 +1,26 @@ +//@ check-pass +// This test should compile without an ICE. +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +pub struct A; + +pub trait Array { + #[type_const] + const LEN: usize; + fn arr() -> [u8; Self::LEN]; +} + +impl Array for A { + #[type_const] + const LEN: usize = 4; + + #[allow(unused_braces)] + fn arr() -> [u8; const { Self::LEN }] { + return [0u8; const { Self::LEN }]; + } +} + +fn main() { + let _ = A::arr(); +} diff --git a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs new file mode 100644 index 000000000000..b2c734098009 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.rs @@ -0,0 +1,12 @@ +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +struct A; + +impl A { + #[type_const] + const B = 4; + //~^ ERROR: missing type for `const` item +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr new file mode 100644 index 000000000000..b44e47cd7e61 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-inherent-const-omitted-type.stderr @@ -0,0 +1,13 @@ +error: missing type for `const` item + --> $DIR/type_const-inherent-const-omitted-type.rs:8:12 + | +LL | const B = 4; + | ^ + | +help: provide a type for the item + | +LL | const B: = 4; + | ++++++++ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs new file mode 100644 index 000000000000..ab613859aa5c --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.rs @@ -0,0 +1,22 @@ +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +trait BadTr { + const NUM: usize; +} + +struct GoodS; + +impl BadTr for GoodS { + #[type_const] + const NUM: = 84; + //~^ ERROR: missing type for `const` item + +} + +fn accept_bad_tr>(_x: &T) {} +//~^ ERROR use of trait associated const without `#[type_const]` + +fn main() { + accept_bad_tr::<84, _>(&GoodS); +} diff --git a/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr new file mode 100644 index 000000000000..16f312454ed2 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-only-in-impl-omitted-type.stderr @@ -0,0 +1,16 @@ +error: missing type for `const` item + --> $DIR/type_const-only-in-impl-omitted-type.rs:12:15 + | +LL | const NUM: = 84; + | ^ help: provide a type for the associated constant: `usize` + +error: use of trait associated const without `#[type_const]` + --> $DIR/type_const-only-in-impl-omitted-type.rs:17:43 + | +LL | fn accept_bad_tr>(_x: &T) {} + | ^^^^^^^^^^^ + | + = note: the declaration in the trait must be marked with `#[type_const]` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/type_const-recursive.rs b/tests/ui/const-generics/mgca/type_const-recursive.rs new file mode 100644 index 000000000000..15e49f747ed6 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-recursive.rs @@ -0,0 +1,8 @@ +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +#[type_const] +const A: u8 = A; +//~^ ERROR: overflow normalizing the unevaluated constant `A` [E0275] + +fn main() {} diff --git a/tests/ui/const-generics/mgca/type_const-recursive.stderr b/tests/ui/const-generics/mgca/type_const-recursive.stderr new file mode 100644 index 000000000000..947319ec7eda --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-recursive.stderr @@ -0,0 +1,11 @@ +error[E0275]: overflow normalizing the unevaluated constant `A` + --> $DIR/type_const-recursive.rs:5:1 + | +LL | const A: u8 = A; + | ^^^^^^^^^^^ + | + = note: in case this is a recursive type alias, consider using a struct, enum, or union instead + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/const-generics/mgca/type_const-use.rs b/tests/ui/const-generics/mgca/type_const-use.rs new file mode 100644 index 000000000000..04362cd28538 --- /dev/null +++ b/tests/ui/const-generics/mgca/type_const-use.rs @@ -0,0 +1,13 @@ +//@ check-pass +// This test should compile without an ICE. +#![expect(incomplete_features)] +#![feature(min_generic_const_args)] + +#[type_const] +const CONST: usize = 1; + +fn uses_const() { + CONST; +} + +fn main() {} diff --git a/tests/ui/eii/auxiliary/codegen1.rs b/tests/ui/eii/auxiliary/other_crate_privacy1.rs similarity index 100% rename from tests/ui/eii/auxiliary/codegen1.rs rename to tests/ui/eii/auxiliary/other_crate_privacy1.rs diff --git a/tests/ui/eii/auxiliary/codegen3.rs b/tests/ui/eii/auxiliary/other_crate_privacy2.rs similarity index 100% rename from tests/ui/eii/auxiliary/codegen3.rs rename to tests/ui/eii/auxiliary/other_crate_privacy2.rs diff --git a/tests/ui/eii/duplicate/eii_conflict_with_macro.rs b/tests/ui/eii/duplicate/eii_conflict_with_macro.rs new file mode 100644 index 000000000000..d8118f8417a0 --- /dev/null +++ b/tests/ui/eii/duplicate/eii_conflict_with_macro.rs @@ -0,0 +1,7 @@ +//@ compile-flags: --crate-type rlib +//@ build-pass +#![feature(extern_item_impls)] + +macro_rules! foo_impl { () => {} } +#[eii] +fn foo_impl() {} diff --git a/tests/ui/eii/multiple_decls.rs b/tests/ui/eii/duplicate/multiple_decls.rs similarity index 100% rename from tests/ui/eii/multiple_decls.rs rename to tests/ui/eii/duplicate/multiple_decls.rs diff --git a/tests/ui/eii/multiple_decls.stderr b/tests/ui/eii/duplicate/multiple_decls.stderr similarity index 100% rename from tests/ui/eii/multiple_decls.stderr rename to tests/ui/eii/duplicate/multiple_decls.stderr diff --git a/tests/ui/eii/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs similarity index 100% rename from tests/ui/eii/multiple_impls.rs rename to tests/ui/eii/duplicate/multiple_impls.rs diff --git a/tests/ui/eii/multiple_impls.run.stdout b/tests/ui/eii/duplicate/multiple_impls.run.stdout similarity index 100% rename from tests/ui/eii/multiple_impls.run.stdout rename to tests/ui/eii/duplicate/multiple_impls.run.stdout diff --git a/tests/ui/eii/auxiliary/codegen2.rs b/tests/ui/eii/linking/auxiliary/codegen_cross_crate_other_crate.rs similarity index 100% rename from tests/ui/eii/auxiliary/codegen2.rs rename to tests/ui/eii/linking/auxiliary/codegen_cross_crate_other_crate.rs diff --git a/tests/ui/eii/codegen_cross_crate.rs b/tests/ui/eii/linking/codegen_cross_crate.rs similarity index 82% rename from tests/ui/eii/codegen_cross_crate.rs rename to tests/ui/eii/linking/codegen_cross_crate.rs index a1fa617491bd..4016712e7504 100644 --- a/tests/ui/eii/codegen_cross_crate.rs +++ b/tests/ui/eii/linking/codegen_cross_crate.rs @@ -1,6 +1,6 @@ //@ run-pass //@ check-run-results -//@ aux-build: codegen2.rs +//@ aux-build: codegen_cross_crate_other_crate.rs //@ compile-flags: -O //@ ignore-backends: gcc // FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 @@ -8,7 +8,7 @@ // Tests whether calling EIIs works with the declaration in another crate. #![feature(extern_item_impls)] -extern crate codegen2 as codegen; +extern crate codegen_cross_crate_other_crate as codegen; #[codegen::eii1] fn eii1_impl(x: u64) { diff --git a/tests/ui/eii/codegen_cross_crate.run.stdout b/tests/ui/eii/linking/codegen_cross_crate.run.stdout similarity index 100% rename from tests/ui/eii/codegen_cross_crate.run.stdout rename to tests/ui/eii/linking/codegen_cross_crate.run.stdout diff --git a/tests/ui/eii/codegen_single_crate.rs b/tests/ui/eii/linking/codegen_single_crate.rs similarity index 100% rename from tests/ui/eii/codegen_single_crate.rs rename to tests/ui/eii/linking/codegen_single_crate.rs diff --git a/tests/ui/eii/codegen_single_crate.run.stdout b/tests/ui/eii/linking/codegen_single_crate.run.stdout similarity index 100% rename from tests/ui/eii/codegen_single_crate.run.stdout rename to tests/ui/eii/linking/codegen_single_crate.run.stdout diff --git a/tests/ui/eii/same-symbol.rs b/tests/ui/eii/linking/same-symbol.rs similarity index 100% rename from tests/ui/eii/same-symbol.rs rename to tests/ui/eii/linking/same-symbol.rs diff --git a/tests/ui/eii/same-symbol.run.stdout b/tests/ui/eii/linking/same-symbol.run.stdout similarity index 100% rename from tests/ui/eii/same-symbol.run.stdout rename to tests/ui/eii/linking/same-symbol.run.stdout diff --git a/tests/ui/eii/privacy1.rs b/tests/ui/eii/privacy1.rs index b5bbf68bfdaf..72aec83d2cee 100644 --- a/tests/ui/eii/privacy1.rs +++ b/tests/ui/eii/privacy1.rs @@ -1,13 +1,13 @@ //@ run-pass //@ check-run-results -//@ aux-build: codegen1.rs +//@ aux-build: other_crate_privacy1.rs //@ ignore-backends: gcc -// FIXME: linking on windows (speciifcally mingw) not yet supported, see tracking issue #125418 +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 //@ ignore-windows // Tests whether re-exports work. #![feature(extern_item_impls)] -extern crate codegen1 as codegen; +extern crate other_crate_privacy1 as codegen; #[codegen::eii1] fn eii1_impl(x: u64) { diff --git a/tests/ui/eii/privacy2.rs b/tests/ui/eii/privacy2.rs index a4ae188bd0ec..e3f1f8c863da 100644 --- a/tests/ui/eii/privacy2.rs +++ b/tests/ui/eii/privacy2.rs @@ -1,8 +1,8 @@ -//@ aux-build:codegen3.rs -// Tests whether name resulution respects privacy properly. +//@ aux-build:other_crate_privacy2.rs +// Tests whether name resolution respects privacy properly. #![feature(extern_item_impls)] -extern crate codegen3 as codegen; +extern crate other_crate_privacy2 as codegen; // has a span but in the other crate //~? ERROR `#[eii2]` required, but not found diff --git a/tests/ui/eii/privacy2.stderr b/tests/ui/eii/privacy2.stderr index 0d44604567e4..9f4fd6a071c9 100644 --- a/tests/ui/eii/privacy2.stderr +++ b/tests/ui/eii/privacy2.stderr @@ -11,24 +11,24 @@ LL | codegen::decl1(42); | ^^^^^ private function | note: the function `decl1` is defined here - --> $DIR/auxiliary/codegen3.rs:7:1 + --> $DIR/auxiliary/other_crate_privacy2.rs:7:1 | LL | fn decl1(x: u64); | ^^^^^^^^^^^^^^^^^ error: `#[eii2]` required, but not found - --> $DIR/auxiliary/codegen3.rs:9:1 + --> $DIR/auxiliary/other_crate_privacy2.rs:9:7 | LL | #[eii(eii2)] - | ^^^^^^^^^^^^ expected because `#[eii2]` was declared here in crate `codegen3` + | ^^^^ expected because `#[eii2]` was declared here in crate `other_crate_privacy2` | = help: expected at least one implementation in crate `privacy2` or any of its dependencies error: `#[eii3]` required, but not found - --> $DIR/auxiliary/codegen3.rs:13:5 + --> $DIR/auxiliary/other_crate_privacy2.rs:13:11 | LL | #[eii(eii3)] - | ^^^^^^^^^^^^ expected because `#[eii3]` was declared here in crate `codegen3` + | ^^^^ expected because `#[eii3]` was declared here in crate `other_crate_privacy2` | = help: expected at least one implementation in crate `privacy2` or any of its dependencies diff --git a/tests/ui/eii/auxiliary/cross_crate_eii_declaration.rs b/tests/ui/eii/type_checking/auxiliary/cross_crate_eii_declaration.rs similarity index 100% rename from tests/ui/eii/auxiliary/cross_crate_eii_declaration.rs rename to tests/ui/eii/type_checking/auxiliary/cross_crate_eii_declaration.rs diff --git a/tests/ui/eii/cross_crate.rs b/tests/ui/eii/type_checking/cross_crate_type_ok.rs similarity index 100% rename from tests/ui/eii/cross_crate.rs rename to tests/ui/eii/type_checking/cross_crate_type_ok.rs diff --git a/tests/ui/eii/cross_crate_wrong_ty.rs b/tests/ui/eii/type_checking/cross_crate_wrong_ty.rs similarity index 100% rename from tests/ui/eii/cross_crate_wrong_ty.rs rename to tests/ui/eii/type_checking/cross_crate_wrong_ty.rs diff --git a/tests/ui/eii/cross_crate_wrong_ty.stderr b/tests/ui/eii/type_checking/cross_crate_wrong_ty.stderr similarity index 100% rename from tests/ui/eii/cross_crate_wrong_ty.stderr rename to tests/ui/eii/type_checking/cross_crate_wrong_ty.stderr diff --git a/tests/ui/error-codes/E0059.stderr b/tests/ui/error-codes/E0059.stderr index d26fadcdbfa7..698ee0a2a902 100644 --- a/tests/ui/error-codes/E0059.stderr +++ b/tests/ui/error-codes/E0059.stderr @@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple --> $DIR/E0059.rs:3:11 | LL | fn foo>(f: F) -> F::Output { f(3) } - | ^^^^^^^ the trait `Tuple` is not implemented for `i32` + | ^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32` | note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -11,7 +11,7 @@ error[E0277]: `i32` is not a tuple --> $DIR/E0059.rs:3:41 | LL | fn foo>(f: F) -> F::Output { f(3) } - | ^^^^ the trait `Tuple` is not implemented for `i32` + | ^^^^ the trait `std::marker::Tuple` is not implemented for `i32` error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit --> $DIR/E0059.rs:3:41 diff --git a/tests/ui/feature-gates/feature-gate-multiple_supertrait_upcastable.stderr b/tests/ui/feature-gates/feature-gate-multiple_supertrait_upcastable.stderr index 0c7e68a599cd..0fd987e40289 100644 --- a/tests/ui/feature-gates/feature-gate-multiple_supertrait_upcastable.stderr +++ b/tests/ui/feature-gates/feature-gate-multiple_supertrait_upcastable.stderr @@ -5,6 +5,7 @@ LL | #![deny(multiple_supertrait_upcastable)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the `multiple_supertrait_upcastable` lint is unstable + = note: see issue #150833 for more information = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: `#[warn(unknown_lints)]` on by default @@ -16,6 +17,7 @@ LL | #![warn(multiple_supertrait_upcastable)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the `multiple_supertrait_upcastable` lint is unstable + = note: see issue #150833 for more information = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date diff --git a/tests/ui/fmt/format-string-error-2.rs b/tests/ui/fmt/format-string-error-2.rs index 357dd7b10a3e..e14a4064aa83 100644 --- a/tests/ui/fmt/format-string-error-2.rs +++ b/tests/ui/fmt/format-string-error-2.rs @@ -83,4 +83,7 @@ raw { \n println!(r#"\x7B}\u8 {"#, 1); //~^ ERROR invalid format string: unmatched `}` found + + println!("{x?}, world!",); + //~^ ERROR invalid format string: expected `}`, found `?` } diff --git a/tests/ui/fmt/format-string-error-2.stderr b/tests/ui/fmt/format-string-error-2.stderr index 6d8c34fdb709..b117fb57cf07 100644 --- a/tests/ui/fmt/format-string-error-2.stderr +++ b/tests/ui/fmt/format-string-error-2.stderr @@ -177,5 +177,16 @@ LL | println!(r#"\x7B}\u8 {"#, 1); | = note: if you intended to print `}`, you can escape it using `}}` -error: aborting due to 18 previous errors +error: invalid format string: expected `}`, found `?` + --> $DIR/format-string-error-2.rs:87:17 + | +LL | println!("{x?}, world!",); + | ^ + | | + | expected `:` before `?` to format with `Debug` in format string + | help: add a colon before the format specifier: `:?` + | + = note: to print `{`, you can escape it using `{{` + +error: aborting due to 19 previous errors diff --git a/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.rs b/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.rs new file mode 100644 index 000000000000..69b57486138d --- /dev/null +++ b/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.rs @@ -0,0 +1,39 @@ +//! Don't suggest breaking with value from `for` or `while` loops +//! +//! Regression test for https://github.com/rust-lang/rust/issues/150850 + +fn returns_i32() -> i32 { 0 } + +fn suggest_breaking_from_loop() { + let _ = loop { + returns_i32() //~ ERROR mismatched types + //~^ SUGGESTION ; + //~| SUGGESTION break + }; +} + +fn dont_suggest_breaking_from_for() { + let _ = for _ in 0.. { + returns_i32() //~ ERROR mismatched types + //~^ SUGGESTION ; + }; +} + +fn dont_suggest_breaking_from_while() { + let cond = true; + let _ = while cond { + returns_i32() //~ ERROR mismatched types + //~^ SUGGESTION ; + }; +} + +fn dont_suggest_breaking_from_for_nested_in_loop() { + let _ = loop { + for _ in 0.. { + returns_i32() //~ ERROR mismatched types + //~^ SUGGESTION ; + } + }; +} + +fn main() {} diff --git a/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.stderr b/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.stderr new file mode 100644 index 000000000000..880cc53f9a7c --- /dev/null +++ b/tests/ui/for-loop-while/dont-suggest-break-from-unbreakable-loops.stderr @@ -0,0 +1,42 @@ +error[E0308]: mismatched types + --> $DIR/dont-suggest-break-from-unbreakable-loops.rs:9:9 + | +LL | returns_i32() + | ^^^^^^^^^^^^^ expected `()`, found `i32` + | +help: consider using a semicolon here + | +LL | returns_i32(); + | + +help: you might have meant to break the loop with this value + | +LL | break returns_i32(); + | +++++ + + +error[E0308]: mismatched types + --> $DIR/dont-suggest-break-from-unbreakable-loops.rs:17:9 + | +LL | returns_i32() + | ^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `i32` + +error[E0308]: mismatched types + --> $DIR/dont-suggest-break-from-unbreakable-loops.rs:25:9 + | +LL | returns_i32() + | ^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `i32` + +error[E0308]: mismatched types + --> $DIR/dont-suggest-break-from-unbreakable-loops.rs:33:13 + | +LL | returns_i32() + | ^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | expected `()`, found `i32` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr index df3b646b6cec..ce4f1d77ad2d 100644 --- a/tests/ui/force-inlining/invalid.stderr +++ b/tests/ui/force-inlining/invalid.stderr @@ -152,7 +152,7 @@ LL | #[rustc_force_inline] | = help: `#[rustc_force_inline]` can only be applied to functions -error: `#[rustc_force_inline]` attribute cannot be used on function params +error: `#[rustc_force_inline]` attribute cannot be used on type parameters --> $DIR/invalid.rs:72:10 | LL | enum Bar<#[rustc_force_inline] T> { diff --git a/tests/ui/generics/wrong-number-of-args.rs b/tests/ui/generics/wrong-number-of-args.rs index 6524bd538b6b..8bc384a3d817 100644 --- a/tests/ui/generics/wrong-number-of-args.rs +++ b/tests/ui/generics/wrong-number-of-args.rs @@ -321,10 +321,6 @@ mod stdlib { //~| ERROR struct takes at least 2 //~| HELP add missing - type D = HashMap; - //~^ ERROR struct takes at most 3 - //~| HELP remove the - type E = HashMap<>; //~^ ERROR struct takes at least 2 generic arguments but 0 generic arguments //~| HELP add missing diff --git a/tests/ui/generics/wrong-number-of-args.stderr b/tests/ui/generics/wrong-number-of-args.stderr index bac0d26b622d..bedeeb812fc9 100644 --- a/tests/ui/generics/wrong-number-of-args.stderr +++ b/tests/ui/generics/wrong-number-of-args.stderr @@ -926,16 +926,8 @@ help: add missing generic arguments LL | type C = HashMap<'static, K, V>; | ++++++ -error[E0107]: struct takes at most 3 generic arguments but 4 generic arguments were supplied - --> $DIR/wrong-number-of-args.rs:324:18 - | -LL | type D = HashMap; - | ^^^^^^^ ----- help: remove the unnecessary generic argument - | | - | expected at most 3 generic arguments - error[E0107]: struct takes at least 2 generic arguments but 0 generic arguments were supplied - --> $DIR/wrong-number-of-args.rs:328:18 + --> $DIR/wrong-number-of-args.rs:324:18 | LL | type E = HashMap<>; | ^^^^^^^ expected at least 2 generic arguments @@ -946,7 +938,7 @@ LL | type E = HashMap; | ++++ error[E0107]: missing generics for enum `Result` - --> $DIR/wrong-number-of-args.rs:334:18 + --> $DIR/wrong-number-of-args.rs:330:18 | LL | type A = Result; | ^^^^^^ expected 2 generic arguments @@ -957,7 +949,7 @@ LL | type A = Result; | ++++++ error[E0107]: enum takes 2 generic arguments but 1 generic argument was supplied - --> $DIR/wrong-number-of-args.rs:338:18 + --> $DIR/wrong-number-of-args.rs:334:18 | LL | type B = Result; | ^^^^^^ ------ supplied 1 generic argument @@ -970,7 +962,7 @@ LL | type B = Result; | +++ error[E0107]: enum takes 0 lifetime arguments but 1 lifetime argument was supplied - --> $DIR/wrong-number-of-args.rs:342:18 + --> $DIR/wrong-number-of-args.rs:338:18 | LL | type C = Result<'static>; | ^^^^^^--------- help: remove the unnecessary generics @@ -978,7 +970,7 @@ LL | type C = Result<'static>; | expected 0 lifetime arguments error[E0107]: enum takes 2 generic arguments but 0 generic arguments were supplied - --> $DIR/wrong-number-of-args.rs:342:18 + --> $DIR/wrong-number-of-args.rs:338:18 | LL | type C = Result<'static>; | ^^^^^^ expected 2 generic arguments @@ -989,7 +981,7 @@ LL | type C = Result<'static, T, E>; | ++++++ error[E0107]: enum takes 2 generic arguments but 3 generic arguments were supplied - --> $DIR/wrong-number-of-args.rs:348:18 + --> $DIR/wrong-number-of-args.rs:344:18 | LL | type D = Result; | ^^^^^^ ------ help: remove the unnecessary generic argument @@ -997,7 +989,7 @@ LL | type D = Result; | expected 2 generic arguments error[E0107]: enum takes 2 generic arguments but 0 generic arguments were supplied - --> $DIR/wrong-number-of-args.rs:352:18 + --> $DIR/wrong-number-of-args.rs:348:18 | LL | type E = Result<>; | ^^^^^^ expected 2 generic arguments @@ -1007,7 +999,7 @@ help: add missing generic arguments LL | type E = Result; | ++++ -error: aborting due to 71 previous errors +error: aborting due to 70 previous errors Some errors have detailed explanations: E0106, E0107. For more information about an error, try `rustc --explain E0106`. diff --git a/tests/ui/hygiene/duplicate_lifetimes.rs b/tests/ui/hygiene/duplicate_lifetimes.rs index 8971fb62626c..4d128da7fdbf 100644 --- a/tests/ui/hygiene/duplicate_lifetimes.rs +++ b/tests/ui/hygiene/duplicate_lifetimes.rs @@ -3,7 +3,7 @@ #![feature(decl_macro, rustc_attrs)] -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] macro m($a:lifetime) { fn g<$a, 'a>() {} //~ ERROR the name `'a` is already used for a generic parameter } diff --git a/tests/ui/hygiene/generic_params.rs b/tests/ui/hygiene/generic_params.rs index def9be3a1b64..bb0de15d9a0a 100644 --- a/tests/ui/hygiene/generic_params.rs +++ b/tests/ui/hygiene/generic_params.rs @@ -11,7 +11,7 @@ mod type_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($T:ident) { fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) { (t1.clone(), t2.clone()) @@ -43,7 +43,7 @@ mod lifetime_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($a:lifetime) { fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { (t1, t2) @@ -75,7 +75,7 @@ mod const_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($C:ident) { fn g(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { (t1, t2) diff --git a/tests/ui/hygiene/rustc-macro-transparency.rs b/tests/ui/hygiene/rustc-macro-transparency.rs index 1a78a7543cfd..0c680abb4153 100644 --- a/tests/ui/hygiene/rustc-macro-transparency.rs +++ b/tests/ui/hygiene/rustc-macro-transparency.rs @@ -5,7 +5,7 @@ macro transparent() { struct Transparent; let transparent = 0; } -#[rustc_macro_transparency = "semitransparent"] +#[rustc_macro_transparency = "semiopaque"] macro semiopaque() { struct SemiOpaque; let semiopaque = 0; diff --git a/tests/ui/impl-trait/where-allowed.stderr b/tests/ui/impl-trait/where-allowed.stderr index 6cab4fabb1ff..1a8d6509e137 100644 --- a/tests/ui/impl-trait/where-allowed.stderr +++ b/tests/ui/impl-trait/where-allowed.stderr @@ -384,11 +384,11 @@ LL | fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { pani | = note: multiple `impl`s satisfying `_: Fn()` found in the following crates: `alloc`, `core`: - impl Fn for &F - where A: Tuple, F: Fn, F: ?Sized; + where A: std::marker::Tuple, F: Fn, F: ?Sized; - impl Fn for Box - where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; + where Args: std::marker::Tuple, F: Fn, A: Allocator, F: ?Sized; - impl Fn for Exclusive - where F: Sync, F: Fn, Args: Tuple; + where F: Sync, F: Fn, Args: std::marker::Tuple; error[E0118]: no nominal type found for inherent implementation --> $DIR/where-allowed.rs:241:1 diff --git a/tests/ui/imports/ambiguous-14.stderr b/tests/ui/imports/ambiguous-14.stderr index 4efa31c61e32..2a3557c31f12 100644 --- a/tests/ui/imports/ambiguous-14.stderr +++ b/tests/ui/imports/ambiguous-14.stderr @@ -8,15 +8,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/ambiguous-14.rs:13:13 + --> $DIR/ambiguous-14.rs:18:13 | LL | pub use a::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/ambiguous-14.rs:14:13 + --> $DIR/ambiguous-14.rs:19:13 | -LL | pub use b::*; +LL | pub use f::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default @@ -34,15 +34,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/ambiguous-14.rs:13:13 + --> $DIR/ambiguous-14.rs:18:13 | LL | pub use a::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/ambiguous-14.rs:14:13 + --> $DIR/ambiguous-14.rs:19:13 | -LL | pub use b::*; +LL | pub use f::*; | ^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default diff --git a/tests/ui/imports/duplicate.stderr b/tests/ui/imports/duplicate.stderr index 5cd3b0c2c8a5..74829fc21e22 100644 --- a/tests/ui/imports/duplicate.stderr +++ b/tests/ui/imports/duplicate.stderr @@ -78,15 +78,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/duplicate.rs:24:13 + --> $DIR/duplicate.rs:29:13 | LL | pub use crate::a::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/duplicate.rs:25:13 + --> $DIR/duplicate.rs:30:13 | -LL | pub use crate::b::*; +LL | pub use crate::f::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default @@ -106,15 +106,15 @@ LL | g::foo(); = note: for more information, see issue #114095 = note: ambiguous because of multiple glob imports of a name in the same module note: `foo` could refer to the function imported here - --> $DIR/duplicate.rs:24:13 + --> $DIR/duplicate.rs:29:13 | LL | pub use crate::a::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate note: `foo` could also refer to the function imported here - --> $DIR/duplicate.rs:25:13 + --> $DIR/duplicate.rs:30:13 | -LL | pub use crate::b::*; +LL | pub use crate::f::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `foo` to disambiguate = note: `#[deny(ambiguous_glob_imports)]` (part of `#[deny(future_incompatible)]`) on by default diff --git a/tests/ui/imports/issue-55884-1.rs b/tests/ui/imports/issue-55884-1.rs index 21744aa5d7bf..3c9c033c158e 100644 --- a/tests/ui/imports/issue-55884-1.rs +++ b/tests/ui/imports/issue-55884-1.rs @@ -17,5 +17,5 @@ mod m { fn main() { use m::S; //~ ERROR `S` is ambiguous - let s = S {}; + let s = S {}; //~ ERROR `S` is ambiguous } diff --git a/tests/ui/imports/issue-55884-1.stderr b/tests/ui/imports/issue-55884-1.stderr index ae8edb049564..a0b63ddc9108 100644 --- a/tests/ui/imports/issue-55884-1.stderr +++ b/tests/ui/imports/issue-55884-1.stderr @@ -18,6 +18,26 @@ LL | pub use self::m2::*; | ^^^^^^^^^^^ = help: consider adding an explicit import of `S` to disambiguate -error: aborting due to 1 previous error +error[E0659]: `S` is ambiguous + --> $DIR/issue-55884-1.rs:20:13 + | +LL | let s = S {}; + | ^ ambiguous name + | + = note: ambiguous because of multiple glob imports of a name in the same module +note: `S` could refer to the struct imported here + --> $DIR/issue-55884-1.rs:14:13 + | +LL | pub use self::m1::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate +note: `S` could also refer to the struct imported here + --> $DIR/issue-55884-1.rs:15:13 + | +LL | pub use self::m2::*; + | ^^^^^^^^^^^ + = help: consider adding an explicit import of `S` to disambiguate + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0659`. diff --git a/tests/ui/inference/issue-71732.stderr b/tests/ui/inference/issue-71732.stderr index af8b310fd1d9..e79131694856 100644 --- a/tests/ui/inference/issue-71732.stderr +++ b/tests/ui/inference/issue-71732.stderr @@ -10,7 +10,7 @@ LL | .get(&"key".into()) - impl Borrow for String; - impl Borrow for T where T: ?Sized; -note: required by a bound in `HashMap::::get` +note: required by a bound in `HashMap::::get` --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL help: consider specifying the generic argument | diff --git a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr index 75ee936d9e8b..e7cb82687fa7 100644 --- a/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr +++ b/tests/ui/layout/rust-call-abi-not-a-tuple-ice-81974.stderr @@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 | LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | note: required by a bound in `FnOnce` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -15,7 +15,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:24:12 | LL | impl FnOnce for CachedFun - | ^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | note: required by a bound in `FnOnce` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -28,7 +28,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 | LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | note: required by a bound in `FnOnce` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -41,7 +41,7 @@ error[E0059]: type parameter to bare `FnMut` trait must be a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:39:12 | LL | impl FnMut for CachedFun - | ^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | note: required by a bound in `FnMut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -54,7 +54,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:31:5 | LL | extern "rust-call" fn call_once(mut self, a: A) -> Self::Output { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | help: consider further restricting type parameter `A` with unstable trait `Tuple` | @@ -65,7 +65,7 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:45:5 | LL | extern "rust-call" fn call_mut(&mut self, a: A) -> Self::Output { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `A` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `A` | help: consider further restricting type parameter `A` with unstable trait `Tuple` | @@ -76,7 +76,7 @@ error[E0277]: `A` is not a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:34:19 | LL | self.call_mut(a) - | -------- ^ the trait `Tuple` is not implemented for `A` + | -------- ^ the trait `std::marker::Tuple` is not implemented for `A` | | | required by a bound introduced by this call | @@ -91,7 +91,7 @@ error[E0277]: `i32` is not a tuple --> $DIR/rust-call-abi-not-a-tuple-ice-81974.rs:59:26 | LL | cachedcoso.call_once(1); - | --------- ^ the trait `Tuple` is not implemented for `i32` + | --------- ^ the trait `std::marker::Tuple` is not implemented for `i32` | | | required by a bound introduced by this call | diff --git a/tests/ui/lint/unused/unused_attributes-must_use.stderr b/tests/ui/lint/unused/unused_attributes-must_use.stderr index 9b9a6a9c9f3d..3192c0994548 100644 --- a/tests/ui/lint/unused/unused_attributes-must_use.stderr +++ b/tests/ui/lint/unused/unused_attributes-must_use.stderr @@ -93,7 +93,7 @@ LL | #[must_use] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[must_use]` can be applied to data types, functions, traits, and unions -error: `#[must_use]` attribute cannot be used on function params +error: `#[must_use]` attribute cannot be used on type parameters --> $DIR/unused_attributes-must_use.rs:77:8 | LL | fn qux<#[must_use] T>(_: T) {} diff --git a/tests/ui/lint/unused/unused_parens/parens-around-guard-patterns-not-unused.rs b/tests/ui/lint/unused/unused_parens/parens-around-guard-patterns-not-unused.rs new file mode 100644 index 000000000000..d5b8365ee612 --- /dev/null +++ b/tests/ui/lint/unused/unused_parens/parens-around-guard-patterns-not-unused.rs @@ -0,0 +1,13 @@ +//! Guard patterns require parentheses to disambiguate precedence +//! +//! Regression test for https://github.com/rust-lang/rust/issues/149594 + +//@ check-pass + +#![feature(guard_patterns)] +#![expect(incomplete_features)] +#![warn(unused_parens)] + +fn main() { + let (_ if false) = (); +} diff --git a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr index 6272455cc57e..af0f67b7c1c0 100644 --- a/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr +++ b/tests/ui/moves/suggest-clone-when-some-obligation-is-unmet.stderr @@ -6,7 +6,7 @@ LL | let mut copy: Vec = map.clone().into_values().collect(); | | | move occurs because value has type `HashMap`, which does not implement the `Copy` trait | -note: `HashMap::::into_values` takes ownership of the receiver `self`, which moves value +note: `HashMap::::into_values` takes ownership of the receiver `self`, which moves value --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL note: if `Hash128_1` implemented `Clone`, you could clone the value --> $DIR/suggest-clone-when-some-obligation-is-unmet.rs:8:1 diff --git a/tests/ui/overloaded/overloaded-calls-nontuple.stderr b/tests/ui/overloaded/overloaded-calls-nontuple.stderr index 22598f3a3901..54a9d1f09b52 100644 --- a/tests/ui/overloaded/overloaded-calls-nontuple.stderr +++ b/tests/ui/overloaded/overloaded-calls-nontuple.stderr @@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `FnMut` trait must be a tuple --> $DIR/overloaded-calls-nontuple.rs:10:6 | LL | impl FnMut for S { - | ^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize` + | ^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize` | note: required by a bound in `FnMut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -11,7 +11,7 @@ error[E0059]: type parameter to bare `FnOnce` trait must be a tuple --> $DIR/overloaded-calls-nontuple.rs:18:6 | LL | impl FnOnce for S { - | ^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize` + | ^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize` | note: required by a bound in `FnOnce` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -20,19 +20,19 @@ error[E0277]: functions with the "rust-call" ABI must take a single non-self tup --> $DIR/overloaded-calls-nontuple.rs:12:5 | LL | extern "rust-call" fn call_mut(&mut self, z: isize) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize` error[E0277]: functions with the "rust-call" ABI must take a single non-self tuple argument --> $DIR/overloaded-calls-nontuple.rs:21:5 | LL | extern "rust-call" fn call_once(mut self, z: isize) -> isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `isize` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `isize` error[E0277]: `isize` is not a tuple --> $DIR/overloaded-calls-nontuple.rs:23:23 | LL | self.call_mut(z) - | -------- ^ the trait `Tuple` is not implemented for `isize` + | -------- ^ the trait `std::marker::Tuple` is not implemented for `isize` | | | required by a bound introduced by this call | @@ -53,7 +53,7 @@ error[E0277]: `isize` is not a tuple --> $DIR/overloaded-calls-nontuple.rs:29:10 | LL | drop(s(3)) - | ^^^^ the trait `Tuple` is not implemented for `isize` + | ^^^^ the trait `std::marker::Tuple` is not implemented for `isize` error: aborting due to 7 previous errors diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.rs b/tests/ui/pin-ergonomics/pin_v2-attr.rs index cfafe4b0b899..ba25d3587edd 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.rs +++ b/tests/ui/pin-ergonomics/pin_v2-attr.rs @@ -25,8 +25,8 @@ union Union { // disallowed enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { - //~^ ERROR `#[pin_v2]` attribute cannot be used on function params - //~| ERROR `#[pin_v2]` attribute cannot be used on function params + //~^ ERROR `#[pin_v2]` attribute cannot be used on type parameters + //~| ERROR `#[pin_v2]` attribute cannot be used on type parameters #[pin_v2] //~ ERROR `#[pin_v2]` attribute cannot be used on enum variants UnitVariant, TupleVariant(#[pin_v2] T), //~ ERROR `#[pin_v2]` attribute cannot be used on struct fields diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr index 81e086f5a7ef..c297afa87a73 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -20,7 +20,7 @@ LL | #![pin_v2] | = help: `#[pin_v2]` can be applied to data types and unions -error: `#[pin_v2]` attribute cannot be used on function params +error: `#[pin_v2]` attribute cannot be used on type parameters --> $DIR/pin_v2-attr.rs:27:10 | LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { @@ -28,7 +28,7 @@ LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { | = help: `#[pin_v2]` can be applied to data types and unions -error: `#[pin_v2]` attribute cannot be used on function params +error: `#[pin_v2]` attribute cannot be used on type parameters --> $DIR/pin_v2-attr.rs:27:23 | LL | enum Foo<#[pin_v2] T, #[pin_v2] U = ()> { diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs index ff585387020e..87ee13d15edb 100644 --- a/tests/ui/privacy/suggest-box-new.rs +++ b/tests/ui/privacy/suggest-box-new.rs @@ -14,7 +14,7 @@ fn main() { let _ = std::collections::HashMap(); //~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap` let _ = std::collections::HashMap {}; - //~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields + //~^ ERROR cannot construct `HashMap<_, _, _, _>` with struct literal syntax due to private fields let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields // test that we properly instantiate the parameter of `Box::::new` with an inference variable diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index e3a7e5f62017..7367672351d6 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -5,6 +5,7 @@ LL | let _ = std::collections::HashMap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL + ::: $SRC_DIR/std/src/collections/hash/map.rs:LL:COL | = note: `std::collections::HashMap` defined here help: you might have meant to use an associated function to build this type @@ -12,14 +13,15 @@ help: you might have meant to use an associated function to build this type LL | let _ = std::collections::HashMap::new(); | +++++ LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::new_in(_); + | +LL - let _ = std::collections::HashMap(); LL + let _ = std::collections::HashMap::with_capacity(_); | LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_hasher(_); - | -LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); | + = and 4 other candidates help: consider using the `Default` trait | LL | let _ = ::default(); @@ -73,7 +75,7 @@ LL - })), LL + wtf: Some(::default()), | -error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields +error: cannot construct `HashMap<_, _, _, _>` with struct literal syntax due to private fields --> $DIR/suggest-box-new.rs:16:13 | LL | let _ = std::collections::HashMap {}; @@ -86,14 +88,15 @@ LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::new(); | LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::new_in(_); + | +LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::with_capacity(_); | LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_hasher(_); - | -LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); | + = and 4 other candidates help: consider using the `Default` trait | LL - let _ = std::collections::HashMap {}; diff --git a/tests/ui/proc-macro/generate-mod.stderr b/tests/ui/proc-macro/generate-mod.stderr index af90df2c3dc3..371fd73e507b 100644 --- a/tests/ui/proc-macro/generate-mod.stderr +++ b/tests/ui/proc-macro/generate-mod.stderr @@ -47,7 +47,6 @@ LL | #[derive(generate_mod::CheckDerive)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:18:10 @@ -57,7 +56,6 @@ LL | #[derive(generate_mod::CheckDerive)] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `FromOutside` in this scope --> $DIR/generate-mod.rs:25:14 @@ -67,7 +65,6 @@ LL | #[derive(generate_mod::CheckDerive)] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: cannot find type `OuterDerive` in this scope --> $DIR/generate-mod.rs:25:14 @@ -77,7 +74,6 @@ LL | #[derive(generate_mod::CheckDerive)] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 8 previous errors @@ -92,7 +88,6 @@ LL | #[derive(generate_mod::CheckDerive)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope @@ -104,7 +99,6 @@ LL | #[derive(generate_mod::CheckDerive)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `FromOutside` in this scope @@ -116,7 +110,6 @@ LL | #[derive(generate_mod::CheckDerive)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: error: cannot find type `OuterDerive` in this scope @@ -128,7 +121,6 @@ LL | #[derive(generate_mod::CheckDerive)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 = note: `#[deny(proc_macro_derive_resolution_fallback)]` (part of `#[deny(future_incompatible)]`) on by default - = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: warning: cannot find type `FromOutside` in this scope @@ -139,7 +131,6 @@ LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 - = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info) Future breakage diagnostic: warning: cannot find type `OuterDeriveLint` in this scope @@ -150,5 +141,4 @@ LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #83583 - = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/reachable/guard_read_for_never.rs b/tests/ui/reachable/guard_read_for_never.rs index 7061da635301..763dfd354b58 100644 --- a/tests/ui/reachable/guard_read_for_never.rs +++ b/tests/ui/reachable/guard_read_for_never.rs @@ -2,7 +2,7 @@ // //@ check-pass #![feature(guard_patterns, never_type)] -#![expect(incomplete_features, unused_parens)] +#![expect(incomplete_features)] #![deny(unreachable_code)] fn main() { diff --git a/tests/ui/reflection/dump.rs b/tests/ui/reflection/dump.rs new file mode 100644 index 000000000000..3bf4f32b6641 --- /dev/null +++ b/tests/ui/reflection/dump.rs @@ -0,0 +1,30 @@ +#![feature(type_info)] +//@ run-pass +//@ check-run-results +#![allow(dead_code)] + +use std::mem::type_info::Type; + +struct Foo { + a: u32, +} + +enum Bar { + Some(u32), + None, + Foomp { a: (), b: &'static str }, +} + +struct Unsized { + x: u16, + s: str, +} + +fn main() { + println!("{:#?}", const { Type::of::<(u8, u8, ())>() }.kind); + println!("{:#?}", const { Type::of::() }.kind); + println!("{:#?}", const { Type::of::() }.kind); + println!("{:#?}", const { Type::of::<&Unsized>() }.kind); + println!("{:#?}", const { Type::of::<&str>() }.kind); + println!("{:#?}", const { Type::of::<&[u8]>() }.kind); +} diff --git a/tests/ui/reflection/dump.run.stdout b/tests/ui/reflection/dump.run.stdout new file mode 100644 index 000000000000..71fd80b46658 --- /dev/null +++ b/tests/ui/reflection/dump.run.stdout @@ -0,0 +1,23 @@ +Tuple( + Tuple { + fields: [ + Field { + ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb), + offset: 0, + }, + Field { + ty: TypeId(0x0596b48cc04376e64d5c788c2aa46bdb), + offset: 1, + }, + Field { + ty: TypeId(0x41223169ff28813ba79b7268a2a968d9), + offset: 2, + }, + ], + }, +) +Other +Other +Other +Other +Other diff --git a/tests/ui/reflection/feature_gate.rs b/tests/ui/reflection/feature_gate.rs new file mode 100644 index 000000000000..2dde26809793 --- /dev/null +++ b/tests/ui/reflection/feature_gate.rs @@ -0,0 +1,8 @@ +use std::mem::type_info::Type; +//~^ ERROR: use of unstable library feature `type_info` + +fn main() { + let ty = std::mem::type_info::Type::of::<()>(); + //~^ ERROR: use of unstable library feature `type_info` + //~| ERROR: use of unstable library feature `type_info` +} diff --git a/tests/ui/reflection/feature_gate.stderr b/tests/ui/reflection/feature_gate.stderr new file mode 100644 index 000000000000..76ad18ffb0ba --- /dev/null +++ b/tests/ui/reflection/feature_gate.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `type_info` + --> $DIR/feature_gate.rs:1:5 + | +LL | use std::mem::type_info::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146922 for more information + = help: add `#![feature(type_info)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_info` + --> $DIR/feature_gate.rs:5:14 + | +LL | let ty = std::mem::type_info::Type::of::<()>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146922 for more information + = help: add `#![feature(type_info)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `type_info` + --> $DIR/feature_gate.rs:5:14 + | +LL | let ty = std::mem::type_info::Type::of::<()>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146922 for more information + = help: add `#![feature(type_info)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/reflection/tuples.rs b/tests/ui/reflection/tuples.rs new file mode 100644 index 000000000000..eab25a691efe --- /dev/null +++ b/tests/ui/reflection/tuples.rs @@ -0,0 +1,36 @@ +#![feature(type_info)] + +//@ run-pass + +use std::mem::type_info::{Type, TypeKind}; + +fn assert_tuple_arity() { + const { + match &Type::of::().kind { + TypeKind::Tuple(tup) => { + assert!(tup.fields.len() == N); + } + _ => unreachable!(), + } + } +} + +fn main() { + assert_tuple_arity::<(), 0>(); + assert_tuple_arity::<(u8,), 1>(); + assert_tuple_arity::<(u8, u8), 2>(); + const { + match &Type::of::<(u8, u8)>().kind { + TypeKind::Tuple(tup) => { + let [a, b] = tup.fields else { unreachable!() }; + assert!(a.offset == 0); + assert!(b.offset == 1); + match (&a.ty.info().kind, &b.ty.info().kind) { + (TypeKind::Leaf, TypeKind::Leaf) => {} + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } +} diff --git a/tests/ui/repeat-expr/can-have-side-effects-consider-element.rs b/tests/ui/repeat-expr/can-have-side-effects-consider-element.rs new file mode 100644 index 000000000000..274942f9379f --- /dev/null +++ b/tests/ui/repeat-expr/can-have-side-effects-consider-element.rs @@ -0,0 +1,20 @@ +//@ check-pass + +#![allow(unused)] + +// Test if `Expr::can_have_side_effects` considers element of repeat expressions. + +fn drop_repeat_in_arm_body() { + // Built-in lint `dropping_copy_types` relies on `Expr::can_have_side_effects` + // (See rust-clippy#9482 and rust#113231) + + match () { + () => drop([0; 1]), // No side effects + //~^ WARNING calls to `std::mem::drop` with a value that implements `Copy` does nothing + } + match () { + () => drop([return; 1]), // Definitely has side effects + } +} + +fn main() {} diff --git a/tests/ui/repeat-expr/can-have-side-effects-consider-element.stderr b/tests/ui/repeat-expr/can-have-side-effects-consider-element.stderr new file mode 100644 index 000000000000..70c7e8b404e0 --- /dev/null +++ b/tests/ui/repeat-expr/can-have-side-effects-consider-element.stderr @@ -0,0 +1,13 @@ +warning: calls to `std::mem::drop` with a value that implements `Copy` does nothing + --> $DIR/can-have-side-effects-consider-element.rs:12:15 + | +LL | () => drop([0; 1]), // No side effects + | ^^^^^------^ + | | + | argument has type `[i32; 1]` + | + = note: use `let _ = ...` to ignore the expression or result + = note: `#[warn(dropping_copy_types)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/resolve/resolve-assoc-suggestions.stderr b/tests/ui/resolve/resolve-assoc-suggestions.stderr index ef519ac0d921..7d94fb5ca35f 100644 --- a/tests/ui/resolve/resolve-assoc-suggestions.stderr +++ b/tests/ui/resolve/resolve-assoc-suggestions.stderr @@ -31,6 +31,10 @@ help: you might have meant to use the associated type | LL | let _: Self::Type; | ++++++ +help: consider importing this struct + | +LL + use std::mem::type_info::Type; + | error[E0531]: cannot find tuple struct or tuple variant `Type` in this scope --> $DIR/resolve-assoc-suggestions.rs:25:13 diff --git a/tests/ui/scalable-vectors/invalid.rs b/tests/ui/scalable-vectors/invalid.rs index beb3ad66b78b..90e9839c9e11 100644 --- a/tests/ui/scalable-vectors/invalid.rs +++ b/tests/ui/scalable-vectors/invalid.rs @@ -48,7 +48,7 @@ type Foo = u32; #[rustc_scalable_vector(4)] //~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on enums enum Bar<#[rustc_scalable_vector(4)] T> { -//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on function params +//~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on type parameters #[rustc_scalable_vector(4)] //~^ ERROR: `#[rustc_scalable_vector]` attribute cannot be used on enum variants Baz(std::marker::PhantomData), diff --git a/tests/ui/scalable-vectors/invalid.stderr b/tests/ui/scalable-vectors/invalid.stderr index 43be84a8b005..d73b5abf7030 100644 --- a/tests/ui/scalable-vectors/invalid.stderr +++ b/tests/ui/scalable-vectors/invalid.stderr @@ -92,7 +92,7 @@ LL | #[rustc_scalable_vector(4)] | = help: `#[rustc_scalable_vector]` can only be applied to structs -error: `#[rustc_scalable_vector]` attribute cannot be used on function params +error: `#[rustc_scalable_vector]` attribute cannot be used on type parameters --> $DIR/invalid.rs:50:10 | LL | enum Bar<#[rustc_scalable_vector(4)] T> { diff --git a/tests/ui/single-use-lifetime/issue-104440.rs b/tests/ui/single-use-lifetime/issue-104440.rs index 0795e95303a1..cecd17fb930b 100644 --- a/tests/ui/single-use-lifetime/issue-104440.rs +++ b/tests/ui/single-use-lifetime/issue-104440.rs @@ -8,7 +8,7 @@ mod type_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($T:ident) { fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) { (t1.clone(), t2.clone()) @@ -40,7 +40,7 @@ mod lifetime_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($a:lifetime) { fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { (t1, t2) @@ -72,7 +72,7 @@ mod const_params { } } - #[rustc_macro_transparency = "semitransparent"] + #[rustc_macro_transparency = "semiopaque"] macro n($C:ident) { fn g(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { (t1, t2) diff --git a/tests/ui/static/static-initializer-acyclic-issue-146787.rs b/tests/ui/static/static-initializer-acyclic-issue-146787.rs new file mode 100644 index 000000000000..715133bd5d3c --- /dev/null +++ b/tests/ui/static/static-initializer-acyclic-issue-146787.rs @@ -0,0 +1,29 @@ +//@ add-minicore +//@ needs-llvm-components: nvptx +//@ compile-flags: --target nvptx64-nvidia-cuda --emit link +//@ ignore-backends: gcc +#![crate_type = "rlib"] +#![feature(no_core)] +#![no_std] +#![no_core] + +extern crate minicore; +use minicore::*; + +struct Foo(&'static Foo); +impl Sync for Foo {} + +static A: Foo = Foo(&A); //~ ERROR static initializer forms a cycle involving `A` + +static B0: Foo = Foo(&B1); //~ ERROR static initializer forms a cycle involving `B0` +static B1: Foo = Foo(&B0); + +static C0: Foo = Foo(&C1); //~ ERROR static initializer forms a cycle involving `C0` +static C1: Foo = Foo(&C2); +static C2: Foo = Foo(&C0); + +struct Bar(&'static u32); +impl Sync for Bar {} + +static BAR: Bar = Bar(&INT); +static INT: u32 = 42u32; diff --git a/tests/ui/static/static-initializer-acyclic-issue-146787.stderr b/tests/ui/static/static-initializer-acyclic-issue-146787.stderr new file mode 100644 index 000000000000..af2e4e0f5df8 --- /dev/null +++ b/tests/ui/static/static-initializer-acyclic-issue-146787.stderr @@ -0,0 +1,32 @@ +error: static initializer forms a cycle involving `C0` + --> $DIR/static-initializer-acyclic-issue-146787.rs:21:1 + | +LL | static C0: Foo = Foo(&C1); + | ^^^^^^^^^^^^^^ part of this cycle +LL | static C1: Foo = Foo(&C2); + | -------------- part of this cycle +LL | static C2: Foo = Foo(&C0); + | -------------- part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: static initializer forms a cycle involving `B0` + --> $DIR/static-initializer-acyclic-issue-146787.rs:18:1 + | +LL | static B0: Foo = Foo(&B1); + | ^^^^^^^^^^^^^^ part of this cycle +LL | static B1: Foo = Foo(&B0); + | -------------- part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: static initializer forms a cycle involving `A` + --> $DIR/static-initializer-acyclic-issue-146787.rs:16:1 + | +LL | static A: Foo = Foo(&A); + | ^^^^^^^^^^^^^ part of this cycle + | + = note: cyclic static initializers are not supported for target `nvptx64-nvidia-cuda` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/suggestions/fn-trait-notation.stderr b/tests/ui/suggestions/fn-trait-notation.stderr index 9b47c8c02a78..9d0845478527 100644 --- a/tests/ui/suggestions/fn-trait-notation.stderr +++ b/tests/ui/suggestions/fn-trait-notation.stderr @@ -32,7 +32,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple --> $DIR/fn-trait-notation.rs:4:8 | LL | F: Fn, - | ^^^^^^^^^^^^^^^^^^^^^ the trait `Tuple` is not implemented for `i32` + | ^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `i32` | note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL @@ -47,7 +47,7 @@ error[E0277]: `i32` is not a tuple --> $DIR/fn-trait-notation.rs:9:5 | LL | f(3); - | ^^^^ the trait `Tuple` is not implemented for `i32` + | ^^^^ the trait `std::marker::Tuple` is not implemented for `i32` error[E0308]: mismatched types --> $DIR/fn-trait-notation.rs:17:5 diff --git a/tests/ui/suggestions/multi-suggestion.ascii.stderr b/tests/ui/suggestions/multi-suggestion.ascii.stderr index c2ae19b1ae9a..2da0f9bd12c2 100644 --- a/tests/ui/suggestions/multi-suggestion.ascii.stderr +++ b/tests/ui/suggestions/multi-suggestion.ascii.stderr @@ -5,6 +5,7 @@ LL | let _ = std::collections::HashMap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL + ::: $SRC_DIR/std/src/collections/hash/map.rs:LL:COL | = note: `std::collections::HashMap` defined here help: you might have meant to use an associated function to build this type @@ -12,14 +13,15 @@ help: you might have meant to use an associated function to build this type LL | let _ = std::collections::HashMap::new(); | +++++ LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::new_in(_); + | +LL - let _ = std::collections::HashMap(); LL + let _ = std::collections::HashMap::with_capacity(_); | LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_hasher(_); - | -LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); | + = and 4 other candidates help: consider using the `Default` trait | LL | let _ = ::default(); @@ -73,7 +75,7 @@ LL - })), LL + wtf: Some(::default()), | -error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields +error: cannot construct `HashMap<_, _, _, _>` with struct literal syntax due to private fields --> $DIR/multi-suggestion.rs:19:13 | LL | let _ = std::collections::HashMap {}; @@ -86,14 +88,15 @@ LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::new(); | LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::new_in(_); + | +LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::with_capacity(_); | LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_hasher(_); - | -LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); | + = and 4 other candidates help: consider using the `Default` trait | LL - let _ = std::collections::HashMap {}; diff --git a/tests/ui/suggestions/multi-suggestion.rs b/tests/ui/suggestions/multi-suggestion.rs index 99d2407aa212..6d822a8e5aff 100644 --- a/tests/ui/suggestions/multi-suggestion.rs +++ b/tests/ui/suggestions/multi-suggestion.rs @@ -17,6 +17,6 @@ fn main() { let _ = std::collections::HashMap(); //[ascii]~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap` let _ = std::collections::HashMap {}; - //[ascii]~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields + //[ascii]~^ ERROR cannot construct `HashMap<_, _, _, _>` with struct literal syntax due to private fields let _ = Box {}; //[ascii]~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields } diff --git a/tests/ui/suggestions/multi-suggestion.unicode.stderr b/tests/ui/suggestions/multi-suggestion.unicode.stderr index fc187cac91d1..69529b67b775 100644 --- a/tests/ui/suggestions/multi-suggestion.unicode.stderr +++ b/tests/ui/suggestions/multi-suggestion.unicode.stderr @@ -5,6 +5,7 @@ LL │ let _ = std::collections::HashMap(); │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━ ╰╴ ╭▸ $SRC_DIR/std/src/collections/hash/map.rs:LL:COL + ⸬ $SRC_DIR/std/src/collections/hash/map.rs:LL:COL │ ╰ note: `std::collections::HashMap` defined here help: you might have meant to use an associated function to build this type @@ -12,14 +13,15 @@ help: you might have meant to use an associated function to build this type LL │ let _ = std::collections::HashMap::new(); ├╴ +++++ LL - let _ = std::collections::HashMap(); +LL + let _ = std::collections::HashMap::new_in(_); + ├╴ +LL - let _ = std::collections::HashMap(); LL + let _ = std::collections::HashMap::with_capacity(_); ├╴ LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_hasher(_); - ├╴ -LL - let _ = std::collections::HashMap(); -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); - ╰╴ +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); + │ + ╰ and 4 other candidates help: consider using the `Default` trait ╭╴ LL │ let _ = ::default(); @@ -73,7 +75,7 @@ LL - })), LL + wtf: Some(::default()), ╰╴ -error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields +error: cannot construct `HashMap<_, _, _, _>` with struct literal syntax due to private fields ╭▸ $DIR/multi-suggestion.rs:19:13 │ LL │ let _ = std::collections::HashMap {}; @@ -86,14 +88,15 @@ LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::new(); ├╴ LL - let _ = std::collections::HashMap {}; +LL + let _ = std::collections::HashMap::new_in(_); + ├╴ +LL - let _ = std::collections::HashMap {}; LL + let _ = std::collections::HashMap::with_capacity(_); ├╴ LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_hasher(_); - ├╴ -LL - let _ = std::collections::HashMap {}; -LL + let _ = std::collections::HashMap::with_capacity_and_hasher(_, _); - ╰╴ +LL + let _ = std::collections::HashMap::with_capacity_in(_, _); + │ + ╰ and 4 other candidates help: consider using the `Default` trait ╭╴ LL - let _ = std::collections::HashMap {}; diff --git a/tests/ui/thir-print/offset_of.stdout b/tests/ui/thir-print/offset_of.stdout index ab924091ba7a..dcf60a86af9b 100644 --- a/tests/ui/thir-print/offset_of.stdout +++ b/tests/ui/thir-print/offset_of.stdout @@ -68,7 +68,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).10)) - span: $DIR/offset_of.rs:37:5: 1437:57 (#0) + span: $DIR/offset_of.rs:37:5: 1440:57 (#0) } } Stmt { @@ -117,7 +117,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).20)) - span: $DIR/offset_of.rs:38:5: 1437:57 (#0) + span: $DIR/offset_of.rs:38:5: 1440:57 (#0) } } Stmt { @@ -166,7 +166,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).30)) - span: $DIR/offset_of.rs:39:5: 1437:57 (#0) + span: $DIR/offset_of.rs:39:5: 1440:57 (#0) } } Stmt { @@ -215,7 +215,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).40)) - span: $DIR/offset_of.rs:40:5: 1437:57 (#0) + span: $DIR/offset_of.rs:40:5: 1440:57 (#0) } } Stmt { @@ -264,7 +264,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::concrete).50)) - span: $DIR/offset_of.rs:41:5: 1437:57 (#0) + span: $DIR/offset_of.rs:41:5: 1440:57 (#0) } } ] @@ -864,7 +864,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).12)) - span: $DIR/offset_of.rs:45:5: 1437:57 (#0) + span: $DIR/offset_of.rs:45:5: 1440:57 (#0) } } Stmt { @@ -913,7 +913,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).24)) - span: $DIR/offset_of.rs:46:5: 1437:57 (#0) + span: $DIR/offset_of.rs:46:5: 1440:57 (#0) } } Stmt { @@ -962,7 +962,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).36)) - span: $DIR/offset_of.rs:47:5: 1437:57 (#0) + span: $DIR/offset_of.rs:47:5: 1440:57 (#0) } } Stmt { @@ -1011,7 +1011,7 @@ body: ) else_block: None lint_level: Explicit(HirId(DefId(offset_of::generic).48)) - span: $DIR/offset_of.rs:48:5: 1437:57 (#0) + span: $DIR/offset_of.rs:48:5: 1440:57 (#0) } } ] diff --git a/tests/ui/traits/ignore-err-impls.stderr b/tests/ui/traits/ignore-err-impls.stderr index 68077c435135..46a2a7d55a2e 100644 --- a/tests/ui/traits/ignore-err-impls.stderr +++ b/tests/ui/traits/ignore-err-impls.stderr @@ -4,10 +4,10 @@ error[E0425]: cannot find type `Type` in this scope LL | impl Generic for S {} | ^^^^ not found in this scope | -help: you might be missing a type parameter +help: consider importing this struct + | +LL + use std::mem::type_info::Type; | -LL | impl Generic for S {} - | ++++++ error: aborting due to 1 previous error diff --git a/tests/ui/traits/issue-77982.stderr b/tests/ui/traits/issue-77982.stderr index edc7f56ea467..4bc24e81215a 100644 --- a/tests/ui/traits/issue-77982.stderr +++ b/tests/ui/traits/issue-77982.stderr @@ -10,7 +10,7 @@ LL | opts.get(opt.as_ref()); - impl Borrow for String; - impl Borrow for T where T: ?Sized; -note: required by a bound in `HashMap::::get` +note: required by a bound in `HashMap::::get` --> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL help: consider specifying the generic argument | diff --git a/tests/ui/traits/next-solver/well-formed-in-relate.stderr b/tests/ui/traits/next-solver/well-formed-in-relate.stderr index d79e465b3e38..264ab9bebbb9 100644 --- a/tests/ui/traits/next-solver/well-formed-in-relate.stderr +++ b/tests/ui/traits/next-solver/well-formed-in-relate.stderr @@ -9,11 +9,11 @@ LL | x = unconstrained_map(); | = note: multiple `impl`s satisfying `_: Fn()` found in the following crates: `alloc`, `core`: - impl Fn for &F - where A: Tuple, F: Fn, F: ?Sized; + where A: std::marker::Tuple, F: Fn, F: ?Sized; - impl Fn for Box - where Args: Tuple, F: Fn, A: Allocator, F: ?Sized; + where Args: std::marker::Tuple, F: Fn, A: Allocator, F: ?Sized; - impl Fn for Exclusive - where F: Sync, F: Fn, Args: Tuple; + where F: Sync, F: Fn, Args: std::marker::Tuple; note: required by a bound in `unconstrained_map` --> $DIR/well-formed-in-relate.rs:21:25 | diff --git a/tests/ui/tuple/builtin-fail.stderr b/tests/ui/tuple/builtin-fail.stderr index 44e79578f4c9..0dec88ded7ce 100644 --- a/tests/ui/tuple/builtin-fail.stderr +++ b/tests/ui/tuple/builtin-fail.stderr @@ -2,7 +2,7 @@ error[E0277]: `T` is not a tuple --> $DIR/builtin-fail.rs:8:23 | LL | assert_is_tuple::(); - | ^ the trait `Tuple` is not implemented for `T` + | ^ the trait `std::marker::Tuple` is not implemented for `T` | note: required by a bound in `assert_is_tuple` --> $DIR/builtin-fail.rs:3:23 @@ -18,7 +18,7 @@ error[E0277]: `i32` is not a tuple --> $DIR/builtin-fail.rs:13:23 | LL | assert_is_tuple::(); - | ^^^ the trait `Tuple` is not implemented for `i32` + | ^^^ the trait `std::marker::Tuple` is not implemented for `i32` | note: required by a bound in `assert_is_tuple` --> $DIR/builtin-fail.rs:3:23 @@ -30,7 +30,7 @@ error[E0277]: `i32` is not a tuple --> $DIR/builtin-fail.rs:15:24 | LL | assert_is_tuple::<(i32)>(); - | ^^^ the trait `Tuple` is not implemented for `i32` + | ^^^ the trait `std::marker::Tuple` is not implemented for `i32` | note: required by a bound in `assert_is_tuple` --> $DIR/builtin-fail.rs:3:23 @@ -44,7 +44,7 @@ error[E0277]: `TupleStruct` is not a tuple LL | assert_is_tuple::(); | ^^^^^^^^^^^ unsatisfied trait bound | -help: the trait `Tuple` is not implemented for `TupleStruct` +help: the trait `std::marker::Tuple` is not implemented for `TupleStruct` --> $DIR/builtin-fail.rs:5:1 | LL | struct TupleStruct(i32, i32); diff --git a/tests/ui/typeck/issue-57404.stderr b/tests/ui/typeck/issue-57404.stderr index 4c1bfc0cbf77..f1d28e475a07 100644 --- a/tests/ui/typeck/issue-57404.stderr +++ b/tests/ui/typeck/issue-57404.stderr @@ -2,7 +2,7 @@ error[E0277]: `&mut ()` is not a tuple --> $DIR/issue-57404.rs:6:41 | LL | handlers.unwrap().as_mut().call_mut(&mut ()); - | -------- ^^^^^^^ the trait `Tuple` is not implemented for `&mut ()` + | -------- ^^^^^^^ the trait `std::marker::Tuple` is not implemented for `&mut ()` | | | required by a bound introduced by this call | diff --git a/tests/ui/unboxed-closures/non-tupled-arg-mismatch.stderr b/tests/ui/unboxed-closures/non-tupled-arg-mismatch.stderr index 0d4265ddf8bd..621a533dd1c5 100644 --- a/tests/ui/unboxed-closures/non-tupled-arg-mismatch.stderr +++ b/tests/ui/unboxed-closures/non-tupled-arg-mismatch.stderr @@ -2,7 +2,7 @@ error[E0059]: type parameter to bare `Fn` trait must be a tuple --> $DIR/non-tupled-arg-mismatch.rs:3:9 | LL | fn a>(f: F) {} - | ^^^^^^^^^ the trait `Tuple` is not implemented for `usize` + | ^^^^^^^^^ the trait `std::marker::Tuple` is not implemented for `usize` | note: required by a bound in `Fn` --> $SRC_DIR/core/src/ops/function.rs:LL:COL diff --git a/triagebot.toml b/triagebot.toml index 59c5e5f24330..89118fdb5948 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1003,6 +1003,13 @@ cc = ["@lcnr"] message = "HIR ty lowering was modified" cc = ["@fmease"] +[mentions."library/core/src/mem/type_info.rs"] +message = """ +The reflection data structures are tied exactly to the implementation +in the compiler. Make sure to also adjust `rustc_const_eval/src/const_eval/type_info.rs +""" +cc = ["@oli-obk"] + [mentions."compiler/rustc_error_codes/src/lib.rs"] message = "Some changes occurred in diagnostic error codes" cc = ["@GuillaumeGomez"] @@ -1358,11 +1365,11 @@ message = "The rustc-dev-guide subtree was changed. If this PR *only* touches th cc = ["@BoxyUwU", "@jieyouxu", "@kobzol", "@tshepang"] [mentions."compiler/rustc_passes/src/check_attr.rs"] -cc = ["@jdonszelmann"] +cc = ["@jdonszelmann", "@JonathanBrouwer"] [mentions."compiler/rustc_attr_parsing"] -cc = ["@jdonszelmann"] +cc = ["@jdonszelmann", "@JonathanBrouwer"] [mentions."compiler/rustc_hir/src/attrs"] -cc = ["@jdonszelmann"] +cc = ["@jdonszelmann", "@JonathanBrouwer"] [mentions."src/tools/enzyme"] cc = ["@ZuseZ4"]