ValueFlow: Fixed constant folding of sizeof(enum) and sizeof(enumerator) (#7564)
This commit is contained in:
parent
a808549af0
commit
eca805ba3b
|
@ -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()) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in New Issue