SymbolDatabase: Improved handling of 'normal' non simplified token list

This commit is contained in:
Daniel Marjamäki 2019-03-15 19:00:42 +01:00
parent 4e051ef865
commit a2a216bbe3
4 changed files with 72 additions and 99 deletions

View File

@ -61,7 +61,6 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
createSymbolDatabaseSetVariablePointers(); createSymbolDatabaseSetVariablePointers();
createSymbolDatabaseSetTypePointers(); createSymbolDatabaseSetTypePointers();
createSymbolDatabaseEnums(); createSymbolDatabaseEnums();
createSymbolDatabaseUnknownArrayDimensions();
} }
static const Token* skipScopeIdentifiers(const Token* tok) 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 // set all unknown array dimensions
for (const Variable *var : mVariableList) { for (const Variable *var : mVariableList) {
@ -1268,78 +1267,44 @@ void SymbolDatabase::createSymbolDatabaseUnknownArrayDimensions()
// check each array dimension // check each array dimension
for (const Dimension &const_dimension : var->dimensions()) { for (const Dimension &const_dimension : var->dimensions()) {
Dimension &dimension = const_cast<Dimension &>(const_dimension); Dimension &dimension = const_cast<Dimension &>(const_dimension);
if (dimension.num != 0) if (dimension.num != 0 || !dimension.tok)
continue; continue;
dimension.known = false; dimension.known = false;
// check for a single token dimension // check for a single token dimension
if (dimension.start && (dimension.start == dimension.end)) { if (dimension.tok->hasKnownIntValue()) {
// check for an enumerator dimension.known = true;
if (dimension.start->enumerator()) { dimension.num = dimension.tok->getKnownIntValue();
if (dimension.start->enumerator()->value_known) { continue;
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
}
}
}
} }
// check for qualified enumerator
else if (dimension.start) {
// rhs of [
const Token *rhs = dimension.start->previous()->astOperand2();
// constant folding of expression: else if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) {
ValueFlow::valueFlowConstantFoldAST(rhs, mSettings); 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 (bits > 0 && bits < 64) {
if (rhs && rhs->hasKnownIntValue()) { if (dimension.tok->valueType()->sign == ValueType::Sign::SIGNED)
dimension.num = rhs->values().front().intvalue; dimension.num = 1LL << (bits - 1);
dimension.known = true; else
dimension.num = 1LL << bits;
} }
} }
} }
@ -1626,7 +1591,7 @@ void Variable::evaluate(const Settings* settings)
const Library * const lib = settings ? &settings->library : nullptr; const Library * const lib = settings ? &settings->library : nullptr;
if (mNameToken) if (mNameToken)
setFlag(fIsArray, arrayDimensions(lib)); setFlag(fIsArray, arrayDimensions(settings));
if (mTypeStartToken) if (mTypeStartToken)
setValueType(ValueType::parseDecl(mTypeStartToken,settings)); setValueType(ValueType::parseDecl(mTypeStartToken,settings));
@ -1689,7 +1654,7 @@ void Variable::evaluate(const Settings* settings)
tok = tok->link()->previous(); tok = tok->link()->previous();
// add array dimensions if present // add array dimensions if present
if (tok && tok->next()->str() == "[") if (tok && tok->next()->str() == "[")
setFlag(fIsArray, arrayDimensions(lib)); setFlag(fIsArray, arrayDimensions(settings));
} }
if (!tok) if (!tok)
return; return;
@ -2469,9 +2434,9 @@ bool Type::isDerivedFrom(const std::string & ancestor) const
return false; 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) { if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) {
const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); const Token* tok = Token::findsimplematch(mTypeStartToken, "<");
if (tok) { if (tok) {
@ -2481,16 +2446,17 @@ bool Variable::arrayDimensions(const Library* lib)
tok = tok->nextTemplateArgument(); tok = tok->nextTemplateArgument();
} }
if (tok) { if (tok) {
dimension_.start = tok; while (!tok->astParent() && !Token::Match(tok->next(), "[,<>]"))
dimension_.end = Token::findmatch(tok, ",|>"); tok = tok->next();
if (dimension_.end) while (tok->astParent() && !Token::Match(tok->astParent(), "[,<>]"))
dimension_.end = dimension_.end->previous(); tok = tok->astParent();
if (dimension_.start == dimension_.end) { dimension_.tok = tok;
dimension_.num = MathLib::toLongNumber(dimension_.start->str()); ValueFlow::valueFlowConstantFoldAST(dimension_.tok, settings);
if (tok->hasKnownIntValue()) {
dimension_.num = tok->getKnownIntValue();
dimension_.known = true; dimension_.known = true;
} }
} }
assert((dimension_.start == nullptr) == (dimension_.end == nullptr));
mDimensions.push_back(dimension_); mDimensions.push_back(dimension_);
return true; return true;
} }
@ -2512,16 +2478,16 @@ bool Variable::arrayDimensions(const Library* lib)
bool arr = false; bool arr = false;
while (dim && dim->next() && dim->str() == "[") { while (dim && dim->next() && dim->str() == "[") {
Dimension dimension_; Dimension dimension_;
dimension_.known = false;
// check for empty array dimension [] // check for empty array dimension []
if (dim->next()->str() != "]") { if (dim->next()->str() != "]") {
dimension_.start = dim->next(); dimension_.tok = dim->astOperand2();
dimension_.end = dim->link()->previous(); ValueFlow::valueFlowConstantFoldAST(dimension_.tok, settings);
if (dimension_.start == dimension_.end && dimension_.start->isNumber()) { if (dimension_.tok && dimension_.tok->hasKnownIntValue()) {
dimension_.num = MathLib::toLongNumber(dimension_.start->str()); dimension_.num = dimension_.tok->getKnownIntValue();
dimension_.known = true; dimension_.known = true;
} }
} }
assert((dimension_.start == nullptr) == (dimension_.end == nullptr));
mDimensions.push_back(dimension_); mDimensions.push_back(dimension_);
dim = dim->link()->next(); dim = dim->link()->next();
arr = true; arr = true;

View File

@ -51,10 +51,9 @@ enum AccessControl { Public, Protected, Private, Global, Namespace, Argument, Lo
* @brief Array dimension information. * @brief Array dimension information.
*/ */
struct Dimension { 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 *tok; ///< size token
const Token *end; ///< size end token
MathLib::bigint num; ///< (assumed) dimension length when size is a number, 0 if not known MathLib::bigint num; ///< (assumed) dimension length when size is a number, 0 if not known
bool known; ///< Known size bool known; ///< Known size
}; };
@ -213,10 +212,10 @@ class CPPCHECKLIB Variable {
/** /**
* @brief parse and save array dimension information * @brief parse and save array dimension information
* @param lib Library instance * @param settings Platform settings and library
* @return true if array, false if not * @return true if array, false if not
*/ */
bool arrayDimensions(const Library* lib); bool arrayDimensions(const Settings* settings);
public: public:
Variable(const Token *name_, const Token *start_, const Token *end_, Variable(const Token *name_, const Token *start_, const Token *end_,
@ -1218,6 +1217,9 @@ public:
*/ */
unsigned int sizeOfType(const Token *type) const; unsigned int sizeOfType(const Token *type) const;
/** Set array dimensions when valueflow analysis is completed */
void setArrayDimensionsUsingValueFlow();
private: private:
friend class Scope; friend class Scope;
friend class Function; friend class Function;
@ -1237,7 +1239,6 @@ private:
void createSymbolDatabaseSetVariablePointers(); void createSymbolDatabaseSetVariablePointers();
void createSymbolDatabaseSetTypePointers(); void createSymbolDatabaseSetTypePointers();
void createSymbolDatabaseEnums(); void createSymbolDatabaseEnums();
void createSymbolDatabaseUnknownArrayDimensions();
void addClassFunction(Scope **scope, const Token **tok, const Token *argStart); void addClassFunction(Scope **scope, const Token **tok, const Token *argStart);
Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart);

View File

@ -1797,6 +1797,8 @@ bool Tokenizer::simplifyTokens1(const std::string &configuration)
ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings); ValueFlow::setValues(&list, mSymbolDatabase, mErrorLogger, mSettings);
} }
mSymbolDatabase->setArrayDimensionsUsingValueFlow();
printDebugOutput(1); printDebugOutput(1);
return true; return true;

View File

@ -401,15 +401,19 @@ private:
ASSERT_EQUALS(12U, v->dimension(0)); ASSERT_EQUALS(12U, v->dimension(0));
} }
void stlarray() const { void stlarray() {
std::istringstream code("std::array<int, 20> arr;"); GET_SYMBOL_DB("std::array<int, (16 + 4)> arr;");
TokenList list(nullptr); ASSERT(db != nullptr);
list.createTokens(code, "test.c"); if (!db)
list.front()->tokAt(3)->link(list.front()->tokAt(7)); return;
Variable v(list.front()->next(), list.front(), list.back(), 0, Public, nullptr, nullptr, &settings1); ASSERT(db->variableList().size() == 2); // the first one is not used
ASSERT(v.isArray()); const Variable * v = db->getVariableFromVarId(1);
ASSERT_EQUALS(1U, v.dimensions().size()); ASSERT(v != nullptr);
ASSERT_EQUALS(20U, v.dimension(0)); if (!v)
return;
ASSERT(v->isArray());
ASSERT_EQUALS(1U, v->dimensions().size());
ASSERT_EQUALS(20U, v->dimension(0));
} }
void test_isVariableDeclarationCanHandleNull() { void test_isVariableDeclarationCanHandleNull() {