* Tokenize: Set varId for variables in enum
Set varIds in enum values. It was previously disabled in 5119ae84b8
to avoid issues with enums named the same as global variables. Take care
to only set varids to variables used to set the value of an enumerator,
not the enumerator itself. This is somewhat complicated by the fact that
at the time this happens, astOperand1(), astOperand2(), astParent() etc
are not set. The current implementation is not perfect, for example in
the code below, y will not have a varid set, but x and z will. This is
deemed sufficient for now.
int x, y, z;
enum E { a = f(x, y, z); };
* Fix #9647: Value of enums with variables as init values
C++ allows enum values to be set using constexprs, which cppcheck did
not handle before. To solve this, add a new pass to valueflow to update
enum values after global consts have been processed. In order to do so,
I moved all settings of enum values to valueflow. After setting the enum
values, we need another call to valueFlowNumber() to actually set users
of the enums.
There is still room for improvements, since each pass of
valueFlowGlobalConstVar() and valueFlowEnumValue() only sets variables
that are possible to set directly, and not if setting the value of a
variable allows us to set the value of another. For example
constexpr int a = 5;
constexpr int b = a + 5;
enum E { X = a };
constexpr E e = X;
Here both b and e will not have their values set, even though cppcheck
should be possible to figure out their values. That's for another PR
though.
This was tested by running test-my-pr.py with 500 packages. The only
difference was one error message in fairy-stockfish_11.1, where cppcheck
now printed the correct size of an array instead of 2147483648 which I
assume is some kind of default value. In that package, using a constexpr
when setting enum values is common, but as mentioned, there was no
change in the number of warnings.
This commit is contained in:
parent
64638d82bb
commit
d7a8e25d92
|
@ -1272,14 +1272,10 @@ void SymbolDatabase::createSymbolDatabaseEnums()
|
|||
const_cast<Token *>(i.name)->enumerator(&i);
|
||||
}
|
||||
|
||||
// fill in enumerator values
|
||||
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
||||
if (it->type != Scope::eEnum)
|
||||
continue;
|
||||
|
||||
MathLib::bigint value = 0;
|
||||
bool prev_enum_is_known = true;
|
||||
|
||||
for (Enumerator & enumerator : it->enumeratorList) {
|
||||
// look for initialization tokens that can be converted to enumerators and convert them
|
||||
if (enumerator.start) {
|
||||
|
@ -1292,28 +1288,6 @@ void SymbolDatabase::createSymbolDatabaseEnums()
|
|||
const_cast<Token *>(tok3)->enumerator(e);
|
||||
}
|
||||
}
|
||||
|
||||
// look for possible constant folding expressions
|
||||
// rhs of operator:
|
||||
Token *rhs = enumerator.start->previous()->astOperand2();
|
||||
|
||||
// constant folding of expression:
|
||||
ValueFlow::valueFlowConstantFoldAST(rhs, mSettings);
|
||||
|
||||
// get constant folded value:
|
||||
if (rhs && rhs->hasKnownIntValue()) {
|
||||
enumerator.value = rhs->values().front().intvalue;
|
||||
enumerator.value_known = true;
|
||||
value = enumerator.value + 1;
|
||||
prev_enum_is_known = true;
|
||||
} else
|
||||
prev_enum_is_known = false;
|
||||
}
|
||||
|
||||
// not initialized so use default value
|
||||
else if (prev_enum_is_known) {
|
||||
enumerator.value = value++;
|
||||
enumerator.value_known = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3605,7 +3605,7 @@ void Tokenizer::setVarIdPass1()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!scopeStack.top().isEnum) {
|
||||
if (!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) {
|
||||
const std::map<std::string, int>::const_iterator it = variableMap.find(tok->str());
|
||||
if (it != variableMap.end()) {
|
||||
tok->varId(it->second);
|
||||
|
|
|
@ -1628,6 +1628,34 @@ static void valueFlowOppositeCondition(SymbolDatabase *symboldatabase, const Set
|
|||
}
|
||||
}
|
||||
|
||||
static void valueFlowEnumValue(SymbolDatabase * symboldatabase, const Settings * settings)
|
||||
{
|
||||
|
||||
for (Scope & scope : symboldatabase->scopeList) {
|
||||
if (scope.type != Scope::eEnum)
|
||||
continue;
|
||||
MathLib::bigint value = 0;
|
||||
bool prev_enum_is_known = true;
|
||||
|
||||
for (Enumerator & enumerator : scope.enumeratorList) {
|
||||
if (enumerator.start) {
|
||||
Token *rhs = enumerator.start->previous()->astOperand2();
|
||||
ValueFlow::valueFlowConstantFoldAST(rhs, settings);
|
||||
if (rhs && rhs->hasKnownIntValue()) {
|
||||
enumerator.value = rhs->values().front().intvalue;
|
||||
enumerator.value_known = true;
|
||||
value = enumerator.value + 1;
|
||||
prev_enum_is_known = true;
|
||||
} else
|
||||
prev_enum_is_known = false;
|
||||
} else if (prev_enum_is_known) {
|
||||
enumerator.value = value++;
|
||||
enumerator.value_known = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void valueFlowGlobalConstVar(TokenList* tokenList, const Settings *settings)
|
||||
{
|
||||
// Get variable values...
|
||||
|
@ -6791,11 +6819,14 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
|
||||
tok->clearValueFlow();
|
||||
|
||||
valueFlowEnumValue(symboldatabase, settings);
|
||||
valueFlowNumber(tokenlist);
|
||||
valueFlowString(tokenlist);
|
||||
valueFlowArray(tokenlist);
|
||||
valueFlowUnknownFunctionReturn(tokenlist, settings);
|
||||
valueFlowGlobalConstVar(tokenlist, settings);
|
||||
valueFlowEnumValue(symboldatabase, settings);
|
||||
valueFlowNumber(tokenlist);
|
||||
valueFlowGlobalStaticVar(tokenlist, settings);
|
||||
valueFlowPointerAlias(tokenlist);
|
||||
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
|
||||
|
|
|
@ -337,6 +337,7 @@ private:
|
|||
TEST_CASE(enum6);
|
||||
TEST_CASE(enum7);
|
||||
TEST_CASE(enum8);
|
||||
TEST_CASE(enum9);
|
||||
|
||||
TEST_CASE(sizeOfType);
|
||||
|
||||
|
@ -5034,6 +5035,19 @@ private:
|
|||
ASSERT(!X5->value_known);
|
||||
}
|
||||
|
||||
void enum9() {
|
||||
GET_SYMBOL_DB("const int x = 7; enum E { X0 = x, X1 };\n");
|
||||
ASSERT(db != nullptr);
|
||||
const Enumerator *X0 = db->scopeList.back().findEnumerator("X0");
|
||||
ASSERT(X0);
|
||||
ASSERT(X0->value_known);
|
||||
ASSERT_EQUALS(X0->value, 7);
|
||||
const Enumerator *X1 = db->scopeList.back().findEnumerator("X1");
|
||||
ASSERT(X1);
|
||||
ASSERT(X1->value_known);
|
||||
ASSERT_EQUALS(X1->value, 8);
|
||||
}
|
||||
|
||||
void sizeOfType() {
|
||||
// #7615 - crash in Symboldatabase::sizeOfType()
|
||||
GET_SYMBOL_DB("enum e;\n"
|
||||
|
|
|
@ -194,6 +194,12 @@ private:
|
|||
TEST_CASE(varidclass20); // #7578: int (*p)[2]
|
||||
TEST_CASE(varid_classnameshaddowsvariablename); // #3990
|
||||
|
||||
TEST_CASE(varidenum1);
|
||||
TEST_CASE(varidenum2);
|
||||
TEST_CASE(varidenum3);
|
||||
TEST_CASE(varidenum4);
|
||||
TEST_CASE(varidenum5);
|
||||
|
||||
TEST_CASE(varidnamespace1);
|
||||
TEST_CASE(varidnamespace2);
|
||||
TEST_CASE(usingNamespace1);
|
||||
|
@ -3084,6 +3090,70 @@ private:
|
|||
ASSERT_EQUALS(expected, tokenize(code));
|
||||
}
|
||||
|
||||
void varidenum1() {
|
||||
const char code[] = "const int eStart = 6;\n"
|
||||
"enum myEnum {\n"
|
||||
" A = eStart;\n"
|
||||
"};\n";
|
||||
const char expected[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = eStart@1 ;\n"
|
||||
"4: } ;\n";
|
||||
ASSERT_EQUALS(expected, tokenize(code));
|
||||
}
|
||||
|
||||
void varidenum2() {
|
||||
const char code[] = "const int eStart = 6;\n"
|
||||
"enum myEnum {\n"
|
||||
" A = f(eStart);\n"
|
||||
"};\n";
|
||||
const char expected[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = f ( eStart@1 ) ;\n"
|
||||
"4: } ;\n";
|
||||
ASSERT_EQUALS(expected, tokenize(code));
|
||||
}
|
||||
|
||||
void varidenum3() {
|
||||
const char code[] = "const int eStart = 6;\n"
|
||||
"enum myEnum {\n"
|
||||
" A = f(eStart, x);\n"
|
||||
"};\n";
|
||||
const char expected[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = f ( eStart@1 , x ) ;\n"
|
||||
"4: } ;\n";
|
||||
ASSERT_EQUALS(expected, tokenize(code));
|
||||
}
|
||||
|
||||
void varidenum4() {
|
||||
const char code[] = "const int eStart = 6;\n"
|
||||
"enum myEnum {\n"
|
||||
" A = f(x, eStart);\n"
|
||||
"};\n";
|
||||
const char expected[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = f ( x , eStart@1 ) ;\n"
|
||||
"4: } ;\n";
|
||||
ASSERT_EQUALS(expected, tokenize(code));
|
||||
}
|
||||
|
||||
void varidenum5() {
|
||||
const char code[] = "const int eStart = 6;\n"
|
||||
"enum myEnum {\n"
|
||||
" A = f(x, eStart, y);\n"
|
||||
"};\n";
|
||||
const char expected[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = f ( x , eStart@1 , y ) ;\n"
|
||||
"4: } ;\n";
|
||||
const char current[] = "1: const int eStart@1 = 6 ;\n"
|
||||
"2: enum myEnum {\n"
|
||||
"3: A = f ( x , eStart , y ) ;\n"
|
||||
"4: } ;\n";
|
||||
TODO_ASSERT_EQUALS(expected, current, tokenize(code));
|
||||
}
|
||||
|
||||
void varid_classnameshaddowsvariablename() {
|
||||
const char code[] = "class Data;\n"
|
||||
"void strange_declarated(const Data& Data);\n"
|
||||
|
|
Loading…
Reference in New Issue