[clang] Nameof operator (PR #104777)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 19 06:27:32 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Urvi Rav (ravurvi20)
<details>
<summary>Changes</summary>
This Pull Request introduces the `__nameof` operator, enabling users to obtain the symbolic name of any enumerator value. When the `__nameof` operator is used, the compiler will return the fully qualified name of the enum constant, enhancing readability and debugging capabilities.
*Problem Statement:*
C++ enum classes allow developers to define enumerator values, which can simplify code and improve readability. However, there are times when understanding or logging the actual names of these enumerator values is necessary, especially in complex codebases or during debugging. Developers need a straightforward way to retrieve the symbolic names of enum constants to better understand and manage their code.
*Proposed Solution:*
The proposed solution is to implement a compiler feature that allows the use of a `__nameof` operator to retrieve the symbolic names of enumerator values. This operator will construct and return the fully qualified name of the enum constant, providing clarity and aiding in debugging.
Implementation:
*1. EnumNameVisitor Class:*
Introduced the `EnumNameVisitor` class to handle the `__nameof` operator.
Implemented logic to construct the fully qualified name of the enum constant in the format `EnumName::EnumConstant`.
Added methods to traverse the expression tree, identify `DeclRefExpr` nodes, and fetch the name from the node.
*2. Parser Modifications:*
Updated the parser to recognize and parse the `__nameof` operator.
Integrated the handling of the `__nameof` operator into the semantic analysis phase to ensure accurate name retrieval.
*3. Test Case:*
```
#include<iostream>
using namespace std;
// Define an enumeration
enum Day {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
enum Month{
January,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
};
int main() {
cout<<"Symbolic Name for "<<Day::Tuesday<<" is: "<<__nameof(Day::Tuesday)<<endl;
cout<<"Symbolic Name for "<<Month::August<<" is: "<<__nameof(Month::August)<<endl;
}
```
*Output:*
Symbolic Name for 1 is: Day::Tuesday
Symbolic Name for 7 is: Month::August
*Benefits to the Community:*
Improved Code Comprehension: Developers can easily see the fully qualified names of enum constants, which enhances their understanding of the code.
Enhanced Debugging: During debugging, knowing the exact names of enum constants can help diagnose issues more efficiently.
Consistency: Ensures that enum names are retrieved consistently and accurately across the codebase.
---
Full diff: https://github.com/llvm/llvm-project/pull/104777.diff
6 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
- (modified) clang/include/clang/Basic/TokenKinds.def (+1)
- (modified) clang/include/clang/Sema/Sema.h (+1)
- (modified) clang/lib/Parse/ParseExpr.cpp (+6-1)
- (modified) clang/lib/Sema/SemaExpr.cpp (+41-2)
- (added) clang/test/Sema/nameof_operator.c (+39)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 8f85371df3b85b..4d1bd3a51d0231 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -103,6 +103,9 @@ def note_ice_conversion_here : Note<
def err_ice_ambiguous_conversion : Error<
"ambiguous conversion from type %0 to an integral or unscoped "
"enumeration type">;
+def err_invalid_enum_decl : Error<
+ "unsupported declaration type. "
+ "Only enum constants are supported">;
def err_ice_too_large : Error<
"integer constant expression evaluates to value %0 that cannot be "
"represented in a %1-bit %select{signed|unsigned}2 integer type">;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 9c4b17465e18a1..965784ef3d2dc3 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -311,6 +311,7 @@ KEYWORD(register , KEYALL)
KEYWORD(return , KEYALL)
KEYWORD(short , KEYALL)
KEYWORD(signed , KEYALL)
+UNARY_EXPR_OR_TYPE_TRAIT(__nameof, NameOf, KEYALL)
UNARY_EXPR_OR_TYPE_TRAIT(sizeof, SizeOf, KEYALL)
UNARY_EXPR_OR_TYPE_TRAIT(__datasizeof, DataSizeOf, KEYCXX)
KEYWORD(static , KEYALL)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 174b9dbc6d980c..2aa9488a5c56ea 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -244,6 +244,7 @@ class PossiblyUnreachableDiag;
class RISCVIntrinsicManager;
class SemaPPCallbacks;
class TemplateDeductionInfo;
+class EnumNameVector{ static std::vector<std::string> enumvector; };
} // namespace sema
namespace threadSafety {
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index eb7447fa038e47..30990f024b3273 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1491,6 +1491,8 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
// unary-expression: '__datasizeof' unary-expression
// unary-expression: '__datasizeof' '(' type-name ')'
case tok::kw___datasizeof:
+ //unary-experssion: The `nameof` operator takes an enum value as input and returns the fully qualified name of the enum and its value.
+ case tok::kw___nameof:
case tok::kw_vec_step: // unary-expression: OpenCL 'vec_step' expression
// unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
case tok::kw___builtin_omp_required_simd_align:
@@ -2521,7 +2523,7 @@ ExprResult Parser::ParseSYCLUniqueStableNameExpression() {
/// [C++11] 'alignof' '(' type-id ')'
/// \endverbatim
ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
- assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___alignof,
+ assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___datasizeof, tok::kw___nameof, tok::kw___alignof,
tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
tok::kw___builtin_omp_required_simd_align,
tok::kw___builtin_vectorelements) &&
@@ -2612,6 +2614,9 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
case tok::kw___datasizeof:
ExprKind = UETT_DataSizeOf;
break;
+ case tok::kw___nameof:
+ ExprKind = UETT_NameOf;
+ break;
case tok::kw___builtin_vectorelements:
ExprKind = UETT_VectorElements;
break;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index acaff304be193f..65044fe995ab91 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -67,7 +67,33 @@
using namespace clang;
using namespace sema;
-
+class EnumNameVisitor : public RecursiveASTVisitor<EnumNameVisitor> {
+private:
+ std::string EnumName;
+public:
+ void VisitDeclRefExpr(DeclRefExpr *Node) {
+ const Decl *D = Node->getDecl();
+ // Check if the declaration referenced is an enum constant
+ if (const auto *ECD = dyn_cast<EnumConstantDecl>(D)) {
+ if (const auto *ED = dyn_cast<EnumDecl>(ECD->getDeclContext())) {
+ // Construct the fully qualified name
+ EnumName = ED->getName().str() + "::" + ECD->getName().str();
+ }
+ }
+ }
+ bool VisitExpr(Expr *Ex) {
+ for (auto *Child : Ex->children()) {
+ if (auto *DRE = dyn_cast<DeclRefExpr>(Child)) {
+ VisitDeclRefExpr(DRE);
+ }
+ // Recursively visit the child nodes
+ RecursiveASTVisitor<EnumNameVisitor>::VisitStmt(Child);
+ }
+ return true; // Continue traversal
+ }
+ // Method to retrieve the last fetched string
+ StringRef getEnumName() const { return StringRef(EnumName); }
+};
/// Determine whether the use of this declaration is valid, without
/// emitting diagnostics.
bool Sema::CanUseDecl(NamedDecl *D, bool TreatUnavailableAsInvalid) {
@@ -4716,7 +4742,20 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc,
if (PE.isInvalid()) return ExprError();
E = PE.get();
}
-
+ if (ExprKind == UETT_NameOf) {
+ EnumNameVisitor obj;
+ obj.VisitExpr(E);
+ StringRef EnumName = obj.getEnumName();
+ QualType StrTy =
+ Context.getStringLiteralArrayType(Context.CharTy, EnumName.size());
+ StringLiteral *EnumStr =
+ StringLiteral::Create(Context, EnumName, StringLiteralKind::Ordinary,
+ /*Pascal*/ false, StrTy, OpLoc);
+ if (EnumStr->getString().empty()) {
+ Diag(E->getExprLoc(), diag::err_invalid_enum_decl);
+ }
+ return EnumStr;
+ }
// C99 6.5.3.4p4: the type (an unsigned integer type) is size_t.
return new (Context) UnaryExprOrTypeTraitExpr(
ExprKind, E, Context.getSizeType(), OpLoc, E->getSourceRange().getEnd());
diff --git a/clang/test/Sema/nameof_operator.c b/clang/test/Sema/nameof_operator.c
new file mode 100644
index 00000000000000..dd58f08209d53a
--- /dev/null
+++ b/clang/test/Sema/nameof_operator.c
@@ -0,0 +1,39 @@
+// RUN: %clangxx -std=c++11 -fsyntax-only -Xclang -verify %s
+
+int printf(const char *restrict, ...);
+
+// Define an enumeration
+enum Day {
+ Monday,
+ Tuesday,
+ Wednesday,
+ Thursday,
+ Friday,
+ Saturday,
+ Sunday
+};
+
+enum Month {
+ January,
+ February,
+ March,
+ April,
+ May,
+ June,
+ July,
+ August,
+ September,
+ October,
+ November,
+ December
+};
+
+void test_enum_nameof() {
+ // Check valid usage
+ printf("Symbolic Name for %d is: %s ", Day::Tuesday, __nameof(Day::Tuesday)); // expected-output {{Symbolic Name for 1 is: Day::Tuesday}}
+ printf("Symbolic Name for %d is: %s ", Month::August, __nameof(Month::August)); // expected-output {{Symbolic Name for 7 is: Month::August}}
+
+ // Check invalid usage
+ printf("Symbolic Name: %s ", __nameof(1)); // expected-error {{unsupported declaration type. Only enum constants are supported}}
+ printf("Symbolic Name: %s ", __nameof("")); // expected-error {{unsupported declaration type. Only enum constants are supported}}
+}
\ No newline at end of file
``````````
</details>
https://github.com/llvm/llvm-project/pull/104777
More information about the cfe-commits
mailing list