ValueFlow: Fixed constant folding of sizeof(enum) and sizeof(enumerator) (#7564)

This commit is contained in:
Robert Reif 2016-07-08 20:39:34 +02:00 committed by PKEuS
parent a808549af0
commit eca805ba3b
5 changed files with 97 additions and 10 deletions

View File

@ -1260,7 +1260,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
const Token *rhs = enumerator.start->previous()->astOperand2(); const Token *rhs = enumerator.start->previous()->astOperand2();
// constant folding of expression: // constant folding of expression:
ValueFlow::valueFlowConstantFoldAST(rhs); ValueFlow::valueFlowConstantFoldAST(rhs, _settings);
// get constant folded value: // get constant folded value:
if (rhs && rhs->values.size() == 1U && rhs->values.front().isKnown()) { if (rhs && rhs->values.size() == 1U && rhs->values.front().isKnown()) {
@ -1361,7 +1361,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
const Token *rhs = dimension.start->previous()->astOperand2(); const Token *rhs = dimension.start->previous()->astOperand2();
// constant folding of expression: // constant folding of expression:
ValueFlow::valueFlowConstantFoldAST(rhs); ValueFlow::valueFlowConstantFoldAST(rhs, _settings);
// get constant folded value: // get constant folded value:
if (rhs && rhs->values.size() == 1U && rhs->values.front().isKnown()) { if (rhs && rhs->values.size() == 1U && rhs->values.front().isKnown()) {

View File

@ -40,6 +40,10 @@ public:
_settings = settings; _settings = settings;
} }
const Settings *getSettings() const {
return _settings;
}
/** @return the source file path. e.g. "file.cpp" */ /** @return the source file path. e.g. "file.cpp" */
const std::string& getSourceFilePath() const; const std::string& getSourceFilePath() const;

View File

@ -572,7 +572,7 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value)
// Handle various constants.. // Handle various constants..
static void valueFlowSetConstantValue(const Token *tok) static Token * valueFlowSetConstantValue(const Token *tok, const Settings *settings)
{ {
if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) { if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) {
ValueFlow::Value value(MathLib::toLongNumber(tok->str())); ValueFlow::Value value(MathLib::toLongNumber(tok->str()));
@ -582,14 +582,48 @@ static void valueFlowSetConstantValue(const Token *tok)
ValueFlow::Value value(tok->enumerator()->value); ValueFlow::Value value(tok->enumerator()->value);
value.setKnown(); value.setKnown();
setTokenValue(const_cast<Token *>(tok), value); setTokenValue(const_cast<Token *>(tok), value);
} else if (Token::simpleMatch(tok, "sizeof (") && tok->tokAt(2)) {
const Token *tok2 = tok->tokAt(2);
if (tok2->enumerator() && tok2->enumerator()->scope) {
long long size = settings->sizeof_int;
const Token * type = tok2->enumerator()->scope->enumType;
if (type) {
size = type->str() == "char" ? 1 :
type->str() == "short" ? settings->sizeof_short :
type->str() == "int" ? settings->sizeof_int :
(type->str() == "long" && type->isLong()) ? settings->sizeof_long_long :
type->str() == "long" ? settings->sizeof_long : 0;
}
ValueFlow::Value value(size);
value.setKnown();
setTokenValue(const_cast<Token *>(tok), value);
setTokenValue(const_cast<Token *>(tok->next()), value);
} else if (tok2->type() && tok2->type()->isEnumType()) {
long long size = settings->sizeof_int;
const Token * type = tok2->type()->classScope->enumType;
if (type) {
size = type->str() == "char" ? 1 :
type->str() == "short" ? settings->sizeof_short :
type->str() == "int" ? settings->sizeof_int :
(type->str() == "long" && type->isLong()) ? settings->sizeof_long_long :
type->str() == "long" ? settings->sizeof_long : 0;
}
ValueFlow::Value value(size);
value.setKnown();
setTokenValue(const_cast<Token *>(tok), value);
setTokenValue(const_cast<Token *>(tok->next()), value);
}
// skip over enum
tok = tok->linkAt(1);
} }
return tok->next();
} }
static void valueFlowNumber(TokenList *tokenlist) static void valueFlowNumber(TokenList *tokenlist)
{ {
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) { for (Token *tok = tokenlist->front(); tok;) {
valueFlowSetConstantValue(tok); tok = valueFlowSetConstantValue(tok, tokenlist->getSettings());
} }
if (tokenlist->isCPP()) { if (tokenlist->isCPP()) {
@ -2375,12 +2409,12 @@ static void valueFlowFunctionReturn(TokenList *tokenlist, ErrorLogger *errorLogg
} }
} }
const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(const Token *expr) const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(const Token *expr, const Settings *settings)
{ {
if (expr && expr->values.empty()) { if (expr && expr->values.empty()) {
valueFlowConstantFoldAST(expr->astOperand1()); valueFlowConstantFoldAST(expr->astOperand1(), settings);
valueFlowConstantFoldAST(expr->astOperand2()); valueFlowConstantFoldAST(expr->astOperand2(), settings);
valueFlowSetConstantValue(expr); valueFlowSetConstantValue(expr, settings);
} }
return expr && expr->values.size() == 1U && expr->values.front().isKnown() ? &expr->values.front() : nullptr; return expr && expr->values.size() == 1U && expr->values.front().isKnown() ? &expr->values.front() : nullptr;
} }

