]> granicus.if.org Git - clang/commitdiff
make our existing "switch on bool" warning work for C. Since
authorChris Lattner <sabre@nondot.org>
Fri, 16 Apr 2010 23:34:13 +0000 (23:34 +0000)
committerChris Lattner <sabre@nondot.org>
Fri, 16 Apr 2010 23:34:13 +0000 (23:34 +0000)
the result of comparisons are 'int' in C, it doesn't work to
test just the result type of the expression.

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

include/clang/AST/Expr.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/Expr.cpp
lib/Sema/SemaStmt.cpp
test/Sema/statements.c

index 68ca2680e918467625d9c1f3fcf0298bd21deeaa..88e87812d197d7ab5e2433a2c15fe843e260f3ee 100644 (file)
@@ -199,6 +199,12 @@ public:
   /// \brief Returns whether this expression refers to a vector element.
   bool refersToVectorElement() const;
   
+  /// isKnownToHaveBooleanValue - Return true if this is an integer expression
+  /// that is known to return 0 or 1.  This happens for _Bool/bool expressions
+  /// but also int expressions which are produced by things like comparisons in
+  /// C.
+  bool isKnownToHaveBooleanValue() const;
+  
   /// isIntegerConstantExpr - Return true if this expression is a valid integer
   /// constant expression, and, if so, return its value in Result.  If not a
   /// valid i-c-e, return false and fill in Loc (if specified) with the location
@@ -304,7 +310,7 @@ public:
   ///  its subexpression.  If that subexpression is also a ParenExpr,
   ///  then this method recursively returns its subexpression, and so forth.
   ///  Otherwise, the method returns the current Expr.
-  ExprIgnoreParens();
+  Expr *IgnoreParens();
 
   /// IgnoreParenCasts - Ignore parentheses and casts.  Strip off any ParenExpr
   /// or CastExprs, returning their operand.
@@ -333,7 +339,7 @@ public:
   /// temporary object.
   const Expr *getTemporaryObject() const;
 
-  const ExprIgnoreParens() const {
+  const Expr *IgnoreParens() const {
     return const_cast<Expr*>(this)->IgnoreParens();
   }
   const Expr *IgnoreParenCasts() const {
index 85ca9e9fbb497ad60217342b844847b3000fb641..69c12398af706e0147723562ad6070850dca7704 100644 (file)
@@ -2782,7 +2782,7 @@ def err_default_not_in_switch : Error<
   "'default' statement not in switch statement">;
 def err_case_not_in_switch : Error<"'case' statement not in switch statement">;
 def warn_bool_switch_condition : Warning<
-  "switch condition is a bool">;
+  "switch condition has boolean value">;
 def warn_case_value_overflow : Warning<
   "overflow converting case value to switch condition type (%0 to %1)">,
   InGroup<DiagGroup<"switch">>;
index 72359c245a5c6f60263bbc50c5f51d2368d6df08..7c715bd3c3a55784421cdcfe246aaa107508d000 100644 (file)
 #include <algorithm>
 using namespace clang;
 
+/// isKnownToHaveBooleanValue - Return true if this is an integer expression
+/// that is known to return 0 or 1.  This happens for _Bool/bool expressions
+/// but also int expressions which are produced by things like comparisons in
+/// C.
+bool Expr::isKnownToHaveBooleanValue() const {
+  // If this value has _Bool type, it is obvious 0/1.
+  if (getType()->isBooleanType()) return true;
+  // If this is a non-scalar-integer type, we don't care enough to try. 
+  if (!getType()->isIntegralType()) return false;
+  
+  if (const ParenExpr *PE = dyn_cast<ParenExpr>(this))
+    return PE->getSubExpr()->isKnownToHaveBooleanValue();
+  
+  if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(this)) {
+    switch (UO->getOpcode()) {
+    case UnaryOperator::Plus:
+    case UnaryOperator::Extension:
+      return UO->getSubExpr()->isKnownToHaveBooleanValue();
+    default:
+      return false;
+    }
+  }
+  
+  if (const CastExpr *CE = dyn_cast<CastExpr>(this))
+    return CE->getSubExpr()->isKnownToHaveBooleanValue();
+  
+  if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(this)) {
+    switch (BO->getOpcode()) {
+    default: return false;
+    case BinaryOperator::LT:   // Relational operators.
+    case BinaryOperator::GT:
+    case BinaryOperator::LE:
+    case BinaryOperator::GE:
+    case BinaryOperator::EQ:   // Equality operators.
+    case BinaryOperator::NE:
+    case BinaryOperator::LAnd: // AND operator.
+    case BinaryOperator::LOr:  // Logical OR operator.
+      return true;
+        
+    case BinaryOperator::And:  // Bitwise AND operator.
+    case BinaryOperator::Xor:  // Bitwise XOR operator.
+    case BinaryOperator::Or:   // Bitwise OR operator.
+      // Handle things like (x==2)|(y==12).
+      return BO->getLHS()->isKnownToHaveBooleanValue() &&
+             BO->getRHS()->isKnownToHaveBooleanValue();
+        
+    case BinaryOperator::Comma:
+    case BinaryOperator::Assign:
+      return BO->getRHS()->isKnownToHaveBooleanValue();
+    }
+  }
+  
+  if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(this))
+    return CO->getTrueExpr()->isKnownToHaveBooleanValue() &&
+           CO->getFalseExpr()->isKnownToHaveBooleanValue();
+  
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // Primary Expressions.
 //===----------------------------------------------------------------------===//
index 74c64d3881843817a83c5d59e473b733d63989aa..cb553932596cbe13d9c2627f0fffd48c864fb9d8 100644 (file)
@@ -593,7 +593,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch,
       return StmtError();
     }
 
-    if (CondTypeBeforePromotion->isBooleanType()) {
+    if (CondExpr->isKnownToHaveBooleanValue()) {
       // switch(bool_expr) {...} is often a programmer error, e.g.
       //   switch(n && mask) { ... }  // Doh - should be "n & mask".
       // One can always use an if statement instead of switch(bool_expr).
index 52b9c7543d4c20164e7e4f6d031c778ae3d12e19..e3c41f3e1aaf331e6d60c414ec1c5984acb3f67a 100644 (file)
@@ -41,3 +41,13 @@ void test11(int bit) {
   {
   }
 }
+
+// rdar://3271964
+enum Numbers { kOne,  kTwo,  kThree,  kFour};
+int test12(enum Numbers num) {
+  switch (num == kOne) {// expected-warning {{switch condition has boolean value}}
+  default: 
+  case kThree:
+    break;
+  }
+}
\ No newline at end of file