diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index e2c14e95f..56328d782 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -61,7 +61,6 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti createSymbolDatabaseSetVariablePointers(); createSymbolDatabaseSetTypePointers(); createSymbolDatabaseEnums(); - createSymbolDatabaseUnknownArrayDimensions(); } static const Token* skipScopeIdentifiers(const Token* tok) @@ -1258,7 +1257,7 @@ void SymbolDatabase::createSymbolDatabaseEnums() } } -void SymbolDatabase::createSymbolDatabaseUnknownArrayDimensions() +void SymbolDatabase::setArrayDimensionsUsingValueFlow() { // set all unknown array dimensions for (const Variable *var : mVariableList) { @@ -1268,78 +1267,44 @@ void SymbolDatabase::createSymbolDatabaseUnknownArrayDimensions() // check each array dimension for (const Dimension &const_dimension : var->dimensions()) { Dimension &dimension = const_cast(const_dimension); - if (dimension.num != 0) + if (dimension.num != 0 || !dimension.tok) continue; dimension.known = false; + // check for a single token dimension - if (dimension.start && (dimension.start == dimension.end)) { - // check for an enumerator - if (dimension.start->enumerator()) { - if (dimension.start->enumerator()->value_known) { - dimension.num = dimension.start->enumerator()->value; - dimension.known = true; - } - } - - // check for a variable - else if (dimension.start->varId()) { - // get maximum size from type - // find where this type is defined - const Variable *var = getVariableFromVarId(dimension.start->varId()); - - // make sure it is in the database - if (!var) - break; - // get type token - const Token *index_type = var->typeEndToken(); - - if (index_type->str() == "char") { - if (index_type->isUnsigned()) - dimension.num = UCHAR_MAX + 1; - else if (index_type->isSigned()) - dimension.num = SCHAR_MAX + 1; - else - dimension.num = CHAR_MAX + 1; - } else if (index_type->str() == "short") { - if (index_type->isUnsigned()) - dimension.num = USHRT_MAX + 1; - else - dimension.num = SHRT_MAX + 1; - } - - // checkScope assumes size is signed int so we limit the following sizes to INT_MAX - else if (index_type->str() == "int") { - if (index_type->isUnsigned()) - dimension.num = UINT_MAX + 1ULL; - else - dimension.num = INT_MAX + 1ULL; - } else if (index_type->str() == "long") { - if (index_type->isUnsigned()) { - if (index_type->isLong()) - dimension.num = ULLONG_MAX; // should be ULLONG_MAX + 1ULL - else - dimension.num = ULONG_MAX; // should be ULONG_MAX + 1ULL - } else { - if (index_type->isLong()) - dimension.num = LLONG_MAX; // should be LLONG_MAX + 1LL - else - dimension.num = LONG_MAX; // should be LONG_MAX + 1LL - } - } - } + if (dimension.tok->hasKnownIntValue()) { + dimension.known = true; + dimension.num = dimension.tok->getKnownIntValue(); + continue; } - // check for qualified enumerator - else if (dimension.start) { - // rhs of [ - const Token *rhs = dimension.start->previous()->astOperand2(); - // constant folding of expression: - ValueFlow::valueFlowConstantFoldAST(rhs, mSettings); + else if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) { + int bits = 0; + switch (dimension.tok->valueType()->type) { + case ValueType::Type::CHAR: + bits = mSettings->char_bit; + break; + case ValueType::Type::SHORT: + bits = mSettings->short_bit; + break; + case ValueType::Type::INT: + bits = mSettings->int_bit; + break; + case ValueType::Type::LONG: + bits = mSettings->long_bit; + break; + case ValueType::Type::LONGLONG: + bits = mSettings->long_long_bit; + break; + default: + break; + }; - // get constant folded value: - if (rhs && rhs->hasKnownIntValue()) { - dimension.num = rhs->values().front().intvalue; - dimension.known = true; + if (bits > 0 && bits < 64) { + if (dimension.tok->valueType()->sign == ValueType::Sign::SIGNED) + dimension.num = 1LL << (bits - 1); + else + dimension.num = 1LL << bits; } } } @@ -1626,7 +1591,7 @@ void Variable::evaluate(const Settings* settings) const Library * const lib = settings ? &settings->library : nullptr; if (mNameToken) - setFlag(fIsArray, arrayDimensions(lib)); + setFlag(fIsArray, arrayDimensions(settings)); if (mTypeStartToken) setValueType(ValueType::parseDecl(mTypeStartToken,settings)); @@ -1689,7 +1654,7 @@ void Variable::evaluate(const Settings* settings) tok = tok->link()->previous(); // add array dimensions if present if (tok && tok->next()->str() == "[") - setFlag(fIsArray, arrayDimensions(lib)); + setFlag(fIsArray, arrayDimensions(settings)); } if (!tok) return; @@ -2469,9 +2434,9 @@ bool Type::isDerivedFrom(const std::string & ancestor) const return false; } -bool Variable::arrayDimensions(const Library* lib) +bool Variable::arrayDimensions(const Settings* settings) { - const Library::Container* container = lib->detectContainer(mTypeStartToken); + const Library::Container* container = settings->library.detectContainer(mTypeStartToken); if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); if (tok) { @@ -2481,16 +2446,17 @@ bool Variable::arrayDimensions(const Library* lib) tok = tok->nextTemplateArgument(); } if (tok) { - dimension_.start = tok; - dimension_.end = Token::findmatch(tok, ",|>"); - if (dimension_.end) - dimension_.end = dimension_.end->previous(); - if (dimension_.start == dimension_.end) { - dimension_.num = MathLib::toLongNumber(dimension_.start->str()); + while (!tok->astParent() && !Token::Match(tok->next(), "[,<>]")) + tok = tok->next(); + while (tok->astParent() && !Token::Match(tok->astParent(), "[,<>]")) + tok = tok->astParent(); + dimension_.tok = tok; + ValueFlow::valueFlowConstantFoldAST(dimension_.tok, settings); + if (tok->hasKnownIntValue()) { + dimension_.num = tok->getKnownIntValue(); dimension_.known = true; } } - assert((dimension_.start == nullptr) == (dimension_.end == nullptr)); mDimensions.push_back(dimension_); return true; } @@ -2512,16 +2478,16 @@ bool Variable::arrayDimensions(const Library* lib) bool arr = false; while (dim && dim->next() && dim->str() == "[") { Dimension dimension_; + dimension_.known = false; // check for empty array dimension [] if (dim->next()->str() != "]") { - dimension_.start = dim->next(); - dimension_.end = dim->link()->previous(); - if (dimension_.start == dimension_.end && dimension_.start->isNumber()) { - dimension_.num = MathLib::toLongNumber(dimension_.start->str()); + dimension_.tok = dim->astOperand2(); + ValueFlow::valueFlowConstantFoldAST(dimension_.tok, settings); + if (dimension_.tok && dimension_.tok->hasKnownIntValue()) { + dimension_.num = dimension_.tok->getKnownIntValue(); dimension_.known = true; } } - assert((dimension_.start == nullptr) == (dimension_.end == nullptr)); mDimensions.push_back(dimension_); dim = dim->link()->next(); arr = true; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index dcfd55a73..b30cf926c 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -51,10 +51,9 @@ enum AccessControl { Public, Protected, Private, Global, Namespace, Argument, Lo * @brief Array dimension information. */ struct Dimension { - Dimension() : start(nullptr), end(nullptr), num(0), known(true) { } + Dimension() : tok(nullptr), num(0), known(true) { } - const Token *start; ///< size start token - const Token *end; ///< size end token + const Token *tok; ///< size token MathLib::bigint num; ///< (assumed) dimension length when size is a number, 0 if not known bool known; ///< Known size }; @@ -213,10 +212,10 @@ class CPPCHECKLIB Variable { /** * @brief parse and save array dimension information - * @param lib Library instance + * @param settings Platform settings and library * @return true if array, false if not */ - bool arrayDimensions(const Library* lib); + bool arrayDimensions(const Settings* settings); public: Variable(const Token *name_, const Token *start_, const Token *end_, @@ -1218,6 +1217,9 @@ public: */ unsigned int sizeOfType(const Token *type) const; + /** Set array dimensions when valueflow analysis is completed */ + void setArrayDimensionsUsingValueFlow(); + private: friend class Scope; friend class Function; @@ -1237,7 +1239,6 @@ private: void createSymbolDatabaseSetVariablePointers(); void createSymbolDatabaseSetTypePointers(); void createSymbolDatabaseEnums(); - void createSymbolDatabaseUnknownArrayDimensions(); void addClassFunction(Scope **scope, const Token **tok, const Token *argStart); Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 0084c28f4..aaa25afb1 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -1797,6 +1797,8 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration) ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); } + mSymbolDatabase->setArrayDimensionsUsingValueFlow(); + printDebugOutput(1); return true; diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index bc9a10d09..2b5dab3ed 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -401,15 +401,19 @@ private: ASSERT_EQUALS(12U, v->dimension(0)); } - void stlarray() const { - std::istringstream code("std::array arr;"); - TokenList list(nullptr); - list.createTokens(code, "test.c"); - list.front()->tokAt(3)->link(list.front()->tokAt(7)); - Variable v(list.front()->next(), list.front(), list.back(), 0, Public, nullptr, nullptr, &settings1); - ASSERT(v.isArray()); - ASSERT_EQUALS(1U, v.dimensions().size()); - ASSERT_EQUALS(20U, v.dimension(0)); + void stlarray() { + GET_SYMBOL_DB("std::array arr;"); + ASSERT(db != nullptr); + if (!db) + return; + ASSERT(db->variableList().size() == 2); // the first one is not used + const Variable * v = db->getVariableFromVarId(1); + ASSERT(v != nullptr); + if (!v) + return; + ASSERT(v->isArray()); + ASSERT_EQUALS(1U, v->dimensions().size()); + ASSERT_EQUALS(20U, v->dimension(0)); } void test_isVariableDeclarationCanHandleNull() {