From 02594aca98436a94f89756f83824d866974d4f47 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Wed, 15 Oct 2025 20:23:11 -0500 Subject: [PATCH 1/8] align gnu enum output with msvc enum output --- src/etc/lldb_commands | 3 +- src/etc/lldb_lookup.py | 23 ++++++- src/etc/lldb_providers.py | 122 +++++++++++++++++++++++--------------- 3 files changed, 95 insertions(+), 53 deletions(-) diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index 508296c3a5aec..eff065d545657 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -1,6 +1,5 @@ # Forces test-compliant formatting to all other types type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust -type summary add -F _ -e -x -h "^.*$" --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 @@ -66,6 +65,7 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)Pa 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 # 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 @@ -74,6 +74,7 @@ type synthetic add -l lldb_lookup.synthetic_lookup -x "^enum2\$<.+>::.*$" --cate 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 ## 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 diff --git a/src/etc/lldb_lookup.py b/src/etc/lldb_lookup.py index f43d2c6a7252e..2b90d4022f7fb 100644 --- a/src/etc/lldb_lookup.py +++ b/src/etc/lldb_lookup.py @@ -10,9 +10,6 @@ def is_hashbrown_hashmap(hash_map: lldb.SBValue) -> bool: def classify_rust_type(type: lldb.SBType) -> str: - if type.IsPointerType(): - type = type.GetPointeeType() - type_class = type.GetTypeClass() if type_class == lldb.eTypeClassStruct: return classify_struct(type.name, type.fields) @@ -88,6 +85,26 @@ def synthetic_lookup(valobj: lldb.SBValue, _dict: LLDBOpaque) -> object: if rust_type == RustType.SINGLETON_ENUM: return synthetic_lookup(valobj.GetChildAtIndex(0), _dict) 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 + # alternative would be to shove every single type through `summary_lookup`, which is + # incredibly wasteful. Once these scripts are updated for LLDB 19.0 and we can use + # `--recognizer-function`, this hack will only be needed for backwards compatibility. + summary: lldb.SBTypeSummary = valobj.GetTypeSummary() + if ( + summary.summary_data is None + or summary.summary_data.strip() + != "lldb_lookup.ClangEncodedEnumSummaryProvider(valobj,internal_dict)" + ): + rust_category: lldb.SBTypeCategory = lldb.debugger.GetCategory("Rust") + rust_category.AddTypeSummary( + lldb.SBTypeNameSpecifier(valobj.GetTypeName()), + lldb.SBTypeSummary().CreateWithFunctionName( + "lldb_lookup.ClangEncodedEnumSummaryProvider" + ), + ) + return ClangEncodedEnumProvider(valobj, _dict) if rust_type == RustType.STD_VEC: return StdVecSyntheticProvider(valobj, _dict) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 13c66ee8298b5..582471622baa6 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -1,7 +1,6 @@ from __future__ import annotations -import re import sys -from typing import List, TYPE_CHECKING, Generator +from typing import Generator, List, TYPE_CHECKING from lldb import ( SBData, @@ -12,6 +11,8 @@ eFormatChar, ) +from rust_types import is_tuple_fields + if TYPE_CHECKING: from lldb import SBValue, SBType, SBTypeStaticField, SBTarget @@ -206,6 +207,34 @@ def resolve_msvc_template_arg(arg_name: str, target: SBTarget) -> SBType: return result +def StructSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: + # structs need the field name before the field value + output = ( + f"{valobj.GetChildAtIndex(i).GetName()}:{child}" + for i, child in enumerate(aggregate_field_summary(valobj, _dict)) + ) + + return "{" + ", ".join(output) + "}" + + +def TupleSummaryProvider(valobj: SBValue, _dict: LLDBOpaque): + return "(" + ", ".join(aggregate_field_summary(valobj, _dict)) + ")" + + +def aggregate_field_summary(valobj: SBValue, _dict) -> Generator[str, None, None]: + for i in range(0, valobj.GetNumChildren()): + child: SBValue = valobj.GetChildAtIndex(i) + summary = child.summary + if summary is None: + summary = child.value + if summary is None: + if is_tuple_fields(child): + summary = TupleSummaryProvider(child, _dict) + else: + summary = StructSummaryProvider(child, _dict) + yield summary + + def SizeSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: return "size=" + str(valobj.GetNumChildren()) @@ -454,49 +483,55 @@ def get_type_name(self): return "&str" -def _getVariantName(variant) -> str: +def _getVariantName(variant: SBValue) -> str: """ Since the enum variant's type name is in the form `TheEnumName::TheVariantName$Variant`, we can extract `TheVariantName` from it for display purpose. """ s = variant.GetType().GetName() - match = re.search(r"::([^:]+)\$Variant$", s) - return match.group(1) if match else "" + if not s.endswith("$Variant"): + return "" + + # trim off path and "$Variant" + # len("$Variant") == 8 + return s.rsplit("::", 1)[1][:-8] class ClangEncodedEnumProvider: """Pretty-printer for 'clang-encoded' enums support implemented in LLDB""" + valobj: SBValue + variant: SBValue + value: SBValue + DISCRIMINANT_MEMBER_NAME = "$discr$" VALUE_MEMBER_NAME = "value" + __slots__ = ("valobj", "variant", "value") + def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj self.update() def has_children(self) -> bool: - return True + return self.value.MightHaveChildren() def num_children(self) -> int: - return 1 + return self.value.GetNumChildren() - def get_child_index(self, _name: str) -> int: - return -1 + def get_child_index(self, name: str) -> int: + return self.value.GetIndexOfChildWithName(name) def get_child_at_index(self, index: int) -> SBValue: - if index == 0: - value = self.variant.GetChildMemberWithName( - ClangEncodedEnumProvider.VALUE_MEMBER_NAME - ) - return value.CreateChildAtOffset( - _getVariantName(self.variant), 0, value.GetType() - ) - return None + return self.value.GetChildAtIndex(index) def update(self): all_variants = self.valobj.GetChildAtIndex(0) index = self._getCurrentVariantIndex(all_variants) self.variant = all_variants.GetChildAtIndex(index) + self.value = self.variant.GetChildMemberWithName( + ClangEncodedEnumProvider.VALUE_MEMBER_NAME + ).GetSyntheticValue() def _getCurrentVariantIndex(self, all_variants: SBValue) -> int: default_index = 0 @@ -514,6 +549,23 @@ def _getCurrentVariantIndex(self, all_variants: SBValue) -> int: return default_index +def ClangEncodedEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: + enum_synth = ClangEncodedEnumProvider(valobj.GetNonSyntheticValue(), _dict) + variant = enum_synth.variant + name = _getVariantName(variant) + + if valobj.GetNumChildren() == 0: + return name + + child_name: str = valobj.GetChildAtIndex(0).name + if child_name == "0" or child_name == "__0": + # enum variant is a tuple struct + return name + TupleSummaryProvider(valobj, _dict) + else: + # enum variant is a regular struct + return name + StructSummaryProvider(valobj, _dict) + + class MSVCEnumSyntheticProvider: """ Synthetic provider for sum-type enums on MSVC. For a detailed explanation of the internals, @@ -522,12 +574,14 @@ class MSVCEnumSyntheticProvider: https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs """ + valobj: SBValue + variant: SBValue + value: SBValue + __slots__ = ["valobj", "variant", "value"] def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj - self.variant: SBValue - self.value: SBValue self.update() def update(self): @@ -695,21 +749,6 @@ def get_type_name(self) -> str: return name -def StructSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: - output = [] - for i in range(valobj.GetNumChildren()): - child: SBValue = valobj.GetChildAtIndex(i) - summary = child.summary - if summary is None: - summary = child.value - if summary is None: - summary = StructSummaryProvider(child, _dict) - summary = child.GetName() + ":" + summary - output.append(summary) - - return "{" + ", ".join(output) + "}" - - def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict) variant_names: SBType = valobj.target.FindFirstType( @@ -826,21 +865,6 @@ def get_type_name(self) -> str: return "(" + name + ")" -def TupleSummaryProvider(valobj: SBValue, _dict: LLDBOpaque): - output: List[str] = [] - - for i in range(0, valobj.GetNumChildren()): - child: SBValue = valobj.GetChildAtIndex(i) - summary = child.summary - if summary is None: - summary = child.value - if summary is None: - summary = "{...}" - output.append(summary) - - return "(" + ", ".join(output) + ")" - - class StdVecSyntheticProvider: """Pretty-printer for alloc::vec::Vec From b32411eadb97b68f9c1976a4cbdd72f361a40ebf Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Wed, 15 Oct 2025 20:23:42 -0500 Subject: [PATCH 2/8] update tests --- tests/debuginfo/basic-types-globals.rs | 2 +- tests/debuginfo/borrowed-enum.rs | 6 +++--- tests/debuginfo/c-style-enum-in-composite.rs | 2 +- tests/debuginfo/coroutine-objects.rs | 2 +- tests/debuginfo/enum-thinlto.rs | 2 +- .../debuginfo/generic-method-on-generic-struct.rs | 4 ++-- tests/debuginfo/issue-57822.rs | 2 +- tests/debuginfo/method-on-generic-struct.rs | 4 ++-- tests/debuginfo/msvc-pretty-enums.rs | 1 + tests/debuginfo/option-like-enum.rs | 10 +++++----- tests/debuginfo/strings-and-strs.rs | 5 ++--- tests/debuginfo/struct-in-enum.rs | 7 ++++--- tests/debuginfo/struct-style-enum.rs | 8 ++++---- tests/debuginfo/tuple-in-tuple.rs | 14 +++++++------- tests/debuginfo/tuple-style-enum.rs | 8 ++++---- tests/debuginfo/union-smoke.rs | 4 ++-- tests/debuginfo/unique-enum.rs | 6 +++--- tests/debuginfo/vec-slices.rs | 2 +- 18 files changed, 45 insertions(+), 44 deletions(-) diff --git a/tests/debuginfo/basic-types-globals.rs b/tests/debuginfo/basic-types-globals.rs index d8997b3f75a9f..da46a73a8a7f3 100644 --- a/tests/debuginfo/basic-types-globals.rs +++ b/tests/debuginfo/basic-types-globals.rs @@ -13,7 +13,7 @@ // lldb-command:v I // lldb-check: ::I::[...] = -1 // lldb-command:v --format=d C -// lldb-check: ::C::[...] = 97 +// lldb-check: ::C::[...] = 97 U+0x00000061 U'a' // lldb-command:v --format=d I8 // lldb-check: ::I8::[...] = 68 // lldb-command:v I16 diff --git a/tests/debuginfo/borrowed-enum.rs b/tests/debuginfo/borrowed-enum.rs index 48dce139a2f31..f3769172558b3 100644 --- a/tests/debuginfo/borrowed-enum.rs +++ b/tests/debuginfo/borrowed-enum.rs @@ -23,11 +23,11 @@ // lldb-command:run // lldb-command:v *the_a_ref -// lldb-check:(borrowed_enum::ABC) *the_a_ref = { TheA = { x = 0 y = 8970181431921507452 } } +// lldb-check:(borrowed_enum::ABC) *the_a_ref = TheA{x:0, y:8970181431921507452} { x = 0 y = 8970181431921507452 } // lldb-command:v *the_b_ref -// lldb-check:(borrowed_enum::ABC) *the_b_ref = { TheB = { 0 = 0 1 = 286331153 2 = 286331153 } } +// lldb-check:(borrowed_enum::ABC) *the_b_ref = TheB(0, 286331153, 286331153) { 0 = 0 1 = 286331153 2 = 286331153 } // lldb-command:v *univariant_ref -// lldb-check:(borrowed_enum::Univariant) *univariant_ref = { TheOnlyCase = { 0 = 4820353753753434 } } +// lldb-check:(borrowed_enum::Univariant) *univariant_ref = TheOnlyCase(4820353753753434) { 0 = 4820353753753434 } #![allow(unused_variables)] diff --git a/tests/debuginfo/c-style-enum-in-composite.rs b/tests/debuginfo/c-style-enum-in-composite.rs index dd5e4f8b65d71..137cb2656fc57 100644 --- a/tests/debuginfo/c-style-enum-in-composite.rs +++ b/tests/debuginfo/c-style-enum-in-composite.rs @@ -35,7 +35,7 @@ // lldb-check:[...] { 0 = 0 1 = OneHundred } // lldb-command:v tuple_padding_at_end -// lldb-check:[...] { 0 = { 0 = 1 1 = OneThousand } 1 = 2 } +// lldb-check:[...] ((1, OneThousand), 2) { 0 = (1, OneThousand) { 0 = 1 1 = OneThousand } 1 = 2 } // lldb-command:v tuple_different_enums // lldb-check:[...] { 0 = OneThousand 1 = MountainView 2 = OneMillion 3 = Vienna } diff --git a/tests/debuginfo/coroutine-objects.rs b/tests/debuginfo/coroutine-objects.rs index f1165de4bfaaf..b9d015ef6d1b7 100644 --- a/tests/debuginfo/coroutine-objects.rs +++ b/tests/debuginfo/coroutine-objects.rs @@ -27,7 +27,7 @@ // lldb-command:run // lldb-command:v b -// lldb-check:(coroutine_objects::main::{coroutine_env#0}) b = { value = { _ref__a = 0x[...] } $discr$ = [...] } +// lldb-check:(coroutine_objects::main::{coroutine_env#0}) b = 0{_ref__a:0x[...]} { _ref__a = 0x[...] } // === CDB TESTS =================================================================================== diff --git a/tests/debuginfo/enum-thinlto.rs b/tests/debuginfo/enum-thinlto.rs index 789755438a6b9..9b77b1d9ba4a8 100644 --- a/tests/debuginfo/enum-thinlto.rs +++ b/tests/debuginfo/enum-thinlto.rs @@ -15,7 +15,7 @@ // lldb-command:run // lldb-command:v *abc -// lldb-check:(enum_thinlto::ABC) *abc = { value = { x = 0 y = 8970181431921507452 } $discr$ = 0 } +// lldb-check:(enum_thinlto::ABC) *abc = TheA{x:0, y:8970181431921507452} { x = 0 y = 8970181431921507452 } #![allow(unused_variables)] diff --git a/tests/debuginfo/generic-method-on-generic-struct.rs b/tests/debuginfo/generic-method-on-generic-struct.rs index c549a14153659..9c1d87ac8c876 100644 --- a/tests/debuginfo/generic-method-on-generic-struct.rs +++ b/tests/debuginfo/generic-method-on-generic-struct.rs @@ -58,7 +58,7 @@ // STACK BY REF // lldb-command:v *self -// lldb-check:[...] { x = { 0 = 8888 1 = -8888 } } +// lldb-check:[...] { x = (8888, -8888) { 0 = 8888 1 = -8888 } } // lldb-command:v arg1 // lldb-check:[...] -1 // lldb-command:v arg2 @@ -67,7 +67,7 @@ // STACK BY VAL // lldb-command:v self -// lldb-check:[...] { x = { 0 = 8888 1 = -8888 } } +// lldb-check:[...] { x = (8888, -8888) { 0 = 8888 1 = -8888 } } // lldb-command:v arg1 // lldb-check:[...] -3 // lldb-command:v arg2 diff --git a/tests/debuginfo/issue-57822.rs b/tests/debuginfo/issue-57822.rs index 59c0759737dd0..aa829dfb21adb 100644 --- a/tests/debuginfo/issue-57822.rs +++ b/tests/debuginfo/issue-57822.rs @@ -24,7 +24,7 @@ // lldb-check:(issue_57822::main::{closure_env#1}) g = { f = { x = 1 } } // lldb-command:v b -// lldb-check:(issue_57822::main::{coroutine_env#3}) b = { value = { a = { value = { y = 2 } $discr$ = '\x02' } } $discr$ = '\x02' } +// lldb-check:(issue_57822::main::{coroutine_env#3}) b = 2{a:2{y:2}} { a = 2{y:2} { y = 2 } } #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] diff --git a/tests/debuginfo/method-on-generic-struct.rs b/tests/debuginfo/method-on-generic-struct.rs index b9627f27b9123..01e7ffc749da1 100644 --- a/tests/debuginfo/method-on-generic-struct.rs +++ b/tests/debuginfo/method-on-generic-struct.rs @@ -58,7 +58,7 @@ // STACK BY REF // lldb-command:v *self -// lldb-check:[...]Struct<(u32, i32)>) *self = { x = { 0 = 8888 1 = -8888 } } +// lldb-check:[...]Struct<(u32, i32)>) *self = { x = (8888, -8888) { 0 = 8888 1 = -8888 } } // lldb-command:v arg1 // lldb-check:[...] -1 // lldb-command:v arg2 @@ -67,7 +67,7 @@ // STACK BY VAL // lldb-command:v self -// lldb-check:[...]Struct<(u32, i32)>) self = { x = { 0 = 8888 1 = -8888 } } +// lldb-check:[...]Struct<(u32, i32)>) self = { x = (8888, -8888) { 0 = 8888 1 = -8888 } } // lldb-command:v arg1 // lldb-check:[...] -3 // lldb-command:v arg2 diff --git a/tests/debuginfo/msvc-pretty-enums.rs b/tests/debuginfo/msvc-pretty-enums.rs index aa6629ef1e1be..7d9f867efcdd7 100644 --- a/tests/debuginfo/msvc-pretty-enums.rs +++ b/tests/debuginfo/msvc-pretty-enums.rs @@ -1,3 +1,4 @@ +//@ only-msvc //@ min-lldb-version: 1800 //@ ignore-gdb //@ compile-flags:-g diff --git a/tests/debuginfo/option-like-enum.rs b/tests/debuginfo/option-like-enum.rs index 5a55b143fbbd5..0e419978d62e4 100644 --- a/tests/debuginfo/option-like-enum.rs +++ b/tests/debuginfo/option-like-enum.rs @@ -40,31 +40,31 @@ // lldb-command:run // lldb-command:v some -// lldb-check:[...] Some(&0x[...]) +// lldb-check:[...] Some(0x[...]) { 0 = 0x[...] } // lldb-command:v none // lldb-check:[...] None // lldb-command:v full -// lldb-check:[...] Full(454545, &0x[...], 9988) +// lldb-check:[...] Full(454545, 0x[...], 9988) { 0 = 454545 1 = 0x[...] 2 = 9988 } // lldb-command:v empty // lldb-check:[...] Empty // lldb-command:v droid -// lldb-check:[...] Droid { id: 675675, range: 10000001, internals: &0x[...] } +// lldb-check:[...] Droid{id:675675, range:10000001, internals:0x[...]} { id = 675675 range = 10000001 internals = 0x[...] } // lldb-command:v void_droid // lldb-check:[...] Void // lldb-command:v some_str -// lldb-check:[...] Some("abc") +// lldb-check:[...] Some("abc") [...] // lldb-command:v none_str // lldb-check:[...] None // lldb-command:v nested_non_zero_yep -// lldb-check:[...] Yep(10.5, NestedNonZeroField { a: 10, b: 20, c: &[...] }) +// lldb-check:[...] Yep(10.5, {a:10, b:20, c:[...]}) { 0 = 10.5 1 = { a = 10 b = 20 c = [...] } } // lldb-command:v nested_non_zero_nope // lldb-check:[...] Nope diff --git a/tests/debuginfo/strings-and-strs.rs b/tests/debuginfo/strings-and-strs.rs index 392cf697e110b..baeebda26a902 100644 --- a/tests/debuginfo/strings-and-strs.rs +++ b/tests/debuginfo/strings-and-strs.rs @@ -31,15 +31,14 @@ // lldb-check:(&str) plain_str = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } // lldb-command:v str_in_struct -// lldb-check:((&str, &str)) str_in_tuple = { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } } +// lldb-check:(strings_and_strs::Foo) str_in_struct = { inner = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } // lldb-command:v str_in_tuple -// lldb-check:((&str, &str)) str_in_tuple = { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } } +// lldb-check:((&str, &str)) str_in_tuple = ("Hello", "World") { 0 = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } 1 = "World" { [0] = 'W' [1] = 'o' [2] = 'r' [3] = 'l' [4] = 'd' } } // lldb-command:v str_in_rc // lldb-check:(alloc::rc::Rc<&str, alloc::alloc::Global>) str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } - #![allow(unused_variables)] pub struct Foo<'a> { diff --git a/tests/debuginfo/struct-in-enum.rs b/tests/debuginfo/struct-in-enum.rs index bd61e7cb14377..640066b8d0b13 100644 --- a/tests/debuginfo/struct-in-enum.rs +++ b/tests/debuginfo/struct-in-enum.rs @@ -24,12 +24,13 @@ // lldb-command:run // lldb-command:v case1 -// lldb-check:[...] Case1(0, Struct { x: 2088533116, y: 2088533116, z: 31868 }) +// lldb-check:[...] Case1(0, {x:2088533116, y:2088533116, z:31868}) { 0 = 0 1 = { x = 2088533116 y = 2088533116 z = 31868 } } + // lldb-command:v case2 -// lldb-check:[...] Case2(0, 1229782938247303441, 4369) +// lldb-check:[...] Case2(0, 1229782938247303441, 4369) { 0 = 0 1 = 1229782938247303441 2 = 4369 } // lldb-command:v univariant -// lldb-check:[...] TheOnlyCase(Struct { x: 123, y: 456, z: 789 }) +// lldb-check:[...] TheOnlyCase({x:123, y:456, z:789}) { 0 = { x = 123 y = 456 z = 789 } } #![allow(unused_variables)] diff --git a/tests/debuginfo/struct-style-enum.rs b/tests/debuginfo/struct-style-enum.rs index 48ef97896ac15..4bbd879257042 100644 --- a/tests/debuginfo/struct-style-enum.rs +++ b/tests/debuginfo/struct-style-enum.rs @@ -26,16 +26,16 @@ // lldb-command:run // lldb-command:v case1 -// lldb-check:(struct_style_enum::Regular) case1 = { value = { a = 0 b = 31868 c = 31868 d = 31868 e = 31868 } $discr$ = 0 } +// lldb-check:(struct_style_enum::Regular) case1 = Case1{a:0, b:31868, c:31868, d:31868, e:31868} { a = 0 b = 31868 c = 31868 d = 31868 e = 31868 } // lldb-command:v case2 -// lldb-check:(struct_style_enum::Regular) case2 = { value = { a = 0 b = 286331153 c = 286331153 } $discr$ = 1 } +// lldb-check:(struct_style_enum::Regular) case2 = Case2{a:0, b:286331153, c:286331153} { a = 0 b = 286331153 c = 286331153 } // lldb-command:v case3 -// lldb-check:(struct_style_enum::Regular) case3 = { value = { a = 0 b = 6438275382588823897 } $discr$ = 2 } +// lldb-check:(struct_style_enum::Regular) case3 = Case3{a:0, b:6438275382588823897} { a = 0 b = 6438275382588823897 } // lldb-command:v univariant -// lldb-check:(struct_style_enum::Univariant) univariant = { value = { a = -1 } } +// lldb-check:(struct_style_enum::Univariant) univariant = TheOnlyCase{a:-1} { a = -1 } #![allow(unused_variables)] diff --git a/tests/debuginfo/tuple-in-tuple.rs b/tests/debuginfo/tuple-in-tuple.rs index e906cbed67db7..40e88f1b20848 100644 --- a/tests/debuginfo/tuple-in-tuple.rs +++ b/tests/debuginfo/tuple-in-tuple.rs @@ -29,21 +29,21 @@ // lldb-command:run // lldb-command:v no_padding1 -// lldb-check:[...] { 0 = { 0 = 0 1 = 1 } 1 = 2 2 = 3 } +// lldb-check:[...] ((0, 1), 2, 3) { 0 = (0, 1) { 0 = 0 1 = 1 } 1 = 2 2 = 3 } // lldb-command:v no_padding2 -// lldb-check:[...] { 0 = 4 1 = { 0 = 5 1 = 6 } 2 = 7 } +// lldb-check:[...] (4, (5, 6), 7) { 0 = 4 1 = (5, 6) { 0 = 5 1 = 6 } 2 = 7 } // lldb-command:v no_padding3 -// lldb-check:[...] { 0 = 8 1 = 9 2 = { 0 = 10 1 = 11 } } +// lldb-check:[...] (8, 9, (10, 11)) { 0 = 8 1 = 9 2 = (10, 11) { 0 = 10 1 = 11 } } // lldb-command:v internal_padding1 -// lldb-check:[...] { 0 = 12 1 = { 0 = 13 1 = 14 } } +// lldb-check:[...] (12, (13, 14)) { 0 = 12 1 = (13, 14) { 0 = 13 1 = 14 } } // lldb-command:v internal_padding2 -// lldb-check:[...] { 0 = 15 1 = { 0 = 16 1 = 17 } } +// lldb-check:[...] (15, (16, 17)) { 0 = 15 1 = (16, 17) { 0 = 16 1 = 17 } } // lldb-command:v padding_at_end1 -// lldb-check:[...] { 0 = 18 1 = { 0 = 19 1 = 20 } } +// lldb-check:[...] (18, (19, 20)) { 0 = 18 1 = (19, 20) { 0 = 19 1 = 20 } } // lldb-command:v padding_at_end2 -// lldb-check:[...] { 0 = { 0 = 21 1 = 22 } 1 = 23 } +// lldb-check:[...] ((21, 22), 23) { 0 = (21, 22) { 0 = 21 1 = 22 } 1 = 23 } // === CDB TESTS ================================================================================== diff --git a/tests/debuginfo/tuple-style-enum.rs b/tests/debuginfo/tuple-style-enum.rs index a452c57b30423..6568a2f70ab31 100644 --- a/tests/debuginfo/tuple-style-enum.rs +++ b/tests/debuginfo/tuple-style-enum.rs @@ -27,16 +27,16 @@ // lldb-command:run // lldb-command:v case1 -// lldb-check:(tuple_style_enum::Regular) case1 = { value = { 0 = 0 1 = 31868 2 = 31868 3 = 31868 4 = 31868 } $discr$ = 0 } +// lldb-check:(tuple_style_enum::Regular) case1 = Case1(0, 31868, 31868, 31868, 31868) { 0 = 0 1 = 31868 2 = 31868 3 = 31868 4 = 31868 } // lldb-command:v case2 -// lldb-check:(tuple_style_enum::Regular) case2 = { value = { 0 = 0 1 = 286331153 2 = 286331153 } $discr$ = 1 } +// lldb-check:(tuple_style_enum::Regular) case2 = Case2(0, 286331153, 286331153) { 0 = 0 1 = 286331153 2 = 286331153 } // lldb-command:v case3 -// lldb-check:(tuple_style_enum::Regular) case3 = { value = { 0 = 0 1 = 6438275382588823897 } $discr$ = 2 } +// lldb-check:(tuple_style_enum::Regular) case3 = Case3(0, 6438275382588823897) { 0 = 0 1 = 6438275382588823897 } // lldb-command:v univariant -// lldb-check:(tuple_style_enum::Univariant) univariant = { value = { 0 = -1 } } +// lldb-check:(tuple_style_enum::Univariant) univariant = TheOnlyCase(-1) { 0 = -1 } #![allow(unused_variables)] diff --git a/tests/debuginfo/union-smoke.rs b/tests/debuginfo/union-smoke.rs index beaf4c8fa8fe3..be6a9d578646f 100644 --- a/tests/debuginfo/union-smoke.rs +++ b/tests/debuginfo/union-smoke.rs @@ -14,10 +14,10 @@ // lldb-command:run // lldb-command:v u -// lldb-check:[...] { a = { 0 = '\x02' 1 = '\x02' } b = 514 } +// lldb-check:[...] { a = ('\x02', '\x02') { 0 = '\x02' 1 = '\x02' } b = 514 } // lldb-command:print union_smoke::SU -// lldb-check:[...] { a = { 0 = '\x01' 1 = '\x01' } b = 257 } +// lldb-check:[...] { a = ('\x01', '\x01') { 0 = '\x01' 1 = '\x01' } b = 257 } #![allow(unused)] diff --git a/tests/debuginfo/unique-enum.rs b/tests/debuginfo/unique-enum.rs index a9b928456e936..6a742f9378dfc 100644 --- a/tests/debuginfo/unique-enum.rs +++ b/tests/debuginfo/unique-enum.rs @@ -23,13 +23,13 @@ // lldb-command:run // lldb-command:v *the_a -// lldb-check:(unique_enum::ABC) *the_a = { value = { x = 0 y = 8970181431921507452 } $discr$ = 0 } +// lldb-check:(unique_enum::ABC) *the_a = TheA{x:0, y:8970181431921507452} { x = 0 y = 8970181431921507452 } // lldb-command:v *the_b -// lldb-check:(unique_enum::ABC) *the_b = { value = { 0 = 0 1 = 286331153 2 = 286331153 } $discr$ = 1 } +// lldb-check:(unique_enum::ABC) *the_b = TheB(0, 286331153, 286331153) { 0 = 0 1 = 286331153 2 = 286331153 } // lldb-command:v *univariant -// lldb-check:(unique_enum::Univariant) *univariant = { value = { 0 = 123234 } } +// lldb-check:(unique_enum::Univariant) *univariant = TheOnlyCase(123234) { 0 = 123234 } #![allow(unused_variables)] diff --git a/tests/debuginfo/vec-slices.rs b/tests/debuginfo/vec-slices.rs index 7ed6b0cb66401..fdfc7019add69 100644 --- a/tests/debuginfo/vec-slices.rs +++ b/tests/debuginfo/vec-slices.rs @@ -67,7 +67,7 @@ // lldb-check:[...] size=2 { [0] = 3 [1] = 4 } // lldb-command:v padded_tuple -// lldb-check:[...] size=2 { [0] = { 0 = 6 1 = 7 } [1] = { 0 = 8 1 = 9 } } +// lldb-check:[...] size=2 { [0] = (6, 7) { 0 = 6 1 = 7 } [1] = (8, 9) { 0 = 8 1 = 9 } } // lldb-command:v padded_struct // lldb-check:[...] size=2 { [0] = { x = 10 y = 11 z = 12 } [1] = { x = 13 y = 14 z = 15 } } From 63f4b36406016ad62cfe23580ed90ef1eabd4bfb Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 11 Nov 2025 22:36:50 -0500 Subject: [PATCH 3/8] provide an error if an autodiff user does not set in their Cargo.toml --- compiler/rustc_codegen_llvm/messages.ftl | 1 + compiler/rustc_codegen_llvm/src/errors.rs | 4 ++++ compiler/rustc_codegen_llvm/src/intrinsic.rs | 5 ++++- compiler/rustc_session/src/session.rs | 8 -------- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index ce9a51b539d83..c9d28160d66f7 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -1,4 +1,5 @@ codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable +codegen_llvm_autodiff_without_lto = using the autodiff feature requires setting `lto="fat"` in your Cargo.toml codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 627b0c9ff3b33..629afee8a6677 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -32,6 +32,10 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { } } +#[derive(Diagnostic)] +#[diag(codegen_llvm_autodiff_without_lto)] +pub(crate) struct AutoDiffWithoutLto; + #[derive(Diagnostic)] #[diag(codegen_llvm_autodiff_without_enable)] pub(crate) struct AutoDiffWithoutEnable; diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 0626cb3f2f16b..bbe3b5b59f5b4 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -24,7 +24,7 @@ use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call}; use crate::context::CodegenCx; -use crate::errors::AutoDiffWithoutEnable; +use crate::errors::{AutoDiffWithoutEnable, AutoDiffWithoutLto}; use crate::llvm::{self, Metadata, Type, Value}; use crate::type_of::LayoutLlvmExt; use crate::va_arg::emit_va_arg; @@ -1145,6 +1145,9 @@ fn codegen_autodiff<'ll, 'tcx>( if !tcx.sess.opts.unstable_opts.autodiff.contains(&rustc_session::config::AutoDiff::Enable) { let _ = tcx.dcx().emit_almost_fatal(AutoDiffWithoutEnable); } + if tcx.sess.lto() != rustc_session::config::Lto::Fat { + let _ = tcx.dcx().emit_almost_fatal(AutoDiffWithoutLto); + } let fn_args = instance.args; let callee_ty = instance.ty(tcx, bx.typing_env()); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9fb3c35d5ef73..fe13bb821c9b7 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -594,14 +594,6 @@ impl Session { /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { - // Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types. - // fat-lto is the easiest solution to this requirement, but quite expensive. - // FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto. - // Don't apply fat-lto to proc-macro crates as they cannot use fat-lto without -Zdylib-lto - if self.opts.autodiff_enabled() && !self.opts.crate_types.contains(&CrateType::ProcMacro) { - return config::Lto::Fat; - } - // If our target has codegen requirements ignore the command line if self.target.requires_lto { return config::Lto::Fat; From c8bae8c06ed1dd08a55a6cdd1b80d51c59a21359 Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Tue, 11 Nov 2025 22:46:17 -0500 Subject: [PATCH 4/8] add a test to verify a good error for enabling autodiff without lto --- tests/ui/autodiff/no_lto_flag.no_lto.stderr | 4 +++ tests/ui/autodiff/no_lto_flag.rs | 31 +++++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/autodiff/no_lto_flag.no_lto.stderr create mode 100644 tests/ui/autodiff/no_lto_flag.rs diff --git a/tests/ui/autodiff/no_lto_flag.no_lto.stderr b/tests/ui/autodiff/no_lto_flag.no_lto.stderr new file mode 100644 index 0000000000000..abc6af5b62965 --- /dev/null +++ b/tests/ui/autodiff/no_lto_flag.no_lto.stderr @@ -0,0 +1,4 @@ +error: using the autodiff feature requires setting `lto="fat"` in your Cargo.toml + +error: aborting due to 1 previous error + diff --git a/tests/ui/autodiff/no_lto_flag.rs b/tests/ui/autodiff/no_lto_flag.rs new file mode 100644 index 0000000000000..6194b1effc34f --- /dev/null +++ b/tests/ui/autodiff/no_lto_flag.rs @@ -0,0 +1,31 @@ +//@ needs-enzyme +//@ no-prefer-dynamic +//@ revisions: with_lto no_lto +//@[with_lto] compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@[no_lto] compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=thin + +#![feature(autodiff)] +//@[no_lto] build-fail +//@[with_lto] build-pass + +// Autodiff requires users to enable lto=fat (for now). +// In the past, autodiff did not run if users forget to enable fat-lto, which caused functions to +// returning zero-derivatives. That's obviously wrong and confusing to users. We now added a check +// which will abort compilation instead. + +use std::autodiff::autodiff_reverse; +//[no_lto]~? ERROR using the autodiff feature requires setting `lto="fat"` in your Cargo.toml + +#[autodiff_reverse(d_square, Duplicated, Active)] +fn square(x: &f64) -> f64 { + *x * *x +} + +fn main() { + let xf64: f64 = std::hint::black_box(3.0); + + let mut df_dxf64: f64 = std::hint::black_box(0.0); + + let _output_f64 = d_square(&xf64, &mut df_dxf64, 1.0); + assert_eq!(6.0, df_dxf64); +} From 64f0579973c890824197d760e1cd1bcf25261792 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 15 Nov 2025 00:41:45 -0500 Subject: [PATCH 5/8] Run wasm32-wasip1 codegen tests in PR CI --- src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index c005eceb6ef4a..c9c3e3d2a3306 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -21,6 +21,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ mingw-w64 \ && rm -rf /var/lib/apt/lists/* +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz | \ + tar -xz +ENV WASI_SDK_PATH=/wasi-sdk-27.0-x86_64-linux + ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3" COPY scripts/sccache.sh /scripts/ @@ -30,6 +34,10 @@ ENV SCRIPT \ python3 ../x.py check && \ python3 ../x.py clippy ci --stage 2 && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ + # Elsewhere, we run all tests for the host. A number of codegen tests are sensitive to the target pointer + # width, for example because they mention a usize. wasm32-wasip1 in test-various, so using it here can't make + # PR CI more strict than full CI. + python3 ../x.py test --stage 1 tests/codegen-llvm --target wasm32-wasip1 && \ python3 ../x.py test --stage 1 src/tools/compiletest && \ python3 ../x.py doc bootstrap --stage 1 && \ # Build both public and internal documentation. From c287f6e4650dc209aff02d47927a0d0c8c6cfdc9 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Sun, 16 Nov 2025 20:28:56 +0000 Subject: [PATCH 6/8] compiletest: Avoid race condition in file deletion --- src/tools/compiletest/src/runtest.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8527f7e8128f4..ddcda91c13d01 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2766,12 +2766,11 @@ impl<'test> TestCx<'test> { .map_err(|err| format!("failed to load expected output from `{}`: {}", path, err)) } + /// Attempts to delete a file, succeeding if the file does not exist. fn delete_file(&self, file: &Utf8Path) { - if !file.exists() { - // Deleting a nonexistent file would error. - return; - } - if let Err(e) = fs::remove_file(file.as_std_path()) { + if let Err(e) = fs::remove_file(file.as_std_path()) + && e.kind() != io::ErrorKind::NotFound + { self.fatal(&format!("failed to delete `{}`: {}", file, e,)); } } From 7194c921cad605e65a044d524a30415555e2e809 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 4 Nov 2025 23:16:15 +0100 Subject: [PATCH 7/8] add larger test for `proc_macro` `FromStr` implementations --- .../auxiliary/nonfatal-parsing-body.rs | 143 +++++++++++++ .../proc-macro/auxiliary/nonfatal-parsing.rs | 11 + tests/ui/proc-macro/nonfatal-parsing.rs | 19 ++ tests/ui/proc-macro/nonfatal-parsing.stderr | 197 ++++++++++++++++++ tests/ui/proc-macro/nonfatal-parsing.stdout | 54 +++++ 5 files changed, 424 insertions(+) create mode 100644 tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs create mode 100644 tests/ui/proc-macro/auxiliary/nonfatal-parsing.rs create mode 100644 tests/ui/proc-macro/nonfatal-parsing.rs create mode 100644 tests/ui/proc-macro/nonfatal-parsing.stderr create mode 100644 tests/ui/proc-macro/nonfatal-parsing.stdout diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs new file mode 100644 index 0000000000000..854d27d7ed323 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs @@ -0,0 +1,143 @@ +use std::fmt::Debug; +use std::panic::catch_unwind; +use std::str::FromStr; + +use proc_macro::*; + +use self::Mode::*; + +// FIXME: all cases should become `NormalOk` or `NormalErr` +#[derive(PartialEq, Clone, Copy)] +enum Mode { + NormalOk, + NormalErr, + OtherError, + OtherWithPanic, +} + +fn parse(s: &str, mode: Mode) +where + T: FromStr + Debug, +{ + match mode { + NormalOk => { + let t = T::from_str(s); + println!("{:?}", t); + assert!(t.is_ok()); + } + NormalErr => { + let t = T::from_str(s); + println!("{:?}", t); + assert!(t.is_err()); + } + OtherError => { + println!("{:?}", T::from_str(s)); + } + OtherWithPanic => { + if catch_unwind(|| println!("{:?}", T::from_str(s))).is_ok() { + eprintln!("{s} did not panic"); + } + } + } +} + +fn stream(s: &str, mode: Mode) { + parse::(s, mode); +} + +fn lit(s: &str, mode: Mode) { + parse::(s, mode); + if mode == NormalOk { + let Ok(lit) = Literal::from_str(s) else { + panic!("literal was not ok"); + }; + let Ok(stream) = TokenStream::from_str(s) else { + panic!("tokenstream was not ok, but literal was"); + }; + let Some(tree) = stream.into_iter().next() else { + panic!("tokenstream should have a tokentree"); + }; + if let TokenTree::Literal(tokenstream_lit) = tree { + assert_eq!(lit.to_string(), tokenstream_lit.to_string()); + } + } +} + +pub fn run() { + // returns Ok(valid instance) + lit("123", NormalOk); + lit("\"ab\"", NormalOk); + lit("\'b\'", NormalOk); + lit("'b'", NormalOk); + lit("b\"b\"", NormalOk); + lit("c\"b\"", NormalOk); + lit("cr\"b\"", NormalOk); + lit("b'b'", NormalOk); + lit("256u8", NormalOk); + lit("-256u8", NormalOk); + stream("-256u8", NormalOk); + lit("0b11111000000001111i16", NormalOk); + lit("0xf32", NormalOk); + lit("0b0f32", NormalOk); + lit("2E4", NormalOk); + lit("2.2E-4f64", NormalOk); + lit("18u8E", NormalOk); + lit("18.0u8E", NormalOk); + lit("cr#\"// /* // \n */\"#", NormalOk); + lit("'\\''", NormalOk); + lit("'\\\''", NormalOk); + lit(&format!("r{0}\"a\"{0}", "#".repeat(255)), NormalOk); + stream("fn main() { println!(\"Hello, world!\") }", NormalOk); + stream("18.u8E", NormalOk); + stream("18.0f32", NormalOk); + stream("18.0f34", NormalOk); + stream("18.bu8", NormalOk); + stream("3//\n4", NormalOk); + stream( + "\'c\'/*\n + */", + NormalOk, + ); + stream("/*a*/ //", NormalOk); + + println!("### ERRORS"); + + // returns Err(LexError) + lit("\'c\'/**/", NormalErr); + lit(" 0", NormalErr); + lit("0 ", NormalErr); + lit("0//", NormalErr); + lit("3//\n4", NormalErr); + lit("18.u8E", NormalErr); + lit("/*a*/ //", NormalErr); + // FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet. + + // emits diagnostics and returns LexError + lit("r'r'", OtherError); + lit("c'r'", OtherError); + + // emits diagnostic and returns a seemingly valid tokenstream + stream("r'r'", OtherError); + stream("c'r'", OtherError); + + for parse in [stream as fn(&str, Mode), lit] { + // emits diagnostic(s), then panics + parse("1 ) 2", OtherWithPanic); + parse("( x [ ) ]", OtherWithPanic); + parse("r#", OtherWithPanic); + + // emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. }) + parse("0b2", OtherError); + parse("0bf32", OtherError); + parse("0b0.0f32", OtherError); + parse("'\''", OtherError); + parse( + "' +'", OtherError, + ); + parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), OtherWithPanic); + + // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar + parse("/*a*/ 0b2 //", OtherError); + } +} diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing.rs new file mode 100644 index 0000000000000..75db8ee3c5fc8 --- /dev/null +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing.rs @@ -0,0 +1,11 @@ +extern crate proc_macro; +use proc_macro::*; + +#[path = "nonfatal-parsing-body.rs"] +mod body; + +#[proc_macro] +pub fn run(_: TokenStream) -> TokenStream { + body::run(); + TokenStream::new() +} diff --git a/tests/ui/proc-macro/nonfatal-parsing.rs b/tests/ui/proc-macro/nonfatal-parsing.rs new file mode 100644 index 0000000000000..853cbfd075e7b --- /dev/null +++ b/tests/ui/proc-macro/nonfatal-parsing.rs @@ -0,0 +1,19 @@ +//@ proc-macro: nonfatal-parsing.rs +//@ needs-unwind +//@ edition: 2024 +//@ dont-require-annotations: ERROR +//@ ignore-backends: gcc +// FIXME: should be a run-pass test once invalidly parsed tokens no longer result in diagnostics + +extern crate proc_macro; +extern crate nonfatal_parsing; + +#[path = "auxiliary/nonfatal-parsing-body.rs"] +mod body; + +fn main() { + nonfatal_parsing::run!(); + // FIXME: enable this once the standalone backend exists + // https://github.com/rust-lang/rust/issues/130856 + // body::run(); +} diff --git a/tests/ui/proc-macro/nonfatal-parsing.stderr b/tests/ui/proc-macro/nonfatal-parsing.stderr new file mode 100644 index 0000000000000..00c5c6ecfa8ea --- /dev/null +++ b/tests/ui/proc-macro/nonfatal-parsing.stderr @@ -0,0 +1,197 @@ +error: prefix `r` is unknown + --> :1:1 + | +LL | r'r' + | ^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | r 'r' + | + + +error: prefix `c` is unknown + --> :1:1 + | +LL | c'r' + | ^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: consider inserting whitespace here + | +LL | c 'r' + | + + +error: unexpected closing delimiter: `)` + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ unexpected closing delimiter + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected closing delimiter: `]` + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | -^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the nearest open delimiter + | missing open `(` for this delimiter + | unexpected closing delimiter + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0768]: no valid digits found for number + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: binary float literal is not supported + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: character constant must be escaped: `'` + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape the character + | +LL - nonfatal_parsing::run!(); +LL + nonfatal_parsing::run!(\'; + | + +error: character constant must be escaped: `\n` + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape the character + | +LL - nonfatal_parsing::run!(); +LL + nonfatal_parsing::run!(\n; + | + +error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unexpected closing delimiter: `)` + --> :1:3 + | +LL | 1 ) 2 + | ^ unexpected closing delimiter + +error: unexpected closing delimiter: `]` + --> :1:10 + | +LL | ( x [ ) ] + | - - ^ unexpected closing delimiter + | | | + | | missing open `(` for this delimiter + | the nearest open delimiter + +error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} + --> :1:1 + | +LL | r# + | ^^ + +error: invalid digit for a base 2 literal + --> :1:3 + | +LL | 0b2 + | ^ + +error[E0768]: no valid digits found for number + --> :1:1 + | +LL | 0bf32 + | ^^ + +error: binary float literal is not supported + --> :1:1 + | +LL | 0b0.0f32 + | ^^^^^ + +error: character constant must be escaped: `'` + --> :1:2 + | +LL | ''' + | ^ + | +help: escape the character + | +LL | '\'' + | + + +error: character constant must be escaped: `\n` + --> :1:2 + | +LL | ' + | __^ +LL | | ' + | |_^ + | +help: escape the character + | +LL | '\n' + | ++ + +error: too many `#` symbols: raw strings may be delimited by up to 255 `#` symbols, but found 256 + --> :1:1 + | +LL | r#######################################...################################################## + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: invalid digit for a base 2 literal + --> :1:9 + | +LL | /*a*/ 0b2 // + | ^ + +error: aborting due to 22 previous errors + +For more information about this error, try `rustc --explain E0768`. diff --git a/tests/ui/proc-macro/nonfatal-parsing.stdout b/tests/ui/proc-macro/nonfatal-parsing.stdout new file mode 100644 index 0000000000000..08e0e6b1f8439 --- /dev/null +++ b/tests/ui/proc-macro/nonfatal-parsing.stdout @@ -0,0 +1,54 @@ +Ok(Literal { kind: Integer, symbol: "123", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Str, symbol: "ab", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Char, symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Char, symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: ByteStr, symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: CStr, symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: CStrRaw(0), symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Byte, symbol: "b", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: #44 bytes(361..385) }) +Ok(Literal { kind: Integer, symbol: "-256", suffix: Some("u8"), span: #44 bytes(361..385) }) +Ok(TokenStream [Punct { ch: '-', spacing: Alone, span: #44 bytes(361..385) }, Literal { kind: Integer, symbol: "256", suffix: Some("u8"), span: #44 bytes(361..385) }]) +Ok(Literal { kind: Integer, symbol: "0b11111000000001111", suffix: Some("i16"), span: #44 bytes(361..385) }) +Ok(Literal { kind: Integer, symbol: "0xf32", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Integer, symbol: "0b0", suffix: Some("f32"), span: #44 bytes(361..385) }) +Ok(Literal { kind: Float, symbol: "2E4", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Float, symbol: "2.2E-4", suffix: Some("f64"), span: #44 bytes(361..385) }) +Ok(Literal { kind: Integer, symbol: "18", suffix: Some("u8E"), span: #44 bytes(361..385) }) +Ok(Literal { kind: Float, symbol: "18.0", suffix: Some("u8E"), span: #44 bytes(361..385) }) +Ok(Literal { kind: CStrRaw(1), symbol: "// /* // \n */", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: Char, symbol: "\'", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: StrRaw(255), symbol: "a", suffix: None, span: #44 bytes(361..385) }) +Ok(TokenStream [Ident { ident: "fn", span: #44 bytes(361..385) }, Ident { ident: "main", span: #44 bytes(361..385) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: #44 bytes(361..385) }, Group { delimiter: Brace, stream: TokenStream [Ident { ident: "println", span: #44 bytes(361..385) }, Punct { ch: '!', spacing: Alone, span: #44 bytes(361..385) }, Group { delimiter: Parenthesis, stream: TokenStream [Literal { kind: Str, symbol: "Hello, world!", suffix: None, span: #44 bytes(361..385) }], span: #44 bytes(361..385) }], span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: #44 bytes(361..385) }, Punct { ch: '.', spacing: Alone, span: #44 bytes(361..385) }, Ident { ident: "u8E", span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f32"), span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Float, symbol: "18.0", suffix: Some("f34"), span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "18", suffix: None, span: #44 bytes(361..385) }, Punct { ch: '.', spacing: Alone, span: #44 bytes(361..385) }, Ident { ident: "bu8", span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Integer, symbol: "3", suffix: None, span: #44 bytes(361..385) }, Literal { kind: Integer, symbol: "4", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: Char, symbol: "c", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream []) +### ERRORS +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Err(LexError) +Ok(TokenStream [Ident { ident: "r", span: #44 bytes(361..385) }, Literal { kind: Char, symbol: "r", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Ident { ident: "c", span: #44 bytes(361..385) }, Literal { kind: Char, symbol: "r", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: #44 bytes(361..385) }]) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }]) +Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: #44 bytes(361..385) }) +Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: #44 bytes(361..385) }) +Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: #44 bytes(361..385) }) +Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: #44 bytes(361..385) }) +Err(LexError) From 70a0af3f4e4a3e0b14f6c3acd774ba1210aaad61 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 16 Nov 2025 16:11:15 -0800 Subject: [PATCH 8/8] Remove jsha from notifications for rustdoc HTML --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 0bf310e8fd7ef..f901dc7efc1e6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1070,7 +1070,6 @@ source file via `./x run src/tools/unicode-table-generator` instead of editing \ message = "Some changes occurred in HTML/CSS/JS." cc = [ "@GuillaumeGomez", - "@jsha", "@lolbinarycat", ]