[clang] [Clang][Sema] Fix missing warning when comparing mismatched enums in … (PR #81418)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Feb 11 07:33:29 PST 2024
https://github.com/44-2-Kupa-Martin created https://github.com/llvm/llvm-project/pull/81418
…C mode
Factored logic from `CheckImplicitConversion` into new methods `Expr::getEnumConstantDecl` and `Expr::getEnumCoercedType` for use in `checkEnumArithmeticConversions`.
Fix #29217
>From a1748e5c7867a8a4dc4dbdfd5f19460733adc3f1 Mon Sep 17 00:00:00 2001
From: 44-2-Kupa-Martin <kupamartinclassroom at gmail.com>
Date: Sun, 11 Feb 2024 12:22:45 -0300
Subject: [PATCH] [Clang][Sema] Fix missing warning when comparing mismatched
enums in C mode
Factored logic from `CheckImplicitConversion` into new methods
`Expr::getEnumConstantDecl` and `Expr::getEnumCoercedType` for use in
`checkEnumArithmeticConversions`.
Fix #29217
---
clang/docs/ReleaseNotes.rst | 3 ++
clang/include/clang/AST/Expr.h | 13 ++++++
clang/lib/AST/Expr.cpp | 16 +++++++
clang/lib/Sema/SemaChecking.cpp | 11 +----
clang/lib/Sema/SemaExpr.cpp | 3 +-
clang/test/Sema/builtins-elementwise-math.c | 4 ++
.../Sema/warn-compare-enum-types-mismatch.c | 42 +++++++++++++++++++
...=> warn-conditional-enum-types-mismatch.c} | 2 +-
clang/test/Sema/warn-overlap.c | 4 +-
9 files changed, 85 insertions(+), 13 deletions(-)
create mode 100644 clang/test/Sema/warn-compare-enum-types-mismatch.c
rename clang/test/Sema/{warn-conditional-emum-types-mismatch.c => warn-conditional-enum-types-mismatch.c} (88%)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ece6013f672621..00ddf0b9656a31 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -161,6 +161,9 @@ Improvements to Clang's time-trace
Bug Fixes in This Version
-------------------------
+- Fixed missing warnings when comparing mismatched enumeration constants
+ in C (`#29217 <https://github.com/llvm/llvm-project/issues/29217>`).
+
- Clang now accepts elaborated-type-specifiers that explicitly specialize
a member class template for an implicit instantiation of a class template.
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 3fc481a62a78a9..bf0622bdeca30e 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -153,6 +153,12 @@ class Expr : public ValueStmt {
TR = t;
}
+ /// If this expression is an enumeration constant, return the
+ /// enumeration type under which said constant was declared.
+ /// Otherwise return the expression's type.
+ /// Note this effectively circumvents the weak typing of C's enum constants
+ QualType getEnumCoercedType(const ASTContext &Ctx) const;
+
ExprDependence getDependence() const {
return static_cast<ExprDependence>(ExprBits.Dependent);
}
@@ -471,6 +477,13 @@ class Expr : public ValueStmt {
/// bit-fields, but it will return null for a conditional bit-field.
FieldDecl *getSourceBitField();
+ /// If this expression refers to an enum constant, retrieve its declaration
+ EnumConstantDecl *getEnumConstantDecl();
+
+ const EnumConstantDecl *getEnumConstantDecl() const {
+ return const_cast<Expr *>(this)->getEnumConstantDecl();
+ }
+
const FieldDecl *getSourceBitField() const {
return const_cast<Expr*>(this)->getSourceBitField();
}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 8b10e289583260..78a0aba5abc3f1 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -263,6 +263,14 @@ namespace {
}
}
+QualType Expr::getEnumCoercedType(const ASTContext &Ctx) const {
+ bool NotEnumType = dyn_cast<EnumType>(this->getType()) == nullptr;
+ if (NotEnumType)
+ if (const EnumConstantDecl *ECD = this->getEnumConstantDecl())
+ return Ctx.getTypeDeclType(cast<EnumDecl>(ECD->getDeclContext()));
+ return this->getType();
+}
+
SourceLocation Expr::getExprLoc() const {
switch (getStmtClass()) {
case Stmt::NoStmtClass: llvm_unreachable("statement without class");
@@ -4097,6 +4105,14 @@ FieldDecl *Expr::getSourceBitField() {
return nullptr;
}
+EnumConstantDecl *Expr::getEnumConstantDecl() {
+ Expr *E = this->IgnoreParenImpCasts();
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
+ if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl()))
+ return ECD;
+ return nullptr;
+}
+
bool Expr::refersToVectorElement() const {
// FIXME: Why do we not just look at the ObjectKind here?
const Expr *E = this->IgnoreParens();
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 71e6e7230fc455..1a869e16c987f4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -16050,15 +16050,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
// Diagnose conversions between different enumeration types.
// In C, we pretend that the type of an EnumConstantDecl is its enumeration
// type, to give us better diagnostics.
- QualType SourceType = E->getType();
- if (!S.getLangOpts().CPlusPlus) {
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
- if (EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
- EnumDecl *Enum = cast<EnumDecl>(ECD->getDeclContext());
- SourceType = S.Context.getTypeDeclType(Enum);
- Source = S.Context.getCanonicalType(SourceType).getTypePtr();
- }
- }
+ QualType SourceType = E->getEnumCoercedType(S.Context);
+ Source = S.Context.getCanonicalType(SourceType).getTypePtr();
if (const EnumType *SourceEnum = Source->getAs<EnumType>())
if (const EnumType *TargetEnum = Target->getAs<EnumType>())
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4049ab3bf6cafb..2b6accfd4a4d50 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1497,7 +1497,8 @@ static void checkEnumArithmeticConversions(Sema &S, Expr *LHS, Expr *RHS,
//
// Warn on this in all language modes. Produce a deprecation warning in C++20.
// Eventually we will presumably reject these cases (in C++23 onwards?).
- QualType L = LHS->getType(), R = RHS->getType();
+ QualType L = LHS->getEnumCoercedType(S.Context),
+ R = RHS->getEnumCoercedType(S.Context);
bool LEnum = L->isUnscopedEnumerationType(),
REnum = R->isUnscopedEnumerationType();
bool IsCompAssign = ACK == Sema::ACK_CompAssign;
diff --git a/clang/test/Sema/builtins-elementwise-math.c b/clang/test/Sema/builtins-elementwise-math.c
index e7b36285fa7dcf..2e05337273ee41 100644
--- a/clang/test/Sema/builtins-elementwise-math.c
+++ b/clang/test/Sema/builtins-elementwise-math.c
@@ -76,6 +76,7 @@ void test_builtin_elementwise_add_sat(int i, short s, double d, float4 v, int3 i
enum f { three };
enum f x = __builtin_elementwise_add_sat(one, three);
+ // expected-warning at -1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
ext = __builtin_elementwise_add_sat(ext, ext);
@@ -134,6 +135,7 @@ void test_builtin_elementwise_sub_sat(int i, short s, double d, float4 v, int3 i
enum f { three };
enum f x = __builtin_elementwise_sub_sat(one, three);
+ // expected-warning at -1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
ext = __builtin_elementwise_sub_sat(ext, ext);
@@ -189,6 +191,7 @@ void test_builtin_elementwise_max(int i, short s, double d, float4 v, int3 iv, u
enum f { three };
enum f x = __builtin_elementwise_max(one, three);
+ // expected-warning at -1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
ext = __builtin_elementwise_max(ext, ext);
@@ -244,6 +247,7 @@ void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, u
enum f { three };
enum f x = __builtin_elementwise_min(one, three);
+ // expected-warning at -1 {{comparison of different enumeration types ('enum e' and 'enum f')}}
_BitInt(32) ext; // expected-warning {{'_BitInt' in C17 and earlier is a Clang extension}}
ext = __builtin_elementwise_min(ext, ext);
diff --git a/clang/test/Sema/warn-compare-enum-types-mismatch.c b/clang/test/Sema/warn-compare-enum-types-mismatch.c
new file mode 100644
index 00000000000000..1c50f35516353f
--- /dev/null
+++ b/clang/test/Sema/warn-compare-enum-types-mismatch.c
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare -Wno-unused-comparison %s
+
+typedef enum EnumA {
+ A
+} EnumA;
+
+enum EnumB {
+ B
+};
+
+enum {
+ C
+};
+
+void foo(void) {
+ enum EnumA a = A;
+ enum EnumB b = B;
+ A == B;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ a == (B);
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ a == b;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ A > B;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ A >= b;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ a > b;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ (A) <= ((B));
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ a < B;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+ a < b;
+ // expected-warning at -1 {{comparison of different enumeration types}}
+
+ // In the following cases we purposefully differ from GCC and dont warn
+ a == C;
+ A < C;
+ b >= C;
+}
\ No newline at end of file
diff --git a/clang/test/Sema/warn-conditional-emum-types-mismatch.c b/clang/test/Sema/warn-conditional-enum-types-mismatch.c
similarity index 88%
rename from clang/test/Sema/warn-conditional-emum-types-mismatch.c
rename to clang/test/Sema/warn-conditional-enum-types-mismatch.c
index c9e2eddc7764bd..f039245b6fabe5 100644
--- a/clang/test/Sema/warn-conditional-emum-types-mismatch.c
+++ b/clang/test/Sema/warn-conditional-enum-types-mismatch.c
@@ -21,7 +21,7 @@ int get_flag(int cond) {
#ifdef __cplusplus
// expected-warning at -2 {{conditional expression between different enumeration types ('ro' and 'rw')}}
#else
- // expected-no-diagnostics
+ // expected-warning at -4 {{conditional expression between different enumeration types ('enum ro' and 'enum rw')}}
#endif
}
diff --git a/clang/test/Sema/warn-overlap.c b/clang/test/Sema/warn-overlap.c
index 1eddfd1077fd92..2db07ebcd17b87 100644
--- a/clang/test/Sema/warn-overlap.c
+++ b/clang/test/Sema/warn-overlap.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare %s
-// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wtautological-overlap-compare -Wno-enum-compare %s
+// RUN: %clang_cc1 -fsyntax-only -verify -Wall -Wno-unused -Wno-loop-analysis -Wno-enum-compare %s
#define mydefine 2
More information about the cfe-commits
mailing list