[clang] [clang][Tooling] Recognize standard user-defined literal operators (PR #205719)
Achal Jhawar via cfe-commits
cfe-commits at lists.llvm.org
Mon Jul 6 02:39:15 PDT 2026
https://github.com/achaljhawar updated https://github.com/llvm/llvm-project/pull/205719
>From e2c41b02955dbc34871ae34f71a72e8f2a6e282b Mon Sep 17 00:00:00 2001
From: achaljhawar <achaljhawar03 at gmail.com>
Date: Thu, 25 Jun 2026 12:44:18 +1000
Subject: [PATCH] [clang][Tooling] Recognize standard user-defined literal
operators
---
.../Inclusions/Stdlib/StandardLibrary.cpp | 10 ++++-
.../Inclusions/Stdlib/StdSpecialSymbolMap.inc | 14 ++++++
.../unittests/Tooling/StandardLibraryTest.cpp | 44 +++++++++++++++++++
3 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
index 807a8d8a34ad7..e4de3b3850507 100644
--- a/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
+++ b/clang/lib/Tooling/Inclusions/Stdlib/StandardLibrary.cpp
@@ -298,6 +298,7 @@ std::optional<Symbol> Recognizer::operator()(const Decl *D) {
if (!Symbols)
return std::nullopt;
+ std::string NameStorage;
llvm::StringRef Name = [&]() -> llvm::StringRef {
for (const auto *SymDC : llvm::reverse(IntermediateDecl)) {
DeclarationName N = cast<NamedDecl>(SymDC)->getDeclName();
@@ -306,9 +307,16 @@ std::optional<Symbol> Recognizer::operator()(const Decl *D) {
if (!N.isEmpty())
return ""; // e.g. operator<: give up
}
- if (const auto *ND = llvm::dyn_cast<NamedDecl>(D))
+ if (const auto *ND = llvm::dyn_cast<NamedDecl>(D)) {
if (const auto *II = ND->getIdentifier())
return II->getName();
+ DeclarationName DN = ND->getDeclName();
+ if (DN.getNameKind() == DeclarationName::CXXLiteralOperatorName) {
+ NameStorage =
+ "operator\"\"" + DN.getCXXLiteralIdentifier()->getName().str();
+ return NameStorage;
+ }
+ }
return "";
}();
if (Name.empty())
diff --git a/clang/lib/Tooling/Inclusions/Stdlib/StdSpecialSymbolMap.inc b/clang/lib/Tooling/Inclusions/Stdlib/StdSpecialSymbolMap.inc
index c5b022e87ec33..d84f230232d8a 100644
--- a/clang/lib/Tooling/Inclusions/Stdlib/StdSpecialSymbolMap.inc
+++ b/clang/lib/Tooling/Inclusions/Stdlib/StdSpecialSymbolMap.inc
@@ -851,3 +851,17 @@ SYMBOL(UINT64_C, None, <cstdint>)
SYMBOL(UINT64_C, None, <stdint.h>)
SYMBOL(UINTMAX_C, None, <cstdint>)
SYMBOL(UINTMAX_C, None, <stdint.h>)
+
+SYMBOL(operator""h, std::, <chrono>)
+SYMBOL(operator""min, std::, <chrono>)
+SYMBOL(operator""s, std::, <chrono>)
+SYMBOL(operator""s, std::, <string>)
+SYMBOL(operator""ms, std::, <chrono>)
+SYMBOL(operator""us, std::, <chrono>)
+SYMBOL(operator""ns, std::, <chrono>)
+SYMBOL(operator""d, std::, <chrono>)
+SYMBOL(operator""y, std::, <chrono>)
+SYMBOL(operator""sv, std::, <string_view>)
+SYMBOL(operator""i, std::, <complex>)
+SYMBOL(operator""if, std::, <complex>)
+SYMBOL(operator""il, std::, <complex>)
diff --git a/clang/unittests/Tooling/StandardLibraryTest.cpp b/clang/unittests/Tooling/StandardLibraryTest.cpp
index e4c109f3d580d..78fb7d10fe24e 100644
--- a/clang/unittests/Tooling/StandardLibraryTest.cpp
+++ b/clang/unittests/Tooling/StandardLibraryTest.cpp
@@ -10,6 +10,7 @@
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclarationName.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/Testing/TestAST.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h"
@@ -174,6 +175,49 @@ TEST(StdlibTest, Recognizer) {
EXPECT_EQ(Recognizer(Sec), std::nullopt);
}
+TEST(StdlibTest, LiteralOperatorMappings) {
+ auto Chrono = stdlib::Header::named("<chrono>");
+ auto Str = stdlib::Header::named("<string>");
+ ASSERT_TRUE(Chrono);
+ ASSERT_TRUE(Str);
+
+ auto Ms = stdlib::Symbol::named("std::", "operator\"\"ms");
+ ASSERT_TRUE(Ms);
+ EXPECT_EQ(Ms->qualifiedName(), "std::operator\"\"ms");
+ EXPECT_EQ(Ms->header(), *Chrono);
+
+ auto S = stdlib::Symbol::named("std::", "operator\"\"s");
+ ASSERT_TRUE(S);
+ EXPECT_THAT(S->headers(), ElementsAre(*Chrono, *Str));
+
+ EXPECT_EQ(stdlib::Symbol::named("std::", "operator\"\"sv")->header(),
+ stdlib::Header::named("<string_view>"));
+}
+
+TEST(StdlibTest, RecognizerLiteralOperator) {
+ TestAST AST(R"cpp(
+ namespace std {
+ inline namespace literals {
+ inline namespace chrono_literals {
+ struct dur {};
+ dur operator"" ms(unsigned long long);
+ } // chrono_literals
+ } // literals
+ } // std
+
+ using namespace std::chrono_literals;
+ auto x = 5ms;
+ )cpp");
+
+ const VarDecl &X = cast<VarDecl>(lookup(AST, "x"));
+ const auto *UDL = cast<UserDefinedLiteral>(X.getInit()->IgnoreImplicit());
+ const FunctionDecl *Op = UDL->getDirectCallee();
+ ASSERT_TRUE(Op);
+
+ stdlib::Recognizer Recognizer;
+ EXPECT_EQ(Recognizer(Op), stdlib::Symbol::named("std::", "operator\"\"ms"));
+}
+
TEST(StdlibTest, RecognizerForC99) {
TestInputs Input("typedef char uint8_t;");
Input.Language = TestLanguage::Lang_C99;
More information about the cfe-commits
mailing list