From d9d4cc879d7dadc866451611cefbe1f2146b2145 Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Sat, 4 Oct 2025 17:04:28 -0400 Subject: [PATCH 1/2] [ADT] Add `DefaultUnreachable("msg")` to StringSwitch Similar to TypeSwitch (#161970), allow for explicit unreachable default case with a custom error message on unhandled cases. StringSwitch already allowed for checking if any of the cases matched with the conversion operator, but `DefaultUnreachable` is more explicit and allows for a custom message. --- llvm/include/llvm/ADT/StringSwitch.h | 11 ++++++++--- llvm/unittests/ADT/StringSwitchTest.cpp | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/ADT/StringSwitch.h b/llvm/include/llvm/ADT/StringSwitch.h index 86e591c71c92e..18eadcd10fb9b 100644 --- a/llvm/include/llvm/ADT/StringSwitch.h +++ b/llvm/include/llvm/ADT/StringSwitch.h @@ -14,6 +14,7 @@ #define LLVM_ADT_STRINGSWITCH_H #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" #include #include #include @@ -180,11 +181,15 @@ class StringSwitch { return Value; } - [[nodiscard]] operator R() { - assert(Result && "Fell off the end of a string-switch"); - return std::move(*Result); + [[nodiscard]] R DefaultUnreachable( + const char *Message = "Fell off the end of a string-switch") { + if (Result) + return std::move(*Result); + llvm_unreachable(Message); } + [[nodiscard]] operator R() { return DefaultUnreachable(); } + private: // Returns true when `Str` matches the `S` argument, and stores the result. bool CaseImpl(T &Value, StringLiteral S) { diff --git a/llvm/unittests/ADT/StringSwitchTest.cpp b/llvm/unittests/ADT/StringSwitchTest.cpp index 2953f4b0a381b..bcb1521baa287 100644 --- a/llvm/unittests/ADT/StringSwitchTest.cpp +++ b/llvm/unittests/ADT/StringSwitchTest.cpp @@ -230,3 +230,19 @@ TEST(StringSwitchTest, CasesCopies) { "Foo", "Bar", "Baz", "Qux", Copyable{NumCopies}); EXPECT_EQ(NumCopies, 1u); } + +TEST(StringSwitchTest, DefaultUnreachable) { + auto Translate = [](StringRef S) { + return llvm::StringSwitch(S) + .Case("A", 0) + .Case("B", 1) + .DefaultUnreachable("Unhandled case"); + }; + + EXPECT_EQ(0, Translate("A")); + EXPECT_EQ(1, Translate("B")); + +#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG) + EXPECT_DEATH((void)Translate("C"), "Unhandled case"); +#endif +} From 7422e8a2d97faf57783af366a828e09de5ac580b Mon Sep 17 00:00:00 2001 From: Jakub Kuderski Date: Sat, 4 Oct 2025 17:11:27 -0400 Subject: [PATCH 2/2] Add comment --- llvm/include/llvm/ADT/StringSwitch.h | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/include/llvm/ADT/StringSwitch.h b/llvm/include/llvm/ADT/StringSwitch.h index 18eadcd10fb9b..0ce7c57a358f2 100644 --- a/llvm/include/llvm/ADT/StringSwitch.h +++ b/llvm/include/llvm/ADT/StringSwitch.h @@ -181,6 +181,7 @@ class StringSwitch { return Value; } + /// Declare default as unreachable, making sure that all cases were handled. [[nodiscard]] R DefaultUnreachable( const char *Message = "Fell off the end of a string-switch") { if (Result)