]> granicus.if.org Git - clang/commitdiff
[Diagnostics] Warn if enumeration type mismatch in conditional expression
authorDavid Bolvansky <david.bolvansky@gmail.com>
Mon, 30 Sep 2019 19:55:50 +0000 (19:55 +0000)
committerDavid Bolvansky <david.bolvansky@gmail.com>
Mon, 30 Sep 2019 19:55:50 +0000 (19:55 +0000)
Summary:
- Useful warning
- GCC compatibility (GCC warns in C++ mode)

Reviewers: rsmith, aaron.ballman

Reviewed By: aaron.ballman

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D67919

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373252 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaChecking.cpp
test/Sema/warn-conditional-emum-types-mismatch.c [new file with mode: 0644]

index 28f4e06cd7278a0611afe26ebf9afeca8642b243..7c37932058ef7760de87145b7600edefcad850e5 100644 (file)
@@ -11308,6 +11308,32 @@ static const IntegerLiteral *getIntegerLiteral(Expr *E) {
   return IL;
 }
 
+static void CheckConditionalWithEnumTypes(Sema &S, SourceLocation Loc,
+                                          Expr *LHS, Expr *RHS) {
+  QualType LHSStrippedType = LHS->IgnoreParenImpCasts()->getType();
+  QualType RHSStrippedType = RHS->IgnoreParenImpCasts()->getType();
+
+  const auto *LHSEnumType = LHSStrippedType->getAs<EnumType>();
+  if (!LHSEnumType)
+    return;
+  const auto *RHSEnumType = RHSStrippedType->getAs<EnumType>();
+  if (!RHSEnumType)
+    return;
+
+  // Ignore anonymous enums.
+  if (!LHSEnumType->getDecl()->hasNameForLinkage())
+    return;
+  if (!RHSEnumType->getDecl()->hasNameForLinkage())
+    return;
+
+  if (S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType))
+    return;
+
+  S.Diag(Loc, diag::warn_conditional_mixed_enum_types)
+      << LHSStrippedType << RHSStrippedType << LHS->getSourceRange()
+      << RHS->getSourceRange();
+}
+
 static void DiagnoseIntInBoolContext(Sema &S, Expr *E) {
   E = E->IgnoreParenImpCasts();
   SourceLocation ExprLoc = E->getExprLoc();
@@ -11799,6 +11825,8 @@ static void CheckConditionalOperator(Sema &S, ConditionalOperator *E,
   bool Suspicious = false;
   CheckConditionalOperand(S, E->getTrueExpr(), T, CC, Suspicious);
   CheckConditionalOperand(S, E->getFalseExpr(), T, CC, Suspicious);
+  CheckConditionalWithEnumTypes(S, E->getBeginLoc(), E->getTrueExpr(),
+                                E->getFalseExpr());
 
   if (T->isBooleanType())
     DiagnoseIntInBoolContext(S, E);
diff --git a/test/Sema/warn-conditional-emum-types-mismatch.c b/test/Sema/warn-conditional-emum-types-mismatch.c
new file mode 100644 (file)
index 0000000..4325bb0
--- /dev/null
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -x c -fsyntax-only -verify -Wenum-compare %s
+// RUN: %clang_cc1 -x c -fsyntax-only -verify  %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify -Wenum-compare %s
+// RUN: %clang_cc1 -x c++ -fsyntax-only -verify %s
+
+enum ro { A = 0x10 };
+enum rw { B = 0xFF };
+enum { C = 0x1A};
+
+enum {
+  STATUS_SUCCESS,
+  STATUS_FAILURE,
+  MAX_BASE_STATUS_CODE
+};
+
+enum ExtendedStatusCodes {
+  STATUS_SOMETHING_INTERESTING = MAX_BASE_STATUS_CODE + 1000,
+};
+
+
+int get_flag(int cond) {
+  return cond ? A : B; 
+  #ifdef __cplusplus
+  // expected-warning@-2 {{enumeration type mismatch in conditional expression ('ro' and 'rw')}}
+  #else 
+  // expected-no-diagnostics
+  #endif
+}
+
+// In the following cases we purposefully differ from GCC and dont warn because
+// this code pattern is quite sensitive and we dont want to produce so many false positives.
+
+int get_flag_anon_enum(int cond) {
+  return cond ? A : C;
+}
+
+int foo(int c) {
+  return c ? STATUS_SOMETHING_INTERESTING : STATUS_SUCCESS;
+}