diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index e29b29fc6ccb0..9049639925ddb 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -60,12 +60,18 @@ fn eii_( ) -> Vec { let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span); - let (item, stmt) = if let Annotatable::Item(item) = item { - (item, false) + let (item, wrap_item): (_, &dyn Fn(_) -> _) = if let Annotatable::Item(item) = item { + (item, &Annotatable::Item) } else if let Annotatable::Stmt(ref stmt) = item && let StmtKind::Item(ref item) = stmt.kind { - (item.clone(), true) + (item.clone(), &|item| { + Annotatable::Stmt(Box::new(Stmt { + id: DUMMY_NODE_ID, + kind: StmtKind::Item(item), + span: eii_attr_span, + })) + }) } else { ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { span: eii_attr_span, @@ -74,23 +80,25 @@ fn eii_( return vec![item]; }; - let orig_item = item.clone(); - - let item = *item; - - let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = item else { + let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } = + item.as_ref() + else { ecx.dcx().emit_err(EiiSharedMacroExpectedFunction { span: eii_attr_span, name: path_to_string(&meta_item.path), }); - return vec![Annotatable::Item(Box::new(item))]; + return vec![wrap_item(item)]; }; + // only clone what we need + let attrs = attrs.clone(); + let func = (**func).clone(); + let vis = vis.clone(); let attrs_from_decl = filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path); let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else { - return vec![Annotatable::Item(orig_item)]; + return vec![wrap_item(item)]; }; // span of the declaring item without attributes @@ -115,7 +123,7 @@ fn eii_( ecx, eii_attr_span, item_span, - *func, + func, vis, &attrs_from_decl, ))); @@ -128,20 +136,7 @@ fn eii_( decl_span, ))); - if stmt { - return_items - .into_iter() - .map(|i| { - Annotatable::Stmt(Box::new(Stmt { - id: DUMMY_NODE_ID, - kind: StmtKind::Item(i), - span: eii_attr_span, - })) - }) - .collect() - } else { - return_items.into_iter().map(|i| Annotatable::Item(i)).collect() - } + return_items.into_iter().map(wrap_item).collect() } /// Decide on the name of the macro that can be used to implement the EII. diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 9f75578aa636c..cd1e573ea28df 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -416,7 +416,7 @@ impl<'a> Id<'a> { /// it in the generated .dot file. They can also provide more /// elaborate (and non-unique) label text that is used in the graphviz /// rendered output. - +/// /// The graph instance is responsible for providing the DOT compatible /// identifiers for the nodes and (optionally) rendered labels for the nodes and /// edges, as well as an identifier for the graph itself. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 471bd1d937e94..9e4692b96418e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2654,7 +2654,7 @@ struct InternedInSet<'tcx, T: ?Sized + PointeeSized>(&'tcx T); impl<'tcx, T: 'tcx + ?Sized + PointeeSized> Clone for InternedInSet<'tcx, T> { fn clone(&self) -> Self { - InternedInSet(self.0) + *self } } diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index d9bb93a829b59..f3308086ddc64 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -141,7 +141,7 @@ pub struct MatchArm<'p, Cx: PatCx> { impl<'p, Cx: PatCx> Clone for MatchArm<'p, Cx> { fn clone(&self) -> Self { - Self { pat: self.pat, has_guard: self.has_guard, arm_data: self.arm_data } + *self } } diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 901b72e53995e..51d0703f9c5ce 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -174,10 +174,7 @@ pub(crate) enum PatOrWild<'p, Cx: PatCx> { impl<'p, Cx: PatCx> Clone for PatOrWild<'p, Cx> { fn clone(&self) -> Self { - match self { - PatOrWild::Wild => PatOrWild::Wild, - PatOrWild::Pat(pat) => PatOrWild::Pat(pat), - } + *self } } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 19446a1efe9cb..971616b189148 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -824,7 +824,7 @@ struct PlaceCtxt<'a, Cx: PatCx> { impl<'a, Cx: PatCx> Copy for PlaceCtxt<'a, Cx> {} impl<'a, Cx: PatCx> Clone for PlaceCtxt<'a, Cx> { fn clone(&self) -> Self { - Self { cx: self.cx, ty: self.ty } + *self } } diff --git a/compiler/rustc_thread_pool/src/registry.rs b/compiler/rustc_thread_pool/src/registry.rs index 4a018ea743aea..92bb8961e7dfc 100644 --- a/compiler/rustc_thread_pool/src/registry.rs +++ b/compiler/rustc_thread_pool/src/registry.rs @@ -153,8 +153,8 @@ pub struct Registry { terminate_count: AtomicUsize, } -/// //////////////////////////////////////////////////////////////////////// -/// Initialization +/////////////////////////////////////////////////////////////////////////// +// Initialization static mut THE_REGISTRY: Option> = None; static THE_REGISTRY_SET: Once = Once::new(); @@ -407,12 +407,12 @@ impl Registry { } } - /// //////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// /// MAIN LOOP /// /// So long as all of the worker threads are hanging out in their /// top-level loop, there is no work to be done. - + /// /// Push a job into the given `registry`. If we are running on a /// worker thread for the registry, this will push onto the /// deque. Else, it will inject from the outside (which is slower). @@ -668,8 +668,8 @@ impl ThreadInfo { } } -/// //////////////////////////////////////////////////////////////////////// -/// WorkerThread identifiers +/////////////////////////////////////////////////////////////////////////// +// WorkerThread identifiers pub(super) struct WorkerThread { /// the "worker" half of our local deque @@ -1019,8 +1019,6 @@ impl WorkerThread { } } -/// //////////////////////////////////////////////////////////////////////// - unsafe fn main_loop(thread: ThreadBuilder) { let worker_thread = &WorkerThread::from(thread); unsafe { WorkerThread::set_current(worker_thread) }; diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs index 6c71e2216a71b..af4469fad2cc6 100644 --- a/library/core/src/cell/once.rs +++ b/library/core/src/cell/once.rs @@ -353,7 +353,8 @@ impl OnceCell { } #[stable(feature = "once_cell", since = "1.70.0")] -impl Default for OnceCell { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for OnceCell { #[inline] fn default() -> Self { Self::new() diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 977fd1926fd0c..a5d9a5352dfcf 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -118,13 +118,6 @@ pub use crate::macros::builtin::deref; )] pub use crate::macros::builtin::define_opaque; -#[unstable( - feature = "derive_from", - issue = "144889", - reason = "`derive(From)` is unstable" -)] -pub use crate::macros::builtin::From; - #[unstable(feature = "extern_item_impls", issue = "125418")] pub use crate::macros::builtin::{eii, unsafe_eii}; diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 0601019abbd5c..1eae06ebd33e7 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1151,8 +1151,8 @@ impl AtomicBool { /// assert_eq!(foo.fetch_or(false, Ordering::SeqCst), true); /// assert_eq!(foo.load(Ordering::SeqCst), true); /// - /// let foo = AtomicBool::new(true); - /// assert_eq!(foo.fetch_or(true, Ordering::SeqCst), true); + /// let foo = AtomicBool::new(false); + /// assert_eq!(foo.fetch_or(true, Ordering::SeqCst), false); /// assert_eq!(foo.load(Ordering::SeqCst), true); /// /// let foo = AtomicBool::new(false); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 508a6ed852610..4fcaff4339085 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -274,6 +274,7 @@ #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] +#![feature(const_default)] #![feature(const_trait_impl)] #![feature(core_float_math)] #![feature(decl_macro)] diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index d1bbe0ff843cf..b9a7077e420e6 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -582,7 +582,8 @@ impl RefUnwindSafe for OnceLock {} impl UnwindSafe for OnceLock {} #[stable(feature = "once_cell", since = "1.70.0")] -impl Default for OnceLock { +#[rustc_const_unstable(feature = "const_default", issue = "143894")] +impl const Default for OnceLock { /// Creates a new uninitialized cell. /// /// # Example diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 1566f1cb1dac6..74d99e0f1270e 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -189,8 +189,10 @@ impl OutputFormatter for JunitFormatter { compilation_time: f64, ) -> io::Result<()> { self.write_message(&format!( - "\n", - )) + "", + ))?; + self.out.write_all(b"\n")?; + Ok(()) } } diff --git a/src/etc/gdb_lookup.py b/src/etc/gdb_lookup.py index d368f7ed1ec5c..c70944790d2b5 100644 --- a/src/etc/gdb_lookup.py +++ b/src/etc/gdb_lookup.py @@ -3,7 +3,7 @@ import re from gdb_providers import * -from rust_types import * +from rust_types import RustType, classify_struct, classify_union _gdb_version_matched = re.search("([0-9]+)\\.([0-9]+)", gdb.VERSION) @@ -28,7 +28,7 @@ def classify_rust_type(type): if type_class == gdb.TYPE_CODE_UNION: return classify_union(type.fields()) - return RustType.OTHER + return RustType.Other def check_enum_discriminant(valobj): @@ -85,7 +85,7 @@ def __init__(self, name): def add(self, rust_type, provider): # Just use the rust_type as the name. - printer = PrintByRustType(rust_type, provider) + printer = PrintByRustType(rust_type.name, provider) self.type_map[rust_type] = printer self.subprinters.append(printer) @@ -99,23 +99,23 @@ def __call__(self, valobj): printer = RustPrettyPrinter("rust") # use enum provider only for GDB <7.12 if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): - printer.add(RustType.ENUM, enum_provider) -printer.add(RustType.STD_STRING, StdStringProvider) -printer.add(RustType.STD_OS_STRING, StdOsStringProvider) -printer.add(RustType.STD_STR, StdStrProvider) -printer.add(RustType.STD_SLICE, StdSliceProvider) -printer.add(RustType.STD_VEC, StdVecProvider) -printer.add(RustType.STD_VEC_DEQUE, StdVecDequeProvider) -printer.add(RustType.STD_BTREE_SET, StdBTreeSetProvider) -printer.add(RustType.STD_BTREE_MAP, StdBTreeMapProvider) -printer.add(RustType.STD_HASH_MAP, hashmap_provider) -printer.add(RustType.STD_HASH_SET, hashset_provider) -printer.add(RustType.STD_RC, StdRcProvider) -printer.add(RustType.STD_ARC, lambda valobj: StdRcProvider(valobj, is_atomic=True)) - -printer.add(RustType.STD_CELL, StdCellProvider) -printer.add(RustType.STD_REF, StdRefProvider) -printer.add(RustType.STD_REF_MUT, StdRefProvider) -printer.add(RustType.STD_REF_CELL, StdRefCellProvider) - -printer.add(RustType.STD_NONZERO_NUMBER, StdNonZeroNumberProvider) + printer.add(RustType.Enum, enum_provider) +printer.add(RustType.StdString, StdStringProvider) +printer.add(RustType.StdOsString, StdOsStringProvider) +printer.add(RustType.StdStr, StdStrProvider) +printer.add(RustType.StdSlice, StdSliceProvider) +printer.add(RustType.StdVec, StdVecProvider) +printer.add(RustType.StdVecDeque, StdVecDequeProvider) +printer.add(RustType.StdBTreeSet, StdBTreeSetProvider) +printer.add(RustType.StdBTreeMap, StdBTreeMapProvider) +printer.add(RustType.StdHashMap, hashmap_provider) +printer.add(RustType.StdHashSet, hashset_provider) +printer.add(RustType.StdRc, StdRcProvider) +printer.add(RustType.StdArc, lambda valobj: StdRcProvider(valobj, is_atomic=True)) + +printer.add(RustType.StdCell, StdCellProvider) +printer.add(RustType.StdRef, StdRefProvider) +printer.add(RustType.StdRefMut, StdRefProvider) +printer.add(RustType.StdRefCell, StdRefCellProvider) + +printer.add(RustType.StdNonZeroNumber, StdNonZeroNumberProvider) diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index eff065d545657..24198952f4d08 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -1,81 +1,85 @@ +# LLDB iterates through these in reverse order to discover summaries/synthetics that means the top +# of the list can be "overwritten" by items lower on the list. Be careful when reordering items. + # Forces test-compliant formatting to all other types type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust # Std String type synthetic add -l lldb_lookup.StdStringSyntheticProvider -x "^(alloc::([a-z_]+::)+)String$" --category Rust -type summary add -F lldb_lookup.StdStringSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust +type summary add -F lldb_lookup.StdStringSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust + # Std str -type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?str$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?str$" --category Rust +type synthetic add -l lldb_lookup.StdSliceSyntheticProvider -x "^&(mut )?str$" --category Rust +type summary add -F lldb_lookup.StdStrSummaryProvider -e -x -h "^&(mut )?str$" --category Rust + ## MSVC type synthetic add -l lldb_lookup.MSVCStrSyntheticProvider -x "^ref(_mut)?\$$" --category Rust type summary add -F lldb_lookup.StdStrSummaryProvider -e -h -x "^ref(_mut)?\$$" --category Rust -# Array -type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?\\[.+\\]$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?\\[.+\\]$" --category Rust -# Slice + +# Array/Slice +type synthetic add -l lldb_lookup.StdSliceSyntheticProvider -x "^&(mut )?\\[.+\\]$" --category Rust +type summary add -F lldb_lookup.SizeSummaryProvider -e -x -h "^&(mut )?\\[.+\\]$" --category Rust + ## MSVC type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref(_mut)?\$ >" --category Rust type summary add -F lldb_lookup.StdSliceSummaryProvider -e -x -h "^ref(_mut)?\$ >" --category Rust + # OsString -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust +type summary add -F lldb_lookup.StdOsStringSummaryProvider -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust + # Vec -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust +type synthetic add -l lldb_lookup.StdVecSyntheticProvider -x "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust +type summary add -F lldb_lookup.SizeSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust + # VecDeque -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust -# BTreeSet -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust -# BTreeMap -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust +type synthetic add -l lldb_lookup.StdVecDequeSyntheticProvider -x "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust +type summary add -F lldb_lookup.SizeSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust + # HashMap -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust +type synthetic add -l lldb_lookup.classify_hashmap -x "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust +type summary add -F lldb_lookup.SizeSummaryProvider -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust + # HashSet -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust +type synthetic add -l lldb_lookup.classify_hashset -x "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust +type summary add -F lldb_lookup.SizeSummaryProvider -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust + # Rc -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust +type synthetic add -l lldb_lookup.StdRcSyntheticProvider -x "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust +type summary add -F lldb_lookup.StdRcSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust + # Arc -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust +type synthetic add -l lldb_lookup.arc_synthetic -x "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust +type summary add -F lldb_lookup.StdRcSummaryProvider -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust + # Cell -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust +type synthetic add -l lldb_lookup.StdCellSyntheticProvider -x "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust + # RefCell -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust +type synthetic add -l lldb_lookup.StdRefSyntheticProvider -x "^(core::([a-z_]+::)+)Ref(Cell|Mut)?<.+>$" --category Rust +type summary add -F lldb_lookup.StdRefSummaryProvider -e -x -h "^(core::([a-z_]+::)+)Ref(Cell|Mut)?<.+>$" --category Rust + # NonZero -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust -type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)NonZero<.+>$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^core::num::([a-z_]+::)*NonZero.+$" --category Rust +type summary add -F lldb_lookup.StdNonZeroNumberSummaryProvider -e -x -h "^(core::([a-z_]+::)+)NonZero(<.+>|I\d{0,3}|U\d{0,3})$" --category Rust + # PathBuf -type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust +type summary add -F lldb_lookup.StdPathBufSummaryProvider -e -x -h "^(std::([a-z_]+::)+)PathBuf$" --category Rust + # Path -type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust +type summary add -F lldb_lookup.StdPathSummaryProvider -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust + # Enum # type summary add -F lldb_lookup.ClangEncodedEnumSummaryProvider -e -h "lldb_lookup.is_sum_type_enum" --recognizer-function --category Rust ## MSVC type synthetic add -l lldb_lookup.MSVCEnumSyntheticProvider -x "^enum2\$<.+>$" --category Rust type summary add -F lldb_lookup.MSVCEnumSummaryProvider -e -x -h "^enum2\$<.+>$" --category Rust + ## MSVC Variants type synthetic add -l lldb_lookup.synthetic_lookup -x "^enum2\$<.+>::.*$" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h "^enum2\$<.+>::.*$" --category Rust + # Tuple -type synthetic add -l lldb_lookup.synthetic_lookup -x "^\(.*\)$" --category Rust -type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^\(.*\)$" --category Rust +type synthetic add -l lldb_lookup.TupleSyntheticProvider -x "^\(.*\)$" --category Rust + ## MSVC type synthetic add -l lldb_lookup.MSVCTupleSyntheticProvider -x "^tuple\$<.+>$" --category Rust type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust + type category enable Rust diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index 2b90d4022f7fb..67194a09ec4ce 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -1,7 +1,35 @@ +from __future__ import annotations +from typing import List + + import lldb from lldb_providers import * -from rust_types import RustType, classify_struct, classify_union +from rust_types import ( + ENUM_DISR_FIELD_NAME, + ENUM_LLDB_ENCODED_VARIANTS, + RustType, + classify_union, + is_tuple_fields, +) + +#################################################################################################### +# This file contains lookup functions that associate rust types with their synthetic/summary +# providers. +# +# LLDB caches the results of the the commands in `lldb_commands`, but that caching is "shallow". It +# purely associates the type with the function given, whether it is a regular function or a class +# constructor. If the function makes decisions about what type of SyntheticProvider to return, that +# processing is done **each time a value of that type is encountered**. +# +# To reiterate, inspecting a `vec![T; 100_000]` will call `T`'s lookup function/constructor 100,000 +# times. This can lead to significant delays in value visualization if the lookup logic is complex. +# +# As such, lookup functions should be kept as minimal as possible. LLDB technically expects a +# SyntheticProvider class constructor. If you can provide just a class constructor, that should be +# preferred. If extra processing must be done, try to keep it as minimal and as targeted as possible +# (see: `classify_hashmap()` vs `classify_hashset()`). +#################################################################################################### # BACKCOMPAT: rust 1.35 @@ -9,82 +37,90 @@ def is_hashbrown_hashmap(hash_map: lldb.SBValue) -> bool: return len(hash_map.type.fields) == 1 -def classify_rust_type(type: lldb.SBType) -> str: - type_class = type.GetTypeClass() - if type_class == lldb.eTypeClassStruct: - return classify_struct(type.name, type.fields) - if type_class == lldb.eTypeClassUnion: - return classify_union(type.fields) +def classify_hashmap(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: + if is_hashbrown_hashmap(valobj): + return StdHashMapSyntheticProvider(valobj, _dict) + else: + return StdOldHashMapSyntheticProvider(valobj, _dict) - return RustType.OTHER +def classify_hashset(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: + hash_map = valobj.GetChildAtIndex(0) + if is_hashbrown_hashmap(hash_map): + return StdHashMapSyntheticProvider(valobj, _dict, show_values=False) + else: + return StdOldHashMapSyntheticProvider(hash_map, _dict, show_values=False) -def summary_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> str: - """Returns the summary provider for the given value""" - rust_type = classify_rust_type(valobj.GetType()) - if rust_type == RustType.STD_STRING: - return StdStringSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_OS_STRING: - return StdOsStringSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_STR: - return StdStrSummaryProvider(valobj, _dict) +def arc_synthetic(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: + return StdRcSyntheticProvider(valobj, _dict, is_atomic=True) - if rust_type == RustType.STD_VEC: - return SizeSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_VEC_DEQUE: - return SizeSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_SLICE: - return SizeSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_HASH_MAP: - return SizeSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_HASH_SET: - return SizeSummaryProvider(valobj, _dict) +def classify_rust_type(type: lldb.SBType, is_msvc: bool) -> RustType: + if type.IsPointerType(): + return RustType.Indirection - if rust_type == RustType.STD_RC: - return StdRcSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_ARC: - return StdRcSummaryProvider(valobj, _dict) + # there is a bit of code duplication here because we don't want to check all of the standard + # library regexes since LLDB handles that for us + type_class = type.GetTypeClass() + if type_class == lldb.eTypeClassStruct: + fields: List[lldb.SBTypeMember] = type.fields + if len(fields) == 0: + return RustType.Empty - if rust_type == RustType.STD_REF: - return StdRefSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_REF_MUT: - return StdRefSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_REF_CELL: - return StdRefSummaryProvider(valobj, _dict) + # <> is emitted by GDB while LLDB(18.1+) emits "$variants$" + if ( + fields[0].name == ENUM_DISR_FIELD_NAME + or fields[0].name == ENUM_LLDB_ENCODED_VARIANTS + ): + return RustType.Enum - if rust_type == RustType.STD_NONZERO_NUMBER: - return StdNonZeroNumberSummaryProvider(valobj, _dict) + if is_tuple_fields(fields): + return RustType.Tuple - if rust_type == RustType.STD_PATHBUF: - return StdPathBufSummaryProvider(valobj, _dict) - if rust_type == RustType.STD_PATH: - return StdPathSummaryProvider(valobj, _dict) + return RustType.Struct + if type_class == lldb.eTypeClassUnion: + # If we're debugging msvc, sum-type enums should have been caught by the regex in lldb + # commands since they all start with "enum2$<" + if is_msvc: + return RustType.Union + return classify_union(type.fields) - return "" + return RustType.Other def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: """Returns the synthetic provider for the given value""" - rust_type = classify_rust_type(valobj.GetType()) - if rust_type == RustType.STRUCT: + # small hack to check for the DWARF debug info section, since SBTarget.triple and + # SBProcess.triple report lldb's target rather than the executable's. SBProcessInfo.triple + # returns a triple without the ABI. It is also possible for any of those functions to return a + # None object. + # Instead, we look for the GNU `.debug_info` section, as MSVC does not have one with the same + # name + # FIXME: I don't know if this works when the DWARF lives in a separate file + # (see: https://gcc.gnu.org/wiki/DebugFissionDWP). Splitting the DWARF is very uncommon afaik so + # it should be okay for the time being. + is_msvc = not valobj.GetFrame().GetModule().FindSection(".debug_info").IsValid() + + rust_type = classify_rust_type(valobj.GetType(), is_msvc) + + if rust_type == RustType.Struct or rust_type == RustType.Union: return StructSyntheticProvider(valobj, _dict) - if rust_type == RustType.STRUCT_VARIANT: + if rust_type == RustType.StructVariant: return StructSyntheticProvider(valobj, _dict, is_variant=True) - if rust_type == RustType.TUPLE: + if rust_type == RustType.Tuple: return TupleSyntheticProvider(valobj, _dict) - if rust_type == RustType.TUPLE_VARIANT: + if rust_type == RustType.TupleVariant: return TupleSyntheticProvider(valobj, _dict, is_variant=True) - if rust_type == RustType.EMPTY: + if rust_type == RustType.Empty: return EmptySyntheticProvider(valobj, _dict) - if rust_type == RustType.REGULAR_ENUM: + if rust_type == RustType.RegularEnum: discriminant = valobj.GetChildAtIndex(0).GetChildAtIndex(0).GetValueAsUnsigned() return synthetic_lookup(valobj.GetChildAtIndex(discriminant), _dict) - if rust_type == RustType.SINGLETON_ENUM: + if rust_type == RustType.SingletonEnum: return synthetic_lookup(valobj.GetChildAtIndex(0), _dict) - if rust_type == RustType.ENUM: + if rust_type == RustType.Enum: # this little trick lets us treat `synthetic_lookup` as a "recognizer function" for the enum # summary providers, reducing the number of lookups we have to do. This is a huge time save # because there's no way (via type name) to recognize sum-type enums on `*-gnu` targets. The @@ -106,37 +142,7 @@ def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: ) return ClangEncodedEnumProvider(valobj, _dict) - if rust_type == RustType.STD_VEC: - return StdVecSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_VEC_DEQUE: - return StdVecDequeSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_SLICE or rust_type == RustType.STD_STR: - return StdSliceSyntheticProvider(valobj, _dict) - - if rust_type == RustType.STD_HASH_MAP: - if is_hashbrown_hashmap(valobj): - return StdHashMapSyntheticProvider(valobj, _dict) - else: - return StdOldHashMapSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_HASH_SET: - hash_map = valobj.GetChildAtIndex(0) - if is_hashbrown_hashmap(hash_map): - return StdHashMapSyntheticProvider(valobj, _dict, show_values=False) - else: - return StdOldHashMapSyntheticProvider(hash_map, _dict, show_values=False) - - if rust_type == RustType.STD_RC: - return StdRcSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_ARC: - return StdRcSyntheticProvider(valobj, _dict, is_atomic=True) - - if rust_type == RustType.STD_CELL: - return StdCellSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_REF: - return StdRefSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_REF_MUT: - return StdRefSyntheticProvider(valobj, _dict) - if rust_type == RustType.STD_REF_CELL: - return StdRefSyntheticProvider(valobj, _dict, is_cell=True) + if rust_type == RustType.Indirection: + return IndirectionSyntheticProvider(valobj, _dict) return DefaultSyntheticProvider(valobj, _dict) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 582471622baa6..77ee3d4f4e11f 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -95,11 +95,14 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger = Logger.Logger() # logger >> "Default synthetic provider for " + str(valobj.GetName()) self.valobj = valobj + self.is_ptr = valobj.GetType().IsPointerType() def num_children(self) -> int: return self.valobj.GetNumChildren() def get_child_index(self, name: str) -> int: + if self.is_ptr and name == "$$dereference$$": + return self.valobj.Dereference().GetSyntheticValue() return self.valobj.GetIndexOfChildWithName(name) def get_child_at_index(self, index: int) -> SBValue: @@ -111,6 +114,36 @@ def update(self): def has_children(self) -> bool: return self.valobj.MightHaveChildren() + def get_value(self): + return self.valobj.value + + +class IndirectionSyntheticProvider: + def __init__(self, valobj: SBValue, _dict: LLDBOpaque): + self.valobj = valobj + + def num_children(self) -> int: + return 1 + + def get_child_index(self, name: str) -> int: + if self.is_ptr and name == "$$dereference$$": + return 0 + return -1 + + def get_child_at_index(self, index: int) -> SBValue: + if index == 0: + return self.valobj.Dereference().GetSyntheticValue() + return None + + def update(self): + pass + + def has_children(self) -> bool: + return True + + def get_value(self): + return self.valobj.value + class EmptySyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): @@ -249,7 +282,7 @@ def vec_to_string(vec: SBValue) -> str: ) -def StdStringSummaryProvider(valobj, dict): +def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): inner_vec = ( valobj.GetNonSyntheticValue() .GetChildMemberWithName("vec") diff --git a/src/etc/rust_types.py b/src/etc/rust_types.py index af03e8ede9c3f..1ed68458ae3a5 100644 --- a/src/etc/rust_types.py +++ b/src/etc/rust_types.py @@ -1,40 +1,42 @@ +from enum import Enum from typing import List import re -class RustType(object): - OTHER = "Other" - STRUCT = "Struct" - TUPLE = "Tuple" - CSTYLE_VARIANT = "CStyleVariant" - TUPLE_VARIANT = "TupleVariant" - STRUCT_VARIANT = "StructVariant" - ENUM = "Enum" - EMPTY = "Empty" - SINGLETON_ENUM = "SingletonEnum" - REGULAR_ENUM = "RegularEnum" - COMPRESSED_ENUM = "CompressedEnum" - REGULAR_UNION = "RegularUnion" - - STD_STRING = "StdString" - STD_OS_STRING = "StdOsString" - STD_STR = "StdStr" - STD_SLICE = "StdSlice" - STD_VEC = "StdVec" - STD_VEC_DEQUE = "StdVecDeque" - STD_BTREE_SET = "StdBTreeSet" - STD_BTREE_MAP = "StdBTreeMap" - STD_HASH_MAP = "StdHashMap" - STD_HASH_SET = "StdHashSet" - STD_RC = "StdRc" - STD_ARC = "StdArc" - STD_CELL = "StdCell" - STD_REF = "StdRef" - STD_REF_MUT = "StdRefMut" - STD_REF_CELL = "StdRefCell" - STD_NONZERO_NUMBER = "StdNonZeroNumber" - STD_PATH = "StdPath" - STD_PATHBUF = "StdPathBuf" +class RustType(Enum): + Other = 0 + Struct = 1 + Tuple = 2 + CStyleVariant = 3 + TupleVariant = 4 + StructVariant = 5 + Enum = 6 + Empty = 7 + SingletonEnum = 8 + RegularEnum = 9 + CompressedEnum = 10 + Union = 11 + Indirection = 12 + + StdString = 13 + StdOsString = 14 + StdStr = 15 + StdSlice = 16 + StdVec = 17 + StdVecDeque = 18 + StdBTreeSet = 19 + StdBTreeMap = 20 + StdHashMap = 21 + StdHashSet = 22 + StdRc = 23 + StdArc = 24 + StdCell = 25 + StdRef = 26 + StdRefMut = 27 + StdRefCell = 28 + StdNonZeroNumber = 29 + StdPath = 30 + StdPathBuf = 31 STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") @@ -57,42 +59,45 @@ class RustType(object): STD_PATHBUF_REGEX = re.compile(r"^(std::([a-z_]+::)+)PathBuf$") STD_PATH_REGEX = re.compile(r"^&(mut )?(std::([a-z_]+::)+)Path$") -TUPLE_ITEM_REGEX = re.compile(r"__\d+$") - ENCODED_ENUM_PREFIX = "RUST$ENCODED$ENUM$" ENUM_DISR_FIELD_NAME = "<>" ENUM_LLDB_ENCODED_VARIANTS = "$variants$" STD_TYPE_TO_REGEX = { - RustType.STD_STRING: STD_STRING_REGEX, - RustType.STD_OS_STRING: STD_OS_STRING_REGEX, - RustType.STD_STR: STD_STR_REGEX, - RustType.STD_SLICE: STD_SLICE_REGEX, - RustType.STD_VEC: STD_VEC_REGEX, - RustType.STD_VEC_DEQUE: STD_VEC_DEQUE_REGEX, - RustType.STD_HASH_MAP: STD_HASH_MAP_REGEX, - RustType.STD_HASH_SET: STD_HASH_SET_REGEX, - RustType.STD_BTREE_SET: STD_BTREE_SET_REGEX, - RustType.STD_BTREE_MAP: STD_BTREE_MAP_REGEX, - RustType.STD_RC: STD_RC_REGEX, - RustType.STD_ARC: STD_ARC_REGEX, - RustType.STD_REF: STD_REF_REGEX, - RustType.STD_REF_MUT: STD_REF_MUT_REGEX, - RustType.STD_REF_CELL: STD_REF_CELL_REGEX, - RustType.STD_CELL: STD_CELL_REGEX, - RustType.STD_NONZERO_NUMBER: STD_NONZERO_NUMBER_REGEX, - RustType.STD_PATHBUF: STD_PATHBUF_REGEX, - RustType.STD_PATH: STD_PATH_REGEX, + RustType.StdString: STD_STRING_REGEX, + RustType.StdOsString: STD_OS_STRING_REGEX, + RustType.StdStr: STD_STR_REGEX, + RustType.StdSlice: STD_SLICE_REGEX, + RustType.StdVec: STD_VEC_REGEX, + RustType.StdVecDeque: STD_VEC_DEQUE_REGEX, + RustType.StdHashMap: STD_HASH_MAP_REGEX, + RustType.StdHashSet: STD_HASH_SET_REGEX, + RustType.StdBTreeSet: STD_BTREE_SET_REGEX, + RustType.StdBTreeMap: STD_BTREE_MAP_REGEX, + RustType.StdRc: STD_RC_REGEX, + RustType.StdArc: STD_ARC_REGEX, + RustType.StdRef: STD_REF_REGEX, + RustType.StdRefMut: STD_REF_MUT_REGEX, + RustType.StdRefCell: STD_REF_CELL_REGEX, + RustType.StdCell: STD_CELL_REGEX, + RustType.StdNonZeroNumber: STD_NONZERO_NUMBER_REGEX, + RustType.StdPath: STD_PATH_REGEX, + RustType.StdPathBuf: STD_PATHBUF_REGEX, } def is_tuple_fields(fields: List) -> bool: - return all(TUPLE_ITEM_REGEX.match(str(field.name)) for field in fields) + for f in fields: + name = str(f.name) + if not name.startswith("__") or not name[2:].isdigit(): + return False + + return True -def classify_struct(name: str, fields: List) -> str: +def classify_struct(name: str, fields: List) -> RustType: if len(fields) == 0: - return RustType.EMPTY + return RustType.Empty for ty, regex in STD_TYPE_TO_REGEX.items(): if regex.match(name): @@ -103,26 +108,26 @@ def classify_struct(name: str, fields: List) -> str: fields[0].name == ENUM_DISR_FIELD_NAME or fields[0].name == ENUM_LLDB_ENCODED_VARIANTS ): - return RustType.ENUM + return RustType.Enum if is_tuple_fields(fields): - return RustType.TUPLE + return RustType.Tuple - return RustType.STRUCT + return RustType.Struct -def classify_union(fields: List) -> str: +def classify_union(fields: List) -> RustType: if len(fields) == 0: - return RustType.EMPTY + return RustType.Empty first_variant_name = fields[0].name if first_variant_name is None: if len(fields) == 1: - return RustType.SINGLETON_ENUM + return RustType.SingletonEnum else: - return RustType.REGULAR_ENUM + return RustType.RegularEnum elif first_variant_name.startswith(ENCODED_ENUM_PREFIX): assert len(fields) == 1 - return RustType.COMPRESSED_ENUM + return RustType.CompressedEnum else: - return RustType.REGULAR_UNION + return RustType.RegularEnum diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index 81fa7900c5faf..56df783ff6b16 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -211,40 +211,40 @@ //@ lldb-command:run //@ lldb-command:v/d nz_i8 -//@ lldb-check:[...] 11 { __0 = { 0 = 11 } } +//@ lldb-check:[...] 11 { 0 = { 0 = 11 } } //@ lldb-command:v nz_i16 -//@ lldb-check:[...] 22 { __0 = { 0 = 22 } } +//@ lldb-check:[...] 22 { 0 = { 0 = 22 } } //@ lldb-command:v nz_i32 -//@ lldb-check:[...] 33 { __0 = { 0 = 33 } } +//@ lldb-check:[...] 33 { 0 = { 0 = 33 } } //@ lldb-command:v nz_i64 -//@ lldb-check:[...] 44 { __0 = { 0 = 44 } } +//@ lldb-check:[...] 44 { 0 = { 0 = 44 } } //@ lldb-command:v nz_i128 -//@ lldb-check:[...] 55 { __0 = { 0 = 55 } } +//@ lldb-check:[...] 55 { 0 = { 0 = 55 } } //@ lldb-command:v nz_isize -//@ lldb-check:[...] 66 { __0 = { 0 = 66 } } +//@ lldb-check:[...] 66 { 0 = { 0 = 66 } } //@ lldb-command:v/d nz_u8 -//@ lldb-check:[...] 77 { __0 = { 0 = 77 } } +//@ lldb-check:[...] 77 { 0 = { 0 = 77 } } //@ lldb-command:v nz_u16 -//@ lldb-check:[...] 88 { __0 = { 0 = 88 } } +//@ lldb-check:[...] 88 { 0 = { 0 = 88 } } //@ lldb-command:v nz_u32 -//@ lldb-check:[...] 99 { __0 = { 0 = 99 } } +//@ lldb-check:[...] 99 { 0 = { 0 = 99 } } //@ lldb-command:v nz_u64 -//@ lldb-check:[...] 100 { __0 = { 0 = 100 } } +//@ lldb-check:[...] 100 { 0 = { 0 = 100 } } //@ lldb-command:v nz_u128 -//@ lldb-check:[...] 111 { __0 = { 0 = 111 } } +//@ lldb-check:[...] 111 { 0 = { 0 = 111 } } //@ lldb-command:v nz_usize -//@ lldb-check:[...] 122 { __0 = { 0 = 122 } } +//@ lldb-check:[...] 122 { 0 = { 0 = 122 } } use std::num::*; use std::sync::atomic::*; diff --git a/tests/run-make/doctests-junit/doctest-2021.xml b/tests/run-make/doctests-junit/doctest-2021.xml new file mode 100644 index 0000000000000..5facfb80ce622 --- /dev/null +++ b/tests/run-make/doctests-junit/doctest-2021.xml @@ -0,0 +1 @@ + diff --git a/tests/run-make/doctests-junit/doctest-2024.xml b/tests/run-make/doctests-junit/doctest-2024.xml new file mode 100644 index 0000000000000..4f94f01c3e32c --- /dev/null +++ b/tests/run-make/doctests-junit/doctest-2024.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/run-make/doctests-junit/doctest.rs b/tests/run-make/doctests-junit/doctest.rs new file mode 100644 index 0000000000000..1873d63a49c63 --- /dev/null +++ b/tests/run-make/doctests-junit/doctest.rs @@ -0,0 +1,14 @@ +/// ``` +/// assert_eq!(doctest::add(2, 2), 4); +/// ``` +/// +/// ```should_panic +/// assert_eq!(doctest::add(2, 2), 5); +/// ``` +/// +/// ```compile_fail +/// assert_eq!(doctest::add(2, 2), "banana"); +/// ``` +pub fn add(a: i32, b: i32) -> i32 { + a + b +} diff --git a/tests/run-make/doctests-junit/rmake.rs b/tests/run-make/doctests-junit/rmake.rs new file mode 100644 index 0000000000000..e3198502bb174 --- /dev/null +++ b/tests/run-make/doctests-junit/rmake.rs @@ -0,0 +1,45 @@ +// Check rustdoc's test JUnit (XML) output against snapshots. + +//@ ignore-cross-compile (running doctests) +//@ ignore-stage1 (rustdoc depends on a fix in libtest) +//@ needs-unwind (test file contains `should_panic` test) + +use std::path::Path; + +use run_make_support::{cwd, diff, python_command, rustc, rustdoc}; + +fn main() { + let rlib = cwd().join("libdoctest.rlib"); + rustc().input("doctest.rs").crate_type("rlib").output(&rlib).run(); + + run_doctests(&rlib, "2021", "doctest-2021.xml"); + run_doctests(&rlib, "2024", "doctest-2024.xml"); +} + +#[track_caller] +fn run_doctests(rlib: &Path, edition: &str, expected_xml: &str) { + let rustdoc_out = rustdoc() + .input("doctest.rs") + .args(&[ + "--test", + "--test-args=-Zunstable-options", + "--test-args=--test-threads=1", + "--test-args=--format=junit", + ]) + .edition(edition) + .env("RUST_BACKTRACE", "0") + .extern_("doctest", rlib.display().to_string()) + .run(); + let rustdoc_stdout = &rustdoc_out.stdout_utf8(); + + // FIXME: merged output of compile_fail tests is broken + if edition != "2024" { + python_command().arg("validate_junit.py").stdin_buf(rustdoc_stdout).run(); + } + + diff() + .expected_file(expected_xml) + .actual_text("output", rustdoc_stdout) + .normalize(r#"\b(time|total_time|compilation_time)="[0-9.]+""#, r#"$1="$$TIME""#) + .run(); +} diff --git a/tests/run-make/doctests-junit/validate_junit.py b/tests/run-make/doctests-junit/validate_junit.py new file mode 100755 index 0000000000000..a9cb0a059563d --- /dev/null +++ b/tests/run-make/doctests-junit/validate_junit.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Trivial Python script that reads lines from stdin, and checks that each line +# is a well-formed XML document. +# +# This takes advantage of the fact that Python has a built-in XML parser, +# whereas doing the same check in Rust would require us to pull in an XML +# crate just for this relatively-minor test. +# +# If you're trying to remove Python scripts from the test suite, think twice +# before removing this one. You could do so, but it's probably not worth it. + +import sys +import xml.etree.ElementTree as ET + +# Read the entire output and try to decode it as XML. +junit = sys.stdin.read() +try: + ET.fromstring(junit) +except ET.ParseError: + print("Invalid xml: %r" % junit) + raise diff --git a/tests/run-make/libtest-junit/validate_junit.py b/tests/run-make/libtest-junit/validate_junit.py index f92473751b036..a9cb0a059563d 100755 --- a/tests/run-make/libtest-junit/validate_junit.py +++ b/tests/run-make/libtest-junit/validate_junit.py @@ -13,10 +13,10 @@ import sys import xml.etree.ElementTree as ET -# Try to decode line in order to ensure it is a valid XML document -for line in sys.stdin: - try: - ET.fromstring(line) - except ET.ParseError: - print("Invalid xml: %r" % line) - raise +# Read the entire output and try to decode it as XML. +junit = sys.stdin.read() +try: + ET.fromstring(junit) +except ET.ParseError: + print("Invalid xml: %r" % junit) + raise diff --git a/tests/ui/eii/error_statement_position.rs b/tests/ui/eii/error_statement_position.rs new file mode 100644 index 0000000000000..cf81e7e6a8b23 --- /dev/null +++ b/tests/ui/eii/error_statement_position.rs @@ -0,0 +1,16 @@ +#![feature(extern_item_impls)] +// EIIs can, despite not being super useful, be declared in statement position +// nested inside items. Items in statement position, when expanded as part of a macro, +// need to be wrapped slightly differently (in an `ast::Statement`). +// We did this on the happy path (no errors), but when there was an error, we'd +// replace it with *just* an `ast::Item` not wrapped in an `ast::Statement`. +// This caused an ICE (https://github.com/rust-lang/rust/issues/149980). +// this test fails to build, but demonstrates that no ICE is produced. + +fn main() { + struct Bar; + + #[eii] + //~^ ERROR `#[eii]` is only valid on functions + impl Bar {} +} diff --git a/tests/ui/eii/error_statement_position.stderr b/tests/ui/eii/error_statement_position.stderr new file mode 100644 index 0000000000000..01b7394ef00fa --- /dev/null +++ b/tests/ui/eii/error_statement_position.stderr @@ -0,0 +1,8 @@ +error: `#[eii]` is only valid on functions + --> $DIR/error_statement_position.rs:13:5 + | +LL | #[eii] + | ^^^^^^ + +error: aborting due to 1 previous error +