diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6a3ab0c920744..1d270e78949f8 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +github: rustfoundation custom: ["rust-lang.org/funding"] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 7c43693e15f84..6bb3fa884027e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -987,6 +987,7 @@ fn report_invalid_references( // Collect all the implicit positions: let mut spans = Vec::new(); let mut num_placeholders = 0; + let mut has_white_space_only_missing_arg = false; for piece in template { let mut placeholder = None; // `{arg:.*}` @@ -1009,13 +1010,21 @@ fn report_invalid_references( } // `{}` if let FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: FormatArgPosition { kind: FormatArgPositionKind::Implicit, .. }, + argument: FormatArgPosition { kind: FormatArgPositionKind::Implicit, index, .. }, span, .. }) = piece { placeholder = *span; num_placeholders += 1; + // Check whether there's any non-space whitespace in the placeholder. If so, we should emit a note suggesting an escaping `{`. + if index.is_err() + && let Some(span) = span + && let Ok(snippet) = ecx.source_map().span_to_snippet(*span) + && snippet.chars().any(|c| c.is_whitespace() && c != ' ') + { + has_white_space_only_missing_arg = true; + } } // For `{:.*}`, we only push one span. spans.extend(placeholder); @@ -1071,6 +1080,9 @@ fn report_invalid_references( if has_precision_star { e.note("positional arguments are zero-based"); } + if has_white_space_only_missing_arg { + e.note("if you intended to print `{`, you can escape it with `{{`"); + } } else { let mut indexes: Vec<_> = invalid_refs.iter().map(|&(index, _, _, _)| index).collect(); // Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)` diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs index 556158c286a84..f7fe0eb8cb3b8 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs @@ -21,8 +21,8 @@ use crate::debuginfo::metadata::{ file_metadata_from_def_id, type_di_node, unknown_file_metadata, }; use crate::debuginfo::utils::{DIB, create_DIArray, get_namespace_for_item}; -use crate::llvm; use crate::llvm::debuginfo::{DIFlags, DIType}; +use crate::llvm::{self, ToLlvmBool}; mod cpp_like; mod native; @@ -111,16 +111,23 @@ fn build_enumeration_type_di_node<'ll, 'tcx>( let (size, align) = cx.size_and_align_of(base_type); let enumerator_di_nodes: SmallVec> = enumerators - .map(|(name, value)| unsafe { - let value = [value as u64, (value >> 64) as u64]; - Some(llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_c_char_ptr(), - name.len(), - value.as_ptr(), - size.bits() as libc::c_uint, - is_unsigned, - )) + .map(|(name, value)| { + let value_words = [value as u64, (value >> 64) as u64]; + let size_in_bits = size.bits(); + // LLVM computes `NumWords = (SizeInBits + 63) / 64`. + assert!((size_in_bits + 63) / 64 <= value_words.len() as u64); + + let enumerator = unsafe { + llvm::LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision( + DIB(cx), + name.as_ptr(), + name.len(), + size_in_bits, + value_words.as_ptr(), + is_unsigned.to_llvm_bool(), + ) + }; + Some(enumerator) }) .collect(); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 940126e609a92..93ec92c25ac71 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -22,9 +22,8 @@ use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t}; use super::RustString; use super::debuginfo::{ - DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DILocation, - DISPFlags, DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, - DebugNameTableKind, + DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIFile, DIFlags, DILocation, DISPFlags, + DIScope, DISubprogram, DITemplateTypeParameter, DIType, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm::MetadataKindId; use crate::{TryFromU32, llvm}; @@ -752,7 +751,6 @@ pub(crate) mod debuginfo { pub(crate) type DICompositeType = DIDerivedType; pub(crate) type DIVariable = DIDescriptor; pub(crate) type DIArray = DIDescriptor; - pub(crate) type DIEnumerator = DIDescriptor; pub(crate) type DITemplateTypeParameter = DIDescriptor; bitflags! { @@ -1794,6 +1792,15 @@ unsafe extern "C" { Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) ) -> &'ll Metadata; + pub(crate) fn LLVMDIBuilderCreateEnumeratorOfArbitraryPrecision<'ll>( + Builder: &DIBuilder<'ll>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + SizeInBits: u64, + Words: *const u64, // LLVM computes `NumWords = (SizeInBits + 63) / 64`. + IsUnsigned: llvm::Bool, + ) -> &'ll Metadata; + pub(crate) fn LLVMDIBuilderCreateUnionType<'ll>( Builder: &DIBuilder<'ll>, Scope: Option<&'ll Metadata>, @@ -1989,6 +1996,7 @@ unsafe extern "C" { pub(crate) fn LLVMRustDisableSystemDialogsOnCrash(); // Operations on all values + /// FIXME: After dropping LLVM 21, migrate to LLVM-C's `LLVMGlobalAddMetadata`. pub(crate) fn LLVMRustGlobalAddMetadata<'a>( Val: &'a Value, KindID: MetadataKindId, @@ -2058,6 +2066,7 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions + /// FIXME: After dropping LLVM 21, migrate to LLVM-C's `LLVMGetOrInsertFunction`. pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, @@ -2208,6 +2217,9 @@ unsafe extern "C" { ValueLen: size_t, ); + /// We can't use LLVM-C's `LLVMDIBuilderCreateCompileUnit` because it hardcodes + /// `DICompileUnit::DebugNameTableKind::Default`, but we want to be able to + /// pass other values. pub(crate) fn LLVMRustDIBuilderCreateCompileUnit<'a>( Builder: &DIBuilder<'a>, Lang: c_uint, @@ -2225,6 +2237,8 @@ unsafe extern "C" { DebugNameTableKind: DebugNameTableKind, ) -> &'a DIDescriptor; + /// We can't use LLVM-C's `LLVMDIBuilderCreateFileWithChecksum` because it + /// _requires_ a checksum, but we sometimes don't provide one. pub(crate) fn LLVMRustDIBuilderCreateFile<'a>( Builder: &DIBuilder<'a>, Filename: *const c_char, @@ -2238,6 +2252,8 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; + /// We can't use LLVM-C's `LLVMDIBuilderCreateFunction` because it only + /// supports a subset of `DISubprogram::DISPFlags`. pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, @@ -2256,6 +2272,7 @@ unsafe extern "C" { Decl: Option<&'a DIDescriptor>, ) -> &'a DISubprogram; + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDIBuilderCreateMethod<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, @@ -2271,6 +2288,7 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, @@ -2286,15 +2304,7 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateEnumerator<'a>( - Builder: &DIBuilder<'a>, - Name: *const c_char, - NameLen: size_t, - Value: *const u64, - SizeInBits: c_uint, - IsUnsigned: bool, - ) -> &'a DIEnumerator; - + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDIBuilderCreateEnumerationType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, @@ -2309,6 +2319,7 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, @@ -2325,6 +2336,7 @@ unsafe extern "C" { UniqueIdLen: size_t, ) -> &'a DIDerivedType; + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDIBuilderCreateTemplateTypeParameter<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIScope>, @@ -2333,6 +2345,8 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DITemplateTypeParameter; + /// We can't use LLVM-C's `LLVMReplaceArrays` because it doesn't take a + /// `Params` argument. pub(crate) fn LLVMRustDICompositeTypeReplaceArrays<'a>( Builder: &DIBuilder<'a>, CompositeType: &'a DIType, @@ -2340,6 +2354,8 @@ unsafe extern "C" { Params: Option<&'a DIArray>, ); + /// We can't use LLVM-C's `LLVMDIBuilderGetOrCreateSubrange` because it doesn't + /// call the overload that takes a `Metadata` upper bound. pub(crate) fn LLVMRustDIGetOrCreateSubrange<'a>( Builder: &DIBuilder<'a>, CountNode: Option<&'a Metadata>, @@ -2348,6 +2364,8 @@ unsafe extern "C" { Stride: Option<&'a Metadata>, ) -> &'a Metadata; + /// We can't use LLVM-C's `LLVMDIBuilderCreateVectorType` because it doesn't + /// take a `BitStride` argument. pub(crate) fn LLVMRustDICreateVectorType<'a>( Builder: &DIBuilder<'a>, Size: u64, @@ -2357,6 +2375,7 @@ unsafe extern "C" { BitStride: Option<&'a Metadata>, ) -> &'a Metadata; + /// As of LLVM 22 there is no corresponding LLVM-C function. pub(crate) fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>( Location: &'a DILocation, BD: c_uint, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 6a9a2065f4d31..a2ad5f2c95b9b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -18,7 +18,7 @@ use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_hir_analysis::suggest_impl_trait; use rustc_middle::middle::stability::EvalResult; use rustc_middle::span_bug; -use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::print::{with_no_trimmed_paths, with_types_for_suggestion}; use rustc_middle::ty::{ self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast, suggest_constraining_type_params, @@ -2679,8 +2679,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sole_field_ty = sole_field.ty(self.tcx, args).skip_norm_wip(); if self.may_coerce(expr_ty, sole_field_ty) { - let variant_path = - with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); + let variant_path = with_types_for_suggestion!(with_no_trimmed_paths!( + self.tcx.def_path_str(variant.def_id) + )); // FIXME #56861: DRYer prelude filtering if let Some(path) = variant_path.strip_prefix("std::prelude::") && let Some((_, path)) = path.split_once("::") diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 7ba2ac59d1307..ce38ba8338338 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1198,15 +1198,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateEnumerator(LLVMDIBuilderRef Builder, const char *Name, - size_t NameLen, const uint64_t Value[2], - unsigned SizeInBits, bool IsUnsigned) { - return wrap(unwrap(Builder)->createEnumerator( - StringRef(Name, NameLen), - APSInt(APInt(SizeInBits, ArrayRef(Value, 2)), IsUnsigned))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs index 0fb4d186087bc..25b2cb22cb851 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_espidf.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs index 82c6e0cfa3f62..d9f8ccabaf516 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32_none_elf.rs @@ -5,7 +5,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: Some("Xtensa ESP32".into()), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs index 81eab657db712..5a93362eb4aaa 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_espidf.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs index 755f4db3b2ddf..688ae1b184135 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s2_none_elf.rs @@ -5,7 +5,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: Some("Xtensa ESP32-S2".into()), diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs index fb1f4f4719794..c03fabe33c209 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_espidf.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: None, tier: Some(3), host_tools: None, std: None }, diff --git a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs index 07530d874c09a..1f65af439481a 100644 --- a/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/xtensa_esp32s3_none_elf.rs @@ -5,7 +5,7 @@ pub(crate) fn target() -> Target { Target { llvm_target: "xtensa-none-elf".into(), pointer_width: 32, - data_layout: "e-m:e-p:32:32-v1:8:8-i64:64-i128:128-n32".into(), + data_layout: "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32".into(), arch: Arch::Xtensa, metadata: TargetMetadata { description: Some("Xtensa ESP32-S3".into()), diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0dd250acff837..20e28410a6294 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -815,18 +815,17 @@ impl File { /// Acquire an exclusive lock on the file. Blocks until the lock can be acquired. /// - /// This acquires an exclusive lock; no other file handle to this file, in this or any other + /// This acquires an exclusive lock. No *other* file handle to this file, in this or any other /// process, may acquire another lock. + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior + /// is unspecified and platform dependent, including the possibility that it will deadlock. + /// However, if this method returns, then an exclusive lock is held. /// /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle/descriptor, or a clone of it, already holds a lock the exact behavior - /// is unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then an exclusive lock is held. - /// /// If the file is not open for writing, it is unspecified whether this function returns an error. /// /// The lock will be released when this file (along with any other file descriptors/handles @@ -869,18 +868,18 @@ impl File { /// Acquire a shared (non-exclusive) lock on the file. Blocks until the lock can be acquired. /// - /// This acquires a shared lock; more than one file handle, in this or any other process, may - /// hold a shared lock, but none may hold an exclusive lock at the same time. + /// This acquires a shared lock. More than one file handle to this file, in this or any other + /// process, may hold a shared lock, but no *other* file handle may hold an exclusive lock at + /// the same time. + /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact + /// behavior is unspecified and platform dependent, including the possibility that it will + /// deadlock. However, if this method returns, then a shared lock is held. /// /// This lock may be advisory or mandatory. This lock is meant to interact with [`lock`], /// [`try_lock`], [`lock_shared`], [`try_lock_shared`], and [`unlock`]. Its interactions with /// other methods, such as [`read`] and [`write`] are platform specific, and it may or may not /// cause non-lockholders to block. /// - /// If this file handle/descriptor, or a clone of it, already holds a lock, the exact behavior - /// is unspecified and platform dependent, including the possibility that it will deadlock. - /// However, if this method returns, then a shared lock is held. - /// /// The lock will be released when this file (along with any other file descriptors/handles /// duplicated or inherited from it) is closed, or if the [`unlock`] method is called. /// diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 25a5b767699fe..d1d1cd09f37f1 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -305,18 +305,42 @@ fn file_lock_dup() { } #[test] -#[cfg(windows)] -fn file_lock_double_unlock() { +#[cfg_attr( + not(any( + windows, + target_os = "aix", + target_os = "cygwin", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_vendor = "apple", + )), + should_panic +)] +fn file_lock_double() { let tmpdir = tmpdir(); - let filename = &tmpdir.join("file_lock_double_unlock_test.txt"); + let filename = &tmpdir.join("file_lock_double_test.txt"); let f1 = check!(File::create(filename)); let f2 = check!(OpenOptions::new().write(true).open(filename)); - // On Windows a file handle may acquire both a shared and exclusive lock. - // Check that both are released by unlock() + // A file handle may acquire both a shared and exclusive lock. check!(f1.lock()); check!(f1.lock_shared()); + // The behavior here differs between Windows and Unix: on Windows, f1 holds both locks; + // on Unix, the lock got downgraded so f1 only holds the shared lock. assert_matches!(f2.try_lock(), Err(TryLockError::WouldBlock)); + if cfg!(windows) { + assert_matches!(f2.try_lock_shared(), Err(TryLockError::WouldBlock)); + } else { + check!(f2.try_lock_shared()); + check!(f2.unlock()); + } + // Check that both are released by unlock(). check!(f1.unlock()); check!(f2.try_lock()); } diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index 5dc9f17c15fdb..170f1439ecc9c 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -187,6 +187,44 @@ impl Cfg { } } + /// Recursively sorts the configuration tree to ensure deterministic rendering. + /// + /// Sorting groups predicates logically: Targets first, then Target Features, + /// then Crate Features, and finally nested Any/All/Not groupings. + /// Within each group, a fallback alphabetical sort is applied. + pub(crate) fn sort_for_rendering(&mut self) { + fn sort_cfg_entry(cfg: &mut CfgEntry) { + match cfg { + CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => { + for sub_cfg in sub_cfgs.iter_mut() { + sort_cfg_entry(sub_cfg); + } + + sub_cfgs.sort_by_cached_key(|a| { + ( + cfg_category(a), + Display(a, Format::LongPlain).to_string().to_ascii_lowercase(), + ) + }); + } + CfgEntry::Not(box_cfg, _) => sort_cfg_entry(box_cfg), + _ => {} + } + } + + fn cfg_category(cfg: &CfgEntry) -> u8 { + match cfg { + CfgEntry::NameValue { name, .. } if *name == sym::feature => 2, + CfgEntry::NameValue { name, .. } if *name == sym::target_feature => 1, + CfgEntry::NameValue { .. } | CfgEntry::Bool(..) => 0, + CfgEntry::Any(..) | CfgEntry::All(..) | CfgEntry::Not(..) => 3, + _ => 4, + } + } + + sort_cfg_entry(&mut self.0); + } + fn omit_preposition(&self) -> bool { matches!(self.0, CfgEntry::Bool(..)) } @@ -843,14 +881,20 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) { None } else { - Some(Arc::new(cfg_info.current_cfg.clone())) + let mut cfg = cfg_info.current_cfg.clone(); + cfg.sort_for_rendering(); + Some(Arc::new(cfg)) } } else { // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the // hidden ones afterward. match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) { None | Some(CfgEntry::Bool(true, _)) => None, - Some(cfg) => Some(Arc::new(Cfg(cfg))), + Some(cfg_entry) => { + let mut cfg = Cfg(cfg_entry); + cfg.sort_for_rendering(); + Some(Arc::new(cfg)) + } } } } diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index e0c21865d8dff..97f9d1fe71673 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -418,3 +418,34 @@ fn test_simplify_with() { assert_eq!(foobar.simplify_with(&foobarbaz), None); }); } + +#[test] +fn test_sort_for_rendering() { + create_default_session_globals_then(|| { + let mut cfg = cfg_any(thin_vec![ + name_value_cfg_e("feature", "sync"), + name_value_cfg_e("target_os", "linux"), + cfg_all_e(thin_vec![word_cfg_e("unix")]), + name_value_cfg_e("target_feature", "sse2"), + name_value_cfg_e("target_os", "android"), + name_value_cfg_e("feature", "alloc"), + ]); + + cfg.sort_for_rendering(); + + let expected = cfg_any(thin_vec![ + // Category 0: Targets (Sorted Alphabetically: Android -> Linux) + name_value_cfg_e("target_os", "android"), + name_value_cfg_e("target_os", "linux"), + // Category 1: Target Features + name_value_cfg_e("target_feature", "sse2"), + // Category 2: Crate Features (Sorted Alphabetically: alloc -> sync) + name_value_cfg_e("feature", "alloc"), + name_value_cfg_e("feature", "sync"), + // Category 3: Nested logic pushed to the end + cfg_all_e(thin_vec![word_cfg_e("unix")]), + ]); + + assert_eq!(cfg, expected); + }); +} diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml index b53ffb00f1c36..08ace7686e74f 100644 --- a/tests/rustdoc-gui/item-info-overflow.goml +++ b/tests/rustdoc-gui/item-info-overflow.goml @@ -8,7 +8,7 @@ assert-property: (".item-info", {"scrollWidth": "940"}) // Just to be sure we're comparing the correct "item-info": assert-text: ( ".item-info", - "Available on Android or Linux or Emscripten or DragonFly BSD or FreeBSD or NetBSD or OpenBSD", + "Available on Android or DragonFly BSD or Emscripten or FreeBSD or Linux or NetBSD or OpenBSD", STARTS_WITH, ) @@ -26,6 +26,6 @@ assert-property: ( // Just to be sure we're comparing the correct "item-info": assert-text: ( "#impl-SimpleTrait-for-LongItemInfo2 .item-info", - "Available on Android or Linux or Emscripten or DragonFly BSD or FreeBSD or NetBSD or OpenBSD", + "Available on Android or DragonFly BSD or Emscripten or FreeBSD or Linux or NetBSD or OpenBSD", STARTS_WITH, ) diff --git a/tests/rustdoc-gui/item-info.goml b/tests/rustdoc-gui/item-info.goml index 11388c79e0b80..b0cb6b5b9251b 100644 --- a/tests/rustdoc-gui/item-info.goml +++ b/tests/rustdoc-gui/item-info.goml @@ -19,8 +19,8 @@ store-position: ( "//*[@class='stab portability']//code[normalize-space()='Win32_System_Diagnostics']", {"x": second_line_x, "y": second_line_y}, ) -assert: |first_line_x| != |second_line_x| && |first_line_x| == 521 && |second_line_x| == 277 -assert: |first_line_y| != |second_line_y| && |first_line_y| == 676 && |second_line_y| == 699 +assert: |first_line_x| != |second_line_x| && |first_line_x| == 509 && |second_line_x| == 277 +assert: |first_line_y| == |second_line_y| && |first_line_y| == 699 // Now we ensure that they're not rendered on the same line. set-window-size: (1100, 800) diff --git a/tests/rustdoc-html/doc-cfg/all-targets.rs b/tests/rustdoc-html/doc-cfg/all-targets.rs index 5b61d6164ee56..605a27a7d8927 100644 --- a/tests/rustdoc-html/doc-cfg/all-targets.rs +++ b/tests/rustdoc-html/doc-cfg/all-targets.rs @@ -2,10 +2,10 @@ //@ has all_targets/fn.foo.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ -// 'Available on GNU or Catalyst or Managarm C Library or MSVC or musl or Newlib or \ -// Neutrino 7.0 or Neutrino 7.1 or Neutrino 7.1 with io-sock or Neutrino 8.0 or \ -// OpenHarmony or relibc or SGX or Simulator or WASIp1 or WASIp2 or WASIp3 or \ -// uClibc or V5 or target_env=fake_env only.' +// 'Available on target_env=fake_env or Catalyst or GNU or Managarm C Library \ +// or MSVC or musl or Neutrino 7.0 or Neutrino 7.1 or Neutrino 7.1 with io-sock \ +// or Neutrino 8.0 or Newlib or OpenHarmony or relibc or SGX or Simulator or \ +// uClibc or V5 or WASIp1 or WASIp2 or WASIp3 only.' #[doc(cfg(any( target_env = "gnu", target_env = "macabi", @@ -32,12 +32,12 @@ pub fn foo() {} //@ has all_targets/fn.bar.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ -// 'Available on AArch64 or AMD GPU or ARM or ARM64EC or AVR or BPF or C-SKY or \ -// Hexagon or LoongArch32 or LoongArch64 or Motorola 680x0 or MIPS or MIPS release \ -// 6 or MIPS-64 or MIPS-64 release 6 or MSP430 or NVidia GPU or PowerPC or \ -// PowerPC64 or RISC-V RV32 or RISC-V RV64 or s390x or SPARC or SPARC-64 or SPIR-V \ -// or WebAssembly or WebAssembly or x86 or x86-64 or Xtensa or \ -// target_arch=fake_arch only.' +// 'Available on target_arch=fake_arch or AArch64 or AMD GPU or ARM or \ +// ARM64EC or AVR or BPF or C-SKY or Hexagon or LoongArch32 or LoongArch64 \ +// or MIPS or MIPS release 6 or MIPS-64 or MIPS-64 release 6 or Motorola 680x0 \ +// or MSP430 or NVidia GPU or PowerPC or PowerPC64 or RISC-V RV32 or RISC-V RV64 \ +// or s390x or SPARC or SPARC-64 or SPIR-V or WebAssembly or WebAssembly or x86 \ +// or x86-64 or Xtensa only.' #[doc(cfg(any( target_arch = "aarch64", target_arch = "amdgpu", @@ -75,15 +75,16 @@ pub fn bar() {} //@ has all_targets/fn.baz.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ -// 'Available on AIX and AMD HSA and Android and CUDA and Cygwin and DragonFly \ -// BSD and Emscripten and ESP-IDF and FreeBSD and Fuchsia and Haiku and HelenOS \ -// and Hermit and Horizon and GNU/Hurd and illumos and iOS and L4Re and Linux \ -// and LynxOS-178 and macOS and Managarm and Motor OS and NetBSD and bare-metal \ -// and QNX Neutrino and NuttX and OpenBSD and Play Station Portable and Play \ -// Station 1 and QuRT and Redox OS and RTEMS OS and Solaris and SOLID ASP3 and \ -// TEEOS and Trusty and tvOS and UEFI and VEXos and visionOS and Play Station \ -// Vita and VxWorks and WASI and watchOS and Windows and Xous and zero knowledge \ -// Virtual Machine and target_os=unknown and target_os=fake_os only.' +// 'Available on target_os=fake_os and target_os=unknown and AIX and AMD HSA \ +// and Android and bare-metal and CUDA and Cygwin and DragonFly BSD and \ +// Emscripten and ESP-IDF and FreeBSD and Fuchsia and GNU/Hurd and Haiku \ +// and HelenOS and Hermit and Horizon and illumos and iOS and L4Re and Linux \ +// and LynxOS-178 and macOS and Managarm and Motor OS and NetBSD and NuttX \ +// and OpenBSD and Play Station 1 and Play Station Portable and Play Station Vita \ +// and QNX Neutrino and QuRT and Redox OS and RTEMS OS and Solaris and \ +// SOLID ASP3 and TEEOS and Trusty and tvOS and UEFI and VEXos and visionOS \ +// and VxWorks and WASI and watchOS and Windows and Xous and zero knowledge \ +// Virtual Machine only.' #[doc(cfg(all( target_os = "aix", target_os = "amdhsa", diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg-simplification.rs b/tests/rustdoc-html/doc-cfg/doc-cfg-simplification.rs index ce70de289c623..d984b48ff9957 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg-simplification.rs +++ b/tests/rustdoc-html/doc-cfg/doc-cfg-simplification.rs @@ -46,7 +46,7 @@ pub mod ratel { //@ has 'globuliferous/ratel/static.NUNCIATIVE.html' //@ count - '//*[@class="stab portability"]' 1 - //@ matches - '//*[@class="stab portability"]' 'crate features ratel and nunciative' + //@ matches - '//*[@class="stab portability"]' 'crate features nunciative and ratel' #[doc(cfg(feature = "nunciative"))] pub static NUNCIATIVE: () = (); @@ -80,7 +80,7 @@ pub mod ratel { //@ has 'globuliferous/ratel/enum.Cosmotellurian.html' //@ count - '//*[@class="stab portability"]' 10 - //@ matches - '//*[@class="stab portability"]' 'crate features ratel and cosmotellurian' + //@ matches - '//*[@class="stab portability"]' 'crate features cosmotellurian and ratel' //@ matches - '//*[@class="stab portability"]' 'crate feature biotaxy' //@ matches - '//*[@class="stab portability"]' 'crate feature xiphopagus' //@ matches - '//*[@class="stab portability"]' 'crate feature juxtapositive' @@ -158,7 +158,7 @@ pub mod ratel { //@ has 'globuliferous/ratel/trait.Aposiopesis.html' //@ count - '//*[@class="stab portability"]' 4 - //@ matches - '//*[@class="stab portability"]' 'crate features ratel and aposiopesis' + //@ matches - '//*[@class="stab portability"]' 'crate features aposiopesis and ratel' //@ matches - '//*[@class="stab portability"]' 'crate feature umbracious' //@ matches - '//*[@class="stab portability"]' 'crate feature uakari' //@ matches - '//*[@class="stab portability"]' 'crate feature rotograph' diff --git a/tests/rustdoc-html/doc-cfg/doc-cfg.rs b/tests/rustdoc-html/doc-cfg/doc-cfg.rs index ba2a8de5b29e5..730c7e41decb7 100644 --- a/tests/rustdoc-html/doc-cfg/doc-cfg.rs +++ b/tests/rustdoc-html/doc-cfg/doc-cfg.rs @@ -3,7 +3,7 @@ //@ has doc_cfg/struct.Portable.html //@ !has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' '' //@ has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()' -//@ has - '//*[@class="stab portability"]' 'Available on Unix and ARM only.' +//@ has - '//*[@class="stab portability"]' 'Available on ARM and Unix only.' //@ has - '//*[@id="method.wasi_and_wasm32_only_function"]' 'fn wasi_and_wasm32_only_function()' //@ has - '//*[@class="stab portability"]' 'Available on WASI and WebAssembly only.' pub struct Portable; @@ -25,7 +25,7 @@ pub mod unix_only { //@ has doc_cfg/unix_only/trait.ArmOnly.html \ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ - // 'Available on Unix and ARM only.' + // 'Available on ARM and Unix only.' //@ count - '//*[@class="stab portability"]' 1 #[doc(cfg(target_arch = "arm"))] pub trait ArmOnly { diff --git a/tests/rustdoc-html/doc-cfg/duplicate-cfg.rs b/tests/rustdoc-html/doc-cfg/duplicate-cfg.rs index 93f26ab944d34..3cd8b626558b7 100644 --- a/tests/rustdoc-html/doc-cfg/duplicate-cfg.rs +++ b/tests/rustdoc-html/doc-cfg/duplicate-cfg.rs @@ -23,11 +23,11 @@ pub mod bar { } //@ has 'foo/baz/index.html' -//@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.' +//@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.' #[doc(cfg(all(feature = "sync", feature = "send")))] pub mod baz { //@ has 'foo/baz/struct.Baz.html' - //@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.' + //@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.' #[doc(cfg(feature = "sync"))] pub struct Baz; } @@ -37,17 +37,17 @@ pub mod baz { #[doc(cfg(feature = "sync"))] pub mod qux { //@ has 'foo/qux/struct.Qux.html' - //@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.' + //@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.' #[doc(cfg(all(feature = "sync", feature = "send")))] pub struct Qux; } //@ has 'foo/quux/index.html' -//@ has '-' '//*[@class="stab portability"]' 'Available on crate feature sync and crate feature send and foo only.' +//@ has '-' '//*[@class="stab portability"]' 'Available on foo and crate feature send and crate feature sync only.' #[doc(cfg(all(feature = "sync", feature = "send", foo)))] pub mod quux { //@ has 'foo/quux/struct.Quux.html' - //@ has '-' '//*[@class="stab portability"]' 'Available on crate feature sync and crate feature send and foo and bar only.' + //@ has '-' '//*[@class="stab portability"]' 'Available on bar and foo and crate feature send and crate feature sync only.' #[doc(cfg(all(feature = "send", feature = "sync", bar)))] pub struct Quux; } diff --git a/tests/rustdoc-html/doc-cfg/sort.rs b/tests/rustdoc-html/doc-cfg/sort.rs new file mode 100644 index 0000000000000..3aa79b7836271 --- /dev/null +++ b/tests/rustdoc-html/doc-cfg/sort.rs @@ -0,0 +1,42 @@ +// Tests that `doc(cfg)` badges and auto-detected `cfg` badges are sorted deterministically. +//@ edition:2021 +//@ compile-flags: --cfg target_os="linux" + +#![crate_name = "foo"] +#![feature(doc_cfg)] + +// TEST 1: Explicit `#[doc(cfg(...))]` +// Tests that OS targets are sorted alphabetically. +//@ has 'foo/fn.foo.html' +//@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \ +// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.' +#[doc(cfg(any( + target_os = "android", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_vendor = "apple", + target_os = "cygwin" +)))] +pub fn foo() {} + +// TEST 2: Implicit `#[cfg(...)]` via auto-detection +// Tests that targets are sorted alphabetically just like explicit `doc(cfg)`. +//@ has 'foo/fn.bar.html' +//@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \ +// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.' +#[cfg(any( + target_os = "android", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd", + target_os = "nto", + target_vendor = "apple", + target_os = "cygwin" +))] +pub fn bar() {} diff --git a/tests/rustdoc-html/inline_cross/doc-auto-cfg.rs b/tests/rustdoc-html/inline_cross/doc-auto-cfg.rs index 6e0f6e22f8c78..6c69dfe5ac8a9 100644 --- a/tests/rustdoc-html/inline_cross/doc-auto-cfg.rs +++ b/tests/rustdoc-html/inline_cross/doc-auto-cfg.rs @@ -37,11 +37,11 @@ pub mod pre { pub mod post { // issue: //@ has 'it/post/index.html' '//*[@class="stab portability"]' 'extra' - //@ has - '//*[@class="stab portability"]' 'extra and extension' + //@ has - '//*[@class="stab portability"]' 'extension and extra' //@ has 'it/post/struct.Type.html' '//*[@class="stab portability"]' \ // 'Available on crate feature extra only.' //@ has 'it/post/fn.compute.html' '//*[@class="stab portability"]' \ - // 'Available on crate feature extra and extension only.' + // 'Available on extension and crate feature extra only.' #[cfg(feature = "extra")] pub use doc_auto_cfg::*; diff --git a/tests/rustdoc-html/target-feature.rs b/tests/rustdoc-html/target-feature.rs index f2686f81fbff0..ce51735c77d5c 100644 --- a/tests/rustdoc-html/target-feature.rs +++ b/tests/rustdoc-html/target-feature.rs @@ -28,13 +28,13 @@ pub unsafe fn f2_not_safe() {} //@ has 'foo/fn.f3_multifeatures_in_attr.html' //@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ -// 'Available on target features popcnt and avx2 only.' +// 'Available on target features avx2 and popcnt only.' #[target_feature(enable = "popcnt", enable = "avx2")] pub fn f3_multifeatures_in_attr() {} //@ has 'foo/fn.f4_multi_attrs.html' //@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \ -// 'Available on target features popcnt and avx2 only.' +// 'Available on target features avx2 and popcnt only.' #[target_feature(enable = "popcnt")] #[target_feature(enable = "avx2")] pub fn f4_multi_attrs() {} diff --git a/tests/ui/fmt/format-string-error-2.rs b/tests/ui/fmt/format-string-error-2.rs index c1d228bfbc9c4..d28885b58809b 100644 --- a/tests/ui/fmt/format-string-error-2.rs +++ b/tests/ui/fmt/format-string-error-2.rs @@ -91,4 +91,15 @@ raw { \n println!("{x=}"); //~^ ERROR invalid format string: python's f-string debug `=` is not supported in rust, use `dbg(x)` instead + + println!( + "fn main() {\n\ + \n\ + }" + //~^^^ ERROR 1 positional argument in format string + ); + + // Don't emit note suggesting an escaping `{` for `{ }`. + println!("{ }"); + //~^ ERROR 1 positional argument in format string } diff --git a/tests/ui/fmt/format-string-error-2.stderr b/tests/ui/fmt/format-string-error-2.stderr index b12e827853f80..7c440a25220a8 100644 --- a/tests/ui/fmt/format-string-error-2.stderr +++ b/tests/ui/fmt/format-string-error-2.stderr @@ -208,5 +208,22 @@ LL | println!("{x=}"); | = note: to print `{`, you can escape it using `{{` -error: aborting due to 21 previous errors +error: 1 positional argument in format string, but no arguments were given + --> $DIR/format-string-error-2.rs:96:20 + | +LL | "fn main() {\n\ + | ____________________^ +LL | | \n\ +LL | | }" + | |_________^ + | + = note: if you intended to print `{`, you can escape it with `{{` + +error: 1 positional argument in format string, but no arguments were given + --> $DIR/format-string-error-2.rs:103:15 + | +LL | println!("{ }"); + | ^^^ + +error: aborting due to 23 previous errors diff --git a/tests/ui/suggestions/wrap-function-local-constructors.fixed b/tests/ui/suggestions/wrap-function-local-constructors.fixed new file mode 100644 index 0000000000000..bc71d874e3dc2 --- /dev/null +++ b/tests/ui/suggestions/wrap-function-local-constructors.fixed @@ -0,0 +1,63 @@ +//@ run-rustfix + +// Regression test for https://github.com/rust-lang/rust/issues/144319. +// Function-local constructors cannot be named through the enclosing function +// path. The suggestion must omit path segments that cannot be written in source, +// while preserving real path segments like local modules and enums. + +#![allow(dead_code)] + +fn direct_tuple_struct() { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(Foo(false)); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` +} + +fn enum_variant() { + enum LocalResult { + Ok(T), + } + struct Bar(LocalResult); + + _ = Bar(LocalResult::Ok(false)); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `LocalResult::Ok` +} + +fn local_module() { + mod inner { + pub struct Foo(pub bool); + } + struct Bar(inner::Foo); + + _ = Bar(inner::Foo(false)); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `inner::Foo` +} + +fn closure_body() { + let _ = || { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(Foo(false)); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` + }; +} + +fn inline_const_block() { + const { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(Foo(false)); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` + }; +} + +pub fn main() {} diff --git a/tests/ui/suggestions/wrap-function-local-constructors.rs b/tests/ui/suggestions/wrap-function-local-constructors.rs new file mode 100644 index 0000000000000..88ba87edadcbf --- /dev/null +++ b/tests/ui/suggestions/wrap-function-local-constructors.rs @@ -0,0 +1,63 @@ +//@ run-rustfix + +// Regression test for https://github.com/rust-lang/rust/issues/144319. +// Function-local constructors cannot be named through the enclosing function +// path. The suggestion must omit path segments that cannot be written in source, +// while preserving real path segments like local modules and enums. + +#![allow(dead_code)] + +fn direct_tuple_struct() { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(false); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` +} + +fn enum_variant() { + enum LocalResult { + Ok(T), + } + struct Bar(LocalResult); + + _ = Bar(false); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `LocalResult::Ok` +} + +fn local_module() { + mod inner { + pub struct Foo(pub bool); + } + struct Bar(inner::Foo); + + _ = Bar(false); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `inner::Foo` +} + +fn closure_body() { + let _ = || { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(false); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` + }; +} + +fn inline_const_block() { + const { + struct Foo(bool); + struct Bar(Foo); + + _ = Bar(false); + //~^ ERROR mismatched types + //~| HELP try wrapping the expression in `Foo` + }; +} + +pub fn main() {} diff --git a/tests/ui/suggestions/wrap-function-local-constructors.stderr b/tests/ui/suggestions/wrap-function-local-constructors.stderr new file mode 100644 index 0000000000000..e883697721ca1 --- /dev/null +++ b/tests/ui/suggestions/wrap-function-local-constructors.stderr @@ -0,0 +1,95 @@ +error[E0308]: mismatched types + --> $DIR/wrap-function-local-constructors.rs:14:13 + | +LL | _ = Bar(false); + | --- ^^^^^ expected `Foo`, found `bool` + | | + | arguments to this struct are incorrect + | +note: tuple struct defined here + --> $DIR/wrap-function-local-constructors.rs:12:12 + | +LL | struct Bar(Foo); + | ^^^ +help: try wrapping the expression in `Foo` + | +LL | _ = Bar(Foo(false)); + | ++++ + + +error[E0308]: mismatched types + --> $DIR/wrap-function-local-constructors.rs:25:13 + | +LL | _ = Bar(false); + | --- ^^^^^ expected `LocalResult`, found `bool` + | | + | arguments to this struct are incorrect + | + = note: expected enum `LocalResult` + found type `bool` +note: tuple struct defined here + --> $DIR/wrap-function-local-constructors.rs:23:12 + | +LL | struct Bar(LocalResult); + | ^^^ +help: try wrapping the expression in `LocalResult::Ok` + | +LL | _ = Bar(LocalResult::Ok(false)); + | ++++++++++++++++ + + +error[E0308]: mismatched types + --> $DIR/wrap-function-local-constructors.rs:36:13 + | +LL | _ = Bar(false); + | --- ^^^^^ expected `Foo`, found `bool` + | | + | arguments to this struct are incorrect + | +note: tuple struct defined here + --> $DIR/wrap-function-local-constructors.rs:34:12 + | +LL | struct Bar(inner::Foo); + | ^^^ +help: try wrapping the expression in `inner::Foo` + | +LL | _ = Bar(inner::Foo(false)); + | +++++++++++ + + +error[E0308]: mismatched types + --> $DIR/wrap-function-local-constructors.rs:46:17 + | +LL | _ = Bar(false); + | --- ^^^^^ expected `Foo`, found `bool` + | | + | arguments to this struct are incorrect + | +note: tuple struct defined here + --> $DIR/wrap-function-local-constructors.rs:44:16 + | +LL | struct Bar(Foo); + | ^^^ +help: try wrapping the expression in `Foo` + | +LL | _ = Bar(Foo(false)); + | ++++ + + +error[E0308]: mismatched types + --> $DIR/wrap-function-local-constructors.rs:57:17 + | +LL | _ = Bar(false); + | --- ^^^^^ expected `Foo`, found `bool` + | | + | arguments to this struct are incorrect + | +note: tuple struct defined here + --> $DIR/wrap-function-local-constructors.rs:55:16 + | +LL | struct Bar(Foo); + | ^^^ +help: try wrapping the expression in `Foo` + | +LL | _ = Bar(Foo(false)); + | ++++ + + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`.