View File

@ -95,7 +95,7 @@ namespace ValueFlow {
}; };
/// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues).
const ValueFlow::Value * valueFlowConstantFoldAST(const Token *expr); const ValueFlow::Value * valueFlowConstantFoldAST(const Token *expr, const Settings *settings);
/// Perform valueflow analysis. /// Perform valueflow analysis.
void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings); void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings);

View File

@ -248,6 +248,7 @@ private:
TEST_CASE(enum4); TEST_CASE(enum4);
TEST_CASE(enum5); TEST_CASE(enum5);
TEST_CASE(enum6); TEST_CASE(enum6);
TEST_CASE(enum7);
TEST_CASE(isImplicitlyVirtual); TEST_CASE(isImplicitlyVirtual);
TEST_CASE(isPure); TEST_CASE(isPure);
@ -2469,6 +2470,54 @@ private:
ASSERT(function->retType && function->retType->name() == "Enum"); ASSERT(function->retType && function->retType->name() == "Enum");
} }
#define TEST(S) \
v = db->getVariableFromVarId(id++); \
ASSERT(v != nullptr); \
if (!v) \
return; \
ASSERT(v->isArray()); \
ASSERT_EQUALS(1U, v->dimensions().size()); \
ASSERT_EQUALS(S, v->dimension(0))
void enum7() {
GET_SYMBOL_DB("enum E { X };\n"
"enum EC : char { C };\n"
"enum ES : short { S };\n"
"enum EI : int { I };\n"
"enum EL : long { L };\n"
"enum ELL : long long { LL };\n"
"char array1[sizeof(E)];\n"
"char array2[sizeof(X)];\n"
"char array3[sizeof(EC)];\n"
"char array4[sizeof(C)];\n"
"char array5[sizeof(ES)];\n"
"char array6[sizeof(S)];\n"
"char array7[sizeof(EI)];\n"
"char array8[sizeof(I)];\n"
"char array9[sizeof(EL)];\n"
"char array10[sizeof(L)];\n"
"char array11[sizeof(ELL)];\n"
"char array12[sizeof(LL)];\n");
ASSERT(db);
if (!db)
return;
ASSERT(db->getVariableListSize() == 13); // the first one is not used
const Variable * v;
unsigned int id = 1;
TEST(settings.sizeof_int);
TEST(settings.sizeof_int);
TEST(1);
TEST(1);
TEST(settings.sizeof_short);
TEST(settings.sizeof_short);
TEST(settings.sizeof_int);
TEST(settings.sizeof_int);
TEST(settings.sizeof_long);
TEST(settings.sizeof_long);
TEST(settings.sizeof_long_long);
TEST(settings.sizeof_long_long);
}
void isImplicitlyVirtual() { void isImplicitlyVirtual() {
{ {
GET_SYMBOL_DB("class Base {\n" GET_SYMBOL_DB("class Base {\n"