summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/AST/Expr.h10
-rw-r--r--clang/include/clang/Basic/DiagnosticSemaKinds.td2
-rw-r--r--clang/lib/AST/Expr.cpp59
-rw-r--r--clang/lib/Sema/SemaStmt.cpp2
-rw-r--r--clang/test/Sema/statements.c10
5 files changed, 79 insertions, 4 deletions
diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h
index 68ca2680e91..88e87812d19 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -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.
- Expr* IgnoreParens();
+ 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 Expr* IgnoreParens() const {
+ const Expr *IgnoreParens() const {
return const_cast<Expr*>(this)->IgnoreParens();
}
const Expr *IgnoreParenCasts() const {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 85ca9e9fbb4..69c12398af7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -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">>;
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 72359c245a5..7c715bd3c3a 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -27,6 +27,65 @@
#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.
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 74c64d38818..cb553932596 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -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).
diff --git a/clang/test/Sema/statements.c b/clang/test/Sema/statements.c
index 52b9c7543d4..e3c41f3e1aa 100644
--- a/clang/test/Sema/statements.c
+++ b/clang/test/Sema/statements.c
@@ -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
OpenPOWER on IntegriCloud