From 68cc7516a1410ff51aa2289621b5aef41ddb51c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Fri, 12 Jul 2019 11:09:24 +0200 Subject: [PATCH] Annotations: Add annotation __cppcheck_in_range__(low,high) --- cfg/microsoft_sal.cfg | 2 +- lib/token.cpp | 32 ++++++++++++++++++++++++ lib/token.h | 18 ++++++++++++++ lib/tokenize.cpp | 33 +++++++++++++++++++++++++ lib/tokenize.h | 5 ++++ lib/valueflow.cpp | 55 +++++++++++++++++++++++++++--------------- test/testvalueflow.cpp | 8 ++++++ 7 files changed, 133 insertions(+), 20 deletions(-) diff --git a/cfg/microsoft_sal.cfg b/cfg/microsoft_sal.cfg index 847eaff93..33b963044 100644 --- a/cfg/microsoft_sal.cfg +++ b/cfg/microsoft_sal.cfg @@ -108,7 +108,7 @@ - + diff --git a/lib/token.cpp b/lib/token.cpp index 7f4fe9c2f..20efdae3d 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -1867,4 +1867,36 @@ TokenImpl::~TokenImpl() for (auto templateSimplifierPointer : mTemplateSimplifierPointers) { templateSimplifierPointer->token = nullptr; } + + while (mCppcheckAttributes) { + struct CppcheckAttributes *c = mCppcheckAttributes; + mCppcheckAttributes = mCppcheckAttributes->next; + delete c; + } +} + +void TokenImpl::setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) +{ + struct CppcheckAttributes *attr = mCppcheckAttributes; + while (attr && attr->type != type) + attr = attr->next; + if (attr) + attr->value = value; + else { + attr = new CppcheckAttributes; + attr->type = type; + attr->value = value; + attr->next = mCppcheckAttributes; + mCppcheckAttributes = attr; + } +} + +bool TokenImpl::getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const +{ + struct CppcheckAttributes *attr = mCppcheckAttributes; + while (attr && attr->type != type) + attr = attr->next; + if (attr) + *value = attr->value; + return attr != nullptr; } diff --git a/lib/token.h b/lib/token.h index 53cfb8df0..2dd3315d5 100644 --- a/lib/token.h +++ b/lib/token.h @@ -97,6 +97,17 @@ struct TokenImpl { // Pointer to a template in the template simplifier std::set mTemplateSimplifierPointers; + // __cppcheck_in_range__ + struct CppcheckAttributes { + enum Type {LOW,HIGH} type; + MathLib::bigint value; + struct CppcheckAttributes *next; + }; + struct CppcheckAttributes *mCppcheckAttributes; + + void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); + bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint *value) const; + TokenImpl() : mVarId(0) , mFileIndex(0) @@ -114,6 +125,7 @@ struct TokenImpl { , mValues(nullptr) , mBits(0) , mTemplateSimplifierPointers() + , mCppcheckAttributes(nullptr) {} ~TokenImpl(); @@ -498,6 +510,12 @@ public: void isAttributeNodiscard(const bool value) { setFlag(fIsAttributeNodiscard, value); } + void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { + mImpl->setCppcheckAttribute(type, value); + } + bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint *value) const { + return mImpl->getCppcheckAttribute(type, value); + } bool isControlFlowKeyword() const { return getFlag(fIsControlFlowKeyword); } diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index d8efd9572..9753e1028 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4207,6 +4207,9 @@ bool Tokenizer::simplifyTokenList1(const char FileName[]) // remove __attribute__((?)) simplifyAttribute(); + // simplify cppcheck attributes __cppcheck_?__(?) + simplifyCppcheckAttribute(); + // Combine tokens.. combineOperators(); @@ -9788,6 +9791,36 @@ void Tokenizer::simplifyAttribute() } } +void Tokenizer::simplifyCppcheckAttribute() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != "(") + continue; + if (!tok->previous()) + continue; + const std::string &attr = tok->previous()->str(); + if (attr.compare(0, 11, "__cppcheck_") != 0) // TODO: starts_with("__cppcheck_") + continue; + if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") + continue; + + if (attr == "__cppcheck_in_range__") { + Token *vartok = tok->link(); + while (Token::Match(vartok->next(), "%name%|*|&|::")) + vartok = vartok->next(); + if (vartok->isName() && Token::Match(tok, "( %num% , %num% )")) { + vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, MathLib::toLongNumber(tok->next()->str())); + vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, MathLib::toLongNumber(tok->strAt(3))); + } + } + + // Delete cppcheck attribute.. + tok = tok->previous(); + Token::eraseTokens(tok, tok->linkAt(1)->next()); + tok->deleteThis(); + } +} + void Tokenizer::simplifyCPPAttribute() { if (mSettings->standards.cpp < Standards::CPP11 || isC()) diff --git a/lib/tokenize.h b/lib/tokenize.h index f7bf19a36..9a263d14a 100644 --- a/lib/tokenize.h +++ b/lib/tokenize.h @@ -651,6 +651,11 @@ private: */ void simplifyAttribute(); + /** + * Remove \__cppcheck\__ ((?)) + */ + void simplifyCppcheckAttribute(); + /** * Remove keywords "volatile", "inline", "register", and "restrict" */ diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 6e26af491..003428145 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -5407,8 +5407,6 @@ static bool getMinMaxValues(const std::string &typestr, const Settings *settings static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { - if (settings->platformType == cppcheck::Platform::PlatformType::Unspecified) - return; for (const Scope *functionScope : symboldatabase->functionScopes) { if (!functionScope->bodyStart) continue; @@ -5416,28 +5414,47 @@ static void valueFlowSafeFunctions(TokenList *tokenlist, SymbolDatabase *symbold if (!function) continue; - if (!function->isSafe(settings)) - continue; + const bool all = function->isSafe(settings) && settings->platformType != cppcheck::Platform::PlatformType::Unspecified; for (const Variable &arg : function->argumentList) { - MathLib::bigint minValue, maxValue; - if (!getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue)) + if (!arg.nameToken()) continue; - std::list argValues; - argValues.emplace_back(minValue); - argValues.emplace_back(maxValue); + MathLib::bigint low, high; + bool isLow = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, &low); + bool isHigh = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, &high); - valueFlowForward(const_cast(functionScope->bodyStart->next()), - functionScope->bodyEnd, - &arg, - arg.declarationId(), - argValues, - false, - false, - tokenlist, - errorLogger, - settings); + if (!isLow && !isHigh && !all) + continue; + + if ((!isLow || !isHigh) && all) { + MathLib::bigint minValue, maxValue; + if (getMinMaxValues(arg.valueType(), *settings, &minValue, &maxValue)) { + if (!isLow) + low = minValue; + if (!isHigh) + high = maxValue; + isLow = isHigh = true; + } + } + + std::list argValues; + if (isLow) + argValues.emplace_back(low); + if (isHigh) + argValues.emplace_back(high); + + if (!argValues.empty()) + valueFlowForward(const_cast(functionScope->bodyStart->next()), + functionScope->bodyEnd, + &arg, + arg.declarationId(), + argValues, + false, + false, + tokenlist, + errorLogger, + settings); } } } diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 307b3e164..b5d59b88c 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -3919,6 +3919,14 @@ private: ASSERT_EQUALS(2, values.size()); ASSERT_EQUALS(-0x8000, values.front().intvalue); ASSERT_EQUALS(0x7fff, values.back().intvalue); + + code = "void f(__cppcheck_in_range__(0,100) short x) {\n" + " return x + 0;\n" + "}"; + values = tokenValues(code, "+", &s); + ASSERT_EQUALS(2, values.size()); + ASSERT_EQUALS(0, values.front().intvalue); + ASSERT_EQUALS(100, values.back().intvalue); }