diff --git a/compiler/rustc_attr_ir/src/data_structures.rs b/compiler/rustc_attr_ir/src/data_structures.rs index 712ed41ae7759..55c0bedb9c39c 100644 --- a/compiler/rustc_attr_ir/src/data_structures.rs +++ b/compiler/rustc_attr_ir/src/data_structures.rs @@ -283,7 +283,6 @@ impl StrippedCfgItem { #[derive(StableHash, PrintAttribute)] pub enum Linkage { AvailableExternally, - Common, ExternalWeak, External, Internal, diff --git a/compiler/rustc_attr_ir/src/encode_cross_crate.rs b/compiler/rustc_attr_ir/src/encode_cross_crate.rs index 66fbbc1e3cbf7..b6733e8c1db54 100644 --- a/compiler/rustc_attr_ir/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_ir/src/encode_cross_crate.rs @@ -265,7 +265,6 @@ impl AttributeKind { Linkage(linkage, _) => match *linkage { crate::Linkage::Internal => false, crate::Linkage::AvailableExternally - | crate::Linkage::Common | crate::Linkage::External | crate::Linkage::ExternalWeak | crate::Linkage::LinkOnceAny diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index f4d0fa35e3f47..ed911a63eb7fe 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -685,10 +685,7 @@ impl SingleAttributeParser for LinkageParser { ]); const TEMPLATE: AttributeTemplate = template!(NameValueStr: [ "available_externally", - "common", "extern_weak", - "external", - "internal", "linkonce", "linkonce_odr", "weak", @@ -711,10 +708,7 @@ impl SingleAttributeParser for LinkageParser { // and don't have to be, LLVM treats them as no-ops. let linkage = match value { sym::available_externally => Linkage::AvailableExternally, - sym::common => Linkage::Common, sym::extern_weak => Linkage::ExternalWeak, - sym::external => Linkage::External, - sym::internal => Linkage::Internal, sym::linkonce => Linkage::LinkOnceAny, sym::linkonce_odr => Linkage::LinkOnceODR, sym::weak => Linkage::WeakAny, @@ -725,10 +719,7 @@ impl SingleAttributeParser for LinkageParser { name_value.value_span, &[ sym::available_externally, - sym::common, sym::extern_weak, - sym::external, - sym::internal, sym::linkonce, sym::linkonce_odr, sym::weak, diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index 34499c6e7b522..4afb74a1f8cdd 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -32,6 +32,10 @@ jobs: - name: Setup rust toolchain run: rustup show + # The stdarch-gen-common tests run rustfmt. + - name: Install rustfmt + run: rustup component add rustfmt + - name: Setup rust cache uses: Swatinem/rust-cache@v2 diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index c174628d0d188..6467f1a8c3763 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,9 +56,9 @@ dependencies = [ [[package]] name = "gccjit" -version = "6.1.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d85b5754389edaad832ba320709a25086b3081a8c6c0fab2322965e5fb512b3" +checksum = "dfdb84ba432f6679794790de652bfbed86201893a30c03dc669c2bfe7424f472" dependencies = [ "gccjit_sys", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 02be6d56c2310..35046980f0695 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -20,7 +20,7 @@ default = ["master"] [dependencies] object = { version = "0.39.0", default-features = false, features = ["std", "read"] } tempfile = "3.20" -gccjit = { version = "6.1.0", features = ["dlopen"] } +gccjit = { version = "7.0.0", features = ["dlopen"] } #gccjit = { git = "https://github.com/rust-lang/gccjit.rs", branch = "error-dlopen", features = ["dlopen"] } # Local copy. diff --git a/compiler/rustc_codegen_gcc/rust-toolchain.toml b/compiler/rustc_codegen_gcc/rust-toolchain.toml index a7774784e3afa..63af0047e6e00 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain.toml +++ b/compiler/rustc_codegen_gcc/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2026-09-24" +channel = "nightly-2026-09-29" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/base.rs b/compiler/rustc_codegen_gcc/src/base.rs index 07214096b3e49..acf5c75e4d8cd 100644 --- a/compiler/rustc_codegen_gcc/src/base.rs +++ b/compiler/rustc_codegen_gcc/src/base.rs @@ -55,19 +55,14 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => GlobalKind::Exported, + | Linkage::ExternalWeak => GlobalKind::Exported, } } /// The attribute a global *definition* needs on top of its [`GlobalKind`] to get this linkage. #[cfg(feature = "master")] pub fn global_linkage_attribute<'gcc>(linkage: Linkage) -> Option> { - match linkage { - Linkage::Common => Some(VarAttribute::Common), - _ if linkage_needs_weak_attribute(linkage) => Some(VarAttribute::Weak), - _ => None, - } + if linkage_needs_weak_attribute(linkage) { Some(VarAttribute::Weak) } else { None } } /// The type of a function *definition* with an explicit `#[linkage]`. @@ -85,17 +80,12 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => FunctionType::Exported, + | Linkage::ExternalWeak => FunctionType::Exported, } } /// Whether a definition with this linkage must carry the `weak` attribute, so that a strong /// definition in another object file wins over it instead of clashing with it. -/// -/// `common` is in here for functions only: GCC honours that attribute on a variable, but drops it -/// on a function, so a common function falls back to weak. Globals go through -/// `global_linkage_attribute` instead. #[cfg(feature = "master")] pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { match linkage { @@ -103,8 +93,7 @@ pub fn linkage_needs_weak_attribute(linkage: Linkage) -> bool { | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR - | Linkage::ExternalWeak - | Linkage::Common => true, + | Linkage::ExternalWeak => true, Linkage::External | Linkage::AvailableExternally | Linkage::Internal => false, } } diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index c58ab6d2a9bef..0784064547e0f 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -13,8 +13,7 @@ use rustc_hir::def_id::LOCAL_CRATE; use rustc_log::tracing::trace; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::interpret::{ - self, Allocation, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, - read_target_uint, + self, ConstAllocation, CtfeProvenance, ErrorHandled, Scalar as InterpScalar, read_target_uint, }; use rustc_middle::mono::MonoItem; use rustc_middle::ty::layout::LayoutOf; @@ -113,12 +112,7 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); - // A common symbol is storage the linker allocates and zero-fills, so giving the definition - // an initializer — even an all-zero one — takes it back out of `.comm`. A non-zero one is - // kept: the symbol is then an ordinary definition, which is what GCC does with it too. - if attrs.linkage != Some(Linkage::Common) || !is_zero_initializer(alloc) { - global.global_set_initializer_rvalue(value); - } + global.global_set_initializer_rvalue(value); // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. @@ -458,17 +452,6 @@ pub(crate) fn const_alloc_to_gcc_uncached<'gcc>( cx.const_struct(&llvals, true) } -/// Whether this allocation is all zeroes, and so needs no initializer to be spelled out. -fn is_zero_initializer(alloc: &Allocation) -> bool { - alloc.provenance().ptrs().is_empty() - // This `inspect` is okay: it is within the bounds of the allocation, there is no provenance - // to misread, and it does not affect interpreter execution. - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.size().bytes_usize()) - .iter() - .all(|&byte| byte == 0) -} - fn codegen_static_initializer<'gcc, 'tcx>( cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId, diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index 4e4b911666143..de8762d1c06b4 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -954,6 +954,10 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { debug_assert!(value_type.dyncast_array().is_some()); let name_suffix = match self.type_kind(dest_typ) { + TypeKind::Half if dest_typ.is_compatible_with(self.type_f16()) => { + let value = self.int_to_float_cast(signed, value, self.float_type); + return self.context.new_cast(None, value, dest_typ); + } // cSpell:disable TypeKind::Float => "tisf", TypeKind::Double => "tidf", diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f30004c8561f7..b82d05dc211aa 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -237,7 +237,7 @@ impl CodegenBackend for GccCodegenBackend { lto_supported: gccjit::is_lto_supported(), }); - gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); + gccjit::set_global_personality_function_name(c"rust_eh_personality"); } #[cfg(not(feature = "master"))] diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index cf278694399f3..77cb37edec770 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -237,7 +237,6 @@ pub(crate) fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, Linkage::Internal => llvm::Linkage::InternalLinkage, Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 2fa6f3fc06550..5433b9da6f2b6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,7 +228,6 @@ fn prefix_and_suffix<'tcx>( // LTO can fail when internal linkage is used. emit_fatal("naked functions may not have internal linkage") } - Linkage::Common => emit_fatal("Functions may not have common linkage"), Linkage::AvailableExternally => { // this would make the function equal an extern definition emit_fatal("Functions may not have available_externally linkage") diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 210f04a115831..22076a95cea09 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -191,8 +191,16 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) HandledOptions::HelpOnly(matches) => (matches, true), }; - let sopts = config::build_session_options(&mut default_early_dcx, &matches); - // fully initialize ice path static once unstable options are available as context + // For the purposes of checking that an --edition was passed, we ignore `-`. + let warn_unspecified_edition = match &matches.free[..] { + [] => false, + // We explicitly don't emit the note if we're consuming code from stdin. This can happen + // on some cargo invocations too. + [name] if name == "-" => false, + _ => true, + }; + let sopts = + config::build_session_options(&mut default_early_dcx, &matches, warn_unspecified_edition); let ice_file = ice_path_with_config(Some(&sopts.unstable_opts)).clone(); if let Some(ref code) = matches.opt_str("explain") { diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index ff5f7dbb119f1..421f0b52d5a3b 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -40,6 +40,10 @@ pub(crate) struct AssocKindMismatch { #[primary_span] #[label("unexpected {$got}")] pub span: Span, + #[context] + pub item_span: Span, + #[context] + pub enclosing_span: Option, pub expected: &'static str, pub got: &'static str, #[label("expected a {$expected} because of this associated {$expected}")] diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ba715633d30d7..b21f80925aa05 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -394,8 +394,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (ident.span, None, assoc_tag, assoc_item.tag()) }; + let def_kind = tcx.def_kind(self.item_def_id()); + let item_span = tcx.def_span(self.item_def_id()).shrink_to_lo(); + let (item_span, enclosing_span) = match def_kind { + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => ( + item_span, + Some(tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo()), + ), + DefKind::OpaqueTy => { + let item_span = tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo(); + let enclosing_span = + if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = + tcx.def_kind(tcx.parent(self.item_def_id().into())) + { + Some(tcx.def_span(tcx.parent(self.item_def_id().into())).shrink_to_lo()) + } else { + None + }; + (item_span, enclosing_span) + } + _ => (item_span, None), + }; self.dcx().emit_err(diagnostics::AssocKindMismatch { span, + item_span, + enclosing_span, expected: assoc_tag_str(expected), got: assoc_tag_str(got), expected_because_label, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0d584be4ad0b0..3f59afa31de84 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -42,7 +42,7 @@ where { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); let matches = optgroups().parse(args).unwrap(); - let sessopts = build_session_options(&mut early_dcx, &matches); + let sessopts = build_session_options(&mut early_dcx, &matches, true); let target = rustc_session::config::build_target_config( &early_dcx, &sessopts.target_triple, @@ -941,7 +941,7 @@ fn test_edition_parsing() { let mut early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap(); - let sessopts = build_session_options(&mut early_dcx, &matches); + let sessopts = build_session_options(&mut early_dcx, &matches, false); assert!(sessopts.edition == Edition::Edition2018) } @@ -952,7 +952,7 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { // `-Zassumptions-on-binders` alone enables the next solver globally. let matches = optgroups().parse(&["-Zassumptions-on-binders".to_string()]).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); + let opts = build_session_options(&mut early_dcx, &matches, false); assert!(opts.unstable_opts.assumptions_on_binders); assert_eq!(opts.unstable_opts.next_solver, globally); @@ -963,7 +963,7 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { ["-Znext-solver=coherence".to_string(), "-Zassumptions-on-binders".to_string()], ] { let matches = optgroups().parse(&args).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); + let opts = build_session_options(&mut early_dcx, &matches, false); assert!(opts.unstable_opts.assumptions_on_binders); assert_eq!(opts.unstable_opts.next_solver, globally); } @@ -976,7 +976,7 @@ fn test_assumptions_on_binders_enables_next_solver_globally() { ["-Znext-solver=no".to_string(), "-Zassumptions-on-binders".to_string()], ] { let matches = optgroups().parse(&args).unwrap(); - let opts = build_session_options(&mut early_dcx, &matches); + let opts = build_session_options(&mut early_dcx, &matches, false); assert!(opts.unstable_opts.assumptions_on_binders); assert_eq!(opts.unstable_opts.next_solver, globally); } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index cdff1280d69f9..f071028e5937d 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -347,11 +347,16 @@ impl DiagnosticDeriveVariantBuilder { match (&attr.meta, name.as_str()) { (Meta::Path(_), "primary_span") => { report_error_if_not_applied_to_span(attr, &info)?; - return Ok(quote! { diag.span(#binding); }); } + (Meta::Path(_), "context") => { + report_error_if_not_applied_to_span(attr, &info)?; + return Ok(quote! { + diag.span_context(#binding); + }); + } (Meta::Path(_), "subdiagnostic") => { return Ok(quote! { diag.subdiagnostic(#binding); }); } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 09c5717c21213..b522fa00872de 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -190,6 +190,7 @@ decl_derive!( // field attributes primary_span, label, + context, subdiagnostic, suggestion)] => #[doc = "See "] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 43ce9634cc203..16df895b0f728 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1252,7 +1252,6 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio Linkage::WeakODR => "WeakODR", Linkage::Internal => "Internal", Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", }; output.push('['); diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index 52016ca5f8d5c..744e7c22265ea 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1399,6 +1399,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { code, trait_item_span, trait_path, + trait_span, + impl_span, } => self .dcx() .create_err(diagnostics::TraitImplMismatch { @@ -1407,16 +1409,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { kind, trait_path, trait_item_span, + trait_span, + impl_span, }) .with_code(code), - ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { - self.dcx().create_err(diagnostics::TraitImplDuplicate { - span, - name, - trait_item_span, - old_span, - }) - } + ResolutionError::TraitImplDuplicate { + name, + trait_item_span, + old_span, + trait_span, + impl_span, + } => self.dcx().create_err(diagnostics::TraitImplDuplicate { + span, + name, + trait_item_span, + trait_span, + impl_span, + old_span, + }), ResolutionError::InvalidAsmSym => { self.dcx().create_err(diagnostics::InvalidAsmSym { span }) } diff --git a/compiler/rustc_resolve/src/diagnostics/mod.rs b/compiler/rustc_resolve/src/diagnostics/mod.rs index c0e687fc94a38..2b74938c3ec7e 100644 --- a/compiler/rustc_resolve/src/diagnostics/mod.rs +++ b/compiler/rustc_resolve/src/diagnostics/mod.rs @@ -535,6 +535,10 @@ pub(crate) struct TraitImplDuplicate { pub(crate) old_span: Span, #[label("item in trait")] pub(crate) trait_item_span: Span, + #[context] + pub(crate) trait_span: Span, + #[context] + pub(crate) impl_span: Span, pub(crate) name: Ident, } @@ -1410,6 +1414,10 @@ pub(crate) struct TraitImplMismatch { pub(crate) trait_path: String, #[label("item in trait")] pub(crate) trait_item_span: Span, + #[context] + pub(crate) trait_span: Span, + #[context] + pub(crate) impl_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 620e6d3cd9ac6..ab595de3e39e7 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3902,12 +3902,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_trait_items.entry(id_in_trait) { Entry::Occupied(entry) => { + let trait_span = decl.parent_module.unwrap().span.shrink_to_lo(); + let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span; self.report_error( span, ResolutionError::TraitImplDuplicate { name: ident, old_span: *entry.get(), trait_item_span: decl.span, + trait_span, + impl_span, }, ); return; @@ -3940,6 +3944,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } }; let trait_path = path_names_to_string(path); + let trait_span = decl.parent_module.unwrap().span.shrink_to_lo(); + let impl_span = self.current_trait_ref.as_ref().unwrap().1.path.span; self.report_error( span, ResolutionError::TraitImplMismatch { @@ -3948,6 +3954,8 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { code, trait_path, trait_item_span: decl.span, + trait_span, + impl_span, }, ); } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 772fdb8c9f14d..b92da685bc6a5 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -323,9 +323,17 @@ enum ResolutionError<'ra> { trait_path: String, trait_item_span: Span, code: ErrCode, + trait_span: Span, + impl_span: Span, }, /// Error E0201: multiple impl items for the same trait item. - TraitImplDuplicate { name: Ident, trait_item_span: Span, old_span: Span }, + TraitImplDuplicate { + name: Ident, + trait_item_span: Span, + old_span: Span, + trait_span: Span, + impl_span: Span, + }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, /// `self` used instead of `Self` in a generic parameter diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 798dcaf969965..fdfc2aacc79a8 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -21,7 +21,9 @@ use rustc_errors::{ColorConfig, DiagCtxtFlags}; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; -use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; +use rustc_span::edition::{ + DEFAULT_EDITION, EDITION_NAME_LIST, EDITION_NAME_LIST_STABLE, Edition, LATEST_STABLE_EDITION, +}; use rustc_span::source_map::FilePathMapping; use rustc_span::{ FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, Symbol, sym, @@ -2369,22 +2371,35 @@ pub fn parse_error_format( error_format } -pub fn parse_crate_edition(early_dcx: &EarlyDiagCtxt, matches: &getopts::Matches) -> Edition { +pub fn parse_crate_edition( + early_dcx: &EarlyDiagCtxt, + matches: &getopts::Matches, + has_input: bool, +) -> Edition { + let is_nightly = nightly_options::match_is_nightly_build(matches); + let edition_list = if is_nightly { EDITION_NAME_LIST } else { EDITION_NAME_LIST_STABLE }; let edition = match matches.opt_str("edition") { Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| { early_dcx.early_fatal(format!( - "argument for `--edition` must be one of: \ - {EDITION_NAME_LIST}. (instead was `{arg}`)" + "argument for `--edition` must be one of: {edition_list} (instead was `{arg}`)", )) }), - None => DEFAULT_EDITION, + None => { + if has_input { + eprintln!( + "`--edition` is unspecified, defaulting to `{DEFAULT_EDITION}` while the \ + latest is `{LATEST_STABLE_EDITION}`; it must be one of: {edition_list}\n", + ); + } + DEFAULT_EDITION + } }; if !edition.is_stable() && !nightly_options::is_unstable_enabled(matches) { - let is_nightly = nightly_options::match_is_nightly_build(matches); let msg = if !is_nightly { format!( - "the crate requires edition {edition}, but the latest edition supported by this Rust version is {LATEST_STABLE_EDITION}" + "the crate requires edition {edition}, but the latest edition supported by this \ + Rust version is {LATEST_STABLE_EDITION}" ) } else { format!("edition {edition} is unstable and only available with -Z unstable-options") @@ -2681,10 +2696,14 @@ fn parse_remap_path_prefix( // JUSTIFICATION: before wrapper fn is available #[allow(rustc::bad_opt_access)] -pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches) -> Options { +pub fn build_session_options( + early_dcx: &mut EarlyDiagCtxt, + matches: &getopts::Matches, + has_input: bool, +) -> Options { let color = parse_color(early_dcx, matches); - let edition = parse_crate_edition(early_dcx, matches); + let edition = parse_crate_edition(early_dcx, matches, has_input); let crate_name = matches.opt_str("crate-name"); let unstable_features = UnstableFeatures::from_environment(crate_name.as_deref()); @@ -2852,14 +2871,13 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M if !unstable_options_enabled && cg.force_frame_pointers == FramePointer::NonLeaf { early_dcx.early_fatal( "`-Cforce-frame-pointers=non-leaf` or `always` also requires `-Zunstable-options` \ - and a nightly compiler", + and a nightly compiler", ) } if !nightly_options::is_unstable_enabled(matches) && !unstable_opts.offload.is_empty() { early_dcx.early_fatal( - "`-Zoffload=Enable` also requires `-Zunstable-options` \ - and a nightly compiler", + "`-Zoffload=Enable` also requires `-Zunstable-options` and a nightly compiler", ) } @@ -2875,8 +2893,8 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M if let Some(flavor) = cg.linker_flavor { if flavor.is_unstable() { early_dcx.early_fatal(format!( - "the linker flavor `{}` is unstable, the `-Z unstable-options` \ - flag must also be passed to use the unstable values", + "the linker flavor `{}` is unstable, the `-Z unstable-options` flag must also \ + be passed to use the unstable values", flavor.desc() )); } diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index 38165f511b417..d4f3f0e208e34 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -45,7 +45,10 @@ pub const ALL_EDITIONS: &[Edition] = &[ Edition::EditionFuture, ]; +/// All the valid editions that `--edition` will accept on nightly. pub const EDITION_NAME_LIST: &str = "<2015|2018|2021|2024|future>"; +/// All the valid editions on stable, which doesn't include `future`. +pub const EDITION_NAME_LIST_STABLE: &str = "<2015|2018|2021|2024>"; pub const DEFAULT_EDITION: Edition = Edition::Edition2015; @@ -53,13 +56,7 @@ pub const LATEST_STABLE_EDITION: Edition = Edition::Edition2024; impl fmt::Display for Edition { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = match *self { - Edition::Edition2015 => "2015", - Edition::Edition2018 => "2018", - Edition::Edition2021 => "2021", - Edition::Edition2024 => "2024", - Edition::EditionFuture => "future", - }; + let s = self.as_str(); write!(f, "{s}") } } @@ -75,6 +72,16 @@ impl Edition { } } + pub fn as_str(&self) -> &'static str { + match *self { + Edition::Edition2015 => "2015", + Edition::Edition2018 => "2018", + Edition::Edition2021 => "2021", + Edition::Edition2024 => "2024", + Edition::EditionFuture => "future", + } + } + pub fn is_stable(self) -> bool { match self { Edition::Edition2015 => true, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3debf3248ff80..a003f20e3512d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -671,7 +671,6 @@ symbols! { cold_path, collapse_debuginfo, column, - common, compare_bytes, compare_exchange, compare_exchange_weak, @@ -1176,7 +1175,6 @@ symbols! { integer_max, integer_min, integral, - internal, internal_eq_trait_method_impls, internal_features, interrupt, diff --git a/compiler/rustc_trait_selection/src/diagnostics.rs b/compiler/rustc_trait_selection/src/diagnostics.rs index 09689af889799..90f6faaad1dc1 100644 --- a/compiler/rustc_trait_selection/src/diagnostics.rs +++ b/compiler/rustc_trait_selection/src/diagnostics.rs @@ -1221,6 +1221,10 @@ pub(crate) struct TraitImplDiff { pub found_short: String, pub expected: String, pub found: String, + #[context] + pub trait_span: Span, + #[context] + pub impl_span: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 8a14ad3a9c675..a561f9b211d2e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -35,14 +35,21 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ) = error.clone() && let (SubregionOrigin::Subtype(sup_trace), SubregionOrigin::Subtype(sub_trace)) = (&sup_origin, &sub_origin) - && let &ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } = - sub_trace.cause.code() + && let &ObligationCauseCode::CompareImplItem { + trait_item_def_id, impl_item_def_id, .. + } = sub_trace.cause.code() && sub_trace.values == sup_trace.values && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values { // FIXME(compiler-errors): Don't like that this needs `Ty`s, but // all of the region highlighting machinery only deals with those. - let guar = self.emit_err(var_origin.span(), expected, found, trait_item_def_id); + let guar = self.emit_err( + var_origin.span(), + expected, + found, + trait_item_def_id, + impl_item_def_id.into(), + ); return Some(guar); } None @@ -54,6 +61,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { expected: ty::PolyFnSig<'tcx>, found: ty::PolyFnSig<'tcx>, trait_item_def_id: DefId, + impl_item_def_id: DefId, ) -> ErrorGuaranteed { let trait_sp = self.tcx().def_span(trait_item_def_id); @@ -135,6 +143,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { found, expected_short, found_short, + trait_span: self.tcx().def_span(self.tcx().parent(trait_item_def_id)).shrink_to_lo(), + impl_span: self.tcx().def_span(self.tcx().parent(impl_item_def_id)).shrink_to_lo(), }; let mut diag = self.tcx().dcx().create_err(diag); diff --git a/library/core/src/arch.rs b/library/core/src/arch.rs index 737a643ef8659..04fc16f50085b 100644 --- a/library/core/src/arch.rs +++ b/library/core/src/arch.rs @@ -65,11 +65,15 @@ pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))? /// to a trapping instruction (e.g. an undefined instruction) instead, or to some other form of /// target-specific abort that may or may not support convenient resumption. /// -/// The precise behavior and the precise instruction generated are not guaranteed, except that in -/// normal execution with no debug tooling involved this will not continue executing. +/// The precise behavior is not guaranteed, it depends on the architecture and operating system. +/// Not all architectures guarantee that a breakpoint instruction interrupts execution in the absence +/// of a debugger, and not all operating systems and execution environments guarantee that such an +/// interrupt aborts the current process. /// +/// The precise instruction is guaranteed only on the following targets: /// - On x86 targets, this produces an `int3` instruction. /// - On aarch64 targets, this produces a `brk #0xf000` instruction. +// When adding more items above, also add cases to the test in `tests/assembly-llvm/breakpoint.rs`. // When stabilizing this, update the comment on `core::intrinsics::breakpoint`. #[unstable(feature = "breakpoint", issue = "133724")] #[inline(always)] diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e5e443088f36e..1a6f2a477c7ea 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -502,6 +502,57 @@ impl fmt::Debug for SplitPaths<'_> { } } +/// An iterator that splits an environment variable into paths according to +/// platform-specific conventions. +/// +/// The iterator element type is &[Path]. +/// +/// This structure is created by [`env::split_paths_ref()`]. See its +/// documentation for more. +/// +/// [`env::split_paths_ref()`]: split_paths_ref +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub struct SplitPathsRef<'a> { + inner: paths_imp::SplitPathsRef<'a>, +} + +/// Parses input according to platform conventions for the `PATH` +/// environment variable. +/// +/// Unlike [`split_paths`], this function does not allocate and instead yields +/// [`Path`]s borrowed from `unparsed`. Returns `None` on platforms that may +/// require allocations to handle `PATH` splitting conventions. +/// +/// # Platform-specific behavior +/// +/// Returns `Some` on Unix platforms and `None` on all other platforms. +/// Note that this [may change in the future][changes]. +/// +/// [changes]: io#platform-specific-behavior +#[unstable(feature = "env_split_paths_ref", issue = "none")] +pub fn split_paths_ref + ?Sized>(unparsed: &T) -> Option> { + Some(SplitPathsRef { inner: paths_imp::split_paths_ref(unparsed.as_ref())? }) +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.inner.next() + } + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "env_split_paths_ref", issue = "none")] +impl fmt::Debug for SplitPathsRef<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SplitPathsRef").finish_non_exhaustive() + } +} + /// The error type for operations on the `PATH` variable. Possibly returned from /// [`env::join_paths()`]. /// diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 3f0f276551b45..b4561457adb1e 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -49,6 +49,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, fs as fs_imp}; use crate::time::SystemTime; use crate::{error, fmt}; +pub(crate) mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use self::dirs::HomeDirs; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use self::dirs::MediaDirs; + /// An object providing access to an open file on the filesystem. /// /// An instance of a `File` can be read and/or written depending on what options diff --git a/library/std/src/fs/dirs.rs b/library/std/src/fs/dirs.rs new file mode 100644 index 0000000000000..f8b855c994ff2 --- /dev/null +++ b/library/std/src/fs/dirs.rs @@ -0,0 +1,588 @@ +use crate::path::{Path, PathBuf}; +use crate::sys::fs::{ExtraHomeDirs, ExtraMediaDirs}; + +/// Common user directory paths used for user-specific application files. +/// +/// It is not required that the user directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where user directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different home directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `HomeDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug, Clone)] +pub struct HomeDirs { + pub(crate) cache: Option, + pub(crate) config: Option, + pub(crate) data: Option, + pub(crate) state: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra home dirs"))] + pub(crate) extra: ExtraHomeDirs, +} + +/// Common user directory paths used for user-specific media files. +/// +/// It is not required that the media directories are accessible by the current +/// user, nor that there is a directory at that path. A robust application +/// should handle the case where media directories are incorrectly configured. +/// +/// Even when configured correctly, multiple paths may point to the same location. +/// You should not assume that a file written relative to one directory will not +/// conflict with the same relative path in a different media directory. +/// +/// # Platform-specific behavior +/// +/// As the filesystem conventions for discovering directories varies between +/// operating systems, constructors for `MediaDirs` that use the host platform's +/// conventions are provided as extension traits under the `std::os` module. +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[derive(Debug, Clone)] +pub struct MediaDirs { + pub(crate) desktop: Option, + pub(crate) documents: Option, + pub(crate) downloads: Option, + pub(crate) music: Option, + pub(crate) pictures: Option, + pub(crate) videos: Option, + #[cfg_attr(not(unix), expect(dead_code, reason = "no extra media dirs"))] + pub(crate) extra: ExtraMediaDirs, +} + +// NB: HomeDirs and MediaDirs intentionally do not implement Default. Self::empty() +// is a logical default, but users may also intuit the default to use default +// platform conventions. Omitting Default pushes users to explicitly choose. + +impl HomeDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `HomeDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn empty() -> Self { + Self { cache: None, config: None, data: None, state: None, extra: Default::default() } + } + + /// A base directory relative to which user-specific non-essential cache + /// data files should be stored. + /// + /// "Cache" files are temporary data that can be used to cache redundant + /// work of an application, but which can be discarded arbitrarily and + /// recreated as necessary. Files in this directory may potentially be + /// automatically purged any time they are not currently open, or they + /// may not, depending on system configuration. A robust application + /// should ensure that its caches do not grow without a reasonable bound. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific cache files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CACHE_HOME:-$HOME/.cache}` | + /// | [Darwin] (macOS) | [`NSCachesDirectory`] (`$HOME/Library/Caches`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_cache_home`](Self::set_cache_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn cache_home(&self) -> Option<&Path> { + self.cache.as_deref() + } + + /// A base directory relative to which user-specific configuration files + /// should be stored. + /// + /// "Config" files are configuration managed by the user, either by editing + /// the files directly or through a managing application. Configuration is + /// generally expected to be meaningful to the user and portable enough to + /// back up and synchronize across the same user's account on multiple + /// systems. + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific configuration files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_CONFIG_HOME:-$HOME/.config}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_config_home`](Self::set_config_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn config_home(&self) -> Option<&Path> { + self.config.as_deref() + } + + /// A base directory relative to which user-specific data files should be + /// stored. + /// + /// "Data" files are application-specific data that is meaningful to the + /// user in some way and does not implicitly rely on system configuration + /// or details of how the application is installed otherwise irrelevant to + /// the user. As such, data makes sense to back up and synchronize between + /// the same user's account on multiple systems. If a file is specific to + /// a single machine, it's probably [state](Self::state_home). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific data files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_DATA_HOME:-$HOME/.local/share}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_RoamingAppData}`] (`%APPDATA%`) | + /// + /// Other paths can be configured via [`set_data_home`](Self::set_data_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_RoamingAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn data_home(&self) -> Option<&Path> { + self.data.as_deref() + } + + /// A base directory relative to which user-specific state files should be + /// stored. + /// + /// "State" files are data that should persist between application restarts, + /// but which is not important nor portable enough to the user to synchronize + /// between multiple systems like [data](Self::data_home) are. Common examples + /// include history (such as logs, recently used files, etc) and any current + /// state of the application that should be reused (such as view, layout, open + /// files, undo history, etc). + /// + /// This is the same directory for all applications. Applications should + /// use a subdirectory for application-specific state files. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `${XDG_STATE_HOME:-$HOME/.local/state}` | + /// | [Darwin] (macOS) | [`NSApplicationSupportDirectory`] (`$HOME/Library/Application Support`) | + /// | [Windows] | [`{FOLDERID_LocalAppData}`] (`%LOCALAPPDATA%`) | + /// + /// Other paths can be configured via [`set_state_home`](Self::set_state_home). + /// + /// [XDG]: crate::os::unix::fs::HomeDirsExt + /// [Darwin]: crate::os::darwin::fs::HomeDirsExt + /// [Windows]: crate::os::windows::fs::HomeDirsExt + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + /// [`{FOLDERID_LocalAppData}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn state_home(&self) -> Option<&Path> { + self.state.as_deref() + } +} + +impl MediaDirs { + /// Create a known user directory set with no known directories. + /// + /// This is useful with the builder `set_*` methods to create a `MediaDirs` + /// with exactly the directories you want, without any other defaults. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn empty() -> Self { + Self { + desktop: None, + documents: None, + downloads: None, + music: None, + pictures: None, + videos: None, + extra: Default::default(), + } + } + + /// The OS-recognized user "Desktop" directory, often the `Desktop` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DESKTOP` (`$HOME/Desktop`) | + /// | [Darwin] (macOS) | [`NSDesktopDirectory`] (`$HOME/Desktop`) | + /// | [Windows] | [`{FOLDERID_Desktop}`] (`%USERPROFILE%\Desktop`) | + /// + /// Other paths can be configured via [`set_desktop`](Self::set_desktop). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`{FOLDERID_Desktop}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn desktop(&self) -> Option<&Path> { + self.desktop.as_deref() + } + + /// The OS-recognized user "Documents" directory, often the `Documents` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOCUMENTS` (`$HOME/Documents`) | + /// | [Darwin] (macOS) | [`NSDocumentDirectory`] (`$HOME/Documents`) | + /// | [Windows] | [`{FOLDERID_Documents}`] (`%USERPROFILE%\Documents`) | + /// + /// Other paths can be configured via [`set_documents`](Self::set_documents). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`{FOLDERID_Documents}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn documents(&self) -> Option<&Path> { + self.documents.as_deref() + } + + /// The OS-recognized user "Downloads" directory, often the `Downloads` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir DOWNLOAD` (`$HOME/Downloads`) | + /// | [Darwin] (macOS) | [`NSDownloadsDirectory`] (`$HOME/Downloads`) | + /// | [Windows] | [`{FOLDERID_Downloads}`] (`%USERPROFILE%\Downloads`) | + /// + /// Other paths can be configured via [`set_downloads`](Self::set_downloads). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`{FOLDERID_Downloads}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn downloads(&self) -> Option<&Path> { + self.downloads.as_deref() + } + + /// The OS-recognized user "Music" directory, often the `Music` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir MUSIC` (`$HOME/Music`) | + /// | [Darwin] (macOS) | [`NSMusicDirectory`] (`$HOME/Music`) | + /// | [Windows] | [`{FOLDERID_Music}`] (`%USERPROFILE%\Music`) | + /// + /// Other paths can be configured via [`set_music`](Self::set_music). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`{FOLDERID_Music}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn music(&self) -> Option<&Path> { + self.music.as_deref() + } + + /// The OS-recognized user "Pictures" directory, often the `Pictures` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir PICTURES` (`$HOME/Pictures`) | + /// | [Darwin] (macOS) | [`NSPicturesDirectory`] (`$HOME/Pictures`) | + /// | [Windows] | [`{FOLDERID_Pictures}`] (`%USERPROFILE%\Pictures`) | + /// + /// Other paths can be configured via [`set_pictures`](Self::set_pictures). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`{FOLDERID_Pictures}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn pictures(&self) -> Option<&Path> { + self.pictures.as_deref() + } + + /// The OS-recognized user "Videos" directory, often the `Videos` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + /// + /// # Platform-specific behavior + /// + /// When constructed using platform-specific conventions, the value is: + /// + /// | OS | Path | + /// | -- | ---- | + /// | [XDG] (Linux) | `xdg-user-dir VIDEOS` (`$HOME/Videos`) | + /// | [Darwin] (macOS) | [`NSMoviesDirectory`] (`$HOME/Movies`) | + /// | [Windows] | [`{FOLDERID_Videos}`] (`%USERPROFILE%\Videos`) | + /// + /// Other paths can be configured via [`set_videos`](Self::set_videos). + /// + /// [XDG]: crate::os::unix::fs::MediaDirsExt + /// [Darwin]: crate::os::darwin::fs::MediaDirsExt + /// [Windows]: crate::os::windows::fs::MediaDirsExt + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + /// [`{FOLDERID_Videos}`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn videos(&self) -> Option<&Path> { + self.videos.as_deref() + } +} + +impl HomeDirs { + /// Set the path for [Self::cache_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_cache_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "cache home directory path must be absolute"); + self.cache = Some(path); + self + } + + /// Set the path for [Self::config_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_config_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "config home directory path must be absolute"); + self.config = Some(path); + self + } + + /// Set the path for [Self::data_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_data_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "data home directory path must be absolute"); + self.data = Some(path); + self + } + + /// Set the path for [Self::state_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + pub fn set_state_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "state home directory path must be absolute"); + self.state = Some(path); + self + } +} + +impl MediaDirs { + /// Set the path for [Self::desktop]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_desktop(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "desktop directory path must be absolute"); + self.desktop = Some(path); + self + } + + /// Set the path for [Self::documents]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_documents(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "documents directory path must be absolute"); + self.documents = Some(path); + self + } + + /// Set the path for [Self::downloads]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_downloads(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "downloads directory path must be absolute"); + self.downloads = Some(path); + self + } + + /// Set the path for [Self::music]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_music(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "music directory path must be absolute"); + self.music = Some(path); + self + } + + /// Set the path for [Self::pictures]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_pictures(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "pictures directory path must be absolute"); + self.pictures = Some(path); + self + } + + /// Set the path for [Self::videos]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + pub fn set_videos(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "videos directory path must be absolute"); + self.videos = Some(path); + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn test_dir(what: &str) -> PathBuf { + crate::env::current_dir().unwrap().ancestors().last().unwrap().join(what) + } + + #[test] + fn test_home_dirs_field_hookup_matches() { + let mut dirs = HomeDirs::empty(); + + assert_eq!(dirs.config_home(), None); + assert_eq!(dirs.data_home(), None); + assert_eq!(dirs.state_home(), None); + assert_eq!(dirs.cache_home(), None); + + let config = test_dir("config"); + let data = test_dir("data"); + let state = test_dir("state"); + let cache = test_dir("cache"); + + dirs.set_config_home(config.clone()); + dirs.set_data_home(data.clone()); + dirs.set_state_home(state.clone()); + dirs.set_cache_home(cache.clone()); + + assert_eq!(dirs.config_home(), Some(config.as_ref())); + assert_eq!(dirs.data_home(), Some(data.as_ref())); + assert_eq!(dirs.state_home(), Some(state.as_ref())); + assert_eq!(dirs.cache_home(), Some(cache.as_ref())); + } + + #[test] + fn test_media_dirs_field_hookup_matches() { + let mut dirs = MediaDirs::empty(); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), None); + assert_eq!(dirs.videos(), None); + + let desktop = test_dir("desktop"); + let documents = test_dir("documents"); + let downloads = test_dir("downloads"); + let music = test_dir("music"); + let pictures = test_dir("pictures"); + let videos = test_dir("videos"); + + dirs.set_desktop(desktop.clone()); + dirs.set_documents(documents.clone()); + dirs.set_downloads(downloads.clone()); + dirs.set_music(music.clone()); + dirs.set_pictures(pictures.clone()); + dirs.set_videos(videos.clone()); + + assert_eq!(dirs.desktop(), Some(desktop.as_ref())); + assert_eq!(dirs.documents(), Some(documents.as_ref())); + assert_eq!(dirs.downloads(), Some(downloads.as_ref())); + assert_eq!(dirs.music(), Some(music.as_ref())); + assert_eq!(dirs.pictures(), Some(pictures.as_ref())); + assert_eq!(dirs.videos(), Some(videos.as_ref())); + } +} diff --git a/library/std/src/os/darwin/fs.rs b/library/std/src/os/darwin/fs.rs index fc2869d6b13f6..b41391f19dccf 100644 --- a/library/std/src/os/darwin/fs.rs +++ b/library/std/src/os/darwin/fs.rs @@ -7,6 +7,13 @@ use crate::fs::{self, Metadata}; use crate::sys::{AsInner, AsInnerMut, IntoInner}; use crate::time::SystemTime; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// OS-specific extensions to [`fs::Metadata`]. /// /// [`fs::Metadata`]: crate::fs::Metadata diff --git a/library/std/src/os/darwin/fs/dirs.rs b/library/std/src/os/darwin/fs/dirs.rs new file mode 100644 index 0000000000000..6aaa910981b0b --- /dev/null +++ b/library/std/src/os/darwin/fs/dirs.rs @@ -0,0 +1,313 @@ +use crate::env; +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::PathBuf; + +/// Darwin-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// On iOS, tvOS, watchOS, visionOS, and sandboxed macOS applications, + /// these directories are within the application's container. Outside + /// the sandbox, these are subdirectories of the `~/Library` directory on + /// macOS. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `HomeDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`cache_home`] | [`NSCachesDirectory`] (`~/Library/Caches`) | + /// | [`config_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`data_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// | [`state_home`] | [`NSApplicationSupportDirectory`] (`~/Library/Application Support`) | + /// + /// Note that the Application Support directory is used for the config, + /// data, and state directories. It is always possible for multiple user + /// directories to be configured to the same path, but this is the common + /// configuration on Apple platforms, making it even more important to not + /// assume files in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSCachesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/cachesdirectory?language=objc + /// [`NSApplicationSupportDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/applicationsupportdirectory?language=objc + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn sysdir() -> io::Result; +} + +/// Darwin-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the standard user directory paths for the current user. + /// + /// The produced directory paths are not guaranteed to be the canonical + /// paths to the directories; they are allowed to be sandbox-redirected + /// paths as long as the directory is accessible there. + /// + /// The loaded common directories are: + /// + /// | `MediaDirs` | [`NSSearchPathDirectory`] | + /// | ---------- | ----------------------- | + /// | [`desktop`] | [`NSDesktopDirectory`] (`~/Desktop`) | + /// | [`documents`] | [`NSDocumentDirectory`] (`~/Documents`) | + /// | [`downloads`] | [`NSDownloadsDirectory`] (`~/Downloads`) | + /// | [`music`] | [`NSMusicDirectory`] (`~/Music`) | + /// | [`pictures`] | [`NSPicturesDirectory`] (`~/Pictures`) | + /// | [`videos`] | [`NSMoviesDirectory`] (`~/Movies`) | + /// + /// # Errors + /// + /// Errors if the the [user home](env::home_dir) cannot be determined. + // Errors due to the underlying sysdir(3) API should never occur, as + // - the user domain only has one directory for each search path; + // - the user domain always returns subdirectory paths of `~`; + // - the username plus OS defined path segments cannot exceed PATH_MAX; and + // - the username and OS defined path segments are always valid UTF-8. + /// + /// # Implementation-specific behavior + /// + /// Uses the `sysdir(3)` API from `libSystem` to discover the standard + /// user directories. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + /// + /// [`NSSearchPathDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory?language=objc + /// [`NSDesktopDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/desktopdirectory?language=objc + /// [`NSDocumentDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/documentdirectory?language=objc + /// [`NSDownloadsDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/downloadsdirectory?language=objc + /// [`NSMusicDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/musicdirectory?language=objc + /// [`NSPicturesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/picturesdirectory?language=objc + /// [`NSMoviesDirectory`]: https://developer.apple.com/documentation/foundation/filemanager/searchpathdirectory/moviesdirectory?language=objc + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn sysdir() -> io::Result; +} + +fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "home path not absolute")) +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[cfg(target_vendor = "apple")] +impl HomeDirsExt for HomeDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = HomeDirs::empty(); + let home = user_home()?; + + let caches = sys::get_user_dir(&home, SYSDIR_DIRECTORY_CACHES)?; + let application_support = sys::get_user_dir(&home, SYSDIR_DIRECTORY_APPLICATION_SUPPORT)?; + + dirs.cache = caches; + // Apple puts config/data/state all in Application Support + dirs.config = application_support.clone(); + dirs.data = application_support.clone(); + dirs.state = application_support; + + Ok(dirs) + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +#[cfg(target_vendor = "apple")] +impl MediaDirsExt for MediaDirs { + fn sysdir() -> io::Result { + use libc::sysdir_search_path_directory_t::*; + + let mut dirs = MediaDirs::empty(); + let home = user_home()?; + + let desktop = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DESKTOP)?; + let documents = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOCUMENT)?; + let downloads = sys::get_user_dir(&home, SYSDIR_DIRECTORY_DOWNLOADS)?; + let movies = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MOVIES)?; + let music = sys::get_user_dir(&home, SYSDIR_DIRECTORY_MUSIC)?; + let pictures = sys::get_user_dir(&home, SYSDIR_DIRECTORY_PICTURES)?; + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = movies; + + Ok(dirs) + } +} + +/// Safer wrapper around the sysdir(3) API +#[cfg(target_vendor = "apple")] +mod sys { + use crate::ffi::{CStr, c_char}; + use crate::io::{self, ErrorKind, const_error}; + use crate::path::{Path, PathBuf}; + + /// Get the path for a system directory using `sysdir(3)`. + pub fn get_user_dir( + home: &Path, + kind: libc::sysdir_search_path_directory_t, + ) -> io::Result> { + use libc::sysdir_search_path_domain_mask_t::SYSDIR_DOMAIN_MASK_USER; + + // SAFETY: SYSDIR_DOMAIN_MASK_USER < SYSDIR_DOMAIN_MASK_ALL + let mut iter = unsafe { Iter::new(home, kind, SYSDIR_DOMAIN_MASK_USER) }; + let Some(path) = iter.next() else { + return Ok(None); + }; + let path = path?; + + if iter.next().is_some() { + // more than one path returned (shouldn't happen for SYSDIR_DOMAIN_MASK_USER) + return Err(const_error!( + ErrorKind::InvalidData, + "multiple paths returned for standard user directory", + )); + } + + Ok(Some(path)) + } + + struct Iter<'a> { + home: &'a Path, + state: libc::sysdir_search_path_enumeration_state, + } + + impl Drop for Iter<'_> { + fn drop(&mut self) { + for _ in self {} + } + } + + impl<'a> Iter<'a> { + // SAFETY: `mask` must be <= `SYSDIR_DOMAIN_MASK_ALL` + pub unsafe fn new( + home: &'a Path, + kind: libc::sysdir_search_path_directory_t, + mask: libc::sysdir_search_path_domain_mask_t, + ) -> Self { + // SAFETY: forwarded to the caller + let state = unsafe { libc::sysdir_start_search_path_enumeration(kind, mask) }; + Self { home, state } + } + } + + impl Iterator for Iter<'_> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + let mut buf = [0u8; libc::PATH_MAX as usize]; + if self.state != 0 { + // SAFETY: `self.state` is nonzero and comes from prior sysdir_{start|get_next}_search_path_enumeration call + // SAFETY: sysdir_get_next_search_path_enumeration will write at most `PATH_MAX` bytes to `path` + self.state = unsafe { + libc::sysdir_get_next_search_path_enumeration( + self.state, + buf.as_mut_ptr() as *mut c_char, + ) + }; + } + + if self.state == 0 { + // exhausted + return None; + } + + let Ok(path) = CStr::from_bytes_until_nul(&buf) else { + // should be impossible given home-relative paths, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path too long", + ))); + }; + + let Ok(path) = path.to_str() else { + // should be impossible on a working system, but be defensive + return Some(Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not valid UTF-8", + ))); + }; + + // expand `~` shorthand + Some(match path { + "~" => Ok(self.home.into()), + _ if path.starts_with("~/") => Ok(self.home.join(&path[2..])), + _ if path.starts_with("~") => Err(const_error!( + ErrorKind::InvalidData, + "standard user directory relative to different user", + )), + _ => { + let path = PathBuf::from(path); + if path.is_relative() { + Err(const_error!( + ErrorKind::InvalidData, + "standard user directory path not absolute", + )) + } else { + Ok(path) + } + } + }) + } + } +} + +#[cfg(test)] +#[cfg(target_vendor = "apple")] +mod tests { + use super::*; + + #[test] + fn can_fetch_sysdir_paths() { + let dirs = HomeDirs::sysdir().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::sysdir().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs index b93f94225070a..fcafe1c29797f 100644 --- a/library/std/src/os/unix/fs.rs +++ b/library/std/src/os/unix/fs.rs @@ -22,6 +22,13 @@ use crate::{io, sys}; #[cfg(test)] mod tests; +pub(crate) mod dirs; + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::{HomeDirsExt, XdgDirs}; + /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { diff --git a/library/std/src/os/unix/fs/dirs.rs b/library/std/src/os/unix/fs/dirs.rs new file mode 100644 index 0000000000000..5d8e13c7a730a --- /dev/null +++ b/library/std/src/os/unix/fs/dirs.rs @@ -0,0 +1,585 @@ +use crate::env::{self, SplitPathsRef}; +use crate::ffi::{OsStr, OsString}; +use crate::fs::{self, HomeDirs, MediaDirs}; +use crate::io::{self, ErrorKind, const_error}; +use crate::path::{Path, PathBuf}; + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraHomeDirs { + runtime: Option, + config_path: Option, + data_path: Option, +} + +#[derive(Debug, Default, Clone)] +#[non_exhaustive] +pub struct ExtraMediaDirs { + templates: Option, +} + +/// XDG-specific extensions to [`fs::HomeDirs`](HomeDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project in the +/// [XDG Base Directory Specification][xdg-basedir]. These conventions have +/// been largely adopted by Linux distributions. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the user directory paths according to the + /// [XDG Base Directory Specification][xdg-basedir]. + /// + /// Each base directory path is set to the value of its corresponding + /// `XDG_*` environment variable (if it is set and non-empty), else to + /// the default value defined by the specification. + /// + /// | Field | Environment Variable | Default Value | + /// | ----- | -------------------- | ------------- | + /// | [`cache_home`] | `XDG_CACHE_HOME` | `$HOME/.cache` | + /// | [`config_home`] | `XDG_CONFIG_HOME` | `$HOME/.config` | + /// | [`data_home`] | `XDG_DATA_HOME` | `$HOME/.local/share` | + /// | [`state_home`] | `XDG_STATE_HOME` | `$HOME/.local/state` | + /// | [`runtime_home`] | `XDG_RUNTIME_DIR` | (see method docs) | + /// | [`config_dirs`] | `XDG_CONFIG_DIRS` | `/etc/xdg` | + /// | [`data_dirs`] | `XDG_DATA_DIRS` | `/usr/local/share/`, `/usr/share/` | + /// + /// Note that `$HOME` here means [`env::home_dir`], which uses + /// `$HOME` if set and non-empty, but falls back to the system password + /// database if it isn't set. + /// + /// All paths are required to be absolute. If a relative path is configured + /// by the environment, it is ignored and the default value is used instead. + /// + /// `config_dirs` and `data_dirs` are a list of delimited paths using the + /// [`env::split_paths`] delimiter. If some but not all paths in the list are + /// relative, those relative paths are ignored and the remaining absolute + /// paths are used. If there are no valid absolute paths, the default value + /// is used instead. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined. + /// + /// [xdg-basedir]: https://specifications.freedesktop.org/basedir/ + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + /// [`runtime_home`]: HomeDirsExt::runtime_home + /// [`config_dirs`]: HomeDirsExt::config_dirs + /// [`data_dirs`]: HomeDirsExt::data_dirs + /// [`env::split_paths`]: crate::env::split_paths + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn xdg() -> io::Result; + + /// A base directory relative to which user-specific runtime files + /// (such as sockets, named pipes, etc) should be stored. + /// + /// Files in this directory may be subjected to periodic clean-up. + /// Larger files should not be placed here, since it might reside in + /// runtime memory and cannot necessarily be swapped out to disk. + /// + /// This path does not have a default if not set. If it isn't set, + /// applications should fall back to a replacement directory with + /// similar capabilities and print a warning message. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn runtime_home(&self) -> Option<&Path>; + + /// A preference-ordered list of base directories to search for config + /// files *in addition to* [`config_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`config_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`config_home`]: HomeDirs::config_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn config_dirs(&self) -> Option>; + + /// A preference-ordered list of base directories to search for data + /// files *in addition to* [`data_home`]. + /// + /// The order of directories denotes their importance; the first directory + /// is the most important. Information defined relative to the more + /// important base directory takes precedent. [`data_home`] is not + /// necessarily present in this list, and is considered more important + /// than any base directory in this list. + /// + /// [`data_home`]: HomeDirs::data_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn data_dirs(&self) -> Option>; + + /// Set the path for [Self::runtime_home]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self; + + /// Set the paths for [Self::config_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self; + + /// Set the paths for [Self::data_dirs]. + /// + /// Takes one or more paths joined appropriately for the `PATH` environment + /// variable, as by [`env::join_paths`]. + /// + /// # Panics + /// + /// Panics if any of the provided paths are not absolute. + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self; +} + +/// XDG-specific extensions to [`fs::MediaDirs`](MediaDirs). +/// +/// The XDG conventions are defined by the Freedesktop.org project through the +/// [xdg-user-dirs] tool. This configuration is generally present on desktop +/// Linux distributions, although adoption is less widespread than the base +/// directory specification. +/// +/// The XDG conventions are written to be usable on any Unix-like filesystem, +/// thus this extension being provided in `os::unix` rather than `os::linux`. +/// However, while some tooling does use XDG conventions on macOS, note that +/// macOS has its own separate conventions for user directories. Consider +/// carefully what conventions your users will expect your application to +/// follow along with any legacy path compatibility you might need to support. +/// +/// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the user directory paths according to the [xdg-user-dirs] tool. + /// + /// This directly reads and parses the `$XDG_CONFIG_HOME/user-dirs.dirs` + /// file as defined and maintained by the [xdg-user-dirs] tool. + /// + /// # Errors + /// + /// Errors if the user's home directory cannot be determined or if the + /// `$XDG_CONFIG_HOME/user-dirs.dirs` file cannot be read. + /// + /// # Implementation-specific behavior + /// + /// Only the format maintained by xdg-user-dirs-update is supported. Any + /// configuration that does not match the expected format will result in + /// loading an unspecified path or `None` for that directory. To be more + /// specific: + /// + /// - Any line not in the format of `XDG_{NAME}_DIR={path}` where `{NAME}` + /// is one of `DESKTOP`, `DOWNLOAD`, `TEMPLATES`, `PUBLICSHARE`, + /// `DOCUMENTS`, `MUSIC`, `PICTURES`, or `VIDEOS` is ignored. + /// - `{path}` must be a `"`-quoted shell-escaped path. + /// - `{path}` may only start with `/` or `$HOME/`. A home-relative path + /// is returned relative to [`env::home_dir`]; shell expansion is not + /// performed. + /// - A directory set to just `$HOME` marks it as removed, and results in + /// a `None` value for that path. + /// - If shell expansion syntax other than a leading `$HOME` is present, + /// the produced directory path is unspecified. This is invalid config + /// according to the xdg-user-dirs tooling. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load paths that we currently + /// ignore, such as path formats that are not canonically supported by + /// xdg-user-dirs but which may occur in manually-edited `user-dirs.dirs`. + /// + /// [xdg-user-dirs]: https://www.freedesktop.org/wiki/Software/xdg-user-dirs/ + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn xdg() -> io::Result; + + /// The OS-privileged user "Templates" directory, often the `Templates` + /// folder in the user's home directory. + /// + /// As a media directory, this should typically be used as a default path + /// for file selection dialogs, not for automatically accessed file paths. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn templates(&self) -> Option<&Path>; + + /// Set the paths for [Self::templates]. + /// + /// # Panics + /// + /// Panics if the provided path is not absolute. + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn set_templates(&mut self, path: PathBuf) -> &mut Self; +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +#[derive(Debug)] +pub struct XdgDirs<'a>(SplitPathsRef<'a>); + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn xdg() -> io::Result { + let mut dirs = HomeDirs::empty(); + let user_home = xdg::user_home()?; + + dirs.set_cache_home(xdg::dir_or_else(|| user_home.join(".cache"), "XDG_CACHE_HOME")); + dirs.set_config_home(xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME")); + dirs.set_data_home(xdg::dir_or_else(|| user_home.join(".local/share"), "XDG_DATA_HOME")); + dirs.set_state_home(xdg::dir_or_else(|| user_home.join(".local/state"), "XDG_STATE_HOME")); + if let Some(runtime) = xdg::dir("XDG_RUNTIME_DIR") { + dirs.set_runtime_home(runtime); + } + + dirs.set_config_dirs(xdg::dirs_or("/etc/xdg", "XDG_CONFIG_DIRS")); + dirs.set_data_dirs(xdg::dirs_or("/usr/local/share/:/usr/share/", "XDG_DATA_DIRS")); + + Ok(dirs) + } + + fn runtime_home(&self) -> Option<&Path> { + self.extra.runtime.as_deref() + } + + fn config_dirs(&self) -> Option> { + self.extra.config_path.as_deref().map(XdgDirs::new) + } + + fn data_dirs(&self) -> Option> { + self.extra.data_path.as_deref().map(XdgDirs::new) + } + + fn set_runtime_home(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "runtime directory path must be absolute"); + self.extra.runtime = Some(path); + self + } + + fn set_config_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "config directory paths must be absolute"); + } + self.extra.config_path = Some(paths); + self + } + + fn set_data_dirs(&mut self, paths: OsString) -> &mut Self { + for path in split_paths_ref(&paths) { + assert!(path.is_absolute(), "data directory paths must be absolute"); + } + self.extra.data_path = Some(paths); + self + } +} + +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn xdg() -> io::Result { + let user_home = xdg::user_home()?; + let config_home = xdg::dir_or_else(|| user_home.join(".config"), "XDG_CONFIG_HOME"); + + let spec = match fs::read(config_home.join("user-dirs.dirs")) { + Ok(spec) => spec, + Err(e) if e.kind() == ErrorKind::NotFound => { + return Err(const_error!( + ErrorKind::NotFound, + "missing `$XDG_CONFIG_HOME/user-dirs.dirs`", + )); + } + Err(e) => return Err(e), + }; + + Ok(xdg::parse_user_dirs(&spec, &user_home)) + } + + fn templates(&self) -> Option<&Path> { + self.extra.templates.as_deref() + } + + fn set_templates(&mut self, path: PathBuf) -> &mut Self { + assert!(path.is_absolute(), "templates directory path must be absolute"); + self.extra.templates = Some(path); + self + } +} + +impl<'a> XdgDirs<'a> { + fn new(paths: &'a OsStr) -> Self { + XdgDirs(split_paths_ref(paths)) + } +} + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl<'a> Iterator for XdgDirs<'a> { + type Item = &'a Path; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +fn split_paths_ref<'a>(paths: &'a OsStr) -> SplitPathsRef<'a> { + // returns Some on cfg(unix) + env::split_paths_ref(paths).unwrap() +} + +mod xdg { + use super::{MediaDirs, split_paths_ref}; + use crate::env::{self, join_paths, var_os}; + use crate::ffi::OsString; + use crate::io::{self, ErrorKind, const_error}; + use crate::ops::Deref; + use crate::os::unix::ffi::{OsStrExt, OsStringExt}; + use crate::path::{Path, PathBuf}; + + pub fn user_home() -> io::Result { + env::home_dir() + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "user home directory path not absolute")) + } + + pub fn dir(env: &str) -> Option { + var_os(env).filter(|s| !s.is_empty()).map(PathBuf::from).filter(|path| path.is_absolute()) + } + + pub fn dir_or_else(fallback: impl FnOnce() -> PathBuf, env: &str) -> PathBuf { + dir(env).unwrap_or_else(fallback) + } + + fn dirs(env: &str) -> Option { + let dirs = var_os(env).filter(|s| !s.is_empty())?; + if split_paths_ref(&dirs).all(|p| p.is_absolute()) { + return Some(dirs); + } + + struct DerefAsRef(pub T); + impl>, U: ?Sized> AsRef for DerefAsRef { + fn as_ref(&self) -> &U { + (*self.0).as_ref() + } + } + + let paths = split_paths_ref(&dirs).filter(|p| p.is_absolute()).map(DerefAsRef); + join_paths(paths).ok().filter(|s| !s.is_empty()) + } + + pub fn dirs_or(fallback: impl Into, env: &str) -> OsString { + dirs(env).unwrap_or_else(|| fallback.into()) + } + + pub fn parse_user_dirs<'a>(spec: &'a [u8], user_home: &Path) -> MediaDirs { + let mut dirs = MediaDirs::empty(); + + for (xdg, path) in + spec.split(|&b| b == b'\n').flat_map(|line| parse_user_dirs_line(line, user_home)) + { + // load the known user directories + match xdg { + b"XDG_DESKTOP_DIR" => dirs.desktop = path, + b"XDG_DOCUMENTS_DIR" => dirs.documents = path, + b"XDG_DOWNLOAD_DIR" => dirs.downloads = path, + b"XDG_MUSIC_DIR" => dirs.music = path, + b"XDG_PICTURES_DIR" => dirs.pictures = path, + b"XDG_VIDEOS_DIR" => dirs.videos = path, + b"XDG_TEMPLATES_DIR" => dirs.extra.templates = path, + b"XDG_PUBLICSHARE_DIR" => { + // we don't expose this directory yet as Windows also has a "public" directory + // and it's not yet clear if we want to expose this at the target-agnostic level + } + _ => { + // ignore unknown variable assignment + } + } + } + + dirs + } + + fn parse_user_dirs_line<'a>( + line: &'a [u8], + user_home: &Path, + ) -> Option<(&'a [u8], Option)> { + // trim whitespace + let trimmed = line.trim_ascii(); + // skip empty lines and comments + if trimmed.is_empty() || trimmed.starts_with(&[b'#']) { + return None; + } + + // only variable assignment lines are allowed; split on `=` + let mut split = trimmed.splitn(2, |&b| b == b'='); + // extract assignment parts; ignore lines not in this format + let var = split.next()?; + let val = split.next()?; + debug_assert_eq!(split.next(), None); + + // the path value is quoted; unquote it + let path = unquote(val, user_home)?; + + let path = Some(path) + // ignore non-absolute paths + .filter(|path| path.is_absolute()) + // setting to the home dir disables the directory configuration + .filter(|path| path != user_home); + + Some((var, path)) + } + + fn unquote(bytes: &[u8], user_home: &Path) -> Option { + let [b'"', rest @ .., b'"'] = bytes else { return None }; + + // setting to the home dir disables the directory configuration; + // if done symbolically, return None here and bypass later work + if matches!(rest, b"$HOME" | b"$HOME/") { + return None; + } + + let mut rest = rest; + let mut s = Vec::with_capacity(rest.len()); + + // expand leading $HOME + if rest.starts_with(b"$HOME/") { + s.extend_from_slice(user_home.as_os_str().as_bytes()); + if !user_home.has_trailing_sep() { + s.push(b'/'); + } + rest = &rest[6..]; + } + + loop { + let i = rest + .iter() + .position(|&b| matches!(b, b'"' | b'\\' | b'$' | b'`')) + .unwrap_or(rest.len()); + s.extend_from_slice(&rest[..i]); + match &rest[i..] { + [] => break, + [b'\\', c @ (b'"' | b'\\' | b'$' | b'`'), tail @ ..] => { + // supported escapes + s.push(*c); + rest = tail; + } + [b'"' | b'\\' | b'$' | b'`', ..] => { + // unsupported shell syntax + return None; + } + _ => { + if cfg!(debug_assertions) { + unreachable!() + } else { + return None; + } + } + } + } + + Some(PathBuf::from(OsString::from_vec(s))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_xdg_base_dirs() { + let dirs = HomeDirs::xdg().unwrap(); + + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + // dirs.runtime() may not exist + assert!(dirs.config_dirs().is_some()); + assert!(dirs.data_dirs().is_some()); + } + + #[test] + fn can_fetch_xdg_media_dirs() { + let dirs = match MediaDirs::xdg() { + Ok(dirs) => dirs, + Err(e) if e.kind() == ErrorKind::NotFound => { + // xdg-user-dirs not initialized on this system, skip the test + return; + } + Err(e) => panic!("failed to fetch xdg user dirs: {e:?}"), + }; + + // even when user-dirs.dirs is present, directories may be unset, + // so we can't assert anything about the dir paths here + let _ = dirs; + } + + #[test] + fn test_well_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# This file is written by xdg-user-dirs-update +# If you want to change or add directories, just edit the line you're +# interested in. All local changes will be retained on the next run. +# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped +# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an +# absolute path. No other format is supported. +# +XDG_DESKTOP_DIR="$HOME/Desktop" +XDG_DOWNLOAD_DIR="$HOME/Downloads" +XDG_TEMPLATES_DIR="/pub/Templates" +XDG_PUBLICSHARE_DIR="/pub" +XDG_DOCUMENTS_DIR="$HOME/Documents" +XDG_MUSIC_DIR="$HOME/My \"Music\"" +XDG_PICTURES_DIR="$HOME/Pictures" +XDG_VIDEOS_DIR="$HOME" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), Some("/home/ferris/Desktop".as_ref())); + assert_eq!(dirs.downloads(), Some("/home/ferris/Downloads".as_ref())); + assert_eq!(dirs.templates(), Some("/pub/Templates".as_ref())); + assert_eq!(dirs.documents(), Some("/home/ferris/Documents".as_ref())); + assert_eq!(dirs.music(), Some("/home/ferris/My \"Music\"".as_ref())); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures".as_ref())); + assert_eq!(dirs.videos(), None); + } + + #[test] + fn test_ill_user_dirs_parsing() { + const TEST_USERDIRS: &'static str = r#" +# unquoted +XDG_DESKTOP_DIR=$HOME/Desktop +# mixed quoting +XDG_DOWNLOAD_DIR="$HOME"/"Downloads" +# non-HOME env +XDG_TEMPLATES_DIR="/home/$USER/Templates" +# missing PUBLICSHARE +# command substitution +XDG_DOCUMENTS_DIR="`pwd`/Documents" +# non-symbolic homedir disabled +XDG_MUSIC_DIR="/home/ferris" +# set multiple times +XDG_PICTURES_DIR="$HOME/Pictures1" +XDG_PICTURES_DIR="$HOME/Pictures2" +# relative path +XDG_VIDEOS_DIR="Videos" +"#; + + let dirs = xdg::parse_user_dirs(TEST_USERDIRS.as_bytes(), Path::new("/home/ferris")); + + assert_eq!(dirs.desktop(), None); + assert_eq!(dirs.downloads(), None); + assert_eq!(dirs.templates(), None); + assert_eq!(dirs.documents(), None); + assert_eq!(dirs.music(), None); + assert_eq!(dirs.pictures(), Some("/home/ferris/Pictures2".as_ref())); + assert_eq!(dirs.videos(), None); + } +} diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs index bc3dae156ede4..c10766470e873 100644 --- a/library/std/src/os/windows/fs.rs +++ b/library/std/src/os/windows/fs.rs @@ -11,6 +11,13 @@ use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner}; use crate::time::SystemTime; use crate::{io, sys}; +mod dirs; + +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub use dirs::HomeDirsExt; +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub use dirs::MediaDirsExt; + /// Windows-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { diff --git a/library/std/src/os/windows/fs/dirs.rs b/library/std/src/os/windows/fs/dirs.rs new file mode 100644 index 0000000000000..52710375e6eb2 --- /dev/null +++ b/library/std/src/os/windows/fs/dirs.rs @@ -0,0 +1,309 @@ +use crate::fs::{HomeDirs, MediaDirs}; +use crate::io::{ErrorKind, const_error}; +use crate::path::PathBuf; +use crate::{env, io}; + +/// Windows-specific extensions to [`fs::HomeDirs`](HomeDirs). +#[unstable(feature = "fs_home_dirs", issue = "162082")] +pub impl(self) trait HomeDirsExt: Sized { + /// Load the known user folder paths from environment variables. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | Environment Variable | + /// | ---------- | -------------------- | + /// | [`cache_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// | [`config_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`data_home`] | `%APPDATA%` (`%USERPROFILE%\AppData\Roaming`) | + /// | [`state_home`] | `%LOCALAPPDATA%` (`%USERPROFILE%\AppData\Local`) | + /// + /// Note that caches/state are both put in `AppData\Local`, and config/data + /// in `AppData\Roaming`. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if `%APPDATA%` or `%LOCALAPPDATA%` are not set to absolute paths. + /// + /// # Implementation-specific behavior + /// + /// Windows keeps these environment variables updated to contain the paths + /// to the configured folder path, but it is possible for the environment + /// variables to not match the underlying system, such as when the user or + /// a program modifies the environment directly, or if the configuration + /// changed after the environment block was copied from the system. + /// + /// Unlike [`known_folders`](Self::known_folders), this does not require + /// `Shell32.dll` and thus does not require the overhead of linking in + /// DLLs that may result in Windows considering the application as a + /// graphical application. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn appdata_env() -> io::Result; + + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `HomeDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`cache_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// | [`config_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`data_home`] | [`FOLDERID_RoamingAppData`] (`%APPDATA%`) | + /// | [`state_home`] | [`FOLDERID_LocalAppData`] (`%LOCALAPPDATA%`) | + /// + /// Note that caches/state are both put in LocalAppData, and config/data + /// in RoamingAppData. It is always possible for multiple user directories + /// to be configured to the same path, but this is the common configuration + /// on Windows platforms, making it even more important to not assume files + /// in different user directories cannot alias each other. + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_LocalAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_localappdata + /// [`FOLDERID_RoamingAppData`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_roamingappdata + /// [`cache_home`]: HomeDirs::cache_home + /// [`config_home`]: HomeDirs::config_home + /// [`data_home`]: HomeDirs::data_home + /// [`state_home`]: HomeDirs::state_home + #[unstable(feature = "fs_home_dirs", issue = "162082")] + fn known_folders() -> io::Result; +} + +/// Windows-specific extensions to [`fs::MediaDirs`](MediaDirs). +#[unstable(feature = "fs_media_dirs", issue = "162083")] +pub impl(self) trait MediaDirsExt: Sized { + /// Load the known user folder paths using the [Known Folders] API. + /// + /// The loaded known folders are: + /// + /// | `MediaDirs` | [`KNOWNFOLDERID`] | + /// | ---------- | ----------------- | + /// | [`desktop`] | [`FOLDERID_Desktop`] (`%USERPROFILE%\Desktop`) | + /// | [`documents`] | [`FOLDERID_Documents`] (`%USERPROFILE%\Documents`) | + /// | [`downloads`] | [`FOLDERID_Downloads`] (`%USERPROFILE%\Downloads`) | + /// | [`music`] | [`FOLDERID_Music`] (`%USERPROFILE%\Music`) | + /// | [`pictures`] | [`FOLDERID_Pictures`] (`%USERPROFILE%\Pictures`) | + /// | [`videos`] | [`FOLDERID_Videos`] (`%USERPROFILE%\Videos`) | + /// + /// # Errors + /// + /// Errors if the underlying system discovery API returns an error. The + /// lack of a configured path is not considered an error and results in a + /// `None` value. + /// + /// # Implementation-specific behavior + /// + /// Calls [`SHGetKnownFolderPath`] for the current user once for each known + /// folder. Does not create the folder if missing. + /// + /// COM should be initialized on the thread that calls this function or + /// else it may return unexpected errors. + /// + /// This behavior may change in the future. One example change that we + /// explicitly reserve the right to make is to load additional common + /// directories not currently in this list. + /// + /// [Known Folders]: https://learn.microsoft.com/en-us/windows/win32/shell/known-folders + /// [`SHGetKnownFolderPath`]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + /// [`KNOWNFOLDERID`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + /// [`FOLDERID_Desktop`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_desktop + /// [`FOLDERID_Documents`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_documents + /// [`FOLDERID_Downloads`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_downloads + /// [`FOLDERID_Music`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_music + /// [`FOLDERID_Pictures`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_pictures + /// [`FOLDERID_Videos`]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid#folderid_videos + /// [`desktop`]: MediaDirs::desktop + /// [`documents`]: MediaDirs::documents + /// [`downloads`]: MediaDirs::downloads + /// [`music`]: MediaDirs::music + /// [`pictures`]: MediaDirs::pictures + /// [`videos`]: MediaDirs::videos + #[unstable(feature = "fs_media_dirs", issue = "162083")] + fn known_folders() -> io::Result; +} + +#[cfg(windows)] +#[unstable(feature = "fs_home_dirs", issue = "162082")] +impl HomeDirsExt for HomeDirs { + fn appdata_env() -> io::Result { + let roaming_app_data = env::var_os("APPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %APPDATA%"))?; + let local_app_data = env::var_os("LOCALAPPDATA") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .ok_or(const_error!(ErrorKind::InvalidData, "non-absolute %LOCALAPPDATA%"))?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = Some(local_app_data.clone()); + dirs.config = Some(roaming_app_data.clone()); + dirs.data = Some(roaming_app_data); + dirs.state = Some(local_app_data); + + Ok(dirs) + } + + fn known_folders() -> io::Result { + use crate::sys::c; + + let local_app_data = sys::get_known_folder_path(&c::FOLDERID_LocalAppData)?; + let roaming_app_data = sys::get_known_folder_path(&c::FOLDERID_RoamingAppData)?; + + // AppData/Local -- system-local, doesn't make sense to sync to another + // AppData/Roaming -- data that makes sense to sync across machines + + let mut dirs = HomeDirs::empty(); + + dirs.cache = local_app_data.clone(); + dirs.config = roaming_app_data.clone(); + dirs.data = roaming_app_data; + dirs.state = local_app_data; + + Ok(dirs) + } +} + +#[cfg(windows)] +#[unstable(feature = "fs_media_dirs", issue = "162083")] +impl MediaDirsExt for MediaDirs { + fn known_folders() -> io::Result { + use crate::sys::c; + + let desktop = sys::get_known_folder_path(&c::FOLDERID_Desktop)?; + let documents = sys::get_known_folder_path(&c::FOLDERID_Documents)?; + let downloads = sys::get_known_folder_path(&c::FOLDERID_Downloads)?; + let music = sys::get_known_folder_path(&c::FOLDERID_Music)?; + let pictures = sys::get_known_folder_path(&c::FOLDERID_Pictures)?; + let videos = sys::get_known_folder_path(&c::FOLDERID_Videos)?; + + let mut dirs = MediaDirs::empty(); + + dirs.desktop = desktop; + dirs.documents = documents; + dirs.downloads = downloads; + dirs.music = music; + dirs.pictures = pictures; + dirs.videos = videos; + + Ok(dirs) + } +} + +#[cfg(windows)] +mod sys { + use crate::io::{self, ErrorKind, const_error}; + use crate::path::PathBuf; + use crate::sys::{c, os2path}; + use crate::{ptr, slice}; + + /// Retrieve a known folder path from the Windows API. + pub fn get_known_folder_path(id: &c::GUID) -> io::Result> { + // Get the known folder path. hToken = NULL requests the current user + // scope, and we set KF_FLAG_DONT_VERIFY because it's a bit faster and + // we don't guarantee that the directories at the paths exist. + let mut pszPath = ptr::null_mut(); + // SAFETY: rfid/ppszPath are valid pointers, flags are appropriate, and + // a NULL hToken is supported by SHGetKnownFolderPath. + let hr = unsafe { + c::SHGetKnownFolderPath( + /* rfid */ id, + /* dwFlags */ c::KF_FLAG_DONT_VERIFY as _, + /* hToken */ ptr::null_mut(), + /* ppszPath */ &mut pszPath, + ) + }; + + let result = match hr { + c::S_OK => { + // SAFETY: pszPath was populated by a successful call to SHGetKnownFolderPath + // and is valid up to and including its nul terminator + let len = unsafe { c::lstrlenW(pszPath) }; + // SAFETY: *pszPath is valid up to and including its nul terminator + Ok(Some(os2path(unsafe { slice::from_raw_parts(pszPath, len as usize) }))) + } + c::E_FAIL => { + // This known folder id exists but does not have a path + if cfg!(debug_assertions) { + unreachable!("should not call get_known_folder_path on a virtual folder"); + } else { + Err(const_error!( + ErrorKind::InvalidInput, + "virtual known folders do not have paths" + )) + } + } + c::E_INVALIDARG => { + // This known folder id is not present on the system + Ok(None) + } + _ => { + // Miscellaneous error + Err(io::Error::from_raw_os_error(hr)) + } + }; + + // SAFETY: The caller is responsible for freeing the path returned by + // SHGetKnownFolderPath by calling CoTaskMemFree, whether it + // succeeds or not. + unsafe { c::CoTaskMemFree(pszPath.cast()) }; + + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn can_fetch_known_folder_paths() { + let dirs = HomeDirs::known_folders().unwrap(); + assert!(dirs.cache_home().is_some()); + assert!(dirs.config_home().is_some()); + assert!(dirs.data_home().is_some()); + assert!(dirs.state_home().is_some()); + + let dirs = MediaDirs::known_folders().unwrap(); + assert!(dirs.desktop().is_some()); + assert!(dirs.documents().is_some()); + assert!(dirs.downloads().is_some()); + assert!(dirs.music().is_some()); + assert!(dirs.pictures().is_some()); + assert!(dirs.videos().is_some()); + } +} diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs index 1724e6115462e..6a7a1f52c15ac 100644 --- a/library/std/src/sys/fs/common.rs +++ b/library/std/src/sys/fs/common.rs @@ -123,3 +123,11 @@ impl fmt::Debug for Dir { f.debug_struct("Dir").field("path", &self.path).finish() } } + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraHomeDirs {} + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct ExtraMediaDirs {} diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs index 33feee4fc1c97..89f6989b9adbe 100644 --- a/library/std/src/sys/fs/hermit.rs +++ b/library/std/src/sys/fs/hermit.rs @@ -11,7 +11,7 @@ use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Raw use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, copy, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, exists}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::io::DEFAULT_BUF_SIZE; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs index b2666eb2a3da9..93b05b9859d28 100644 --- a/library/std/src/sys/fs/mod.rs +++ b/library/std/src/sys/fs/mod.rs @@ -47,6 +47,9 @@ cfg_select! { mod vexos; use vexos as imp; } + target_vendor = "apple" => { + mod darwin; + } _ => { mod unsupported; use unsupported as imp; @@ -61,8 +64,8 @@ pub fn with_native_path(path: &Path, f: &dyn Fn(&Path) -> io::Result) -> i } pub use imp::{ - Dir, DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions, - ReadDir, + Dir, DirBuilder, DirEntry, ExtraHomeDirs, ExtraMediaDirs, File, FileAttr, FilePermissions, + FileTimes, FileType, OpenOptions, ReadDir, }; pub fn read_dir(path: &Path) -> io::Result { diff --git a/library/std/src/sys/fs/motor.rs b/library/std/src/sys/fs/motor.rs index 938d1537790a1..91cd90a6b0cb3 100644 --- a/library/std/src/sys/fs/motor.rs +++ b/library/std/src/sys/fs/motor.rs @@ -4,7 +4,7 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; use crate::sys::fd::FileDesc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::time::SystemTime; use crate::sys::{ AsInner, AsInnerMut, FromInner, IntoInner, io_slices, io_slices_mut, map_motor_error, diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs index bd963b1d2f038..dfdcacdcdf277 100644 --- a/library/std/src/sys/fs/solid.rs +++ b/library/std/src/sys/fs/solid.rs @@ -9,7 +9,7 @@ use crate::os::raw::{c_int, c_short}; use crate::os::solid::ffi::OsStrExt; use crate::path::{Path, PathBuf}; use crate::sync::Arc; -pub use crate::sys::fs::common::{Dir, exists}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, exists}; use crate::sys::helpers::ignore_notfound; use crate::sys::pal::{abi, error}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs index 08473e245cc8e..5d0c219586e0e 100644 --- a/library/std/src/sys/fs/uefi.rs +++ b/library/std/src/sys/fs/uefi.rs @@ -6,7 +6,7 @@ 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, copy, remove_dir_all}; +pub use crate::sys::fs::common::{Dir, ExtraHomeDirs, ExtraMediaDirs, copy, remove_dir_all}; use crate::sys::pal::{helpers, unsupported}; use crate::sys::time::SystemTime; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 6d1c01be6078f..cc40390fa6200 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -57,6 +57,8 @@ use crate::fmt::{self, Write as _}; use crate::fs::TryLockError; use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; +#[cfg(not(target_os = "wasi"))] +pub use crate::os::unix::fs::dirs::{ExtraHomeDirs, ExtraMediaDirs}; #[cfg(target_family = "unix")] use crate::os::unix::prelude::*; #[cfg(target_os = "wasi")] @@ -65,6 +67,8 @@ use crate::path::{Path, PathBuf}; use crate::sync::Arc; use crate::sys::fd::FileDesc; pub use crate::sys::fs::common::exists; +#[cfg(target_os = "wasi")] +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::time::SystemTime; #[cfg(all(target_os = "linux", target_env = "gnu"))] diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs index 512af27401dfe..26677c7c7410a 100644 --- a/library/std/src/sys/fs/unsupported.rs +++ b/library/std/src/sys/fs/unsupported.rs @@ -4,7 +4,7 @@ use crate::fs::TryLockError; use crate::hash::{Hash, Hasher}; 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, ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::time::SystemTime; use crate::sys::unsupported; diff --git a/library/std/src/sys/fs/vexos.rs b/library/std/src/sys/fs/vexos.rs index 1357787c44c77..5043a4daa83b4 100644 --- a/library/std/src/sys/fs/vexos.rs +++ b/library/std/src/sys/fs/vexos.rs @@ -16,6 +16,8 @@ pub use unsupported_fs::{ symlink, unlink, }; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; + /// VEXos file descriptor. /// /// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index 473d92aface26..c4e0bb6ecf51f 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -11,6 +11,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::os::windows::prelude::*; use crate::path::{Path, PathBuf}; use crate::sync::Arc; +pub use crate::sys::fs::common::{ExtraHomeDirs, ExtraMediaDirs}; use crate::sys::handle::Handle; use crate::sys::pal::api::{self, WinError, set_file_information_by_handle}; use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul}; diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs index 432cd0425ac11..25031968a7e02 100644 --- a/library/std/src/sys/pal/windows/c.rs +++ b/library/std/src/sys/pal/windows/c.rs @@ -154,6 +154,9 @@ pub const MB_ERR_INVALID_CHARS: u32 = cu(windows_sys::MB_ERR_INVALID_CHARS); pub const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION: u32 = cu(windows_sys::CREATE_WAITABLE_TIMER_HIGH_RESOLUTION); +// dwFlags --LoadLibraryExA +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: u32 = cu(windows_sys::LOAD_LIBRARY_SEARCH_SYSTEM32); + // dwCreationDisposition -- CreateFile pub const CREATE_NEW: u32 = cu(windows_sys::CREATE_NEW); pub const OPEN_ALWAYS: u32 = cu(windows_sys::OPEN_ALWAYS); @@ -197,6 +200,16 @@ pub const WAIT_OBJECT_0: u32 = windows_sys::WAIT_OBJECT_0.cast_unsigned(); // LPPROGRESS_ROUTINE return value (DWORD) pub const PROGRESS_CONTINUE: u32 = cu(windows_sys::PROGRESS_CONTINUE); +// KNOWNFOLDERIDs (GUID) +pub const FOLDERID_Desktop: GUID = GUID::from_u128(0xb4bfcc3a_db2c_424c_b029_7fe99a87c641); +pub const FOLDERID_Documents: GUID = GUID::from_u128(0xfdd39ad0_238f_46af_adb4_6c85480369c7); +pub const FOLDERID_Downloads: GUID = GUID::from_u128(0x374de290_123f_4565_9164_39c4925e467b); +pub const FOLDERID_LocalAppData: GUID = GUID::from_u128(0xf1b32785_6fba_4fcf_9d55_7b8e7f157091); +pub const FOLDERID_Music: GUID = GUID::from_u128(0x4bd8d571_6d19_48d3_be97_422220080e43); +pub const FOLDERID_Pictures: GUID = GUID::from_u128(0x33e28130_4e1e_4676_835a_98395c3bc3bb); +pub const FOLDERID_RoamingAppData: GUID = GUID::from_u128(0x3eb685db_65f9_4cf6_a03a_e3ef65729f3d); +pub const FOLDERID_Videos: GUID = GUID::from_u128(0x18989b1d_99b5_455b_841c_ab7c74e4ddfc); + // Errors pub const ERROR_SUCCESS: u32 = cu(windows_sys::ERROR_SUCCESS); pub const ERROR_ACCESS_DENIED: u32 = cu(windows_sys::ERROR_ACCESS_DENIED); @@ -280,6 +293,17 @@ pub fn nt_success(status: NTSTATUS) -> bool { status >= 0 } +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} + impl OBJECT_ATTRIBUTES { pub fn with_length() -> Self { Self { @@ -459,3 +483,24 @@ cfg_select! { unsafe extern "C" { pub fn atexit(cb: unsafe extern "C" fn()) -> c_int; } + +// Functions in DLLs that we want to lazily load +compat_fn_with_fallback! { + // Avoid eagerly linking shell32.dll, which marks the loading application as graphical. + #[lazy] + pub static SHELL32: &CStr = c"shell32"; + + pub fn SHGetKnownFolderPath(rfid : *const KNOWNFOLDERID, dwflags : u32, htoken : HANDLE, ppszpath : *mut PWSTR) -> HRESULT { + unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as u32); E_NOTIMPL } + } +} + +compat_fn_with_fallback! { + // Only used with SHGetKnownFolderPath, so avoid eager load overhead. + #[lazy] + pub static OLE32: &CStr = c"ole32"; + + pub fn CoTaskMemFree(pv : *mut core::ffi::c_void) -> () { + // without OLE32 there's no COM alloc, so no-op COM free is fine + } +} diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt index ebe4b8e128352..70af36f27907e 100644 --- a/library/std/src/sys/pal/windows/c/bindings.txt +++ b/library/std/src/sys/pal/windows/c/bindings.txt @@ -252,6 +252,8 @@ DNS_ERROR_ZONESCOPE_IS_REFERENCED DUPLICATE_CLOSE_SOURCE DUPLICATE_SAME_ACCESS DuplicateHandle +E_FAIL +E_INVALIDARG E_NOTIMPL ENABLE_AUTO_POSITION ENABLE_ECHO_INPUT @@ -2260,8 +2262,12 @@ IPV6_MREQ IPV6_MULTICAST_LOOP IPV6_V6ONLY IsThreadAFiber +KF_FLAG_DONT_VERIFY +KNOWNFOLDERID LINGER listen +LOAD_LIBRARY_SEARCH_SYSTEM32 +LoadLibraryExA LocalFree LOCKFILE_EXCLUSIVE_LOCK LOCKFILE_FAIL_IMMEDIATELY @@ -2350,6 +2356,7 @@ ReleaseSRWLockShared RemoveDirectoryW RtlGenRandom RtlNtStatusToDosError +S_OK SD_BOTH SD_RECEIVE SD_SEND diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs index 34c4407296dcf..0c865494a6e7c 100644 --- a/library/std/src/sys/pal/windows/c/windows_sys.rs +++ b/library/std/src/sys/pal/windows/c/windows_sys.rs @@ -71,6 +71,7 @@ windows_link::link!("kernel32.dll" "system" fn InitOnceBeginInitialize(lpinitonc windows_link::link!("kernel32.dll" "system" fn InitOnceComplete(lpinitonce : LPINIT_ONCE, dwflags : u32, lpcontext : *const core::ffi::c_void) -> BOOL); windows_link::link!("kernel32.dll" "system" fn InitializeProcThreadAttributeList(lpattributelist : *mut _PROC_THREAD_ATTRIBUTE_LIST, dwattributecount : u32, dwflags : u32, lpsize : *mut usize) -> BOOL); windows_link::link!("kernel32.dll" "system" fn IsThreadAFiber() -> BOOL); +windows_link::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : u32) -> HMODULE); windows_link::link!("kernel32.dll" "system" fn LocalFree(hmem : HLOCAL) -> HLOCAL); windows_link::link!("kernel32.dll" "system" fn LockFileEx(hfile : HANDLE, dwflags : u32, dwreserved : u32, nnumberofbytestolocklow : u32, nnumberofbytestolockhigh : u32, lpoverlapped : *mut OVERLAPPED) -> BOOL); windows_link::link!("kernel32.dll" "system" fn MoveFileExW(lpexistingfilename : PCWSTR, lpnewfilename : PCWSTR, dwflags : u32) -> BOOL); @@ -2433,6 +2434,8 @@ impl Default for EXCEPTION_RECORD { } pub const EXCEPTION_STACK_OVERFLOW: i32 = -1073741571; pub const EXTENDED_STARTUPINFO_PRESENT: i32 = 524288; +pub const E_FAIL: HRESULT = 0x80004005_u32 as _; +pub const E_INVALIDARG: HRESULT = 0x80070057_u32 as _; pub const E_NOTIMPL: HRESULT = 0x80004001_u32 as _; pub const ExceptionCollidedUnwind: EXCEPTION_DISPOSITION = 3; pub const ExceptionContinueExecution: EXCEPTION_DISPOSITION = 0; @@ -2925,7 +2928,11 @@ impl Default for IP_MREQ { pub const IP_MULTICAST_LOOP: i32 = 11; pub const IP_MULTICAST_TTL: i32 = 10; pub const IP_TTL: i32 = 4; +pub const KF_FLAG_DONT_VERIFY: KNOWN_FOLDER_FLAG = 16384; +pub type KNOWNFOLDERID = GUID; +pub type KNOWN_FOLDER_FLAG = u32; pub type LINGER = linger; +pub const LOAD_LIBRARY_SEARCH_SYSTEM32: i32 = 2048; pub const LOCKFILE_EXCLUSIVE_LOCK: i32 = 2; pub const LOCKFILE_FAIL_IMMEDIATELY: i32 = 1; pub type LPBYTE = *mut u8; @@ -3295,6 +3302,7 @@ pub struct SYSTEM_INFO_0_0 { pub wProcessorArchitecture: u16, pub wReserved: u16, } +pub const S_OK: HRESULT = 0x0_u32 as _; pub const TCP_NODELAY: i32 = 1; pub const TIMER_ALL_ACCESS: i32 = 2031619; pub const TIMER_MODIFY_STATE: i32 = 2; diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index c465ceb2301ce..7e9f5bd108729 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -20,7 +20,8 @@ //! importing the same function unnecessarily. use crate::ffi::{CStr, c_void}; -use crate::ptr::NonNull; +use crate::ptr::{self, NonNull}; +use crate::sync::LazyLock; use crate::sys::c; // This uses a static initializer to preload some imported functions. @@ -118,6 +119,23 @@ impl Module { } } + /// Try to load a module handle. + /// + /// # SAFETY + /// + /// This should only be use for modules that exist for the lifetime of std. + pub unsafe fn load(name: &CStr) -> Option { + // SAFETY: A CStr is always null terminated. + unsafe { + let module = c::LoadLibraryExA( + /* lpLibFileName */ name.as_ptr().cast::(), + /* hReserved */ ptr::null_mut(), + /* dwFlags */ c::LOAD_LIBRARY_SEARCH_SYSTEM32, + ); + NonNull::new(module).map(Self) + } + } + // Try to get the address of a function. pub fn proc_address(self, name: &CStr) -> Option> { unsafe { @@ -131,6 +149,33 @@ impl Module { } } +unsafe impl Send for Module {} +unsafe impl Sync for Module {} + +type LazyLoadModule = impl FnOnce() -> Option + Send + Sync + 'static; + +#[define_opaque(LazyLoadModule)] +const fn lazy_load_module(name: &'static CStr) -> LazyLoadModule { + move || unsafe { Module::load(name) } +} + +/// Represents a loaded module. +/// +/// Note that the modules std depends on must not be unloaded. +/// Therefore a `Module` is always valid for the lifetime of std. +pub(in crate::sys) struct LazyModule(LazyLock, LazyLoadModule>); +impl LazyModule { + /// Create a lazy-loaded handle to a module. + pub const unsafe fn new(name: &'static CStr) -> Self { + Self(LazyLock::new(lazy_load_module(name))) + } + + /// Get the module handle, loading it if necessary. + pub fn load(&self) -> Option { + *LazyLock::force(&self.0) + } +} + /// Load a function or use a fallback implementation if that fails. macro_rules! compat_fn_with_fallback { (pub static $module:ident: &CStr = $name:expr; $( @@ -193,6 +238,67 @@ macro_rules! compat_fn_with_fallback { #[allow(unused)] $(#[$meta])* $vis use $symbol::call as $symbol; + )*); + (#[lazy] pub static $module:ident: &CStr = $name:expr; $( + $(#[$meta:meta])* + $vis:vis fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback_body:block + )*) => ( + pub static $module: &CStr = $name; + $( + $(#[$meta])* + pub mod $symbol { + #[allow(unused_imports)] + use super::*; + use crate::mem; + use crate::ffi::CStr; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; + use crate::sys::compat::{LazyModule, Module}; + + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; + + /// `PTR` contains a function pointer to one of three functions. + /// It starts with the `load` function. + /// When that is called it attempts to load the requested symbol. + /// If it succeeds, `PTR` is set to the address of that symbol. + /// If it fails, then `PTR` is set to `fallback`. + static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _); + + unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { + static MODULE: LazyModule = unsafe { LazyModule::new($module) }; + unsafe { + let func = load_from_module(MODULE.load()); + func($($argname),*) + } + } + + fn load_from_module(module: Option) -> F { + unsafe { + static SYMBOL_NAME: &CStr = ansi_str!(sym $symbol); + if let Some(f) = module.and_then(|m| m.proc_address(SYMBOL_NAME)) { + PTR.store(f.as_ptr(), Ordering::Relaxed); + mem::transmute(f) + } else { + PTR.store(fallback as unsafe extern "system" fn($($argname: $argtype),*) -> $rettype as *mut _, Ordering::Relaxed); + fallback + } + } + } + + #[allow(unused_variables)] + unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype { + $fallback_body + } + + #[inline(always)] + pub unsafe fn call($($argname: $argtype),*) -> $rettype { + unsafe { + let func: F = mem::transmute(PTR.load(Ordering::Relaxed)); + func($($argname),*) + } + } + } + $(#[$meta])* + $vis use $symbol::call as $symbol; )*) } diff --git a/library/std/src/sys/paths/mod.rs b/library/std/src/sys/paths/mod.rs index 69c814ef06c06..865d06ae8644e 100644 --- a/library/std/src/sys/paths/mod.rs +++ b/library/std/src/sys/paths/mod.rs @@ -6,7 +6,8 @@ cfg_select! { mod imp { pub use super::hermit::{getcwd, temp_dir}; pub use super::unsupported::{ - JoinPathsError, SplitPaths, chdir, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, home_dir, + join_paths, split_paths, split_paths_ref, }; } } @@ -16,8 +17,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::motor::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, - split_paths, temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -28,8 +29,8 @@ cfg_select! { mod imp { pub use super::sgx::chdir; pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, getcwd, home_dir, + join_paths, split_paths, split_paths_ref, temp_dir, }; } } @@ -47,7 +48,8 @@ cfg_select! { mod unsupported; mod imp { pub use super::unsupported::{ - JoinPathsError, SplitPaths, current_exe, home_dir, join_paths, split_paths, + JoinPathsError, SplitPaths, SplitPathsRef, current_exe, home_dir, join_paths, + split_paths, split_paths_ref, }; pub use super::wasi::{chdir, getcwd, temp_dir}; } @@ -63,6 +65,6 @@ cfg_select! { } pub use imp::{ - JoinPathsError, SplitPaths, chdir, current_exe, getcwd, home_dir, join_paths, split_paths, - temp_dir, + JoinPathsError, SplitPaths, SplitPathsRef, chdir, current_exe, getcwd, home_dir, join_paths, + split_paths, split_paths_ref, temp_dir, }; diff --git a/library/std/src/sys/paths/uefi.rs b/library/std/src/sys/paths/uefi.rs index 7fddcfdff7724..d9dcd45267935 100644 --- a/library/std/src/sys/paths/uefi.rs +++ b/library/std/src/sys/paths/uefi.rs @@ -1,6 +1,7 @@ use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; use crate::path::{self, PathBuf}; use crate::sys::pal::{helpers, unsupported_err}; @@ -71,6 +72,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unix.rs b/library/std/src/sys/paths/unix.rs index e023451b13c8c..cee799c36c79b 100644 --- a/library/std/src/sys/paths/unix.rs +++ b/library/std/src/sys/paths/unix.rs @@ -6,7 +6,7 @@ use libc::{c_char, c_int, c_void}; use crate::ffi::{CStr, OsStr, OsString}; use crate::os::unix::prelude::*; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::helpers::run_path_with_cstr; use crate::sys::pal::cvt; use crate::{fmt, io, iter, mem, ptr, slice, str}; @@ -76,6 +76,28 @@ pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { unparsed.as_bytes().split(is_separator).map(into_pathbuf) } +pub type SplitPathsRef<'a> = iter::Map< + slice::Split<'a, u8, impl FnMut(&u8) -> bool + 'static>, + impl FnMut(&[u8]) -> &'_ Path + 'static, +>; + +pub fn split_paths_ref(unparsed: &OsStr) -> Option> { + #[define_opaque(SplitPathsRef)] + fn split_paths_ref(unparsed: &OsStr) -> SplitPathsRef<'_> { + fn is_separator(&b: &u8) -> bool { + b == PATH_SEPARATOR + } + + fn into_path(part: &[u8]) -> &Path { + Path::new(OsStr::from_bytes(part)) + } + + unparsed.as_bytes().split(is_separator).map(into_path) + } + + Some(split_paths_ref(unparsed)) +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/unsupported.rs b/library/std/src/sys/paths/unsupported.rs index 024830a254fd7..204d6b0897e20 100644 --- a/library/std/src/sys/paths/unsupported.rs +++ b/library/std/src/sys/paths/unsupported.rs @@ -1,6 +1,6 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; -use crate::path::{self, PathBuf}; +use crate::path::{self, Path, PathBuf}; use crate::sys::pal::unsupported; use crate::{fmt, io}; @@ -13,11 +13,16 @@ pub fn chdir(_: &path::Path) -> io::Result<()> { } pub struct SplitPaths<'a>(!, PhantomData<&'a ()>); +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> { panic!("unsupported") } +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + impl<'a> Iterator for SplitPaths<'a> { type Item = PathBuf; fn next(&mut self) -> Option { @@ -25,6 +30,13 @@ impl<'a> Iterator for SplitPaths<'a> { } } +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a Path; + fn next(&mut self) -> Option<&'a Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/paths/windows.rs b/library/std/src/sys/paths/windows.rs index 9b9316b2658c0..350a942124694 100644 --- a/library/std/src/sys/paths/windows.rs +++ b/library/std/src/sys/paths/windows.rs @@ -3,6 +3,7 @@ #![allow(nonstandard_style)] use crate::ffi::{OsStr, OsString}; +use crate::marker::PhantomData; use crate::os::windows::ffi::EncodeWide; use crate::os::windows::prelude::*; use crate::path::{self, PathBuf}; @@ -56,6 +57,19 @@ impl<'a> Iterator for SplitPaths<'a> { } } +pub struct SplitPathsRef<'a>(!, PhantomData<&'a ()>); + +pub fn split_paths_ref(_unparsed: &OsStr) -> Option> { + None +} + +impl<'a> Iterator for SplitPathsRef<'a> { + type Item = &'a path::Path; + fn next(&mut self) -> Option<&'a path::Path> { + self.0 + } +} + #[derive(Debug)] pub struct JoinPathsError; diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 11fe569f8e0a9..b7908dea2b5de 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -162,7 +162,6 @@ pub fn available_parallelism() -> io::Result> { target_os = "hurd", target_os = "linux", target_os = "aix", - target_os = "freebsd", target_vendor = "apple", target_os = "cygwin", target_os = "redox", @@ -175,17 +174,9 @@ pub fn available_parallelism() -> io::Result> { #[cfg(any(target_os = "android", target_os = "linux"))] { quota = cgroups::quota().max(1); - } - - #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))] - { - #[cfg(not(target_os = "freebsd"))] - type Cpuset = libc::cpu_set_t; - #[cfg(target_os = "freebsd")] - type Cpuset = libc::cpuset_t; - let mut set: Cpuset = unsafe { mem::zeroed() }; + let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; unsafe { - if libc::sched_getaffinity(0, size_of::(), &mut set) == 0 { + if libc::sched_getaffinity(0, size_of::(), &mut set) == 0 { let count = libc::CPU_COUNT(&set) as usize; let count = count.min(quota); @@ -210,9 +201,34 @@ pub fn available_parallelism() -> io::Result> { } } } - any(target_os = "dragonfly", target_os = "openbsd", target_os = "netbsd") => { + any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "netbsd", + ) => { use crate::ptr; + #[cfg(target_os = "freebsd")] + { + let mut set: libc::cpuset_t = unsafe { mem::zeroed() }; + unsafe { + if libc::cpuset_getaffinity( + libc::CPU_LEVEL_WHICH, + libc::CPU_WHICH_PID, + -1, + size_of::(), + &mut set, + ) == 0 + { + let count = libc::CPU_COUNT(&set) as usize; + if count > 0 { + return Ok(NonZero::new_unchecked(count)); + } + } + } + } + #[cfg(target_os = "netbsd")] { unsafe { diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index fb89a6547a931..170352dabf656 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -777,7 +777,9 @@ impl Options { } } - let edition = config::parse_crate_edition(early_dcx, matches); + // We don't want rustdoc invocations to complain about the lack of `--edition`. + let has_input = false; + let edition = config::parse_crate_edition(early_dcx, matches, has_input); let mut id_map = html::markdown::IdMap::new(); let Some(external_html) = ExternalHtml::load( diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 94e949b78d0a1..1037b2c6375b7 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -405,11 +405,10 @@ impl TestProps { } } - if let Some(edition) = self.edition.or(config.edition) { - // The edition is added at the start, since flags from //@compile-flags must be passed - // to rustc last. - self.compile_flags.insert(0, format!("--edition={edition}")); - } + let edition = self.edition.or(config.edition).unwrap_or(Edition::Year(2015)); + // The edition is added at the start, since flags from //@compile-flags must be passed + // to rustc last. + self.compile_flags.insert(0, format!("--edition={edition}")); } fn update_pass_fail_mode(&mut self, ln: &DirectiveLine<'_>, config: &Config) { diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 5a768519f616d..4e1d1138b3168 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -33,7 +33,10 @@ impl RustdocGuiTestProps { let props = TestProps::from_file(test_file_path, None, &config); - let TestProps { compile_flags, run_flags, .. } = props; + let TestProps { mut compile_flags, run_flags, .. } = props; + // We don't want to pass `--edition=2015` in, which is being set by default by + // `TestProps::from_file`. + compile_flags.remove(0); Self { compile_flags, run_flags } } } diff --git a/tests/assembly-llvm/breakpoint.rs b/tests/assembly-llvm/breakpoint.rs index d119b68e899ba..39ee26a4cf5d5 100644 --- a/tests/assembly-llvm/breakpoint.rs +++ b/tests/assembly-llvm/breakpoint.rs @@ -1,15 +1,30 @@ -//@ revisions: aarch64 x86_64 +//! Verify that the breakpoint operation emits the instructions we guarantee. + +//@ add-minicore //@ assembly-output: emit-asm -//@[aarch64] only-aarch64 -//@[x86_64] only-x86_64 +//@ revisions: AARCH64 I686 X86_64 +//@ [AARCH64] compile-flags: --target aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [I686] compile-flags: --target i686-unknown-linux-gnu +//@ [I686] needs-llvm-components: x86 +//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 -#![feature(breakpoint)] #![crate_type = "lib"] +#![feature(no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] + +extern crate minicore; + +#[rustc_intrinsic] +#[rustc_nounwind] +fn breakpoint(); -// CHECK-LABEL: use_bp -// aarch64: brk #0xf000 -// x86_64: int3 -#[inline(never)] -pub fn use_bp() { - core::arch::breakpoint(); +// CHECK-LABEL: call_breakpoint +// AARCH64: brk #0xf000 +// I686: int3 +// X86_64: int3 +#[unsafe(no_mangle)] +pub fn call_breakpoint() { + breakpoint(); } diff --git a/tests/codegen-llvm/direct-access-external-data.rs b/tests/codegen-llvm/direct-access-external-data.rs index a151bb6012e1e..43ee290d3a058 100644 --- a/tests/codegen-llvm/direct-access-external-data.rs +++ b/tests/codegen-llvm/direct-access-external-data.rs @@ -22,15 +22,6 @@ unsafe extern "C" { // 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 @@ -44,6 +35,5 @@ unsafe extern "C" { #[no_mangle] pub fn refer() { core::hint::black_box(VAR); - core::hint::black_box(EXTERNAL); core::hint::black_box(WEAK); } diff --git a/tests/run-make/broken-pipe-no-ice/rmake.rs b/tests/run-make/broken-pipe-no-ice/rmake.rs index b0a28b6c899da..0e43ebda9c3e1 100644 --- a/tests/run-make/broken-pipe-no-ice/rmake.rs +++ b/tests/run-make/broken-pipe-no-ice/rmake.rs @@ -68,7 +68,7 @@ fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) { fn main() { let mut rustc = bare_rustc(); - rustc.arg("--print=sysroot"); + rustc.arg("--print=sysroot").edition("2015"); let rustc = rustc.into_raw_command(); check_broken_pipe_handled_gracefully(Binary::Rustc, rustc); diff --git a/tests/run-make/compressed-debuginfo/rmake.rs b/tests/run-make/compressed-debuginfo/rmake.rs index 45bfaa6041d97..4a5305a87cbfb 100644 --- a/tests/run-make/compressed-debuginfo/rmake.rs +++ b/tests/run-make/compressed-debuginfo/rmake.rs @@ -12,6 +12,7 @@ use run_make_support::{assert_contains, llvm_readobj, run_in_tmpdir, rustc}; fn check_compression(compression: &str, to_find: &str) { run_in_tmpdir(|| { let out = rustc() + .edition("2015") .crate_name("foo") .crate_type("lib") .emit("obj") diff --git a/tests/run-make/const-destruct-stable-toolchain/rmake.rs b/tests/run-make/const-destruct-stable-toolchain/rmake.rs index c4990582d40ba..47d66cf267ab9 100644 --- a/tests/run-make/const-destruct-stable-toolchain/rmake.rs +++ b/tests/run-make/const-destruct-stable-toolchain/rmake.rs @@ -10,12 +10,14 @@ use run_make_support::{diff, rustc, stable_bare_rustc}; fn main() { let out = stable_bare_rustc() .input("const-drop.rs") + .edition("2015") .run_fail() .assert_stderr_not_contains("consider restricting type parameter `T`") .stderr_utf8(); diff().expected_file("const-drop-stable.stderr").actual_text("(rustc)", &out).run(); let out = rustc() .input("const-drop.rs") + .edition("2015") .ui_testing() .run_fail() .assert_stderr_contains( diff --git a/tests/run-make/const-trait-stable-toolchain/rmake.rs b/tests/run-make/const-trait-stable-toolchain/rmake.rs index c778873463063..a13b1cb936bbb 100644 --- a/tests/run-make/const-trait-stable-toolchain/rmake.rs +++ b/tests/run-make/const-trait-stable-toolchain/rmake.rs @@ -8,6 +8,7 @@ use run_make_support::{diff, rustc, stable_bare_rustc}; fn main() { let out = stable_bare_rustc() + .edition("2015") .input("const-super-trait.rs") .cfg("feature_enabled") .run_fail() @@ -22,6 +23,7 @@ fn main() { .actual_text("(rustc)", &out) .run(); let out = rustc() + .edition("2015") .input("const-super-trait.rs") .cfg("feature_enabled") .ui_testing() @@ -34,6 +36,7 @@ fn main() { .actual_text("(rustc)", &out) .run(); let out = stable_bare_rustc() + .edition("2015") .input("const-super-trait.rs") .run_fail() .assert_stderr_not_contains("enable `#![feature(const_trait_impl)]` in your crate and mark") @@ -44,6 +47,7 @@ fn main() { .actual_text("(rustc)", &out) .run(); let out = rustc() + .edition("2015") .input("const-super-trait.rs") .ui_testing() .run_fail() diff --git a/tests/run-make/crate-loading-crate-depends-on-itself/rmake.rs b/tests/run-make/crate-loading-crate-depends-on-itself/rmake.rs index 57e0cab92f1ef..f49a848052411 100644 --- a/tests/run-make/crate-loading-crate-depends-on-itself/rmake.rs +++ b/tests/run-make/crate-loading-crate-depends-on-itself/rmake.rs @@ -10,9 +10,10 @@ use run_make_support::{diff, rust_lib_name, rustc}; fn main() { - rustc().input("foo-prev.rs").run(); + rustc().edition("2015").input("foo-prev.rs").run(); let out = rustc() + .edition("2015") .extra_filename("current") .metadata("current") .input("foo-current.rs") diff --git a/tests/run-make/crate-loading-multiple-candidates/rmake.rs b/tests/run-make/crate-loading-multiple-candidates/rmake.rs index ce090850500b8..9775a1672f0cb 100644 --- a/tests/run-make/crate-loading-multiple-candidates/rmake.rs +++ b/tests/run-make/crate-loading-multiple-candidates/rmake.rs @@ -9,8 +9,18 @@ use run_make_support::{bare_rustc, diff, rfs, rustc}; fn main() { // Check that relative paths are preserved in the diagnostic rfs::create_dir("mylibs"); - rustc().input("crateresolve1-1.rs").out_dir("mylibs").extra_filename("-1").run(); - rustc().input("crateresolve1-2.rs").out_dir("mylibs").extra_filename("-2").run(); + rustc() + .edition("2015") + .input("crateresolve1-1.rs") + .out_dir("mylibs") + .extra_filename("-1") + .run(); + rustc() + .edition("2015") + .input("crateresolve1-2.rs") + .out_dir("mylibs") + .extra_filename("-2") + .run(); check("./mylibs"); // Check that symlinks aren't followed when printing the diagnostic @@ -21,6 +31,7 @@ fn main() { fn check(library_path: &str) { let out = rustc() + .edition("2015") .input("multiple-candidates.rs") .library_search_path(library_path) .ui_testing() diff --git a/tests/run-make/crate-loading/rmake.rs b/tests/run-make/crate-loading/rmake.rs index 8f2577861239d..79f14e1851eff 100644 --- a/tests/run-make/crate-loading/rmake.rs +++ b/tests/run-make/crate-loading/rmake.rs @@ -6,11 +6,16 @@ use run_make_support::{diff, rust_lib_name, rustc}; fn main() { - rustc().input("dependency-1.rs").run(); - rustc().input("dependency-2.rs").extra_filename("2").metadata("2").run(); - rustc().input("dep-2-reexport.rs").extern_("dependency", rust_lib_name("dependency2")).run(); + rustc().edition("2015").input("dependency-1.rs").run(); + rustc().edition("2015").input("dependency-2.rs").extra_filename("2").metadata("2").run(); + rustc() + .edition("2015") + .input("dep-2-reexport.rs") + .extern_("dependency", rust_lib_name("dependency2")) + .run(); let out = rustc() + .edition("2015") .input("multiple-dep-versions.rs") .extern_("dependency", rust_lib_name("dependency")) .extern_("dep_2_reexport", rust_lib_name("foo")) diff --git a/tests/run-make/emit-to-stdout/rmake.rs b/tests/run-make/emit-to-stdout/rmake.rs index 19c15b72fe475..851ab3cb45881 100644 --- a/tests/run-make/emit-to-stdout/rmake.rs +++ b/tests/run-make/emit-to-stdout/rmake.rs @@ -13,8 +13,9 @@ use run_make_support::{diff, run_in_tmpdir, rustc}; // Test emitting text outputs to stdout works correctly fn run_diff(name: &str, file_args: &[&str]) { - rustc().emit(format!("{name}={name}")).input("test.rs").args(file_args).run(); - let out = rustc().emit(format!("{name}=-")).input("test.rs").run().stdout_utf8(); + rustc().edition("2015").emit(format!("{name}={name}")).input("test.rs").args(file_args).run(); + let out = + rustc().edition("2015").emit(format!("{name}=-")).input("test.rs").run().stdout_utf8(); diff().expected_file(name).actual_text("stdout", &out).run(); } @@ -29,7 +30,13 @@ fn run_terminal_err_diff(name: &str) { let terminal = File::options().read(true).write(true).open(r"\\.\CONOUT$").unwrap(); let err = File::create(name).unwrap(); - rustc().emit(format!("{name}=-")).input("test.rs").stdout(terminal).stderr(err).run_fail(); + rustc() + .edition("2015") + .emit(format!("{name}=-")) + .input("test.rs") + .stdout(terminal) + .stderr(err) + .run_fail(); diff().expected_file(format!("emit-{name}.stderr")).actual_file(name).run(); } @@ -47,6 +54,7 @@ fn main() { // Test error for emitting multiple types to stdout rustc() + .edition("2015") .input("test.rs") .emit("asm=-") .emit("llvm-ir=-") @@ -58,6 +66,7 @@ fn main() { // Same as above, but using `-o` rustc() + .edition("2015") .input("test.rs") .output("-") .emit("asm,llvm-ir,dep-info,mir") @@ -69,6 +78,11 @@ fn main() { .run(); // Test that `-o -` redirected to a file works correctly (#26719) - rustc().input("test.rs").output("-").stdout(File::create("out-stdout").unwrap()).run(); + rustc() + .edition("2015") + .input("test.rs") + .output("-") + .stdout(File::create("out-stdout").unwrap()) + .run(); }); } diff --git a/tests/run-make/jobserver-error/rmake.rs b/tests/run-make/jobserver-error/rmake.rs index 265eec7190d4e..80c1562299e72 100644 --- a/tests/run-make/jobserver-error/rmake.rs +++ b/tests/run-make/jobserver-error/rmake.rs @@ -16,6 +16,7 @@ use run_make_support::{diff, rustc}; fn main() { let out = rustc() + .edition("2015") .stdin_buf(("fn main() {}").as_bytes()) .env("MAKEFLAGS", "--jobserver-auth=1000,1000") .run_fail() @@ -23,6 +24,7 @@ fn main() { diff().expected_file("cannot_open_fd.stderr").actual_text("actual", out).run(); let out = rustc() + .edition("2015") .stdin_buf(("fn main() {}").as_bytes()) .input("-") .env("MAKEFLAGS", "--jobserver-auth=3,3") diff --git a/tests/run-make/linkage-attr-on-static/bar.rs b/tests/run-make/linkage-attr-on-static/bar.rs index 68607cbb65721..cbab76ba6f403 100644 --- a/tests/run-make/linkage-attr-on-static/bar.rs +++ b/tests/run-make/linkage-attr-on-static/bar.rs @@ -1,7 +1,7 @@ #![feature(linkage)] #[no_mangle] -#[linkage = "external"] +#[linkage = "weak"] static BAZ: i32 = 21; #[link(name = "foo", kind = "static")] diff --git a/tests/run-make/linker-warning/rmake.rs b/tests/run-make/linker-warning/rmake.rs index b25d892507907..bf6ef980265f1 100644 --- a/tests/run-make/linker-warning/rmake.rs +++ b/tests/run-make/linker-warning/rmake.rs @@ -5,6 +5,7 @@ use run_make_support::{Rustc, diff, regex, rustc}; fn run_rustc() -> Rustc { let mut rustc = rustc(); rustc + .edition("2015") .arg("main.rs") // NOTE: `link-self-contained` can vary depending on bootstrap.toml. // Make sure we use a consistent value. @@ -23,9 +24,9 @@ fn run_rustc() -> Rustc { fn main() { // first, compile our linker and our dependencies - rustc().arg("fake-linker.rs").output("fake-linker").run(); - rustc().arg("foo.rs").crate_type("rlib").run(); - rustc().arg("bar.rs").crate_type("rlib").run(); + rustc().edition("2015").arg("fake-linker.rs").output("fake-linker").run(); + rustc().edition("2015").arg("foo.rs").crate_type("rlib").run(); + rustc().edition("2015").arg("bar.rs").crate_type("rlib").run(); // Run rustc with our fake linker, and make sure it shows warnings let warnings = run_rustc().link_arg("run_make_warn").run(); @@ -92,12 +93,14 @@ fn main() { // Make sure we show linker warnings even across `-Z no-link` rustc() + .edition("2015") .arg("-Zno-link") .input("-") .stdin_buf("#![deny(linker_messages)] \n fn main() {}") .run() .assert_stderr_equals(""); rustc() + .edition("2015") .arg("-Zlink-only") .arg("rust_out.rlink") .linker("./fake-linker") @@ -111,6 +114,7 @@ fn main() { // Same thing, but with json output. rustc() + .edition("2015") .error_format("json") .arg("-Zlink-only") .arg("rust_out.rlink") diff --git a/tests/run-make/missing-unstable-trait-bound/rmake.rs b/tests/run-make/missing-unstable-trait-bound/rmake.rs index 52a011285c95c..807d24b630fd6 100644 --- a/tests/run-make/missing-unstable-trait-bound/rmake.rs +++ b/tests/run-make/missing-unstable-trait-bound/rmake.rs @@ -11,6 +11,7 @@ use run_make_support::{diff, stable_bare_rustc}; fn main() { let out = stable_bare_rustc() + .edition("2015") .input("missing-bound.rs") .run_fail() .assert_stderr_not_contains("help: consider restricting type parameter `T`") diff --git a/tests/run-make/multiline-args-value/rmake.rs b/tests/run-make/multiline-args-value/rmake.rs index 3964cbbc1e605..f9e6290f7d919 100644 --- a/tests/run-make/multiline-args-value/rmake.rs +++ b/tests/run-make/multiline-args-value/rmake.rs @@ -3,7 +3,8 @@ use run_make_support::{cwd, diff, rustc}; fn test_and_compare(test_name: &str, flag: &str, val: &str) { let mut cmd = rustc(); - let output = cmd.input("").arg("--crate-type=lib").arg(flag).arg(val).run_fail(); + let output = + cmd.edition("2015").input("").arg("--crate-type=lib").arg(flag).arg(val).run_fail(); assert_eq!(output.stdout_utf8(), ""); diff() diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index ce787f83ade6d..8d699a665ae24 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -51,17 +51,6 @@ pub extern "C" fn public_naked_generic() -> u32 { naked_asm!("mov rax, {}", "ret", const T::COUNT) } -#[linkage = "external"] -extern "C" fn vanilla_external_linkage() -> u32 { - 42 -} - -#[unsafe(naked)] -#[linkage = "external"] -extern "C" fn naked_external_linkage() -> u32 { - naked_asm!("mov rax, 42", "ret") -} - #[cfg(not(windows))] #[linkage = "weak"] extern "C" fn vanilla_weak_linkage() -> u32 { diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs index 69317c872f895..1acbe11dc4ae4 100644 --- a/tests/run-make/naked-symbol-visibility/rmake.rs +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -29,9 +29,6 @@ fn main() { // -Zshare-generics. global_function(&rdylib, "public_naked_generic"); - global_function(&rdylib, "vanilla_external_linkage"); - global_function(&rdylib, "naked_external_linkage"); - // FIXME: make this work on windows (gnu and msvc). See the PR // https://github.com/rust-lang/rust/pull/128362 for some approaches // that don't work diff --git a/tests/run-make/non-unicode-env/rmake.rs b/tests/run-make/non-unicode-env/rmake.rs index b7a3c51db5bfd..7a1c7e4322ddc 100644 --- a/tests/run-make/non-unicode-env/rmake.rs +++ b/tests/run-make/non-unicode-env/rmake.rs @@ -6,7 +6,11 @@ fn main() { let non_unicode: &std::ffi::OsStr = std::os::unix::ffi::OsStrExt::from_bytes(&[0xFF]); #[cfg(windows)] let non_unicode: std::ffi::OsString = std::os::windows::ffi::OsStringExt::from_wide(&[0xD800]); - let output = rustc().input("non_unicode_env.rs").env("NON_UNICODE_VAR", non_unicode).run_fail(); + let output = rustc() + .edition("2015") + .input("non_unicode_env.rs") + .env("NON_UNICODE_VAR", non_unicode) + .run_fail(); let expected = rfs::read_to_string("non_unicode_env.stderr"); output.assert_stderr_equals(expected); } diff --git a/tests/run-make/option-output-no-space/rmake.rs b/tests/run-make/option-output-no-space/rmake.rs index 63d2389890155..d77ae4bef12a8 100644 --- a/tests/run-make/option-output-no-space/rmake.rs +++ b/tests/run-make/option-output-no-space/rmake.rs @@ -7,6 +7,7 @@ use run_make_support::rustc; fn main() { // test fake args rustc() + .edition("2015") .input("main.rs") .arg("-optimize") .run() @@ -17,6 +18,7 @@ fn main() { "= note: output filename `-o ptimize` is applied instead of a flag named `optimize`", ); rustc() + .edition("2015") .input("main.rs") .arg("-o0") .run() @@ -26,9 +28,10 @@ fn main() { .assert_stderr_contains( "= note: output filename `-o 0` is applied instead of a flag named `o0`", ); - rustc().input("main.rs").arg("-o1").run(); + rustc().edition("2015").input("main.rs").arg("-o1").run(); // test real args by iter optgroups rustc() + .edition("2015") .input("main.rs") .arg("-out-dir") .run() @@ -43,6 +46,7 @@ fn main() { ); // test real args by iter CG_OPTIONS rustc() + .edition("2015") .input("main.rs") .arg("-opt_level") .run() @@ -58,6 +62,7 @@ fn main() { ); // separater in-sensitive rustc() + .edition("2015") .input("main.rs") .arg("-opt-level") .run() @@ -72,6 +77,7 @@ fn main() { `-o pt-level`", ); rustc() + .edition("2015") .input("main.rs") .arg("-overflow-checks") .run() @@ -88,10 +94,28 @@ fn main() { ); // No warning for Z_OPTIONS - rustc().input("main.rs").arg("-oom").run().assert_stderr_equals(""); + rustc().edition("2015").input("main.rs").arg("-oom").run().assert_stderr_equals(""); // test no warning when there is space between `-o` and arg - rustc().input("main.rs").arg("-o").arg("ptimize").run().assert_stderr_equals(""); - rustc().input("main.rs").arg("--out-dir").arg("xxx").run().assert_stderr_equals(""); - rustc().input("main.rs").arg("-o").arg("out-dir").run().assert_stderr_equals(""); + rustc() + .edition("2015") + .input("main.rs") + .arg("-o") + .arg("ptimize") + .run() + .assert_stderr_equals(""); + rustc() + .edition("2015") + .input("main.rs") + .arg("--out-dir") + .arg("xxx") + .run() + .assert_stderr_equals(""); + rustc() + .edition("2015") + .input("main.rs") + .arg("-o") + .arg("out-dir") + .run() + .assert_stderr_equals(""); } diff --git a/tests/run-make/overwrite-input/rmake.rs b/tests/run-make/overwrite-input/rmake.rs index bdf7860caa8a0..581fee8fe0136 100644 --- a/tests/run-make/overwrite-input/rmake.rs +++ b/tests/run-make/overwrite-input/rmake.rs @@ -8,8 +8,9 @@ use run_make_support::{diff, rustc}; fn main() { - let file_out = rustc().input("main.rs").output("main.rs").run_fail().stderr_utf8(); - let folder_out = rustc().input("main.rs").output(".").run_fail().stderr_utf8(); + let file_out = + rustc().edition("2015").input("main.rs").output("main.rs").run_fail().stderr_utf8(); + let folder_out = rustc().edition("2015").input("main.rs").output(".").run_fail().stderr_utf8(); diff().expected_file("file.stderr").actual_text("actual-file-stderr", file_out).run(); diff().expected_file("folder.stderr").actual_text("actual-folder-stderr", folder_out).run(); } diff --git a/tests/run-make/pointer-auth-link-with-c/rmake.rs b/tests/run-make/pointer-auth-link-with-c/rmake.rs index 1ac68c95559c6..e5793130c6550 100644 --- a/tests/run-make/pointer-auth-link-with-c/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c/rmake.rs @@ -16,6 +16,7 @@ use run_make_support::{build_native_static_lib, cc, is_windows_msvc, llvm_ar, ru fn main() { build_native_static_lib("test"); rustc() + .edition("2015") .arg("-Cunsafe-allow-abi-mismatch=branch-protection") .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") .input("test.rs") @@ -30,6 +31,7 @@ fn main() { let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); rustc() + .edition("2015") .arg("-Cunsafe-allow-abi-mismatch=branch-protection") .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") .input("test.rs") @@ -46,6 +48,7 @@ fn main() { // let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; // llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); // rustc() + // .edition("2015") // .arg("-Cunsafe-allow-abi-mismatch=branch-protection") // .arg("-Zbranch-protection=bti,pac-ret,pc,leaf") // .input("test.rs") diff --git a/tests/run-make/print-request-help-stable-unstable/rmake.rs b/tests/run-make/print-request-help-stable-unstable/rmake.rs index 01572a5aad942..49df84b353cd4 100644 --- a/tests/run-make/print-request-help-stable-unstable/rmake.rs +++ b/tests/run-make/print-request-help-stable-unstable/rmake.rs @@ -8,14 +8,15 @@ use run_make_support::{diff, rustc, similar, stable_bare_rustc}; fn main() { let stable_invalid_print_request_help = - stable_bare_rustc().print("xxx").run_fail().stderr_utf8(); + stable_bare_rustc().edition("2015").print("xxx").run_fail().stderr_utf8(); assert!(!stable_invalid_print_request_help.contains("all-target-specs-json")); diff() .expected_file("stable-invalid-print-request-help.err") .actual_text("stable_invalid_print_request_help", &stable_invalid_print_request_help) .run(); - let unstable_invalid_print_request_help = rustc().print("xxx").run_fail().stderr_utf8(); + let unstable_invalid_print_request_help = + rustc().edition("2015").print("xxx").run_fail().stderr_utf8(); assert!(unstable_invalid_print_request_help.contains("all-target-specs-json")); diff() .expected_file("unstable-invalid-print-request-help.err") diff --git a/tests/run-make/rustdoc/doctest/test_harness/rmake.rs b/tests/run-make/rustdoc/doctest/test_harness/rmake.rs index 608adebbd54f2..87d57ca1f76ac 100644 --- a/tests/run-make/rustdoc/doctest/test_harness/rmake.rs +++ b/tests/run-make/rustdoc/doctest/test_harness/rmake.rs @@ -19,6 +19,7 @@ fn main() { rustc().input(runtool_path).run(); let output = rustdoc() + .edition("2015") .input(doctests_path) .arg("--test") // for the outer test suite diff --git a/tests/run-make/target-cpu-native/rmake.rs b/tests/run-make/target-cpu-native/rmake.rs index 5791bf01bba2b..dbea1bda2cab5 100644 --- a/tests/run-make/target-cpu-native/rmake.rs +++ b/tests/run-make/target-cpu-native/rmake.rs @@ -9,6 +9,7 @@ use run_make_support::{run, rustc}; fn main() { let out = rustc() + .edition("2015") .input("foo.rs") .arg("-Ctarget-cpu=native") .arg("-Zverify-llvm-ir") diff --git a/tests/run-make/unknown-mod-stdin/rmake.rs b/tests/run-make/unknown-mod-stdin/rmake.rs index 101711b0d2c70..56833569e54bf 100644 --- a/tests/run-make/unknown-mod-stdin/rmake.rs +++ b/tests/run-make/unknown-mod-stdin/rmake.rs @@ -14,7 +14,8 @@ use run_make_support::{diff, rustc}; fn main() { - let out = rustc().crate_type("rlib").stdin_buf(b"mod unknown;").arg("-").run_fail(); + let out = + rustc().edition("2015").crate_type("rlib").stdin_buf(b"mod unknown;").arg("-").run_fail(); diff() .actual_text("actual-stdout", out.stdout_utf8()) .expected_file("unknown-mod.stdout") diff --git a/tests/run-make/unspecified-edition/help-unspecified-edition.stdout b/tests/run-make/unspecified-edition/help-unspecified-edition.stdout new file mode 100644 index 0000000000000..eea9ae297848a --- /dev/null +++ b/tests/run-make/unspecified-edition/help-unspecified-edition.stdout @@ -0,0 +1,74 @@ +Usage: rustc [OPTIONS] INPUT + +Options: + -h, --help Display this message + --cfg Configure the compilation environment. + SPEC supports the syntax `[=""]`. + --check-cfg + Provide list of expected cfgs for checking + -L [=] Add a directory to the library search path. The + optional KIND can be one of + (default: + all). + -l [[:]=][:] + Link the generated crate(s) to the specified native + library NAME. The optional KIND can be one of + (default: dylib). + Optional comma separated MODIFIERS + + may be specified each with a prefix of either '+' to + enable or '-' to disable. + --crate-type + Comma separated list of types of crates + for the compiler to emit + --crate-name + Specify the name of the crate being built + --edition <2015|2018|2021|2024|future> + Specify which edition of the compiler to use when + compiling code. The default is 2015 and the latest + stable edition is 2024. + --emit [=] + Comma separated list of types of output for the + compiler to emit. + Each TYPE has the default FILE name: + * asm - CRATE_NAME.s + * llvm-bc - CRATE_NAME.bc + * dep-info - CRATE_NAME.d + * link - (platform and crate-type dependent) + * llvm-ir - CRATE_NAME.ll + * metadata - libCRATE_NAME.rmeta + * mir - CRATE_NAME.mir + * obj - CRATE_NAME.o + * thin-link-bitcode - CRATE_NAME.indexing.o + --print [=] + Compiler information to print on stdout (or to a file) + INFO may be one of + . + -g Equivalent to -C debuginfo=2 + -O Equivalent to -C opt-level=3 + -o Write output to FILENAME + --out-dir Write output to compiler-chosen filename in DIR + --explain Provide a detailed explanation of an error message + --test Build a test harness + --target + Target tuple for which the code is compiled + -A, --allow Set lint allowed + -W, --warn Set lint warnings + --force-warn + Set lint force-warn + -D, --deny Set lint denied + -F, --forbid Set lint forbidden + --cap-lints + Set the most restrictive lint level. More restrictive + lints are capped at this level + -C, --codegen [=] + Set a codegen option + -V, --version Print version info and exit + -v, --verbose Use verbose output + +Additional help: + -C help Print codegen options + -W help Print 'lint' options and default settings + -Z help Print unstable compiler options + --help -v Print the full set of options rustc accepts + diff --git a/tests/run-make/unspecified-edition/main.rs b/tests/run-make/unspecified-edition/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/unspecified-edition/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/unspecified-edition/rmake.rs b/tests/run-make/unspecified-edition/rmake.rs new file mode 100644 index 0000000000000..8cfd304c97686 --- /dev/null +++ b/tests/run-make/unspecified-edition/rmake.rs @@ -0,0 +1,39 @@ +// When calling `rustc` without an explicit edition, emit a note asking the user to specify one, +// clarifying that the default is 2015. +//@ needs-target-std +//@ ignore-cross-compile + +use run_make_support::{bare_rustc, diff, rustc, rustdoc}; + +fn main() { + rustc().edition("2015").input("main.rs").run().assert_stderr_not_contains("--edition"); + let out = rustc().input("main.rs").run().assert_stderr_contains("--edition").stderr_utf8(); + diff().expected_file("unspecified-edition.stderr").actual_text("(rustc)", &out).run(); + + // Ensure that we only mention --edition when compiling code. + let out = rustc().run_fail().assert_stderr_not_contains("--edition").stderr_utf8(); + diff() + .expected_file("unspecified-edition-without-compiling.stderr") + .actual_text("(rustc)", &out) + .run(); + + // Ensure that we dont mention --edition when running rustdoc. + let out = rustdoc().run_fail().assert_stderr_not_contains("--edition").stderr_utf8(); + diff() + .expected_text("(test)", "error: missing file operand\n\n") + .actual_text("(rustc)", &out) + .run(); + + let out = + rustdoc().input("main.rs").run().assert_stderr_not_contains("--edition").stderr_utf8(); + diff().expected_text("(test)", "").actual_text("(rustc)", &out).run(); + + // Ensure that we don't mention --edition when getting help. + let result = rustc().arg("--help").run(); + result.assert_stderr_not_contains("--edition"); + let out = result.stdout_utf8(); + let err = result.stderr_utf8(); + diff().expected_file("help-unspecified-edition.stdout").actual_text("(rustc)", &out).run(); + diff().expected_text("(test)", "").actual_text("(rustc)", &err).run(); + bare_rustc().arg("--version").run().assert_stderr_not_contains("--edition"); +} diff --git a/tests/run-make/unspecified-edition/unspecified-edition-without-compiling.stderr b/tests/run-make/unspecified-edition/unspecified-edition-without-compiling.stderr new file mode 100644 index 0000000000000..c36ecca4af4a2 --- /dev/null +++ b/tests/run-make/unspecified-edition/unspecified-edition-without-compiling.stderr @@ -0,0 +1,4 @@ +error: no input filename given + +error: aborting due to 1 previous error + diff --git a/tests/run-make/unspecified-edition/unspecified-edition.stderr b/tests/run-make/unspecified-edition/unspecified-edition.stderr new file mode 100644 index 0000000000000..0a15e47d49fae --- /dev/null +++ b/tests/run-make/unspecified-edition/unspecified-edition.stderr @@ -0,0 +1,2 @@ +`--edition` is unspecified, defaulting to `2015` while the latest is `2024`; it must be one of: <2015|2018|2021|2024|future> + diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs index 0956556bf5ef2..2351cda7b2195 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -130,6 +130,9 @@ struct MessageWrongType { #[primary_span] //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` foo: String, + #[context] + //~^ ERROR `#[context]` attribute can only be applied to fields of type `Span` or `MultiSpan` + bar: String, } #[derive(Diagnostic)] diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr index f077dc1221ced..0486ac91de8f5 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -138,26 +138,32 @@ error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied t LL | #[primary_span] | ^ +error: derive(Diagnostic): the `#[context]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:133:5 + | +LL | #[context] + | ^ + error: derive(Diagnostic): `#[nonsense]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:138:5 + --> $DIR/diagnostic-derive-inline.rs:141:5 | LL | #[nonsense] | ^ error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive-inline.rs:155:5 + --> $DIR/diagnostic-derive-inline.rs:158:5 | LL | #[label("with a label")] | ^ error: derive(Diagnostic): `name` doesn't refer to a field on this type - --> $DIR/diagnostic-derive-inline.rs:163:46 + --> $DIR/diagnostic-derive-inline.rs:166:46 | LL | #[suggestion("with a suggestion", code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `}` but string was terminated - --> $DIR/diagnostic-derive-inline.rs:168:10 + --> $DIR/diagnostic-derive-inline.rs:171:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ expected `}` in format string @@ -166,7 +172,7 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found - --> $DIR/diagnostic-derive-inline.rs:178:10 + --> $DIR/diagnostic-derive-inline.rs:181:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ unmatched `}` in format string @@ -175,19 +181,19 @@ LL | #[derive(Diagnostic)] = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive-inline.rs:198:5 + --> $DIR/diagnostic-derive-inline.rs:201:5 | LL | #[label("with a label")] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive-inline.rs:217:5 + --> $DIR/diagnostic-derive-inline.rs:220:5 | LL | #[suggestion("with a suggestion")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive-inline.rs:225:39 + --> $DIR/diagnostic-derive-inline.rs:228:39 | LL | #[suggestion("with a suggestion", nonsense = "bar")] | ^^^^^^^^ @@ -195,13 +201,13 @@ LL | #[suggestion("with a suggestion", nonsense = "bar")] = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive-inline.rs:225:5 + --> $DIR/diagnostic-derive-inline.rs:228:5 | LL | #[suggestion("with a suggestion", nonsense = "bar")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/diagnostic-derive-inline.rs:234:39 + --> $DIR/diagnostic-derive-inline.rs:237:39 | LL | #[suggestion("with a suggestion", msg = "bar")] | ^^^ @@ -209,13 +215,13 @@ LL | #[suggestion("with a suggestion", msg = "bar")] = help: only `style`, `code` and `applicability` are valid nested attributes error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive-inline.rs:234:5 + --> $DIR/diagnostic-derive-inline.rs:237:5 | LL | #[suggestion("with a suggestion", msg = "bar")] | ^ error: derive(Diagnostic): wrong field type for suggestion - --> $DIR/diagnostic-derive-inline.rs:257:5 + --> $DIR/diagnostic-derive-inline.rs:260:5 | LL | #[suggestion("with a suggestion", code = "This is suggested code")] | ^ @@ -223,91 +229,91 @@ LL | #[suggestion("with a suggestion", code = "This is suggested code")] = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive-inline.rs:273:24 + --> $DIR/diagnostic-derive-inline.rs:276:24 | LL | suggestion: (Span, Span, Applicability), | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive-inline.rs:273:18 + --> $DIR/diagnostic-derive-inline.rs:276:18 | LL | suggestion: (Span, Span, Applicability), | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive-inline.rs:281:33 + --> $DIR/diagnostic-derive-inline.rs:284:33 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive-inline.rs:281:18 + --> $DIR/diagnostic-derive-inline.rs:284:18 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:288:5 + --> $DIR/diagnostic-derive-inline.rs:291:5 | LL | #[label = "bar"] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive-inline.rs:387:5 + --> $DIR/diagnostic-derive-inline.rs:390:5 | LL | #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] | ^ | note: previously specified here - --> $DIR/diagnostic-derive-inline.rs:389:24 + --> $DIR/diagnostic-derive-inline.rs:392:24 | LL | suggestion: (Span, Applicability), | ^^^^^^^^^^^^^ error: derive(Diagnostic): invalid applicability - --> $DIR/diagnostic-derive-inline.rs:395:69 + --> $DIR/diagnostic-derive-inline.rs:398:69 | LL | #[suggestion("with a suggestion", code = "...", applicability = "batman")] | ^^^^^^^^ error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()` - --> $DIR/diagnostic-derive-inline.rs:458:5 + --> $DIR/diagnostic-derive-inline.rs:461:5 | LL | #[help("with a help")] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/diagnostic-derive-inline.rs:467:29 + --> $DIR/diagnostic-derive-inline.rs:470:29 | LL | #[label("with a label", foo)] | ^^^ error: derive(Diagnostic): a diagnostic message must be the first argument to the attribute - --> $DIR/diagnostic-derive-inline.rs:475:29 + --> $DIR/diagnostic-derive-inline.rs:478:29 | LL | #[label("with a label", "and another one?")] | ^^^^^^^^^^^^^^^^^^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/diagnostic-derive-inline.rs:483:29 + --> $DIR/diagnostic-derive-inline.rs:486:29 | LL | #[label("with a label", foo = "...")] | ^^^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/diagnostic-derive-inline.rs:491:29 + --> $DIR/diagnostic-derive-inline.rs:494:29 | LL | #[label("with a label", foo("..."))] | ^^^ error: derive(Diagnostic): `#[error(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:511:1 + --> $DIR/diagnostic-derive-inline.rs:514:1 | LL | #[error("this is an example message", code = E0123)] | ^ error: derive(Diagnostic): diagnostic message not specified - --> $DIR/diagnostic-derive-inline.rs:511:1 + --> $DIR/diagnostic-derive-inline.rs:514:1 | LL | #[error("this is an example message", code = E0123)] | ^ @@ -315,13 +321,13 @@ LL | #[error("this is an example message", code = E0123)] = help: specify the message as the first argument to the `#[diag(...)]` attribute, such as `#[diag("Example error")]` error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:518:1 + --> $DIR/diagnostic-derive-inline.rs:521:1 | LL | #[warn_("this is an example message", code = E0123)] | ^ error: derive(Diagnostic): diagnostic message not specified - --> $DIR/diagnostic-derive-inline.rs:518:1 + --> $DIR/diagnostic-derive-inline.rs:521:1 | LL | #[warn_("this is an example message", code = E0123)] | ^ @@ -329,13 +335,13 @@ LL | #[warn_("this is an example message", code = E0123)] = help: specify the message as the first argument to the `#[diag(...)]` attribute, such as `#[diag("Example error")]` error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:525:1 + --> $DIR/diagnostic-derive-inline.rs:528:1 | LL | #[lint("this is an example message", code = E0123)] | ^ error: derive(Diagnostic): diagnostic message not specified - --> $DIR/diagnostic-derive-inline.rs:525:1 + --> $DIR/diagnostic-derive-inline.rs:528:1 | LL | #[lint("this is an example message", code = E0123)] | ^ @@ -343,19 +349,19 @@ LL | #[lint("this is an example message", code = E0123)] = help: specify the message as the first argument to the `#[diag(...)]` attribute, such as `#[diag("Example error")]` error: derive(Diagnostic): attribute specified multiple times - --> $DIR/diagnostic-derive-inline.rs:534:53 + --> $DIR/diagnostic-derive-inline.rs:537:53 | LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive-inline.rs:534:39 + --> $DIR/diagnostic-derive-inline.rs:537:39 | LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive-inline.rs:543:24 + --> $DIR/diagnostic-derive-inline.rs:546:24 | LL | suggestion: (Span, usize), | ^^^^^ @@ -363,7 +369,7 @@ LL | suggestion: (Span, usize), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): wrong types for suggestion - --> $DIR/diagnostic-derive-inline.rs:551:17 + --> $DIR/diagnostic-derive-inline.rs:554:17 | LL | suggestion: (Span,), | ^^^^^^^ @@ -371,13 +377,13 @@ LL | suggestion: (Span,), = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/diagnostic-derive-inline.rs:558:5 + --> $DIR/diagnostic-derive-inline.rs:561:5 | LL | #[suggestion("with a suggestion")] | ^ error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:565:1 + --> $DIR/diagnostic-derive-inline.rs:568:1 | LL | #[multipart_suggestion("with a suggestion")] | ^ @@ -385,7 +391,7 @@ LL | #[multipart_suggestion("with a suggestion")] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:568:1 + --> $DIR/diagnostic-derive-inline.rs:571:1 | LL | #[multipart_suggestion()] | ^ @@ -393,7 +399,7 @@ LL | #[multipart_suggestion()] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:572:5 + --> $DIR/diagnostic-derive-inline.rs:575:5 | LL | #[multipart_suggestion("with a suggestion")] | ^ @@ -401,7 +407,7 @@ LL | #[multipart_suggestion("with a suggestion")] = help: consider creating a `Subdiagnostic` instead error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:580:1 + --> $DIR/diagnostic-derive-inline.rs:583:1 | LL | #[suggestion("with a suggestion", code = "...")] | ^ @@ -409,7 +415,7 @@ LL | #[suggestion("with a suggestion", code = "...")] = help: `#[label]` and `#[suggestion]` can only be applied to fields error: derive(Diagnostic): `#[label]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:589:1 + --> $DIR/diagnostic-derive-inline.rs:592:1 | LL | #[label] | ^ @@ -417,67 +423,67 @@ LL | #[label] = help: subdiagnostic message is missing error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:623:5 + --> $DIR/diagnostic-derive-inline.rs:626:5 | LL | #[subdiagnostic(bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:631:5 + --> $DIR/diagnostic-derive-inline.rs:634:5 | LL | #[subdiagnostic = "bad"] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:639:5 + --> $DIR/diagnostic-derive-inline.rs:642:5 | LL | #[subdiagnostic(bad, bad)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:647:5 + --> $DIR/diagnostic-derive-inline.rs:650:5 | LL | #[subdiagnostic("bad")] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:655:5 + --> $DIR/diagnostic-derive-inline.rs:658:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:676:5 + --> $DIR/diagnostic-derive-inline.rs:679:5 | LL | #[subdiagnostic(eager)] | ^ error: derive(Diagnostic): expected at least one string literal for `code(...)` - --> $DIR/diagnostic-derive-inline.rs:707:44 + --> $DIR/diagnostic-derive-inline.rs:710:44 | LL | #[suggestion("with a suggestion", code())] | ^ error: derive(Diagnostic): `code(...)` must contain only string literals - --> $DIR/diagnostic-derive-inline.rs:715:44 + --> $DIR/diagnostic-derive-inline.rs:718:44 | LL | #[suggestion("with a suggestion", code(foo))] | ^^^ error: unexpected token, expected `)` - --> $DIR/diagnostic-derive-inline.rs:715:44 + --> $DIR/diagnostic-derive-inline.rs:718:44 | LL | #[suggestion("with a suggestion", code(foo))] | ^^^ error: expected string literal - --> $DIR/diagnostic-derive-inline.rs:724:46 + --> $DIR/diagnostic-derive-inline.rs:727:46 | LL | #[suggestion("with a suggestion", code = 3)] | ^ error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive-inline.rs:739:5 + --> $DIR/diagnostic-derive-inline.rs:742:5 | LL | #[suggestion("with a suggestion", code = "")] | ^ @@ -487,7 +493,7 @@ LL | #[suggestion("with a suggestion", code = "")] = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` error: derive(Diagnostic): Variable `nosub` not found in diagnostic - --> $DIR/diagnostic-derive-inline.rs:751:8 + --> $DIR/diagnostic-derive-inline.rs:754:8 | LL | #[diag("does not exist: {$nosub}")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -501,13 +507,13 @@ LL | #[nonsense("this is an example message", code = E0123)] | ^^^^^^^^ error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive-inline.rs:138:7 + --> $DIR/diagnostic-derive-inline.rs:141:7 | LL | #[nonsense] | ^^^^^^^^ error: cannot find attribute `error` in this scope - --> $DIR/diagnostic-derive-inline.rs:511:3 + --> $DIR/diagnostic-derive-inline.rs:514:3 | LL | #[error("this is an example message", code = E0123)] | ^^^^^ @@ -519,7 +525,7 @@ LL | struct ErrorAttribute {} | error: cannot find attribute `warn_` in this scope - --> $DIR/diagnostic-derive-inline.rs:518:3 + --> $DIR/diagnostic-derive-inline.rs:521:3 | LL | #[warn_("this is an example message", code = E0123)] | ^^^^^ @@ -531,7 +537,7 @@ LL + #[warn("this is an example message", code = E0123)] | error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive-inline.rs:525:3 + --> $DIR/diagnostic-derive-inline.rs:528:3 | LL | #[lint("this is an example message", code = E0123)] | ^^^^ @@ -548,7 +554,7 @@ LL + #[lift("this is an example message", code = E0123)] | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive-inline.rs:565:3 + --> $DIR/diagnostic-derive-inline.rs:568:3 | LL | #[multipart_suggestion("with a suggestion")] | ^^^^^^^^^^^^^^^^^^^^ @@ -560,7 +566,7 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive-inline.rs:568:3 + --> $DIR/diagnostic-derive-inline.rs:571:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ @@ -572,12 +578,12 @@ LL | struct MultipartSuggestion { | error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive-inline.rs:572:7 + --> $DIR/diagnostic-derive-inline.rs:575:7 | LL | #[multipart_suggestion("with a suggestion")] | ^^^^^^^^^^^^^^^^^^^^ | = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute -error: aborting due to 75 previous errors +error: aborting due to 76 previous errors diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-pass.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-pass.rs new file mode 100644 index 0000000000000..ea116b0f17fd6 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-pass.rs @@ -0,0 +1,38 @@ +//@ check-pass +//@ compile-flags: -Ztrack-diagnostics + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Diagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::Span; + +extern crate rustc_macros; +use rustc_macros::Diagnostic; + +extern crate rustc_errors; +use rustc_errors::ErrCode; + +extern crate rustc_session; + +extern crate core; + +// E0123 is no longer used, so we define our own constant here just for this test. +const E0123: ErrCode = ErrCode::from_u32(0123); + +#[derive(Diagnostic)] +#[diag("message", code = E0123)] +struct Diagnostic { + #[primary_span] + #[label("label text")] + span: Span, + #[context] + context: Span, +} diff --git a/tests/ui/asm/naked-functions/naked-functions.rs b/tests/ui/asm/naked-functions/naked-functions.rs index d994b1151514a..e7da65a169074 100644 --- a/tests/ui/asm/naked-functions/naked-functions.rs +++ b/tests/ui/asm/naked-functions/naked-functions.rs @@ -224,7 +224,7 @@ pub extern "C" fn compatible_doc_attributes() { naked_asm!("", options(raw)); } -#[linkage = "external"] +#[linkage = "weak"] #[unsafe(naked)] pub extern "C" fn compatible_linkage() { naked_asm!("", options(raw)); diff --git a/tests/ui/associated-consts/shadowed-const.stderr b/tests/ui/associated-consts/shadowed-const.stderr index 2db645b3c9662..4c105ddaf2974 100644 --- a/tests/ui/associated-consts/shadowed-const.stderr +++ b/tests/ui/associated-consts/shadowed-const.stderr @@ -1,6 +1,8 @@ error: expected type, found constant --> $DIR/shadowed-const.rs:19:20 | +LL | trait Baz3 { +LL | const BAR: usize; LL | const QUX: Self::BAR; | ^^^ unexpected constant | diff --git a/tests/ui/associated-item/associated-item-duplicate-names-3.stderr b/tests/ui/associated-item/associated-item-duplicate-names-3.stderr index 84a9da0998832..950ee878fb391 100644 --- a/tests/ui/associated-item/associated-item-duplicate-names-3.stderr +++ b/tests/ui/associated-item/associated-item-duplicate-names-3.stderr @@ -1,9 +1,11 @@ error[E0201]: duplicate definitions with name `Bar`: --> $DIR/associated-item-duplicate-names-3.rs:14:5 | +LL | trait Foo { LL | type Bar; | --------- item in trait ... +LL | impl Foo for Baz { LL | type Bar = i16; | --------------- previous definition here LL | type Bar = u16; diff --git a/tests/ui/associated-item/associated-item-duplicate-names.stderr b/tests/ui/associated-item/associated-item-duplicate-names.stderr index f89ea6e57cc43..b9c02a910d070 100644 --- a/tests/ui/associated-item/associated-item-duplicate-names.stderr +++ b/tests/ui/associated-item/associated-item-duplicate-names.stderr @@ -1,9 +1,11 @@ error[E0201]: duplicate definitions with name `Ty`: --> $DIR/associated-item-duplicate-names.rs:11:5 | +LL | trait Foo { LL | type Ty; | -------- item in trait ... +LL | impl Foo for () { LL | type Ty = (); | ------------- previous definition here LL | type Ty = usize; @@ -12,9 +14,13 @@ LL | type Ty = usize; error[E0201]: duplicate definitions with name `BAR`: --> $DIR/associated-item-duplicate-names.rs:13:5 | +LL | trait Foo { +LL | type Ty; LL | const BAR: u32; | --------------- item in trait ... +LL | impl Foo for () { +... LL | const BAR: u32 = 7; | ------------------- previous definition here LL | const BAR: u32 = 8; diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs index 737ebf7f2b144..2ea4e0b5a395c 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.rs +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.rs @@ -11,7 +11,7 @@ trait Test { //~^ ERROR cannot be used on required trait methods [unused_attributes] //~| WARN previously accepted fn method2(&self); - #[linkage = "common"] + #[linkage = "weak"] //~^ ERROR `linkage` attribute cannot be used on required trait methods fn method3(&self); #[track_caller] diff --git a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr index b5452ba2882c0..729231956337d 100644 --- a/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr +++ b/tests/ui/attributes/codegen_attr_on_required_trait_method.stderr @@ -1,7 +1,7 @@ error: the `linkage` attribute cannot be used on required trait methods --> $DIR/codegen_attr_on_required_trait_method.rs:14:7 | -LL | #[linkage = "common"] +LL | #[linkage = "weak"] | ^^^^^^^ | = help: the `linkage` attribute can be applied to foreign functions, foreign statics, functions with a body, and statics diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr index 9756a009ec9b9..a16413d442f9a 100644 --- a/tests/ui/delegation/bad-resolve.stderr +++ b/tests/ui/delegation/bad-resolve.stderr @@ -1,18 +1,25 @@ error[E0324]: item `C` is an associated method, which doesn't match its trait `Trait` --> $DIR/bad-resolve.rs:23:5 | +LL | trait Trait { LL | const C: u32 = 0; | ----------------- item in trait ... +LL | impl Trait for S { +LL | LL | reuse ::C; | ^^^^^^^^^^^^^^^^^^^^^^ does not match trait error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` --> $DIR/bad-resolve.rs:26:5 | +LL | trait Trait { +LL | const C: u32 = 0; LL | type Type; | ---------- item in trait ... +LL | impl Trait for S { +... LL | reuse ::Type; | ^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait diff --git a/tests/ui/delegation/duplicate-definition-inside-trait-impl.stderr b/tests/ui/delegation/duplicate-definition-inside-trait-impl.stderr index 9e2dd8ea84ac9..bd877e24bf2e8 100644 --- a/tests/ui/delegation/duplicate-definition-inside-trait-impl.stderr +++ b/tests/ui/delegation/duplicate-definition-inside-trait-impl.stderr @@ -1,9 +1,11 @@ error[E0201]: duplicate definitions with name `foo`: --> $DIR/duplicate-definition-inside-trait-impl.rs:18:5 | +LL | trait Trait { LL | fn foo(&self) -> u32 { 0 } | -------------------------- item in trait ... +LL | impl Trait for S { LL | reuse to_reuse::foo { self } | ---------------------------- previous definition here LL | reuse Trait::foo; diff --git a/tests/ui/delegation/glob-glob-conflict.stderr b/tests/ui/delegation/glob-glob-conflict.stderr index 4ada3eb0efa69..73831c9e48e33 100644 --- a/tests/ui/delegation/glob-glob-conflict.stderr +++ b/tests/ui/delegation/glob-glob-conflict.stderr @@ -1,9 +1,11 @@ error[E0201]: duplicate definitions with name `method`: --> $DIR/glob-glob-conflict.rs:26:5 | +LL | trait Trait { LL | fn method(&self) -> u8; | ----------------------- item in trait ... +LL | impl Trait for u8 { LL | reuse Trait1::*; | ---------------- previous definition here LL | reuse Trait2::*; @@ -12,9 +14,11 @@ LL | reuse Trait2::*; error[E0201]: duplicate definitions with name `method`: --> $DIR/glob-glob-conflict.rs:32:5 | +LL | trait Trait { LL | fn method(&self) -> u8; | ----------------------- item in trait ... +LL | impl Trait for u16 { LL | reuse Trait1::*; | ---------------- previous definition here LL | reuse Trait1::*; diff --git a/tests/ui/delegation/glob-non-fn.stderr b/tests/ui/delegation/glob-non-fn.stderr index e1a4ffce5ac17..a09bc7a5aa27f 100644 --- a/tests/ui/delegation/glob-non-fn.stderr +++ b/tests/ui/delegation/glob-non-fn.stderr @@ -1,27 +1,35 @@ error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` --> $DIR/glob-non-fn.rs:29:5 | +LL | trait Trait { +LL | fn method(&self); LL | const CONST: u8; | ---------------- item in trait ... +LL | impl Trait for Bad { LL | reuse Trait::* { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` --> $DIR/glob-non-fn.rs:29:5 | +LL | trait Trait { +... LL | type Type; | ---------- item in trait ... +LL | impl Trait for Bad { LL | reuse Trait::* { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait error[E0201]: duplicate definitions with name `method`: --> $DIR/glob-non-fn.rs:29:5 | +LL | trait Trait { LL | fn method(&self); | ----------------- item in trait ... +LL | impl Trait for Bad { LL | reuse Trait::* { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | | diff --git a/tests/ui/delegation/impl-reuse-non-reuse-items.stderr b/tests/ui/delegation/impl-reuse-non-reuse-items.stderr index 2a33982cc6730..cc894d8ef4330 100644 --- a/tests/ui/delegation/impl-reuse-non-reuse-items.stderr +++ b/tests/ui/delegation/impl-reuse-non-reuse-items.stderr @@ -1,6 +1,8 @@ error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` --> $DIR/impl-reuse-non-reuse-items.rs:22:5 | +LL | trait Trait { +LL | fn method(&self); LL | const CONST: u8; | ---------------- item in trait ... @@ -10,6 +12,8 @@ LL | reuse impl Trait for S { &self.0 } error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` --> $DIR/impl-reuse-non-reuse-items.rs:22:5 | +LL | trait Trait { +... LL | type Type; | ---------- item in trait ... @@ -19,6 +23,7 @@ LL | reuse impl Trait for S { &self.0 } error[E0201]: duplicate definitions with name `method`: --> $DIR/impl-reuse-non-reuse-items.rs:22:5 | +LL | trait Trait { LL | fn method(&self); | ----------------- item in trait ... diff --git a/tests/ui/error-codes/E0201.stderr b/tests/ui/error-codes/E0201.stderr index 608ff69177694..bd1ccc00fbd58 100644 --- a/tests/ui/error-codes/E0201.stderr +++ b/tests/ui/error-codes/E0201.stderr @@ -1,9 +1,13 @@ error[E0201]: duplicate definitions with name `baz`: --> $DIR/E0201.rs:17:5 | +LL | trait Baz { +LL | type Quux; LL | fn baz(&self) -> bool; | ---------------------- item in trait ... +LL | impl Baz for Foo { +... LL | fn baz(&self) -> bool { true } | ------------------------------ previous definition here LL | fn baz(&self) -> bool { self.0 > 5 } @@ -12,9 +16,11 @@ LL | fn baz(&self) -> bool { self.0 > 5 } error[E0201]: duplicate definitions with name `Quux`: --> $DIR/E0201.rs:18:5 | +LL | trait Baz { LL | type Quux; | ---------- item in trait ... +LL | impl Baz for Foo { LL | type Quux = u32; | ---------------- previous definition here ... diff --git a/tests/ui/hygiene/impl_items-2.stderr b/tests/ui/hygiene/impl_items-2.stderr index e7128f83e110b..723324c10294e 100644 --- a/tests/ui/hygiene/impl_items-2.stderr +++ b/tests/ui/hygiene/impl_items-2.stderr @@ -1,12 +1,15 @@ error[E0201]: duplicate definitions with name `foo`: --> $DIR/impl_items-2.rs:14:5 | +LL | trait Trait { LL | fn foo() {} | ----------- item in trait ... LL | fn foo() {} | ----------- previous definition here ... +LL | impl Trait for i32 { +LL | trait_impl!(); LL | fn foo() {} | ^^^^^^^^^^^ duplicate definition diff --git a/tests/ui/impl-trait/in-trait/rpitit-duplicate-associated-fn.stderr b/tests/ui/impl-trait/in-trait/rpitit-duplicate-associated-fn.stderr index faa65f45d33cb..5f715ff16fa85 100644 --- a/tests/ui/impl-trait/in-trait/rpitit-duplicate-associated-fn.stderr +++ b/tests/ui/impl-trait/in-trait/rpitit-duplicate-associated-fn.stderr @@ -42,9 +42,11 @@ LL | fn foo() -> impl Sized; error[E0201]: duplicate definitions with name `method`: --> $DIR/rpitit-duplicate-associated-fn.rs:12:5 | +LL | trait Bar { LL | fn method() -> impl Sized; | -------------------------- item in trait ... +LL | impl Bar for () { LL | / fn method() -> impl Sized { LL | | 42 LL | | } @@ -57,9 +59,11 @@ LL | | } error[E0201]: duplicate definitions with name `method`: --> $DIR/rpitit-duplicate-associated-fn.rs:25:5 | +LL | trait T { LL | fn method() -> impl Sized; | -------------------------- item in trait ... +LL | impl T for () { LL | / fn method() -> impl Sized { LL | | 42 LL | | } diff --git a/tests/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr b/tests/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr index 917d204367ffe..ad3500a6ff61d 100644 --- a/tests/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr +++ b/tests/ui/lifetimes/lifetime-mismatch-between-trait-and-impl.stderr @@ -1,9 +1,11 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/lifetime-mismatch-between-trait-and-impl.rs:6:5 | +LL | trait Foo { LL | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32; | ------------------------------------------- expected `fn(&'1 i32, &'a i32) -> &'a i32` ... +LL | impl Foo for () { LL | fn foo<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 i32, &'1 i32) -> &'1 i32` | diff --git a/tests/ui/lifetimes/trait-impl-mismatch-elided-lifetime-issue-65866.stderr b/tests/ui/lifetimes/trait-impl-mismatch-elided-lifetime-issue-65866.stderr index 4c4f6032b0f7b..cfe1e9353316f 100644 --- a/tests/ui/lifetimes/trait-impl-mismatch-elided-lifetime-issue-65866.stderr +++ b/tests/ui/lifetimes/trait-impl-mismatch-elided-lifetime-issue-65866.stderr @@ -1,9 +1,11 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/trait-impl-mismatch-elided-lifetime-issue-65866.rs:17:9 | +LL | trait Bar { LL | fn bar(&self, r: &mut Re); | -------------------------- expected `fn(&'1 Foo, &'2 mut Re<'3>)` ... +LL | impl Bar for Foo { LL | fn bar<'a, 'b>(&'a self, _r: &'b mut Re<'a>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Foo, &'2 mut Re<'1>)` | @@ -20,9 +22,11 @@ LL | fn bar(&self, r: &mut Re); error: `impl` item signature doesn't match `trait` item signature --> $DIR/trait-impl-mismatch-elided-lifetime-issue-65866.rs:40:9 | +LL | trait Bar { LL | fn bar(&self, r: &mut Re); | ------------------------------ expected `fn(&'1 Foo, &'2 mut Re<'3, u8>)` ... +LL | impl Bar for Foo { LL | fn bar<'a, 'b>(&'a self, _r: &'b mut Re<'a, u8>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Foo, &'2 mut Re<'1, u8>)` | diff --git a/tests/ui/linkage-attr/auxiliary/def_external.rs b/tests/ui/linkage-attr/auxiliary/def_external.rs deleted file mode 100644 index 2300930e513af..0000000000000 --- a/tests/ui/linkage-attr/auxiliary/def_external.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![feature(linkage)] -#![crate_type = "lib"] - -#[linkage="external"] -pub static EXTERN: u32 = 0; diff --git a/tests/ui/linkage-attr/auxiliary/def_weak.rs b/tests/ui/linkage-attr/auxiliary/def_weak.rs new file mode 100644 index 0000000000000..a8940f73c5536 --- /dev/null +++ b/tests/ui/linkage-attr/auxiliary/def_weak.rs @@ -0,0 +1,5 @@ +#![feature(linkage)] +#![crate_type = "lib"] + +#[linkage = "weak"] +pub static WEAK: u32 = 0; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs b/tests/ui/linkage-attr/common-linkage-non-zero-init.rs deleted file mode 100644 index 512616251c2f7..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ build-fail -//@ failure-status: 101 -//@ known-bug: #109681 -//@ ignore-wasm32 this appears to SIGABRT on wasm, not fail cleanly -//@ compile-flags: -Z verify-llvm-ir -//@ ignore-backends: gcc - -// This test verifies that we continue to hit the LLVM error for common linkage with non-zero -// initializers, since it generates invalid LLVM IR. -// Linkages are internal features marked as perma-unstable, so we don't need to fix the issue -// for now. -#![crate_type="lib"] -#![feature(linkage)] - -#[linkage = "common"] -#[no_mangle] -pub static TEST: bool = true; diff --git a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr b/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr deleted file mode 100644 index 93015bb2bac12..0000000000000 --- a/tests/ui/linkage-attr/common-linkage-non-zero-init.stderr +++ /dev/null @@ -1,3 +0,0 @@ -'common' global must have a zero initializer! -ptr @TEST -rustc-LLVM ERROR: Broken module found, compilation aborted! diff --git a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs index 0717a2d5a6c9e..e1ad2405b1df8 100644 --- a/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs +++ b/tests/ui/linkage-attr/linkage-attr-does-not-panic-llvm-issue-33992.rs @@ -1,15 +1,9 @@ //@ run-pass //@ ignore-apple -//@ ignore-wasm32 common linkage not implemented right now +//@ ignore-wasm32 linkonce linkage not working right now #![feature(linkage)] -#[linkage = "external"] -pub static TEST2: bool = true; - -#[linkage = "internal"] -pub static TEST3: bool = true; - #[cfg(not(target_env = "msvc"))] #[linkage = "linkonce"] pub static TEST4: bool = true; diff --git a/tests/ui/linkage-attr/linkage-import.rs b/tests/ui/linkage-attr/linkage-import.rs index 838d1fc29a238..51fcf1c133f32 100644 --- a/tests/ui/linkage-attr/linkage-import.rs +++ b/tests/ui/linkage-attr/linkage-import.rs @@ -1,8 +1,8 @@ //@ build-pass -//@ aux-build:def_external.rs +//@ aux-build:def_weak.rs -extern crate def_external as dep; +extern crate def_weak as dep; fn main() { - println!("{:p}", &dep::EXTERN); + println!("{:p}", &dep::WEAK); } diff --git a/tests/ui/linkage-attr/linkage3.stderr b/tests/ui/linkage-attr/linkage3.stderr index aa25cd39ba291..616f80df09c8b 100644 --- a/tests/ui/linkage-attr/linkage3.stderr +++ b/tests/ui/linkage-attr/linkage3.stderr @@ -4,7 +4,7 @@ error[E0539]: malformed `linkage` attribute input LL | #[linkage = "foo"] | ^^^^^^^^^^----- | | - | valid arguments are `available_externally`, `common`, `extern_weak`, `external`, `internal`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` + | valid arguments are `available_externally`, `extern_weak`, `linkonce`, `linkonce_odr`, `weak` or `weak_odr` error: aborting due to 1 previous error diff --git a/tests/ui/linkage-attr/linkage4.rs b/tests/ui/linkage-attr/linkage4.rs index 3b935f9723dc1..25df81189d8f5 100644 --- a/tests/ui/linkage-attr/linkage4.rs +++ b/tests/ui/linkage-attr/linkage4.rs @@ -1,4 +1,4 @@ -#[linkage = "external"] +#[linkage = "weak"] static foo: isize = 0; //~^^ ERROR: the `linkage` attribute is experimental and not portable diff --git a/tests/ui/linkage-attr/linkage4.stderr b/tests/ui/linkage-attr/linkage4.stderr index a5970d100581a..2f04ceee5199d 100644 --- a/tests/ui/linkage-attr/linkage4.stderr +++ b/tests/ui/linkage-attr/linkage4.stderr @@ -1,7 +1,7 @@ error[E0658]: the `linkage` attribute is experimental and not portable across platforms --> $DIR/linkage4.rs:1:3 | -LL | #[linkage = "external"] +LL | #[linkage = "weak"] | ^^^^^^^ | = note: see issue #29603 for more information diff --git a/tests/ui/lint/inline-exported.rs b/tests/ui/lint/inline-exported.rs index 6a23cd58236ff..25ca5f0633079 100644 --- a/tests/ui/lint/inline-exported.rs +++ b/tests/ui/lint/inline-exported.rs @@ -17,12 +17,8 @@ fn export_name() {} #[inline] //~^ ERROR: `#[inline]` is ignored on externally exported functions -#[linkage = "external"] -fn external_linkage() {} +#[linkage = "weak"] +fn weak_linkage() {} #[inline] fn normal() {} - -#[inline] -#[linkage = "internal"] // not exported -fn internal_linkage() {} diff --git a/tests/ui/mismatched_types/issue-75361-mismatched-impl.stderr b/tests/ui/mismatched_types/issue-75361-mismatched-impl.stderr index 5c2278ddfc6af..09556076d787a 100644 --- a/tests/ui/mismatched_types/issue-75361-mismatched-impl.stderr +++ b/tests/ui/mismatched_types/issue-75361-mismatched-impl.stderr @@ -1,9 +1,13 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/issue-75361-mismatched-impl.rs:18:3 | +LL | pub trait Graph { +... LL | fn adjacent_edges(&self) -> Box>; | --------------------------------------------------------------------- expected `fn(&'1 T) -> Box<(dyn MyTrait + 'static)>` ... +LL | impl Graph for T { +... LL | fn adjacent_edges(&self) -> Box + '_> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 T) -> Box<(dyn MyTrait + '1)>` | diff --git a/tests/ui/pin-ergonomics/pinned-drop-check.stderr b/tests/ui/pin-ergonomics/pinned-drop-check.stderr index 3dc6e18a9178e..6087d45dd2900 100644 --- a/tests/ui/pin-ergonomics/pinned-drop-check.stderr +++ b/tests/ui/pin-ergonomics/pinned-drop-check.stderr @@ -1,24 +1,28 @@ error[E0201]: duplicate definitions with name `pin_drop`: --> $DIR/pinned-drop-check.rs:196:9 | +LL | impl Drop for Foo { LL | fn drop(&pin mut self) {} | ------------------------- previous definition here LL | fn pin_drop(&pin mut self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definition | --> $SRC_DIR/core/src/ops/drop.rs:LL:COL + ::: $SRC_DIR/core/src/ops/drop.rs:LL:COL | = note: item in trait error[E0201]: duplicate definitions with name `pin_drop`: --> $DIR/pinned-drop-check.rs:201:9 | +LL | impl Drop for Bar { LL | fn drop(&pin mut self) {} | ------------------------- previous definition here LL | fn pin_drop(&pin mut self) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate definition | --> $SRC_DIR/core/src/ops/drop.rs:LL:COL + ::: $SRC_DIR/core/src/ops/drop.rs:LL:COL | = note: item in trait diff --git a/tests/ui/privacy/unresolved-trait-impl-item.stderr b/tests/ui/privacy/unresolved-trait-impl-item.stderr index 588e47c26bcd1..3307d9114122b 100644 --- a/tests/ui/privacy/unresolved-trait-impl-item.stderr +++ b/tests/ui/privacy/unresolved-trait-impl-item.stderr @@ -10,9 +10,13 @@ LL | async fn unresolved(&self) {} error[E0324]: item `RESOLVED_WRONG` is an associated method, which doesn't match its trait `MyTrait` --> $DIR/unresolved-trait-impl-item.rs:12:5 | +LL | trait MyTrait { +LL | async fn resolved(&self); LL | const RESOLVED_WRONG: u8 = 0; | ----------------------------- item in trait ... +LL | impl MyTrait for i32 { +... LL | async fn RESOLVED_WRONG() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait diff --git a/tests/ui/self/elision/no-shadow-pin-self.stderr b/tests/ui/self/elision/no-shadow-pin-self.stderr index 23485e9de79b7..2b2559d5a4274 100644 --- a/tests/ui/self/elision/no-shadow-pin-self.stderr +++ b/tests/ui/self/elision/no-shadow-pin-self.stderr @@ -1,9 +1,12 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/no-shadow-pin-self.rs:10:5 | +LL | trait Trait { LL | fn method<'a>(self: Pin<&Self>, f: &'a u32) -> &'a u32 { | ------------------------------------------------------ expected `fn(Pin<&'1 Pin

>, &'a u32) -> &'a u32` ... +LL | impl

Trait for Pin

{ +LL | // This should not hide `&Self`, which would cause this to compile. LL | fn method(self: Pin<&Self>, f: &u32) -> &u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(Pin<&'1 Pin

>, &'2 u32) -> &'1 u32` | diff --git a/tests/ui/span/impl-wrong-item-for-trait.stderr b/tests/ui/span/impl-wrong-item-for-trait.stderr index f919092f9ee92..e8066e224df0f 100644 --- a/tests/ui/span/impl-wrong-item-for-trait.stderr +++ b/tests/ui/span/impl-wrong-item-for-trait.stderr @@ -1,27 +1,37 @@ error[E0323]: item `bar` is an associated const, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:12:5 | +LL | trait Foo { LL | fn bar(&self); | -------------- item in trait ... +LL | impl Foo for FooConstForMethod { +LL | LL | const bar: u64 = 1; | ^^^^^^^^^^^^^^^^^^^ does not match trait error[E0324]: item `MY_CONST` is an associated method, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:22:5 | +LL | trait Foo { +LL | fn bar(&self); LL | const MY_CONST: u32; | -------------------- item in trait ... +LL | impl Foo for FooMethodForConst { +... LL | fn MY_CONST() {} | ^^^^^^^^^^^^^^^^ does not match trait error[E0325]: item `bar` is an associated type, which doesn't match its trait `Foo` --> $DIR/impl-wrong-item-for-trait.rs:30:5 | +LL | trait Foo { LL | fn bar(&self); | -------------- item in trait ... +LL | impl Foo for FooTypeForMethod { +LL | LL | type bar = u64; | ^^^^^^^^^^^^^^^ does not match trait diff --git a/tests/ui/traits/issue-8153.stderr b/tests/ui/traits/issue-8153.stderr index 8882097af3390..cc0bf646d147c 100644 --- a/tests/ui/traits/issue-8153.stderr +++ b/tests/ui/traits/issue-8153.stderr @@ -1,9 +1,11 @@ error[E0201]: duplicate definitions with name `bar`: --> $DIR/issue-8153.rs:11:5 | +LL | trait Bar { LL | fn bar(&self) -> isize; | ----------------------- item in trait ... +LL | impl Bar for Foo { LL | fn bar(&self) -> isize {1} | -------------------------- previous definition here LL | fn bar(&self) -> isize {2} diff --git a/tests/ui/traits/param-without-lifetime-constraint.stderr b/tests/ui/traits/param-without-lifetime-constraint.stderr index b8ec1fa5654a1..d5d83675a1e71 100644 --- a/tests/ui/traits/param-without-lifetime-constraint.stderr +++ b/tests/ui/traits/param-without-lifetime-constraint.stderr @@ -1,9 +1,11 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/param-without-lifetime-constraint.rs:14:5 | +LL | pub trait HaveRelationship { LL | fn get_relation(&self) -> To; | ----------------------------- expected `fn(&'1 Article) -> &'2 ProofReader` ... +LL | impl HaveRelationship<&ProofReader> for Article { LL | fn get_relation(&self) -> &ProofReader { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(&'1 Article) -> &'1 ProofReader` | diff --git a/tests/ui/traits/self-without-lifetime-constraint.stderr b/tests/ui/traits/self-without-lifetime-constraint.stderr index a82ff8eb0c269..34cf99b3f0512 100644 --- a/tests/ui/traits/self-without-lifetime-constraint.stderr +++ b/tests/ui/traits/self-without-lifetime-constraint.stderr @@ -1,9 +1,11 @@ error: `impl` item signature doesn't match `trait` item signature --> $DIR/self-without-lifetime-constraint.rs:46:5 | +LL | pub trait FromSql: Sized { LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult; | -------------------------------------------------------------------- expected `fn(ValueRef<'1>) -> Result<(&'2 str, &'1 &'2 str), _>` ... +LL | impl FromSql for &str { LL | fn column_result(value: ValueRef<'_>) -> FromSqlResult<&str, &&str> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ found `fn(ValueRef<'1>) -> Result<(&'1 str, &'1 &'1 str), _>` | diff --git a/triagebot.toml b/triagebot.toml index bad75dc51d740..611f7724f5cae 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -952,53 +952,53 @@ message_on_add = "PR #{number} has been **accepted** for **stable** backport." [notify-zulip."beta-nominated".rustfmt] required_labels = ["T-rustfmt"] zulip_stream = 621384 # #t-rustfmt/backports -topic = "#{number}: beta-nominated" +topic = "rust#{number}: beta-nominated" message_on_add = [ """\ -@*T-rustfmt* @*T-rustfmt-contributors* PR #{number} "{title}" has been nominated +@*T-rustfmt* @*T-rustfmt-contributors* PR rust#{number} "{title}" has been nominated for beta backport. """, """\ -/poll Approve beta backport of #{number}? +/poll Approve beta backport of rust#{number}? approve decline don't know """, ] -message_on_remove = "PR #{number}'s beta-nomination has been removed." +message_on_remove = "PR rust#{number}'s beta-nomination has been removed." [notify-zulip."beta-accepted".rustfmt] required_labels = ["T-rustfmt"] zulip_stream = 621384 # #t-rustfmt/backports # Put it in the same thread as beta-nominated. -topic = "#{number}: beta-nominated" -message_on_add = "PR #{number} has been **accepted** for **beta** backport." +topic = "rust#{number}: beta-nominated" +message_on_add = "PR rust#{number} has been **accepted** for **beta** backport." [notify-zulip."stable-nominated".rustfmt] required_labels = ["T-rustfmt"] zulip_stream = 621384 # #t-rustfmt/backports -topic = "#{number}: stable-nominated" +topic = "rust#{number}: stable-nominated" message_on_add = [ """\ -@*T-rustfmt* @*T-rustfmt-contributors* PR #{number} "{title}" has been nominated +@*T-rustfmt* @*T-rustfmt-contributors* PR rust#{number} "{title}" has been nominated for stable backport. """, """\ -/poll Approve stable backport of #{number}? +/poll Approve stable backport of rust#{number}? approve approve (but does not justify new dot release on its own) decline don't know """, ] -message_on_remove = "PR #{number}'s stable-nomination has been removed." +message_on_remove = "PR rust#{number}'s stable-nomination has been removed." [notify-zulip."stable-accepted".rustfmt] required_labels = ["T-rustfmt"] zulip_stream = 621384 # #t-rustfmt/backports # Put it in the same thread as stable-nominated. -topic = "#{number}: stable-nominated" -message_on_add = "PR #{number} has been **accepted** for **stable** backport." +topic = "rust#{number}: stable-nominated" +message_on_add = "PR rust#{number} has been **accepted** for **stable** backport." [notify-zulip."A-edition-2021"]