This commit is contained in:
Martin Ettl 2014-01-06 18:51:15 +01:00
commit fa04273473
21 changed files with 352 additions and 54 deletions

View File

@ -362,7 +362,7 @@ $(SRCDIR)/tokenize.o: lib/tokenize.cpp lib/tokenize.h lib/errorlogger.h lib/conf
$(SRCDIR)/tokenlist.o: lib/tokenlist.cpp lib/tokenlist.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/path.h lib/preprocessor.h lib/settings.h lib/library.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h $(SRCDIR)/tokenlist.o: lib/tokenlist.cpp lib/tokenlist.h lib/config.h lib/token.h lib/valueflow.h lib/mathlib.h lib/path.h lib/preprocessor.h lib/settings.h lib/library.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/tokenlist.o $(SRCDIR)/tokenlist.cpp $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/tokenlist.o $(SRCDIR)/tokenlist.cpp
$(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/valueflow.h lib/token.h lib/config.h lib/mathlib.h $(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/valueflow.h lib/errorlogger.h lib/config.h lib/suppressions.h lib/mathlib.h lib/settings.h lib/library.h lib/path.h lib/standards.h lib/timer.h lib/symboldatabase.h lib/token.h lib/tokenlist.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/valueflow.o $(SRCDIR)/valueflow.cpp $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CFG) $(CXXFLAGS) -c -o $(SRCDIR)/valueflow.o $(SRCDIR)/valueflow.cpp
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h lib/checkunusedfunctions.h lib/check.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/filelister.h cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/path.h lib/mathlib.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h lib/checkunusedfunctions.h lib/check.h lib/token.h lib/valueflow.h lib/tokenize.h lib/tokenlist.h cli/filelister.h

View File

@ -112,7 +112,7 @@ the latest sources in a zip or tgz archive</a> from the github website.</p>
</ul> </ul>
<h2 id="other">Other</h2> <h2 id="other">Other</h2>
<ul> <ul>
<li><a href="daca2-report/daca2.html">DACA2 - Scanning Debian with latest Cppcheck</a></li> <li>DACA2 - Scanning Debian with Cppcheck. Version: <a href="daca2-cppcheck1.63/daca2.html">1.63</a> / <a href="daca2-report/daca2.html">head</a></li>
<li><a href="coverage_report/">Coverage report</a></li> <li><a href="coverage_report/">Coverage report</a></li>
<li><a href="cpd.txt">CPD report (duplicate code)</a></li> <li><a href="cpd.txt">CPD report (duplicate code)</a></li>
</ul> </ul>

View File

@ -468,7 +468,7 @@ void CheckClass::initializeVarList(const Function &func, std::list<const Functio
} }
} }
} }
} else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), ",|{|%type%")) { } else if (level == 0 && Token::Match(ftok, "%var% {") && ftok->str() != "const" && Token::Match(ftok->next()->link()->next(), "%type%|,|{")) {
initVar(ftok->str(), scope, usage); initVar(ftok->str(), scope, usage);
ftok = ftok->linkAt(1); ftok = ftok->linkAt(1);
} else if (level != 0 && Token::Match(ftok, "%var% =")) // assignment in the initializer: var(value = x) } else if (level != 0 && Token::Match(ftok, "%var% =")) // assignment in the initializer: var(value = x)

View File

@ -22,6 +22,8 @@
#include "symboldatabase.h" #include "symboldatabase.h"
#include <string> #include <string>
#include <set> #include <set>
#include <cstring>
#include <cctype>
// Register this check class (by creating a static instance of it). // Register this check class (by creating a static instance of it).
// Disabled in release builds // Disabled in release builds
@ -48,6 +50,34 @@ void CheckInternal::checkTokenMatchPatterns()
continue; continue;
} }
const char *p = pattern.c_str();
while (*p) {
while (*p && std::isspace(*p))
p++;
const char *start = p;
while (*p && !std::isspace(*p))
p++;
const char *end = p - 1;
if (start < end && !(*start == '[' && *end == ']')) {
bool cmd = (*start=='%' && std::isalpha(*(start+1)));
// check multicompare pattern..
for (const char *s = start; s != end; s++) {
if (*s == '|') {
if (!(*(s+1) == '%' && std::isalpha(*(s+2)))) {
cmd = false;
} else if (!cmd &&
std::strncmp(s+1,"%op%",4)!=0 &&
std::strncmp(s+1,"%or%",4)!=0 &&
std::strncmp(s+1,"%cop%",5)!=0 &&
std::strncmp(s+1,"%var%",5)!=0 &&
std::strncmp(s+1,"%oror%",6)!=0) {
multiComparePatternError(tok, pattern, funcname);
}
}
}
}
}
// Check for signs of complex patterns // Check for signs of complex patterns
if (pattern.find_first_of("[|%") != std::string::npos) if (pattern.find_first_of("[|%") != std::string::npos)
continue; continue;
@ -245,6 +275,13 @@ void CheckInternal::checkRedundantNextPrevious()
} }
} }
void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
{
reportError(tok, Severity::error, "multiComparePatternError",
"Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\""
);
}
void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname)
{ {
reportError(tok, Severity::warning, "simplePatternError", reportError(tok, Severity::warning, "simplePatternError",

View File

@ -73,6 +73,7 @@ public:
void checkRedundantNextPrevious(); void checkRedundantNextPrevious();
private: private:
void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname);
void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname);
void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname);
void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname);
@ -81,6 +82,7 @@ private:
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
CheckInternal c(0, settings, errorLogger); CheckInternal c(0, settings, errorLogger);
c.multiComparePatternError(0, ";|%type%", "Match");
c.simplePatternError(0, "class {", "Match"); c.simplePatternError(0, "class {", "Match");
c.complexPatternError(0, "%type% ( )", "Match"); c.complexPatternError(0, "%type% ( )", "Match");
c.missingPercentCharacterError(0, "%num", "Match"); c.missingPercentCharacterError(0, "%num", "Match");

View File

@ -2158,8 +2158,10 @@ void CheckOther::checkZeroDivision()
std::list<ValueFlow::Value>::const_iterator it; std::list<ValueFlow::Value>::const_iterator it;
for (it = values.begin(); it != values.end(); ++it) { for (it = values.begin(); it != values.end(); ++it) {
if (it->intvalue == 0) { if (it->intvalue == 0) {
if (!it->link || _settings->isEnabled("warning")) if (it->condition == NULL)
zerodivError(tok); zerodivError(tok);
else if (_settings->isEnabled("warning"))
zerodivcondError(it->condition,tok);
} }
} }
} }
@ -2176,6 +2178,11 @@ void CheckOther::checkZeroDivisionOrUselessCondition()
{ {
if (!_settings->isEnabled("warning")) if (!_settings->isEnabled("warning"))
return; return;
// Use experimental checking instead based on value flow analysis
if (_settings->valueFlow)
return;
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
const std::size_t numberOfFunctions = symbolDatabase->functionScopes.size(); const std::size_t numberOfFunctions = symbolDatabase->functionScopes.size();
for (std::size_t functionIndex = 0; functionIndex < numberOfFunctions; ++functionIndex) { for (std::size_t functionIndex = 0; functionIndex < numberOfFunctions; ++functionIndex) {
@ -2297,6 +2304,24 @@ void CheckOther::checkZeroDivisionOrUselessCondition()
} }
} }
// TODO: this utility function should probably be moved to some common file
static std::string astStringify(const Token *top)
{
const Token *start = top;
while (start->astOperand1() && start->astOperand2())
start = start->astOperand1();
const Token *end = top;
while (end->astOperand1() && end->astOperand2())
end = end->astOperand2();
std::string str;
for (const Token *tok = start; tok && tok != end; tok = tok->next()) {
str += tok->str();
if (Token::Match(tok, "%var%|%num% %var%|%num%"))
str += " ";
}
return str + end->str();
}
void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv) void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv)
{ {
std::list<const Token *> callstack; std::list<const Token *> callstack;
@ -2307,9 +2332,13 @@ void CheckOther::zerodivcondError(const Token *tokcond, const Token *tokdiv)
callstack.push_back(tokdiv); callstack.push_back(tokdiv);
} }
std::string condition; std::string condition;
if (Token::Match(tokcond, "%num% <|<=")) { if (!tokcond) {
// getErrorMessages
} else if (Token::Match(tokcond, "%num% <|<=")) {
condition = tokcond->strAt(2) + ((tokcond->strAt(1) == "<") ? ">" : ">=") + tokcond->str(); condition = tokcond->strAt(2) + ((tokcond->strAt(1) == "<") ? ">" : ">=") + tokcond->str();
} else if (tokcond) { } else if (tokcond->isComparisonOp()) {
condition = astStringify(tokcond);
} else {
if (tokcond->str() == "!") if (tokcond->str() == "!")
condition = tokcond->next()->str() + "==0"; condition = tokcond->next()->str() + "==0";
else else

View File

@ -98,8 +98,12 @@ public:
class ArgumentChecks { class ArgumentChecks {
public: public:
ArgumentChecks() { ArgumentChecks() :
notbool = notnull = notuninit = formatstr = strz = false; notbool(false),
notnull(false),
notuninit(false),
formatstr(false),
strz(false) {
} }
bool notbool; bool notbool;

View File

@ -2023,7 +2023,7 @@ bool Function::isImplicitlyVirtual(bool defaultVal) const
bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const
{ {
// check each base class // check each base class
for (unsigned int i = 0; i < baseType->derivedFrom.size(); ++i) { for (std::size_t i = 0; i < baseType->derivedFrom.size(); ++i) {
// check if base class exists in database // check if base class exists in database
if (baseType->derivedFrom[i].type && baseType->derivedFrom[i].type->classScope) { if (baseType->derivedFrom[i].type && baseType->derivedFrom[i].type->classScope) {
const Scope *parent = baseType->derivedFrom[i].type->classScope; const Scope *parent = baseType->derivedFrom[i].type->classScope;
@ -2055,9 +2055,11 @@ bool Function::isImplicitlyVirtual_rec(const ::Type* baseType, bool& safe) const
} }
} }
if (!baseType->derivedFrom[i].type->derivedFrom.empty()) if (!baseType->derivedFrom[i].type->derivedFrom.empty()) {
if (isImplicitlyVirtual_rec(baseType->derivedFrom[i].type, safe)) // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN
if ((baseType != baseType->derivedFrom[i].type) && isImplicitlyVirtual_rec(baseType->derivedFrom[i].type, safe))
return true; return true;
}
} else { } else {
// unable to find base class so assume it has no virtual function // unable to find base class so assume it has no virtual function
safe = false; safe = false;

View File

@ -65,6 +65,10 @@ public:
} needInitialization; } needInitialization;
struct BaseInfo { struct BaseInfo {
BaseInfo() :
type(NULL), nameTok(NULL), access(Public), isVirtual(false) {
}
std::string name; std::string name;
const Type* type; const Type* type;
const Token* nameTok; const Token* nameTok;
@ -73,6 +77,10 @@ public:
}; };
struct FriendInfo { struct FriendInfo {
FriendInfo() :
nameStart(NULL), nameEnd(NULL), type(NULL) {
}
const Token* nameStart; const Token* nameStart;
const Token* nameEnd; const Token* nameEnd;
std::string name; std::string name;

View File

@ -620,7 +620,7 @@ bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::stri
if (patternAfter) { if (patternAfter) {
const Token *tok = instance; const Token *tok = instance;
unsigned int indentlevel = 0; unsigned int indentlevel = 0;
for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0); tok = tok->next()) { for (tok = instance; tok && (tok->str() != ">" || indentlevel > 0) && (tok->str() != ">>" || indentlevel > 1); tok = tok->next()) {
if (Token::Match(tok, "[<,] %var% <") && templateParameters(tok->tokAt(2)) > 0) if (Token::Match(tok, "[<,] %var% <") && templateParameters(tok->tokAt(2)) > 0)
++indentlevel; ++indentlevel;
if (indentlevel > 0 && tok->str() == ">") if (indentlevel > 0 && tok->str() == ">")

View File

@ -206,6 +206,7 @@ void Token::deleteThis()
_function = _next->_function; _function = _next->_function;
_variable = _next->_variable; _variable = _next->_variable;
_originalName = _next->_originalName; _originalName = _next->_originalName;
values = _next->values;
if (_link) if (_link)
_link->link(this); _link->link(this);
@ -229,6 +230,7 @@ void Token::deleteThis()
_function = _previous->_function; _function = _previous->_function;
_variable = _previous->_variable; _variable = _previous->_variable;
_originalName = _previous->_originalName; _originalName = _previous->_originalName;
values = _previous->values;
if (_link) if (_link)
_link->link(this); _link->link(this);

View File

@ -370,6 +370,7 @@ Token * Tokenizer::deleteInvalidTypedef(Token *typeDef)
} }
struct Space { struct Space {
Space() : classEnd(NULL), isNamespace(false) { }
std::string className; std::string className;
const Token * classEnd; const Token * classEnd;
bool isNamespace; bool isNamespace;
@ -1605,7 +1606,7 @@ bool Tokenizer::tokenize(std::istream &code,
list.createAst(); list.createAst();
if (_settings->valueFlow) if (_settings->valueFlow)
ValueFlow::setValues(list.front()); ValueFlow::setValues(&list, _errorLogger, _settings);
return true; return true;
} }
@ -7531,23 +7532,23 @@ bool Tokenizer::duplicateDefinition(Token ** tokPtr, const Token * name) const
class EnumValue { class EnumValue {
public: public:
EnumValue() { EnumValue() :
name = 0; name(NULL),
value = 0; value(NULL),
start = 0; start(NULL),
end = 0; end(NULL) {
} }
EnumValue(const EnumValue &ev) { EnumValue(const EnumValue &ev) :
name = ev.name; name(ev.name),
value = ev.value; value(ev.value),
start = ev.start; start(ev.start),
end = ev.end; end(ev.end) {
} }
EnumValue(Token *name_, Token *value_, Token *start_, Token *end_) { EnumValue(Token *name_, Token *value_, Token *start_, Token *end_) :
name = name_; name(name_),
value = value_; value(value_),
start = start_; start(start_),
end = end_; end(end_) {
} }
void simplify(const std::map<std::string, EnumValue> &enumValues) { void simplify(const std::map<std::string, EnumValue> &enumValues) {

View File

@ -17,20 +17,47 @@
*/ */
#include "valueflow.h" #include "valueflow.h"
#include "token.h" #include "errorlogger.h"
#include "mathlib.h" #include "mathlib.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
#include "tokenlist.h"
static void valueFlowBeforeCondition(Token *tokens) #include <iostream>
static void printvalues(const Token *tok)
{ {
for (Token *tok = tokens; tok; tok = tok->next()) { if (tok->values.empty())
std::cout << "empty";
for (std::list<ValueFlow::Value>::const_iterator it = tok->values.begin(); it != tok->values.end(); ++it)
std::cout << " " << (it->intvalue);
std::cout << std::endl;
}
static void bailout(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist));
ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "ValueFlow bailout: " + what, "valueFlowBailout", false);
errorLogger->reportErr(errmsg);
}
static void valueFlowBeforeCondition(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
unsigned int varid; unsigned int varid;
MathLib::bigint num; MathLib::bigint num;
const Variable *var;
if (Token::Match(tok, "==|!=|>=|<=") && tok->astOperand1() && tok->astOperand2()) { if (Token::Match(tok, "==|!=|>=|<=") && tok->astOperand1() && tok->astOperand2()) {
if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) { if (tok->astOperand1()->isName() && tok->astOperand2()->isNumber()) {
varid = tok->astOperand1()->varId(); varid = tok->astOperand1()->varId();
var = tok->astOperand1()->variable();
num = MathLib::toLongNumber(tok->astOperand2()->str()); num = MathLib::toLongNumber(tok->astOperand2()->str());
} else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) { } else if (tok->astOperand1()->isNumber() && tok->astOperand2()->isName()) {
varid = tok->astOperand2()->varId(); varid = tok->astOperand2()->varId();
var = tok->astOperand2()->variable();
num = MathLib::toLongNumber(tok->astOperand1()->str()); num = MathLib::toLongNumber(tok->astOperand1()->str());
} else { } else {
continue; continue;
@ -38,9 +65,11 @@ static void valueFlowBeforeCondition(Token *tokens)
} else if (Token::Match(tok->previous(), "if|while ( %var% %oror%|&&|)") || } else if (Token::Match(tok->previous(), "if|while ( %var% %oror%|&&|)") ||
Token::Match(tok, "%oror%|&& %var% %oror%|&&|)")) { Token::Match(tok, "%oror%|&& %var% %oror%|&&|)")) {
varid = tok->next()->varId(); varid = tok->next()->varId();
var = tok->next()->variable();
num = 0; num = 0;
} else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) { } else if (tok->str() == "!" && tok->astOperand1() && tok->astOperand1()->isName()) {
varid = tok->astOperand1()->varId(); varid = tok->astOperand1()->varId();
var = tok->astOperand1()->variable();
num = 0; num = 0;
} else { } else {
continue; continue;
@ -49,27 +78,112 @@ static void valueFlowBeforeCondition(Token *tokens)
if (varid == 0U) if (varid == 0U)
continue; continue;
// bailout: global variables
if (var && var->isGlobal()) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok, "global variable " + var->nameToken()->str());
continue;
}
struct ValueFlow::Value val; struct ValueFlow::Value val;
val.link = tok; val.condition = tok;
val.intvalue = num; val.intvalue = num;
for (Token *tok2 = tok->previous(); tok2; tok2 = tok2->previous()) { for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) {
if (tok2->varId() == varid) if (!tok2) {
if (settings->debugwarnings) {
std::list<ErrorLogger::ErrorMessage::FileLocation> callstack;
callstack.push_back(ErrorLogger::ErrorMessage::FileLocation(tok,tokenlist));
ErrorLogger::ErrorMessage errmsg(callstack, Severity::debug, "iterated too far", "debugValueFlowBeforeCondition", false);
errorLogger->reportErr(errmsg);
}
break;
}
if (tok2->varId() == varid) {
// bailout: assignment
if (Token::Match(tok2, "%var% =")) {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
break;
}
tok2->values.push_back(val); tok2->values.push_back(val);
if (tok2->str() == "{") { if (var && tok2 == var->nameToken())
if (!Token::simpleMatch(tok2->previous(), ") {"))
break;
if (!Token::simpleMatch(tok2->previous()->link()->previous(), "if ("))
break; break;
} }
if (tok2->str() == "}") {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " stopping on }");
break;
}
} }
} }
} }
void ValueFlow::setValues(Token *tokens) static void valueFlowSubFunction(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{ {
for (Token *tok = tokens; tok; tok = tok->next()) std::list<ValueFlow::Value> argvalues;
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
if (Token::Match(tok, "[(,] %var% [,)]") && !tok->next()->values.empty())
argvalues = tok->next()->values;
else if (Token::Match(tok, "[(,] %num% [,)]")) {
ValueFlow::Value val;
val.condition = 0;
val.intvalue = MathLib::toLongNumber(tok->next()->str());
argvalues.clear();
argvalues.push_back(val);
} else {
continue;
}
const Token * const argumentToken = tok->next();
// is this a function call?
const Token *ftok = tok;
while (ftok && ftok->str() != "(")
ftok = ftok->astParent();
if (!ftok || !ftok->astOperand1() || !ftok->astOperand2() || !ftok->astOperand1()->function())
continue;
// Get argument nr
unsigned int argnr = 0;
for (const Token *argtok = ftok->next(); argtok && argtok != argumentToken; argtok = argtok->nextArgument())
++ argnr;
// Get function argument, and check if parameter is passed by value
const Function * const function = ftok->astOperand1()->function();
const Variable * const arg = function ? function->getArgumentVar(argnr) : NULL;
if (!Token::Match(arg ? arg->typeStartToken() : NULL, "const| %type% %var% ,|)"))
continue;
// Function scope..
const Scope * const functionScope = function ? function->functionScope : NULL;
if (!functionScope)
continue;
// Set value in function scope..
const unsigned int varid2 = arg->nameToken()->varId();
for (const Token *tok2 = functionScope->classStart->next(); tok2 != functionScope->classEnd; tok2 = tok2->next()) {
if (Token::Match(tok2, "%cop%|return %varid%", varid2)) {
tok2 = tok2->next();
std::list<ValueFlow::Value> &values = const_cast<Token*>(tok2)->values;
values.insert(values.begin(), argvalues.begin(), argvalues.end());
} else if (tok2->varId() == varid2 || tok2->str() == "{") {
if (settings->debugwarnings)
bailout(tokenlist, errorLogger, tok2, "parameter " + arg->nameToken()->str());
continue;
}
}
}
}
void ValueFlow::setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
{
for (Token *tok = tokenlist->front(); tok; tok = tok->next())
tok->values.clear(); tok->values.clear();
valueFlowBeforeCondition(tokens); valueFlowBeforeCondition(tokenlist, errorLogger, settings);
valueFlowSubFunction(tokenlist, errorLogger, settings);
} }

View File

@ -22,15 +22,17 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
class Token; class Token;
class TokenList;
class ErrorLogger;
class Settings;
namespace ValueFlow { namespace ValueFlow {
struct Value { struct Value {
const Token *link; const Token *condition;
long long intvalue; long long intvalue;
}; };
void setValues(Token *tokens); void setValues(TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings);
} }
#endif // valueflowH #endif // valueflowH

View File

@ -40,6 +40,7 @@ private:
TEST_CASE(unknownPattern) TEST_CASE(unknownPattern)
TEST_CASE(redundantNextPrevious) TEST_CASE(redundantNextPrevious)
TEST_CASE(internalError) TEST_CASE(internalError)
TEST_CASE(invalidMultiCompare);
} }
void check(const char code[]) { void check(const char code[]) {
@ -222,7 +223,9 @@ private:
" const Token *tok;\n" " const Token *tok;\n"
" Token::Match(tok, \"foo|%type|bar\");\n" " Token::Match(tok, \"foo|%type|bar\");\n"
"}"); "}");
ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (error) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \"foo|%type|bar\"\n"
"[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n"
, errout.str());
// Make sure we don't take %or% for a broken %oror% // Make sure we don't take %or% for a broken %oror%
check("void f() {\n" check("void f() {\n"
@ -312,6 +315,27 @@ private:
"};"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void invalidMultiCompare() {
// #5310
check("void f() {\n"
" const Token *tok;\n"
" Token::Match(tok, \";|%type%\");\n"
"}");
ASSERT_EQUALS("[test.cpp:3]: (error) Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%var%,%oror%) inside Token::Match() call: \";|%type%\"\n", errout.str());
check("void f() {\n"
" const Token *tok;\n"
" Token::Match(tok, \";|%oror%\");\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() {\n" // The %var%|%num% works..
" const Token *tok;\n"
" Token::Match(tok, \"%var%|%num%\");\n"
"}");
ASSERT_EQUALS("", errout.str());
}
}; };
REGISTER_TEST(TestInternal) REGISTER_TEST(TestInternal)

View File

@ -135,6 +135,7 @@ private:
TEST_CASE(template40); // #5055 - template specialization outside struct TEST_CASE(template40); // #5055 - template specialization outside struct
TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly
TEST_CASE(template42); // #4878 - variadic templates TEST_CASE(template42); // #4878 - variadic templates
TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation
TEST_CASE(template_unhandled); TEST_CASE(template_unhandled);
TEST_CASE(template_default_parameter); TEST_CASE(template_default_parameter);
TEST_CASE(template_default_type); TEST_CASE(template_default_type);
@ -2367,6 +2368,19 @@ private:
tok(code); tok(code);
} }
void template43() { // #5097 - Assert due to '>>' in 'B<A<C>>' not being treated as end of template instantation
const char code[] = "template <typename T> struct C { };"
"template <typename T> struct D { static int f() { return C<T>::f(); } };"
"template <typename T> inline int f2() { return D<T>::f(); }"
"template <typename T> int f1(int x, T *) { int id = f2<T>(); return id; }"
"template <> struct C < B < A >> {"
" static int f() {"
" return f1 < B < A >> (0, reinterpret_cast< B<A> *>(E<void *>::Int(-1)));"
" }"
"};";
tok(code); // Don't assert
}
void template_default_parameter() { void template_default_parameter() {
{ {

View File

@ -1760,6 +1760,19 @@ private:
"};"); "};");
ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(false)); // Default false, but we saw "virtual" -> true ASSERT(db && db->findScopeByName("Deri") && db->findScopeByName("Deri")->functionList.front().isImplicitlyVirtual(false)); // Default false, but we saw "virtual" -> true
} }
// #5289
{
GET_SYMBOL_DB("template<>\n"
"class Bar<void, void> {\n"
"};\n"
"template<typename K, typename V, int KeySize>\n"
"class Bar : private Bar<void, void> {\n"
" void foo() {\n"
" }\n"
"};");
ASSERT(db && db->findScopeByName("Bar") && !db->findScopeByName("Bar")->functionList.front().isImplicitlyVirtual(false));
}
} }
void garbage() { void garbage() {

View File

@ -34,7 +34,8 @@ public:
private: private:
void run() { void run() {
valueFlowBeforeCondition(); TEST_CASE(valueFlowBeforeCondition);
TEST_CASE(valueFlowSubFunction);
} }
bool testValueOfX(const char code[], unsigned int linenr, int value) { bool testValueOfX(const char code[], unsigned int linenr, int value) {
@ -53,19 +54,71 @@ private:
if (it->intvalue == value) if (it->intvalue == value)
return true; return true;
} }
return false;
} }
} }
return false; return false;
} }
void bailout(const char code[]) {
Settings settings;
settings.valueFlow = true; // temporary flag
settings.debugwarnings = true;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
errout.str("");
tokenizer.tokenize(istr, "test.cpp");
}
void valueFlowBeforeCondition() { void valueFlowBeforeCondition() {
const char code[] = "void f(int x) {\n" const char code[] = "void f(int x) {\n"
" int a = x;\n" " int a = x;\n"
" if (x == 123) {}\n" " if (x == 123) {}\n"
"}"; "}";
ASSERT_EQUALS(true, testValueOfX(code, 2U, 123)); ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
// bailout: if/else/etc
bailout("void f(int x) {\n"
" if (x != 123) { b = x; }\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: variable x stopping on }\n", errout.str());
// bailout: assignment
bailout("void f(int x) {\n"
" x = y;\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:2]: (debug) ValueFlow bailout: assignment of x\n", errout.str());
// bailout: global variables
bailout("int x;\n"
"void f() {\n"
" int a = x;\n"
" if (x == 123) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (debug) ValueFlow bailout: global variable x\n", errout.str());
}
void valueFlowSubFunction() {
const char *code;
code = "void f1(int x) { return x; }\n"
"void f2(int x) {\n"
" f1(123);\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 123));
code = "void f1(int x) { return x; }\n"
"void f2(int x) {\n"
" f1(x);\n"
" if (x==0){}\n"
"}";
ASSERT_EQUALS(true, testValueOfX(code, 1U, 0));
} }
}; };

View File

@ -31,13 +31,6 @@ def readdate(data):
return None return None
datepos = datepos + 1 datepos = datepos + 1
if os.path.isfile(os.path.expanduser('~/aws-debian.pem')):
subprocess.call(['scp',
'-i',
os.path.expanduser('~/aws-debian.pem'),
'admin@ec2-54-201-59-232.us-west-2.compute.amazonaws.com:daca2/results-*.txt',
os.path.expanduser('~/daca2/')])
path = '.' path = '.'
if len(sys.argv) == 2: if len(sys.argv) == 2:
path = sys.argv[1] path = sys.argv[1]

0
tools/times-tags.sh Normal file → Executable file
View File

0
tools/times.sh Normal file → Executable file
View File