forked from OSchip/llvm-project
make our existing "switch on bool" warning work for C. Since
the result of comparisons are 'int' in C, it doesn't work to test just the result type of the expression. llvm-svn: 101576
This commit is contained in:
parent
12563b3495
commit
4ebae65d6a
|
@ -199,6 +199,12 @@ public:
|
||||||
/// \brief Returns whether this expression refers to a vector element.
|
/// \brief Returns whether this expression refers to a vector element.
|
||||||
bool refersToVectorElement() const;
|
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
|
/// isIntegerConstantExpr - Return true if this expression is a valid integer
|
||||||
/// constant expression, and, if so, return its value in Result. If not a
|
/// 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
|
/// 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,
|
/// its subexpression. If that subexpression is also a ParenExpr,
|
||||||
/// then this method recursively returns its subexpression, and so forth.
|
/// then this method recursively returns its subexpression, and so forth.
|
||||||
/// Otherwise, the method returns the current Expr.
|
/// Otherwise, the method returns the current Expr.
|
||||||
Expr* IgnoreParens();
|
Expr *IgnoreParens();
|
||||||
|
|
||||||
/// IgnoreParenCasts - Ignore parentheses and casts. Strip off any ParenExpr
|
/// IgnoreParenCasts - Ignore parentheses and casts. Strip off any ParenExpr
|
||||||
/// or CastExprs, returning their operand.
|
/// or CastExprs, returning their operand.
|
||||||
|
@ -333,7 +339,7 @@ public:
|
||||||
/// temporary object.
|
/// temporary object.
|
||||||
const Expr *getTemporaryObject() const;
|
const Expr *getTemporaryObject() const;
|
||||||
|
|
||||||
const Expr* IgnoreParens() const {
|
const Expr *IgnoreParens() const {
|
||||||
return const_cast<Expr*>(this)->IgnoreParens();
|
return const_cast<Expr*>(this)->IgnoreParens();
|
||||||
}
|
}
|
||||||
const Expr *IgnoreParenCasts() const {
|
const Expr *IgnoreParenCasts() const {
|
||||||
|
|
|
@ -2782,7 +2782,7 @@ def err_default_not_in_switch : Error<
|
||||||
"'default' statement not in switch statement">;
|
"'default' statement not in switch statement">;
|
||||||
def err_case_not_in_switch : Error<"'case' statement not in switch statement">;
|
def err_case_not_in_switch : Error<"'case' statement not in switch statement">;
|
||||||
def warn_bool_switch_condition : Warning<
|
def warn_bool_switch_condition : Warning<
|
||||||
"switch condition is a bool">;
|
"switch condition has boolean value">;
|
||||||
def warn_case_value_overflow : Warning<
|
def warn_case_value_overflow : Warning<
|
||||||
"overflow converting case value to switch condition type (%0 to %1)">,
|
"overflow converting case value to switch condition type (%0 to %1)">,
|
||||||
InGroup<DiagGroup<"switch">>;
|
InGroup<DiagGroup<"switch">>;
|
||||||
|
|
|
@ -27,6 +27,65 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
using namespace clang;
|
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.
|
// Primary Expressions.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -593,7 +593,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, StmtArg Switch,
|
||||||
return StmtError();
|
return StmtError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CondTypeBeforePromotion->isBooleanType()) {
|
if (CondExpr->isKnownToHaveBooleanValue()) {
|
||||||
// switch(bool_expr) {...} is often a programmer error, e.g.
|
// switch(bool_expr) {...} is often a programmer error, e.g.
|
||||||
// switch(n && mask) { ... } // Doh - should be "n & mask".
|
// switch(n && mask) { ... } // Doh - should be "n & mask".
|
||||||
// One can always use an if statement instead of switch(bool_expr).
|
// One can always use an if statement instead of switch(bool_expr).
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue