This commit is contained in:
Sébastien Debrard 2011-02-13 21:57:42 +01:00
commit de50e21ada
20 changed files with 872 additions and 503 deletions

View File

@ -60,8 +60,13 @@ void FileListerUnix::recursiveAddFiles2(std::vector<std::string> &relative,
if (filename[filename.length()-1] != '/') if (filename[filename.length()-1] != '/')
{ {
// File // File
#ifdef PATH_MAX
char fname[PATH_MAX]; char fname[PATH_MAX];
if (realpath(filename.c_str(), fname) == NULL) if (realpath(filename.c_str(), fname) == NULL)
#else
char *fname;
if ((fname = realpath(filename.c_str(), NULL)) == NULL)
#endif
{ {
continue; continue;
} }
@ -69,6 +74,9 @@ void FileListerUnix::recursiveAddFiles2(std::vector<std::string> &relative,
// Does absolute path exist? then bail out // Does absolute path exist? then bail out
if (std::find(absolute.begin(), absolute.end(), std::string(fname)) != absolute.end()) if (std::find(absolute.begin(), absolute.end(), std::string(fname)) != absolute.end())
{ {
#ifndef PATH_MAX
free(fname);
#endif
continue; continue;
} }
@ -77,6 +85,10 @@ void FileListerUnix::recursiveAddFiles2(std::vector<std::string> &relative,
relative.push_back(filename); relative.push_back(filename);
absolute.push_back(fname); absolute.push_back(fname);
} }
#ifndef PATH_MAX
free(fname);
#endif
} }
else else
{ {

View File

@ -42,12 +42,18 @@ public:
: Check(myName(), tokenizer, settings, errorLogger) : Check(myName(), tokenizer, settings, errorLogger)
{ } { }
/** @brief Run checks against the normal token list */
void runChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
{
CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger);
checkAutoVariables.returnReference();
}
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
{ {
CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger); CheckAutoVariables checkAutoVariables(tokenizer, settings, errorLogger);
checkAutoVariables.autoVariables(); checkAutoVariables.autoVariables();
checkAutoVariables.returnPointerToLocalArray(); checkAutoVariables.returnPointerToLocalArray();
checkAutoVariables.returnReference();
checkAutoVariables.returncstr(); checkAutoVariables.returncstr();
} }

View File

@ -791,7 +791,7 @@ void CheckBufferOverrun::checkScopeForBody(const Token *tok, const ArrayInfo &ar
return; return;
// Get index variable and stopsize. // Get index variable and stopsize.
bool condition_out_of_bounds = true; bool condition_out_of_bounds = bool(size > 0);
if (MathLib::toLongNumber(max_counter_value) < size) if (MathLib::toLongNumber(max_counter_value) < size)
condition_out_of_bounds = false; condition_out_of_bounds = false;

View File

@ -317,12 +317,21 @@ void CheckOther::checkSelfAssignment()
if (!_settings->_checkCodingStyle) if (!_settings->_checkCodingStyle)
return; return;
// POD variables..
std::set<unsigned int> pod;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
{
if (tok->isStandardType() && tok->next()->varId() && Token::Match(tok->tokAt(2), "[,);]"))
pod.insert(tok->next()->varId());
}
const char selfAssignmentPattern[] = "%var% = %var% ;|=|)"; const char selfAssignmentPattern[] = "%var% = %var% ;|=|)";
const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern); const Token *tok = Token::findmatch(_tokenizer->tokens(), selfAssignmentPattern);
while (tok) while (tok)
{ {
if (Token::Match(tok->previous(), "[;{}]") && if (Token::Match(tok->previous(), "[;{}]") &&
tok->varId() && tok->varId() == tok->tokAt(2)->varId()) tok->varId() && tok->varId() == tok->tokAt(2)->varId() &&
pod.find(tok->varId()) != pod.end())
{ {
selfAssignmentError(tok, tok->str()); selfAssignmentError(tok, tok->str());
} }

View File

@ -25,6 +25,7 @@
#include <iostream> #include <iostream>
#include <cctype> // std::isdigit, std::isalnum, etc #include <cctype> // std::isdigit, std::isalnum, etc
#include <set> #include <set>
#include <stack>
Settings::Settings() Settings::Settings()
{ {
@ -116,6 +117,137 @@ std::string Settings::Suppressions::parseFile(std::istream &istr)
return ""; return "";
} }
bool Settings::Suppressions::FileMatcher::match(const std::string &pattern, const std::string &name)
{
const char *p = pattern.c_str();
const char *n = name.c_str();
std::stack<std::pair<const char *, const char *> > backtrack;
for (;;)
{
bool matching = true;
while (*p != '\0' && matching)
{
switch (*p)
{
case '*':
// Step forward until we match the next character after *
while (*n != '\0' && *n != p[1])
{
n++;
}
if (*n != '\0')
{
// If this isn't the last possibility, save it for later
backtrack.push(std::make_pair(p, n));
}
break;
case '?':
// Any character matches unless we're at the end of the name
if (*n != '\0')
{
n++;
}
else
{
matching = false;
}
break;
default:
// Non-wildcard characters match literally
if (*n == *p)
{
n++;
}
else
{
matching = false;
}
break;
}
p++;
}
// If we haven't failed matching and we've reached the end of the name, then success
if (matching && *n == '\0')
{
return true;
}
// If there are no other paths to tray, then fail
if (backtrack.empty())
{
return false;
}
// Restore pointers from backtrack stack
p = backtrack.top().first;
n = backtrack.top().second;
backtrack.pop();
// Advance name pointer by one because the current position didn't work
n++;
}
}
std::string Settings::Suppressions::FileMatcher::addFile(const std::string &name, unsigned int line)
{
if (name.find_first_of("*?") != std::string::npos)
{
for (std::string::const_iterator i = name.begin(); i != name.end(); ++i)
{
if (*i == '*')
{
std::string::const_iterator j = i + 1;
if (j != name.end() && (*j == '*' || *j == '?'))
{
return "Failed to add suppression. Syntax error in glob.";
}
}
}
_globs[name].insert(line);
}
else
{
_files[name].insert(line);
}
return "";
}
bool Settings::Suppressions::FileMatcher::isSuppressed(const std::string &file, unsigned int line)
{
// Check are all errors of this type filtered out
if (_files.find("") != _files.end())
return true;
std::set<unsigned int> lineset;
std::map<std::string, std::set<unsigned int> >::const_iterator f = _files.find(file);
if (f != _files.end())
{
lineset.insert(f->second.begin(), f->second.end());
}
for (std::map<std::string, std::set<unsigned int> >::iterator g = _globs.begin(); g != _globs.end(); ++g)
{
if (match(g->first, file))
{
lineset.insert(g->second.begin(), g->second.end());
}
}
if (lineset.empty())
return false;
// Check should all errors in this file be filtered out
if (lineset.find(0U) != lineset.end())
return true;
if (lineset.find(line) == lineset.end())
return false;
return true;
}
std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line) std::string Settings::Suppressions::addSuppression(const std::string &errorId, const std::string &file, unsigned int line)
{ {
// Check that errorId is valid.. // Check that errorId is valid..
@ -135,10 +267,7 @@ std::string Settings::Suppressions::addSuppression(const std::string &errorId, c
} }
} }
_suppressions[errorId][file].push_back(line); return _suppressions[errorId].addFile(file, line);
_suppressions[errorId][file].sort();
return "";
} }
bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line) bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std::string &file, unsigned int line)
@ -146,21 +275,7 @@ bool Settings::Suppressions::isSuppressed(const std::string &errorId, const std:
if (_suppressions.find(errorId) == _suppressions.end()) if (_suppressions.find(errorId) == _suppressions.end())
return false; return false;
// Check are all errors of this type filtered out return _suppressions[errorId].isSuppressed(file, line);
if (_suppressions[errorId].find("") != _suppressions[errorId].end())
return true;
if (_suppressions[errorId].find(file) == _suppressions[errorId].end())
return false;
// Check should all errors in this file be filtered out
if (std::find(_suppressions[errorId][file].begin(), _suppressions[errorId][file].end(), 0) != _suppressions[errorId][file].end())
return true;
if (std::find(_suppressions[errorId][file].begin(), _suppressions[errorId][file].end(), line) == _suppressions[errorId][file].end())
return false;
return true;
} }
std::string Settings::addEnabled(const std::string &str) std::string Settings::addEnabled(const std::string &str)

View File

@ -23,6 +23,7 @@
#include <string> #include <string>
#include <istream> #include <istream>
#include <map> #include <map>
#include <set>
/// @addtogroup Core /// @addtogroup Core
/// @{ /// @{
@ -135,8 +136,42 @@ public:
class Suppressions class Suppressions
{ {
private: private:
class FileMatcher
{
private:
/** @brief List of filenames suppressed. */
std::map<std::string, std::set<unsigned int> > _files;
/** @brief List of globs suppressed. */
std::map<std::string, std::set<unsigned int> > _globs;
/**
* @brief Match a name against a glob pattern.
* @param pattern The glob pattern to match.
* @param name The filename to match against the glob pattern.
* @return match success
*/
static bool match(const std::string &pattern, const std::string &name);
public:
/**
* @brief Add a file or glob (and line number).
* @param name File name or glob pattern
* @param line Line number
* @return error message. empty upon success
*/
std::string addFile(const std::string &name, unsigned int line);
/**
* @brief Returns true if the file name matches a previously added file or glob pattern.
* @param name File name to check
* @param line Line number
* @return true if this filename/line matches
*/
bool isSuppressed(const std::string &file, unsigned int line);
};
/** @brief List of error which the user doesn't want to see. */ /** @brief List of error which the user doesn't want to see. */
std::map<std::string, std::map<std::string, std::list<unsigned int> > > _suppressions; std::map<std::string, FileMatcher> _suppressions;
public: public:
/** /**
* @brief Don't show errors listed in the file. * @brief Don't show errors listed in the file.

View File

@ -227,7 +227,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti
// out of line function // out of line function
if (Token::Match(end, ") const| ;") || if (Token::Match(end, ") const| ;") ||
Token::Match(end, ") const| = %any% ;")) Token::Match(end, ") const| = %any%"))
{ {
// find the function implementation later // find the function implementation later
tok = end->next(); tok = end->next();

View File

@ -854,6 +854,65 @@ static Token *splitDefinitionFromTypedef(Token *tok)
return tok; return tok;
} }
/* This function is called when processing function related typedefs.
* If simplifyTypedef generates an "Internal Error" message and the
* code that generated it deals in some way with functions, then this
* fucntion will probably need to be extended to handle a new function
* related pattern */
static Token *processFunc(Token *tok2, bool inOperator)
{
if (tok2->next() && tok2->next()->str() != ")" &&
tok2->next()->str() != ",")
{
// skip over tokens for some types of canonicalization
if (Token::Match(tok2->next(), "( * %type% ) ("))
tok2 = tok2->tokAt(5)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) ("))
tok2 = tok2->tokAt(6)->link();
else if (Token::Match(tok2->next(), "* ( %type% [") &&
Token::Match(tok2->tokAt(4)->link(), "] ) ;|="))
tok2 = tok2->tokAt(4)->link()->next();
else if (Token::Match(tok2->next(), "* ( * %type% ("))
tok2 = tok2->tokAt(5)->link()->next();
else
{
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
else if (!inOperator && !Token::Match(tok2->next(), "[|>|;"))
{
tok2 = tok2->next();
while (Token::Match(tok2, "*|&") &&
!Token::Match(tok2->next(), ")|>"))
tok2 = tok2->next();
// skip over namespace
while (Token::Match(tok2, "%var% ::"))
tok2 = tok2->tokAt(2);
if (tok2->str() == "(" &&
tok2->link()->next()->str() == "(")
{
tok2 = tok2->link();
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
}
// skip over typedef parameter
if (tok2->next()->str() == "(")
{
tok2 = tok2->next()->link();
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
}
}
}
}
return tok2;
}
void Tokenizer::simplifyTypedef() void Tokenizer::simplifyTypedef()
{ {
std::vector<Space> spaceInfo; std::vector<Space> spaceInfo;
@ -1473,49 +1532,7 @@ void Tokenizer::simplifyTypedef()
tok2 = copyTokens(tok2, funcStart, funcEnd); tok2 = copyTokens(tok2, funcStart, funcEnd);
if (!inCast) if (!inCast)
{ tok2 = processFunc(tok2, inOperator);
if (tok2->next() && tok2->next()->str() != ")" &&
tok2->next()->str() != ",")
{
if (Token::Match(tok2->next(), "( * %type% ) ("))
tok2 = tok2->tokAt(5)->link();
else
{
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
else if (!inOperator && !Token::Match(tok2->next(), "[|>|;"))
{
tok2 = tok2->next();
while (Token::Match(tok2, "*|&") &&
!Token::Match(tok2->next(), ")|>"))
tok2 = tok2->next();
// skip over namespace
while (Token::Match(tok2, "%var% ::"))
tok2 = tok2->tokAt(2);
if (tok2->str() == "(" &&
tok2->link()->next()->str() == "(")
{
tok2 = tok2->link();
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
}
// skip over typedef parameter
else if (tok2->next()->str() == "(")
{
tok2 = tok2->next()->link();
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
}
}
}
}
}
tok2->insertToken(")"); tok2->insertToken(")");
tok2 = tok2->next(); tok2 = tok2->next();
@ -1586,45 +1603,7 @@ void Tokenizer::simplifyTypedef()
} }
if (!inCast) if (!inCast)
{ tok2 = processFunc(tok2, inOperator);
if (tok2->next() && tok2->next()->str() != ")" &&
tok2->next()->str() != ",")
{
if (Token::Match(tok2->next(), "( * %type% ) ("))
tok2 = tok2->tokAt(5)->link();
else if (Token::Match(tok2->next(), "* ( * %type% ) ("))
tok2 = tok2->tokAt(6)->link();
else if (Token::Match(tok2->next(), "* ( %type% [") &&
Token::Match(tok2->tokAt(4)->link(), "] ) ;|="))
tok2 = tok2->tokAt(4)->link()->next();
else
{
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
else if (!inOperator && !Token::Match(tok2->next(), "[|>|;"))
{
tok2 = tok2->next();
while (Token::Match(tok2, "*|&") &&
!Token::Match(tok2->next(), ")|>"))
tok2 = tok2->next();
// skip over namespace
while (Token::Match(tok2, "%var% ::"))
tok2 = tok2->tokAt(2);
// skip over typedef parameter
if (tok2->next()->str() == "(")
{
tok2 = tok2->next()->link();
if (tok2->next()->str() == "(")
tok2 = tok2->next()->link();
}
}
}
}
}
if (needParen) if (needParen)
{ {
@ -2290,14 +2269,10 @@ bool Tokenizer::tokenize(std::istream &code,
// typedef.. // typedef..
simplifyTypedef(); simplifyTypedef();
// Fix internal error by updating links (#2376) // catch bad typedef canonicalization
// TODO: Remove this "createLinks". Make sure that the testcase if (!validate())
// TestSimplifyTokens::simplifyTypedefFunction8
// doesn't fail.
if (!createLinks())
{ {
// Source has syntax errors, can't proceed // Source has syntax errors, can't proceed
cppcheckError(0);
return false; return false;
} }
@ -2548,7 +2523,7 @@ static void removeTemplates(Token *tok)
} }
} }
void Tokenizer::simplifyTemplates() std::set<std::string> Tokenizer::simplifyTemplatesExpandSpecialized()
{ {
std::set<std::string> expandedtemplates; std::set<std::string> expandedtemplates;
@ -2614,7 +2589,11 @@ void Tokenizer::simplifyTemplates()
} }
} }
// Locate templates.. return expandedtemplates;
}
std::list<Token *> Tokenizer::simplifyTemplatesGetTemplateDeclarations()
{
std::list<Token *> templates; std::list<Token *> templates;
for (Token *tok = _tokens; tok; tok = tok->next()) for (Token *tok = _tokens; tok; tok = tok->next())
{ {
@ -2639,30 +2618,13 @@ void Tokenizer::simplifyTemplates()
} }
} }
} }
if (templates.empty()) return templates;
{ }
removeTemplates(_tokens);
return;
}
// There are templates.. std::list<Token *> Tokenizer::simplifyTemplatesGetTemplateInstantiations()
// Remove "typename" unless used in template arguments.. {
for (Token *tok = _tokens; tok; tok = tok->next())
{
if (tok->str() == "typename")
tok->deleteThis();
if (Token::simpleMatch(tok, "template <"))
{
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
// Locate possible instantiations of templates..
std::list<Token *> used; std::list<Token *> used;
for (Token *tok = _tokens; tok; tok = tok->next()) for (Token *tok = _tokens; tok; tok = tok->next())
{ {
// template definition.. skip it // template definition.. skip it
@ -2703,18 +2665,14 @@ void Tokenizer::simplifyTemplates()
} }
} }
// No template instantiations? Then remove all templates. return used;
if (used.empty()) }
{
removeTemplates(_tokens);
return;
}
void Tokenizer::simplifyTemplatesUseDefaultArgumentValues(const std::list<Token *> &templates,
const std::list<Token *> &instantiations)
// Template arguments with default values {
for (std::list<Token *>::iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) for (std::list<Token *>::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1)
{ {
// template parameters with default value has syntax such as: // template parameters with default value has syntax such as:
// x = y // x = y
@ -2750,7 +2708,7 @@ void Tokenizer::simplifyTemplates()
continue; continue;
// iterate through all template instantiations // iterate through all template instantiations
for (std::list<Token *>::iterator iter2 = used.begin(); iter2 != used.end(); ++iter2) for (std::list<Token *>::const_iterator iter2 = instantiations.begin(); iter2 != instantiations.end(); ++iter2)
{ {
Token *tok = *iter2; Token *tok = *iter2;
@ -2780,8 +2738,21 @@ void Tokenizer::simplifyTemplates()
{ {
tok->insertToken(","); tok->insertToken(",");
tok = tok->next(); tok = tok->next();
tok->insertToken((*it)->strAt(1)); const Token *from = (*it)->next();
tok = tok->next(); std::stack<Token *> links;
while (from && (!links.empty() || (from->str() != "," && from->str() != ">")))
{
tok->insertToken(from->str());
tok = tok->next();
if (Token::Match(tok, "(|["))
links.push(tok);
else if (!links.empty() && Token::Match(tok, ")|]"))
{
Token::createMutualLinks(links.top(), tok);
links.pop();
}
from = from->next();
}
++it; ++it;
} }
} }
@ -2793,325 +2764,377 @@ void Tokenizer::simplifyTemplates()
(*it)->deleteThis(); (*it)->deleteThis();
} }
} }
}
void Tokenizer::simplifyTemplatesInstantiate(const Token *tok,
std::list<Token *> &used,
std::set<std::string> &expandedtemplates)
{
// this variable is not used at the moment. the intention was to
// allow continous instantiations until all templates has been expanded
bool done = false;
std::vector<const Token *> type;
for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next())
{
if (Token::Match(tok, "%var% ,|>"))
type.push_back(tok);
}
// bail out if the end of the file was reached
if (!tok)
return;
// get the position of the template name
unsigned char namepos = 0;
if (Token::Match(tok, "> class|struct %type% {|:"))
namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2;
else if (Token::Match(tok, "> %type% %type% *|&| %type% ("))
namepos = 3;
else
{
// debug message that we bail out..
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok->linenr();
loc.setfile(file(tok));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"simplifyTemplates: bailing out",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
return;
}
if ((tok->tokAt(namepos)->str() == "*" || tok->tokAt(namepos)->str() == "&"))
++namepos;
// name of template function/class..
const std::string name(tok->strAt(namepos));
const bool isfunc(tok->strAt(namepos + 1) == "(");
// locate template usage..
std::string s(name + " <");
for (unsigned int i = 0; i < type.size(); ++i)
{
if (i > 0)
s += ",";
s += " %any% ";
}
const std::string pattern(s + "> ");
std::string::size_type sz1 = used.size();
unsigned int recursiveCount = 0;
for (std::list<Token *>::const_iterator iter2 = used.begin(); iter2 != used.end(); ++iter2)
{
// If the size of "used" has changed, simplify calculations
if (sz1 != used.size())
{
sz1 = used.size();
simplifyCalculations();
recursiveCount++;
if (recursiveCount > 100)
{
// bail out..
break;
}
}
Token * const tok2 = *iter2;
if (tok2->str() != name)
continue;
if (Token::Match(tok2->previous(), "[;{}=]") &&
!Token::Match(tok2, (pattern + (isfunc ? "(" : "*| %var%")).c_str()))
continue;
// New type..
std::vector<Token> types2;
s = "";
std::string s1(name + " < ");
for (const Token *tok3 = tok2->tokAt(2); tok3 && tok3->str() != ">"; tok3 = tok3->next())
{
if (!tok3->next())
{
s.clear();
break;
}
s1 += tok3->str();
s1 += " ";
if (tok3->str() != ",")
types2.push_back(*tok3);
// add additional type information
if (tok3->isUnsigned())
s += "unsigned";
else if (tok3->isSigned())
s += "signed";
if (tok3->isLong())
s += "long";
s += tok3->str();
}
s1 += ">";
const std::string type2(s);
if (type2.empty() || type.size() != types2.size())
{
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok2->linenr();
loc.setfile(file(tok2));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"Failed to instantiate template. The checking continues anyway.",
"debug");
_errorLogger->reportErr(errmsg);
}
if (type2.empty())
continue;
break;
}
// New classname/funcname..
const std::string name2(name + "<" + type2 + ">");
if (expandedtemplates.find(name2) == expandedtemplates.end())
{
expandedtemplates.insert(name2);
// Copy template..
int _indentlevel = 0;
int _parlevel = 0;
for (const Token *tok3 = _tokens; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++_indentlevel;
else if (tok3->str() == "}")
--_indentlevel;
else if (tok3->str() == "(")
++_parlevel;
else if (tok3->str() == ")")
--_parlevel;
// Start of template..
if (tok3 == tok)
{
tok3 = tok3->next();
}
// member function implemented outside class definition
else if (_indentlevel == 0 && _parlevel == 0 && Token::Match(tok3, (pattern + " :: ~| %var% (").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
while (tok3->str() != "::")
tok3 = tok3->next();
}
// not part of template.. go on to next token
else
continue;
int indentlevel = 0;
std::stack<Token *> braces; // holds "{" tokens
std::stack<Token *> brackets; // holds "(" tokens
std::stack<Token *> brackets2; // holds "[" tokens
for (; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++indentlevel;
else if (tok3->str() == "}")
{
if (indentlevel <= 1 && brackets.empty() && brackets2.empty())
{
// there is a bug if indentlevel is 0
// the "}" token should only be added if indentlevel is 1 but I add it always intentionally
// if indentlevel ever becomes 0, cppcheck will write:
// ### Error: Invalid number of character {
addtoken("}", tok3->linenr(), tok3->fileIndex());
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
break;
}
--indentlevel;
}
if (tok3->isName())
{
// search for this token in the type vector
unsigned int itype = 0;
while (itype < type.size() && type[itype]->str() != tok3->str())
++itype;
// replace type with given type..
if (itype < type.size())
{
addtoken(&types2[itype], tok3->linenr(), tok3->fileIndex());
continue;
}
}
// replace name..
if (Token::Match(tok3, (name + " !!<").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
continue;
}
// copy
addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <"))
{
if (!Token::Match(tok3, (name + " <").c_str()))
done = false;
used.push_back(_tokensBack);
}
// link() newly tokens manually
if (tok3->str() == "{")
{
braces.push(_tokensBack);
}
else if (tok3->str() == "}")
{
assert(braces.empty() == false);
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
}
else if (tok3->str() == "(")
{
brackets.push(_tokensBack);
}
else if (tok3->str() == "[")
{
brackets2.push(_tokensBack);
}
else if (tok3->str() == ")")
{
assert(brackets.empty() == false);
Token::createMutualLinks(brackets.top(), _tokensBack);
brackets.pop();
}
else if (tok3->str() == "]")
{
assert(brackets2.empty() == false);
Token::createMutualLinks(brackets2.top(), _tokensBack);
brackets2.pop();
}
}
assert(braces.empty());
assert(brackets.empty());
}
}
// Replace all these template usages..
for (Token *tok4 = tok2; tok4; tok4 = tok4->next())
{
if (Token::simpleMatch(tok4, s1.c_str()))
{
bool match = true;
Token * tok5 = tok4->tokAt(2);
unsigned int count = 0;
while (tok5->str() != ">")
{
if (tok5->str() != ",")
{
if (tok5->isUnsigned() != types2[count].isUnsigned() ||
tok5->isSigned() != types2[count].isSigned() ||
tok5->isLong() != types2[count].isLong())
{
match = false;
break;
}
count++;
}
tok5 = tok5->next();
}
if (match)
{
tok4->str(name2);
while (tok4->next()->str() != ">")
{
used.remove(tok4->next());
tok4->deleteNext();
}
used.remove(tok4->next());
tok4->deleteNext();
}
}
}
}
}
void Tokenizer::simplifyTemplates()
{
std::set<std::string> expandedtemplates(simplifyTemplatesExpandSpecialized());
// Locate templates..
std::list<Token *> templates(simplifyTemplatesGetTemplateDeclarations());
if (templates.empty())
{
removeTemplates(_tokens);
return;
}
// There are templates..
// Remove "typename" unless used in template arguments..
for (Token *tok = _tokens; tok; tok = tok->next())
{
if (tok->str() == "typename")
tok->deleteThis();
if (Token::simpleMatch(tok, "template <"))
{
while (tok && tok->str() != ">")
tok = tok->next();
if (!tok)
break;
}
}
// Locate possible instantiations of templates..
std::list<Token *> used(simplifyTemplatesGetTemplateInstantiations());
// No template instantiations? Then remove all templates.
if (used.empty())
{
removeTemplates(_tokens);
return;
}
// Template arguments with default values
simplifyTemplatesUseDefaultArgumentValues(templates, used);
// expand templates // expand templates
bool done = false; bool done = false;
//while (!done) //while (!done)
{ {
done = true; done = true;
for (std::list<Token *>::iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1) for (std::list<Token *>::const_iterator iter1 = templates.begin(); iter1 != templates.end(); ++iter1)
{ {
Token *tok = *iter1; simplifyTemplatesInstantiate(*iter1, used, expandedtemplates);
std::vector<const Token *> type;
for (tok = tok->tokAt(2); tok && tok->str() != ">"; tok = tok->next())
{
if (Token::Match(tok, "%var% ,|>"))
type.push_back(tok);
}
// bail out if the end of the file was reached
if (!tok)
break;
// get the position of the template name
unsigned char namepos = 0;
if (Token::Match(tok, "> class|struct %type% {|:"))
namepos = 2;
else if (Token::Match(tok, "> %type% *|&| %type% ("))
namepos = 2;
else if (Token::Match(tok, "> %type% %type% *|&| %type% ("))
namepos = 3;
else
{
// debug message that we bail out..
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok->linenr();
loc.setfile(file(tok));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"simplifyTemplates: bailing out",
"debug");
if (_errorLogger)
_errorLogger->reportErr(errmsg);
else
Check::reportError(errmsg);
}
continue;
}
if ((tok->tokAt(namepos)->str() == "*" || tok->tokAt(namepos)->str() == "&"))
++namepos;
// name of template function/class..
const std::string name(tok->strAt(namepos));
const bool isfunc(tok->strAt(namepos + 1) == "(");
// locate template usage..
std::string s(name + " <");
for (unsigned int i = 0; i < type.size(); ++i)
{
if (i > 0)
s += ",";
s += " %any% ";
}
const std::string pattern(s + "> ");
std::string::size_type sz1 = used.size();
unsigned int recursiveCount = 0;
for (std::list<Token *>::iterator iter2 = used.begin(); iter2 != used.end(); ++iter2)
{
// If the size of "used" has changed, simplify calculations
if (sz1 != used.size())
{
sz1 = used.size();
simplifyCalculations();
recursiveCount++;
if (recursiveCount > 100)
{
// bail out..
break;
}
}
Token * const tok2 = *iter2;
if (tok2->str() != name)
continue;
if (Token::Match(tok2->previous(), "[;{}=]") &&
!Token::Match(tok2, (pattern + (isfunc ? "(" : "*| %var%")).c_str()))
continue;
// New type..
std::vector<Token> types2;
s = "";
std::string s1(name + " < ");
for (const Token *tok3 = tok2->tokAt(2); tok3 && tok3->str() != ">"; tok3 = tok3->next())
{
if (!tok3->next())
{
s.clear();
break;
}
s1 += tok3->str();
s1 += " ";
if (tok3->str() != ",")
types2.push_back(*tok3);
// add additional type information
if (tok3->isUnsigned())
s += "unsigned";
else if (tok3->isSigned())
s += "signed";
if (tok3->isLong())
s += "long";
s += tok3->str();
}
s1 += ">";
const std::string type2(s);
if (type2.empty() || type.size() != types2.size())
{
if (_settings->debugwarnings)
{
std::list<ErrorLogger::ErrorMessage::FileLocation> locationList;
ErrorLogger::ErrorMessage::FileLocation loc;
loc.line = tok2->linenr();
loc.setfile(file(tok2));
locationList.push_back(loc);
const ErrorLogger::ErrorMessage errmsg(locationList,
Severity::debug,
"Failed to instantiate template. The checking continues anyway.",
"debug");
_errorLogger->reportErr(errmsg);
}
if (type2.empty())
continue;
break;
}
// New classname/funcname..
const std::string name2(name + "<" + type2 + ">");
if (expandedtemplates.find(name2) == expandedtemplates.end())
{
expandedtemplates.insert(name2);
// Copy template..
int _indentlevel = 0;
int _parlevel = 0;
for (const Token *tok3 = _tokens; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++_indentlevel;
else if (tok3->str() == "}")
--_indentlevel;
else if (tok3->str() == "(")
++_parlevel;
else if (tok3->str() == ")")
--_parlevel;
// Start of template..
if (tok3 == tok)
{
tok3 = tok3->next();
}
// member function implemented outside class definition
else if (_indentlevel == 0 && _parlevel == 0 && Token::Match(tok3, (pattern + " :: ~| %var% (").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
while (tok3->str() != "::")
tok3 = tok3->next();
}
// not part of template.. go on to next token
else
continue;
int indentlevel = 0;
std::stack<Token *> braces; // holds "{" tokens
std::stack<Token *> brackets; // holds "(" tokens
std::stack<Token *> brackets2; // holds "[" tokens
for (; tok3; tok3 = tok3->next())
{
if (tok3->str() == "{")
++indentlevel;
else if (tok3->str() == "}")
{
if (indentlevel <= 1 && brackets.empty() && brackets2.empty())
{
// there is a bug if indentlevel is 0
// the "}" token should only be added if indentlevel is 1 but I add it always intentionally
// if indentlevel ever becomes 0, cppcheck will write:
// ### Error: Invalid number of character {
addtoken("}", tok3->linenr(), tok3->fileIndex());
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
break;
}
--indentlevel;
}
if (tok3->isName())
{
// search for this token in the type vector
unsigned int itype = 0;
while (itype < type.size() && type[itype]->str() != tok3->str())
++itype;
// replace type with given type..
if (itype < type.size())
{
addtoken(&types2[itype], tok3->linenr(), tok3->fileIndex());
continue;
}
}
// replace name..
if (Token::Match(tok3, (name + " !!<").c_str()))
{
addtoken(name2.c_str(), tok3->linenr(), tok3->fileIndex());
continue;
}
// copy
addtoken(tok3, tok3->linenr(), tok3->fileIndex());
if (Token::Match(tok3, "%type% <"))
{
if (!Token::Match(tok3, (name + " <").c_str()))
done = false;
used.push_back(_tokensBack);
}
// link() newly tokens manually
if (tok3->str() == "{")
{
braces.push(_tokensBack);
}
else if (tok3->str() == "}")
{
assert(braces.empty() == false);
Token::createMutualLinks(braces.top(), _tokensBack);
braces.pop();
}
else if (tok3->str() == "(")
{
brackets.push(_tokensBack);
}
else if (tok3->str() == "[")
{
brackets2.push(_tokensBack);
}
else if (tok3->str() == ")")
{
assert(brackets.empty() == false);
Token::createMutualLinks(brackets.top(), _tokensBack);
brackets.pop();
}
else if (tok3->str() == "]")
{
assert(brackets2.empty() == false);
Token::createMutualLinks(brackets2.top(), _tokensBack);
brackets2.pop();
}
}
assert(braces.empty());
assert(brackets.empty());
}
}
// Replace all these template usages..
for (Token *tok4 = tok2; tok4; tok4 = tok4->next())
{
if (Token::simpleMatch(tok4, s1.c_str()))
{
bool match = true;
Token * tok5 = tok4->tokAt(2);
unsigned int count = 0;
while (tok5->str() != ">")
{
if (tok5->str() != ",")
{
if (tok5->isUnsigned() != types2[count].isUnsigned() ||
tok5->isSigned() != types2[count].isSigned() ||
tok5->isLong() != types2[count].isLong())
{
match = false;
break;
}
count++;
}
tok5 = tok5->next();
}
if (match)
{
tok4->str(name2);
while (tok4->next()->str() != ">")
{
used.remove(tok4->next());
tok4->deleteNext();
}
used.remove(tok4->next());
tok4->deleteNext();
}
}
}
}
} }
} }
@ -8725,7 +8748,7 @@ void Tokenizer::simplifyStructDecl()
} }
// unnamed anonymous struct/union so remove it // unnamed anonymous struct/union so remove it
else if (tok->next()->str() == ";") else if (tok->next() && tok->next()->str() == ";")
{ {
if (tok1->str() == "union") if (tok1->str() == "union")
{ {

View File

@ -24,7 +24,9 @@
#include <string> #include <string>
#include <map> #include <map>
#include <list>
#include <vector> #include <vector>
#include <set>
class Token; class Token;
class ErrorLogger; class ErrorLogger;
@ -395,6 +397,43 @@ public:
*/ */
void simplifyTemplates(); void simplifyTemplates();
/**
* Expand specialized templates : "template<>.."
* @return names of expanded templates
*/
std::set<std::string> simplifyTemplatesExpandSpecialized();
/**
* Get template declarations
* @return list of template declarations
*/
std::list<Token *> simplifyTemplatesGetTemplateDeclarations();
/**
* Get template instantiations
* @return list of template instantiations
*/
std::list<Token *> simplifyTemplatesGetTemplateInstantiations();
/**
* simplify template instantiations (use default argument values)
* @param templates list of template declarations
* @param instantiations list of template instantiations
*/
void simplifyTemplatesUseDefaultArgumentValues(const std::list<Token *> &templates,
const std::list<Token *> &instantiations);
/**
* Simplify templates : expand all instantiatiations for a template
* @todo It seems that inner templates should be instantiated recursively
* @param tok token where the template declaration begins
* @param used a list of template usages (not necessarily just for this template)
* @param expandedtemplates all templates that has been expanded so far. The full names are stored.
*/
void simplifyTemplatesInstantiate(const Token *tok,
std::list<Token *> &used,
std::set<std::string> &expandedtemplates);
/** /**
* Used after simplifyTemplates to perform a little cleanup. * Used after simplifyTemplates to perform a little cleanup.
* Sometimes the simplifyTemplates isn't fully successful and then * Sometimes the simplifyTemplates isn't fully successful and then

View File

@ -307,7 +307,8 @@ Directory name is matched to all parts of the path.</para>
<term><option>--suppressions-list=&lt;file&gt;</option></term> <term><option>--suppressions-list=&lt;file&gt;</option></term>
<listitem> <listitem>
<para>Suppress warnings listed in the file. Filename and line are optional. The format of the single line in file is: [error id]:[filename]:[line]. <para>Suppress warnings listed in the file. Filename and line are optional. The format of the single line in file is: [error id]:[filename]:[line].
You can use --template or --xml to see the error id.</para> You can use --template or --xml to see the error id.
The filename may contain the wildcard characters * or ?.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -385,6 +385,10 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
line flag. Copy and paste the <literal>id</literal> string from the XML line flag. Copy and paste the <literal>id</literal> string from the XML
output.</para> output.</para>
<para>The <literal>filename</literal> may include the wildcard characters
* or ?, which match any sequence of characters or any single character
respectively.</para>
<para>Here is an example:</para> <para>Here is an example:</para>
<programlisting>memleak:file1.cpp <programlisting>memleak:file1.cpp

View File

@ -47,6 +47,10 @@ private:
Tokenizer tokenizer(&settings, this); Tokenizer tokenizer(&settings, this);
std::istringstream istr(code); std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp"); tokenizer.tokenize(istr, "test.cpp");
CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this);
checkAutoVariables.runChecks(&tokenizer, &settings, this);
tokenizer.simplifyTokenList(); tokenizer.simplifyTokenList();
// Assign variable ids // Assign variable ids
@ -56,10 +60,8 @@ private:
tokenizer.fillFunctionList(); tokenizer.fillFunctionList();
// Check auto variables // Check auto variables
CheckAutoVariables checkAutoVariables(&tokenizer, &settings, this);
checkAutoVariables.autoVariables(); checkAutoVariables.autoVariables();
checkAutoVariables.returnPointerToLocalArray(); checkAutoVariables.returnPointerToLocalArray();
checkAutoVariables.returnReference();
checkAutoVariables.returncstr(); checkAutoVariables.returncstr();
} }
@ -81,6 +83,7 @@ private:
// return reference.. // return reference..
TEST_CASE(returnReference1); TEST_CASE(returnReference1);
TEST_CASE(returnReference2); TEST_CASE(returnReference2);
TEST_CASE(returnReference3);
// return c_str().. // return c_str()..
TEST_CASE(returncstr1); TEST_CASE(returncstr1);
@ -351,6 +354,16 @@ private:
ASSERT_EQUALS("[test.cpp:11]: (error) Returning reference to temporary\n", errout.str()); ASSERT_EQUALS("[test.cpp:11]: (error) Returning reference to temporary\n", errout.str());
} }
void returnReference3()
{
check("double & f(double & rd) {\n"
" double ret = getValue();\n"
" rd = ret;\n"
" return rd;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void returncstr1() void returncstr1()
{ {
check("const char *foo()\n" check("const char *foo()\n"

View File

@ -84,7 +84,6 @@ private:
TEST_CASE(array_index_7); TEST_CASE(array_index_7);
TEST_CASE(array_index_8); TEST_CASE(array_index_8);
TEST_CASE(array_index_9); TEST_CASE(array_index_9);
TEST_CASE(array_index_10);
TEST_CASE(array_index_11); TEST_CASE(array_index_11);
TEST_CASE(array_index_12); TEST_CASE(array_index_12);
TEST_CASE(array_index_13); TEST_CASE(array_index_13);
@ -135,6 +134,7 @@ private:
TEST_CASE(buffer_overrun_15); // ticket #1787 TEST_CASE(buffer_overrun_15); // ticket #1787
TEST_CASE(buffer_overrun_16); TEST_CASE(buffer_overrun_16);
TEST_CASE(buffer_overrun_17); // ticket #2548 TEST_CASE(buffer_overrun_17); // ticket #2548
TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable
TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch
// It is undefined behaviour to point out of bounds of an array // It is undefined behaviour to point out of bounds of an array
@ -597,27 +597,6 @@ private:
} }
void array_index_10()
{
check("struct ABC\n"
"{\n"
" char str[10];\n"
"};\n"
"\n"
"static void memclr( char *data )\n"
"{\n"
" data[10] = 0;\n"
"}\n"
"\n"
"static void f(struct ABC *abc)\n"
"{\n"
" memclr(abc->str);\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:13] -> [test.cpp:8]: (possible error) Array index out of bounds\n",
"", errout.str());
}
void array_index_11() void array_index_11()
{ {
check("class ABC\n" check("class ABC\n"
@ -1883,6 +1862,21 @@ private:
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer access out-of-bounds\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (error) Buffer access out-of-bounds\n", errout.str());
} }
void buffer_overrun_18() // ticket #2576
{
check("class A {\n"
" void foo();\n"
" bool b[7];\n"
"};\n"
"\n"
"void A::foo() {\n"
" for (int i=0; i<6; i++) {\n"
" b[i] = b[i+1];\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void buffer_overrun_bailoutIfSwitch() void buffer_overrun_bailoutIfSwitch()
{ {
// No false positive // No false positive

View File

@ -191,6 +191,7 @@ private:
TEST_CASE(symboldatabase10); // ticket #2537 TEST_CASE(symboldatabase10); // ticket #2537
TEST_CASE(symboldatabase11); // ticket #2539 TEST_CASE(symboldatabase11); // ticket #2539
TEST_CASE(symboldatabase12); // ticket #2547 TEST_CASE(symboldatabase12); // ticket #2547
TEST_CASE(symboldatabase13); // ticket #2577
} }
// Check the operator Equal // Check the operator Equal
@ -5528,6 +5529,16 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void symboldatabase13()
{
// ticket #2577 - segmentation fault
checkConst("class foo {\n"
" void bar2 () = A::f;\n"
"};\n");
ASSERT_EQUALS("", errout.str());
}
}; };
REGISTER_TEST(TestClass) REGISTER_TEST(TestClass)

View File

@ -1157,14 +1157,12 @@ private:
check("void foo()\n" check("void foo()\n"
"{\n" "{\n"
" int x = x;\n" " int x = x;\n"
" return 0;\n"
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of \"x\" to itself\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of \"x\" to itself\n", errout.str());
check("void foo()\n" check("void foo()\n"
"{\n" "{\n"
" std::string var = var = \"test\";\n" " std::string var = var = \"test\";\n"
" return 0;\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of \"var\" to itself\n", "", errout.str()); TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant assignment of \"var\" to itself\n", "", errout.str());
@ -1182,6 +1180,13 @@ private:
" *x = x;\n" " *x = x;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// non-primitive type -> there might be some side effects
check("void foo()\n"
"{\n"
" Fred fred; fred = fred;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void testScanf1() void testScanf1()

View File

@ -1233,21 +1233,43 @@ private:
void if_cond7() void if_cond7()
{ {
const char filedata[] = "#define A 1\n" {
"#if A==1\n" const char filedata[] = "#define A 1\n"
"a1;\n" "#if A==1\n"
"#endif\n"; "a1;\n"
"#endif\n";
// Preprocess => actual result.. // Preprocess => actual result..
std::istringstream istr(filedata); std::istringstream istr(filedata);
std::map<std::string, std::string> actual; std::map<std::string, std::string> actual;
Settings settings; Settings settings;
Preprocessor preprocessor(&settings, this); Preprocessor preprocessor(&settings, this);
preprocessor.preprocess(istr, actual, "file.c"); preprocessor.preprocess(istr, actual, "file.c");
// Compare results.. // Compare results..
ASSERT_EQUALS(1, (int)actual.size()); ASSERT_EQUALS(1, (int)actual.size());
ASSERT_EQUALS("\n\na1;\n\n", actual[""]); ASSERT_EQUALS("\n\na1;\n\n", actual[""]);
}
{
const char filedata[] = "#define A 0\n"
"#if A\n"
"foo();\n"
"#endif\n";
// Preprocess => actual result..
std::istringstream istr(filedata);
std::map<std::string, std::string> actual;
Settings settings;
Preprocessor preprocessor(&settings, this);
preprocessor.preprocess(istr, actual, "file.c");
// Compare results..
TODO_ASSERT_EQUALS(2,
1, static_cast<unsigned int>(actual.size()));
TODO_ASSERT_EQUALS("\n\n\n\n",
"\n\nfoo();\n\n", actual[""]);
}
} }

View File

@ -36,6 +36,7 @@ private:
TEST_CASE(suppressionsBadId1); TEST_CASE(suppressionsBadId1);
TEST_CASE(suppressionsDosFormat); // Ticket #1836 TEST_CASE(suppressionsDosFormat); // Ticket #1836
TEST_CASE(suppressionsFileNameWithColon); // Ticket #1919 - filename includes colon TEST_CASE(suppressionsFileNameWithColon); // Ticket #1919 - filename includes colon
TEST_CASE(suppressionsGlob);
} }
void suppressionsBadId1() void suppressionsBadId1()
@ -63,6 +64,41 @@ private:
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 10)); ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 10));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 12)); ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "c:\\bar.cpp", 12));
} }
void suppressionsGlob()
{
// Check for syntax errors in glob
{
Settings::Suppressions suppressions;
std::istringstream s("errorid:**.cpp\n");
ASSERT_EQUALS("Failed to add suppression. Syntax error in glob.", suppressions.parseFile(s));
}
// Check that globbing works
{
Settings::Suppressions suppressions;
std::istringstream s("errorid:x*.cpp\nerrorid:y?.cpp\nerrorid:test.c*");
ASSERT_EQUALS("", suppressions.parseFile(s));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp.cpp", 1));
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "abc.cpp", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "ya.cpp", 1));
ASSERT_EQUALS(false, suppressions.isSuppressed("errorid", "y.cpp", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.c", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "test.cpp", 1));
}
// Check that both a filename match and a glob match apply
{
Settings::Suppressions suppressions;
std::istringstream s("errorid:x*.cpp\nerrorid:xyz.cpp:1\nerrorid:a*.cpp:1\nerrorid:abc.cpp:2");
ASSERT_EQUALS("", suppressions.parseFile(s));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "xyz.cpp", 2));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 1));
ASSERT_EQUALS(true, suppressions.isSuppressed("errorid", "abc.cpp", 2));
}
}
}; };
REGISTER_TEST(TestSettings) REGISTER_TEST(TestSettings)

View File

@ -304,7 +304,8 @@ private:
TEST_CASE(initstruct); TEST_CASE(initstruct);
// struct ABC { } abc; => struct ABC { }; ABC abc; // struct ABC { } abc; => struct ABC { }; ABC abc;
TEST_CASE(simplifyStructDecl); TEST_CASE(simplifyStructDecl1);
TEST_CASE(simplifyStructDecl2); // ticket #2579
// register int var; => int var; // register int var; => int var;
// inline int foo() {} => int foo() {} // inline int foo() {} => int foo() {}
@ -6222,7 +6223,7 @@ private:
tok("; struct A a = { .buf = {0} };")); tok("; struct A a = { .buf = {0} };"));
} }
void simplifyStructDecl() void simplifyStructDecl1()
{ {
{ {
const char code[] = "struct ABC { } abc;"; const char code[] = "struct ABC { } abc;";
@ -6365,6 +6366,13 @@ private:
} }
} }
void simplifyStructDecl2() // ticket #2479 (segmentation fault)
{
const char code[] = "struct { char c; }";
const char expected[] = "struct { char c ; }";
ASSERT_EQUALS(expected, tok(code, false));
}
void removeUnwantedKeywords() void removeUnwantedKeywords()
{ {
ASSERT_EQUALS("int var ;", tok("register int var ;", true)); ASSERT_EQUALS("int var ;", tok("register int var ;", true));

View File

@ -264,6 +264,7 @@ private:
TEST_CASE(removeattribute); TEST_CASE(removeattribute);
TEST_CASE(cpp0xtemplate1); TEST_CASE(cpp0xtemplate1);
TEST_CASE(cpp0xtemplate2); TEST_CASE(cpp0xtemplate2);
TEST_CASE(cpp0xtemplate3);
TEST_CASE(cpp0xdefault); TEST_CASE(cpp0xdefault);
TEST_CASE(arraySize); TEST_CASE(arraySize);
@ -4634,6 +4635,18 @@ private:
"list < list < int >> ints ;", tokenizeAndStringify(code)); "list < list < int >> ints ;", tokenizeAndStringify(code));
} }
void cpp0xtemplate3()
{
// #2549
const char *code = "template<class T, T t = (T)0>\n"
"struct S\n"
"{};\n"
"S<int> s;\n";
TODO_ASSERT_EQUALS(";\n\n\nS < int , ( int ) 0 > s ;", // wanted result
";\n\n\nS < int , ( T ) 0 > s ;", // current result
tokenizeAndStringify(code));
}
void cpp0xdefault() void cpp0xdefault()
{ {
{ {

View File

@ -46,6 +46,7 @@ private:
TEST_CASE(uninitvar_references); // references TEST_CASE(uninitvar_references); // references
TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate
TEST_CASE(uninitvar_func); // analyse functions TEST_CASE(uninitvar_func); // analyse functions
TEST_CASE(uninitvar_func2); // usage of 'void a(int *p) { *p = 0; }'
TEST_CASE(uninitvar_typeof); // typeof TEST_CASE(uninitvar_typeof); // typeof
} }
@ -1420,6 +1421,28 @@ private:
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
// valid and invalid use of 'void a(int *p) { *p = 0; }'
void uninitvar_func2()
{
const std::string funca("void a(int *p) { *p = 0; }\n");
// ok - initialized pointer
checkUninitVar((funca +
"void b() {\n"
" int buf[10];\n"
" a(buf);\n"
"}\n").c_str());
ASSERT_EQUALS("", errout.str());
// not ok - uninitialized pointer
checkUninitVar((funca +
"void b() {\n"
" int *p;\n"
" a(p);\n"
"}\n").c_str());
TODO_ASSERT_EQUALS("error", "", errout.str());
}
void uninitvar_typeof() void uninitvar_typeof()
{ {
checkUninitVar("void f() {\n" checkUninitVar("void f() {\n"