Fixed #1622 (False positive: buf is not assigned a value (pointer aliasing))

This commit is contained in:
Robert Reif 2010-04-29 07:10:50 +02:00 committed by Daniel Marjamäki
parent cccccff96f
commit 70d475f8c2
2 changed files with 924 additions and 79 deletions

View File

@ -425,23 +425,26 @@ static bool isOp(const Token *tok)
class Variables class Variables
{ {
public: public:
enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray };
/** Store information about variable usage */ /** Store information about variable usage */
class VariableUsage class VariableUsage
{ {
public: public:
VariableUsage(const Token *name = 0, VariableUsage(const Token *name = 0,
const Token *type = 0, VariableType type = standard,
bool read = false, bool read = false,
bool write = false, bool write = false,
bool modified = false, bool modified = false,
bool aliased = false) : unsigned int alias = 0) :
_name(name), _name(name),
_type(type), _type(type),
_read(read), _read(read),
_write(write), _write(write),
_modified(modified), _modified(modified)
_aliased(aliased)
{ {
if (alias)
_aliases.insert(alias);
} }
/** variable is used.. set both read+write */ /** variable is used.. set both read+write */
@ -458,11 +461,11 @@ public:
} }
const Token *_name; const Token *_name;
const Token *_type; VariableType _type;
bool _read; bool _read;
bool _write; bool _write;
bool _modified; // read/modify/write bool _modified; // read/modify/write
bool _aliased; // pointer or reference std::set<unsigned int> _aliases;
}; };
typedef std::map<unsigned int, VariableUsage> VariableMap; typedef std::map<unsigned int, VariableUsage> VariableMap;
@ -471,44 +474,167 @@ public:
{ {
_varUsage.clear(); _varUsage.clear();
} }
void addVar(const Token *name, VariableMap &varUsage()
const Token *type,
bool write_ = false,
bool aliased = false)
{
_varUsage.insert(std::make_pair(name->varId(), VariableUsage(name, type, false, write_, false, aliased)));
}
const VariableMap &varUsage() const
{ {
return _varUsage; return _varUsage;
} }
void addVar(const Token *name, VariableType type, bool write_ = false, unsigned int varid = 0);
void read(unsigned int varid); void read(unsigned int varid);
void readAliases(unsigned int varid);
void readAll(unsigned int varid);
void write(unsigned int varid); void write(unsigned int varid);
void writeAliases(unsigned int varid);
void writeAll(unsigned int varid);
void use(unsigned int varid); void use(unsigned int varid);
void modified(unsigned int varid); void modified(unsigned int varid);
VariableUsage *find(unsigned int varid); VariableUsage *find(unsigned int varid);
void alias(unsigned int varid1, unsigned int varid2);
void erase(unsigned int varid)
{
_varUsage.erase(varid);
}
private: private:
VariableMap _varUsage; VariableMap _varUsage;
}; };
void Variables::alias(unsigned int varid1, unsigned int varid2)
{
std::set<unsigned int>::iterator i;
VariableUsage *var1 = find(varid1);
// remove var1 from all aliases
for (i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i)
{
VariableUsage *temp = find(*i);
temp->_aliases.erase(var1->_name->varId());
}
// remove all aliases from var1
var1->_aliases.clear();
VariableUsage *var2 = find(varid2);
// var1 gets all var2s aliases
for (i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i)
{
if (*i != varid1)
var1->_aliases.insert(*i);
}
// var2 is an alias of var1
var2->_aliases.insert(varid1);
var1->_aliases.insert(varid2);
if (var2->_type == Variables::pointer)
var2->_read = true;
}
void Variables::addVar(const Token *name,
VariableType type,
bool write_,
unsigned int varid )
{
_varUsage.insert(std::make_pair(name->varId(), VariableUsage(name, type, false, write_, false, varid)));
VariableUsage *usage = find(varid);
if (usage)
usage->_aliases.insert(name->varId());
}
void Variables::read(unsigned int varid) void Variables::read(unsigned int varid)
{ {
VariableUsage *usage = find(varid); VariableUsage *usage = find(varid);
if (usage) if (usage)
{
usage->_read = true; usage->_read = true;
}
void Variables::readAliases(unsigned int varid)
{
VariableUsage *usage = find(varid);
if (usage)
{
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->_read = true;
}
} }
} }
void Variables::readAll(unsigned int varid)
{
VariableUsage *usage = find(varid);
if (usage)
{
usage->_read = true;
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->_read = true;
}
}
}
void Variables::write(unsigned int varid) void Variables::write(unsigned int varid)
{ {
VariableUsage *usage = find(varid); VariableUsage *usage = find(varid);
if (usage)
usage->_write = true;
}
void Variables::writeAliases(unsigned int varid)
{
VariableUsage *usage = find(varid);
if (usage)
{
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->_write = true;
}
}
}
void Variables::writeAll(unsigned int varid)
{
VariableUsage *usage = find(varid);
if (usage) if (usage)
{ {
usage->_write = true; usage->_write = true;
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->_write = true;
}
} }
} }
@ -519,8 +645,19 @@ void Variables::use(unsigned int varid)
if (usage) if (usage)
{ {
usage->use(); usage->use();
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->use();
}
} }
} }
void Variables::modified(unsigned int varid) void Variables::modified(unsigned int varid)
{ {
VariableUsage *usage = find(varid); VariableUsage *usage = find(varid);
@ -528,6 +665,16 @@ void Variables::modified(unsigned int varid)
if (usage) if (usage)
{ {
usage->_modified = true; usage->_modified = true;
std::set<unsigned int>::iterator aliases;
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
{
VariableUsage *aliased = find(*aliases);
if (aliased)
aliased->_modified = true;
}
} }
} }
@ -536,14 +683,128 @@ Variables::VariableUsage *Variables::find(unsigned int varid)
if (varid) if (varid)
{ {
VariableMap::iterator i = _varUsage.find(varid); VariableMap::iterator i = _varUsage.find(varid);
if (i != _varUsage.end()) if (i != _varUsage.end())
return &i->second; return &i->second;
} }
return 0; return 0;
} }
static int doAssignment(Variables &variables, const Token *tok, bool pointer)
{
int next = 0;
// check for aliased variable
unsigned int varid1 = tok->varId();
Variables::VariableUsage *var1 = variables.find(varid1);
if (var1)
{
Variables::VariableUsage *var2 = 0;
if (Token::Match(tok->tokAt(2), "&| %var%") ||
Token::Match(tok->tokAt(2), "( %type% * ) &| %var%") ||
Token::Match(tok->tokAt(2), "( %type% * ) ( &| %var%") ||
Token::Match(tok->tokAt(2), "%any% < %type% * > ( &| %var%"))
{
unsigned int varid2;
bool addressOf = false;
// check for C style cast
if (tok->tokAt(2)->str() == "(")
{
if (tok->tokAt(6)->str() == "&")
{
addressOf = true;
next = 7;
}
else if (tok->tokAt(6)->str() == "(")
{
if (tok->tokAt(7)->str() == "&")
{
addressOf = true;
next = 8;
}
else
next = 7;
}
else
next = 6;
}
// check for C++ style cast
else if (tok->tokAt(2)->str().find("cast") != std::string::npos)
{
if (tok->tokAt(8)->str() == "&")
{
addressOf = true;
next = 9;
}
else
next = 8;
}
// no cast
else
{
if (tok->tokAt(2)->str() == "&")
{
addressOf = true;
next = 3;
}
else
next = 2;
}
// check if variable is local
varid2 = tok->tokAt(next)->varId();
var2 = variables.find(varid2);
if (var1->_type != Variables::reference && !addressOf)
{
if (var2)
{
if (var1->_type == Variables::pointer)
{
if (!(var2->_type == Variables::array || var2->_type == Variables::pointer))
variables.read(varid2);
}
else
{
if (var2->_type == Variables::pointer && tok->tokAt(next +1)->str() == "[")
variables.readAliases(varid2);
variables.read(varid2);
}
}
}
if (var2) // local variable
{
if (var1->_type == Variables::pointer && !pointer)
{
if (addressOf ||
var2->_type == Variables::array ||
var2->_type == Variables::pointer)
{
variables.alias(varid1, varid2);
}
}
else if (var1->_type == Variables::reference)
{
variables.alias(varid1, varid2);
}
}
else // not a local variable
{
// aliased variables in a larger scope are not supported yet
if (varid2)
variables.erase(varid1);
}
}
}
return next;
}
void CheckOther::functionVariableUsage() void CheckOther::functionVariableUsage()
{ {
if (!_settings->_checkCodingStyle) if (!_settings->_checkCodingStyle)
@ -557,13 +818,13 @@ void CheckOther::functionVariableUsage()
token = token->next(); token = token->next();
// First token for the current scope.. // First token for the current scope..
const Token * const tok1 = token; const Token *const tok1 = token;
// Find next scope that will be checked next time.. // Find next scope that will be checked next time..
token = Token::findmatch(token->link(), ") const| {"); token = Token::findmatch(token->link(), ") const| {");
// varId, usage {read, write, modified} // varId, usage {read, write, modified}
Variables variables; Variables variables;
unsigned int indentlevel = 0; unsigned int indentlevel = 0;
for (const Token *tok = tok1; tok; tok = tok->next()) for (const Token *tok = tok1; tok; tok = tok->next())
@ -597,7 +858,8 @@ void CheckOther::functionVariableUsage()
if (Token::Match(tok, "[;{}] %type% %var% ;|=") && if (Token::Match(tok, "[;{}] %type% %var% ;|=") &&
tok->next()->isStandardType()) tok->next()->isStandardType())
{ {
variables.addVar(tok->tokAt(2), tok->next(), tok->tokAt(3)->str() == "="); variables.addVar(tok->tokAt(2), Variables::standard,
tok->tokAt(3)->str() == "=");
tok = tok->tokAt(2); tok = tok->tokAt(2);
} }
@ -606,11 +868,11 @@ void CheckOther::functionVariableUsage()
else if (Token::Match(tok, "[;{}] %type% %var% ( %any% ) ;") && else if (Token::Match(tok, "[;{}] %type% %var% ( %any% ) ;") &&
tok->next()->isStandardType()) tok->next()->isStandardType())
{ {
variables.addVar(tok->tokAt(2), tok->next(), true); variables.addVar(tok->tokAt(2), Variables::standard, true);
// check if a local variable is used to initialize this variable // check if a local variable is used to initialize this variable
if (tok->tokAt(4)->varId() > 0) if (tok->tokAt(4)->varId() > 0)
variables.read(tok->tokAt(4)->varId()); variables.readAll(tok->tokAt(4)->varId());
tok = tok->tokAt(5); tok = tok->tokAt(5);
} }
@ -619,7 +881,8 @@ void CheckOther::functionVariableUsage()
else if (Token::Match(tok, "[;{}] %type% %var% [ %num% ] ;|=") && else if (Token::Match(tok, "[;{}] %type% %var% [ %num% ] ;|=") &&
tok->next()->isStandardType()) tok->next()->isStandardType())
{ {
variables.addVar(tok->tokAt(2), tok->next(), tok->tokAt(6)->str() == "=", false); variables.addVar(tok->tokAt(2), Variables::array,
tok->tokAt(6)->str() == "=");
tok = tok->tokAt(5); tok = tok->tokAt(5);
} }
@ -629,8 +892,24 @@ void CheckOther::functionVariableUsage()
{ {
if (tok->next()->str() != "return") if (tok->next()->str() != "return")
{ {
variables.addVar(tok->tokAt(3), tok->next(), tok->tokAt(4)->str() == "=", true); Variables::VariableType type;
tok = tok->tokAt(3);
if (tok->tokAt(2)->str() == "*")
type = Variables::pointer;
else
type = Variables::reference;
bool written = tok->tokAt(4)->str() == "=";
variables.addVar(tok->tokAt(3), type, written, 0);
int offset = 0;
// check for assignment
if (written)
offset = doAssignment(variables, tok->tokAt(3), false);
tok = tok->tokAt(3 + offset);
} }
} }
@ -638,24 +917,72 @@ void CheckOther::functionVariableUsage()
// const int * i; const int * j = 0; // const int * i; const int * j = 0;
else if (Token::Match(tok, "[;{}] const %type% *|& %var% ;|=")) else if (Token::Match(tok, "[;{}] const %type% *|& %var% ;|="))
{ {
variables.addVar(tok->tokAt(4), tok->tokAt(2), tok->tokAt(5)->str() == "=", true); Variables::VariableType type;
tok = tok->tokAt(4);
if (tok->tokAt(3)->str() == "*")
type = Variables::pointer;
else
type = Variables::reference;
bool written = tok->tokAt(5)->str() == "=";
variables.addVar(tok->tokAt(4), type, written, 0);
int offset = 0;
// check for assignment
if (written)
offset = doAssignment(variables, tok->tokAt(3), false);
tok = tok->tokAt(4 + offset);
} }
// pointer or reference of struct or union declaration with possible initialization // pointer or reference of struct or union declaration with possible initialization
// struct s * i; struct s * j = 0; // struct s * i; struct s * j = 0;
else if (Token::Match(tok, "[;{}] struct|union %type% *|& %var% ;|=")) else if (Token::Match(tok, "[;{}] struct|union %type% *|& %var% ;|="))
{ {
variables.addVar(tok->tokAt(4), tok->tokAt(2), tok->tokAt(5)->str() == "=", true); Variables::VariableType type;
tok = tok->tokAt(4);
if (tok->tokAt(3)->str() == "*")
type = Variables::pointer;
else
type = Variables::reference;
bool written = tok->tokAt(5)->str() == "=";
variables.addVar(tok->tokAt(4), type, written, 0);
int offset = 0;
// check for assignment
if (written)
offset = doAssignment(variables, tok->tokAt(3), false);
tok = tok->tokAt(4 + offset);
} }
// const pointer or reference of struct or union declaration with possible initialization // const pointer or reference of struct or union declaration with possible initialization
// const struct s * i; const struct s * j = 0; // const struct s * i; const struct s * j = 0;
else if (Token::Match(tok, "[;{}] const struct|union %type% *|& %var% ;|=")) else if (Token::Match(tok, "[;{}] const struct|union %type% *|& %var% ;|="))
{ {
variables.addVar(tok->tokAt(5), tok->tokAt(3), tok->tokAt(6)->str() == "=", true); Variables::VariableType type;
tok = tok->tokAt(5);
if (tok->tokAt(4)->str() == "*")
type = Variables::pointer;
else
type = Variables::reference;
bool written = tok->tokAt(6)->str() == "=";
variables.addVar(tok->tokAt(5), type, written, 0);
int offset = 0;
// check for assignment
if (written)
offset = doAssignment(variables, tok->tokAt(3), false);
tok = tok->tokAt(5 + offset);
} }
// pointer or reference declaration with initialization using constructor // pointer or reference declaration with initialization using constructor
@ -663,44 +990,152 @@ void CheckOther::functionVariableUsage()
else if (Token::Match(tok, "[;{}] %type% &|* %var% ( %any% ) ;") && else if (Token::Match(tok, "[;{}] %type% &|* %var% ( %any% ) ;") &&
(tok->next()->isStandardType() || tok->next()->str() == "void")) (tok->next()->isStandardType() || tok->next()->str() == "void"))
{ {
variables.addVar(tok->tokAt(3), tok->next(), true, true); Variables::VariableType type;
if (tok->tokAt(2)->str() == "*")
type = Variables::pointer;
else
type = Variables::reference;
unsigned int varid = 0;
// check for aliased variable
if (Token::Match(tok->tokAt(5), "%var%"))
varid = tok->tokAt(5)->varId();
variables.addVar(tok->tokAt(3), type, true, varid);
// check if a local variable is used to initialize this variable // check if a local variable is used to initialize this variable
if (tok->tokAt(5)->varId() > 0) if (varid > 0)
{ {
if (tok->tokAt(2)->str() == "&") Variables::VariableUsage *var = variables.find(varid);
variables.read(tok->tokAt(5)->varId());
else if (type == Variables::pointer)
{
variables.use(tok->tokAt(5)->varId()); variables.use(tok->tokAt(5)->varId());
if (var && (var->_type == Variables::array ||
var->_type == Variables::pointer))
var->_aliases.insert(tok->varId());
}
else
{
variables.readAll(tok->tokAt(5)->varId());
if (var)
var->_aliases.insert(tok->varId());
}
} }
tok = tok->tokAt(6); tok = tok->tokAt(6);
} }
else if (Token::Match(tok, "[;{}] %type% *|& %var% [ %num% ] ;|=") && // array of pointer or reference declaration with possible initialization
(tok->next()->isStandardType() || tok->next()->str() == "void")) // int * p[10]; int * q[10] = { 0 };
else if (Token::Match(tok, "[;{}] %type% *|& %var% [ %num% ] ;|="))
{ {
variables.addVar(tok->tokAt(3), tok->next(), tok->tokAt(7)->str() == "=", true); if (tok->next()->str() != "return")
{
variables.addVar(tok->tokAt(3),
tok->tokAt(2)->str() == "*" ? Variables::pointerArray : Variables::referenceArray,
tok->tokAt(7)->str() == "=", false);
tok = tok->tokAt(6);
}
}
// const array of pointer or reference declaration with possible initialization
// const int * p[10]; const int * q[10] = { 0 };
else if (Token::Match(tok, "[;{}] const %type% *|& %var% [ %num% ] ;|="))
{
variables.addVar(tok->tokAt(4),
tok->tokAt(3)->str() == "*" ? Variables::pointerArray : Variables::referenceArray,
tok->tokAt(8)->str() == "=", false);
tok = tok->tokAt(7);
}
// array of pointer or reference of struct or union declaration with possible initialization
// struct S * p[10]; struct T * q[10] = { 0 };
else if (Token::Match(tok, "[;{}] struct|union %type% *|& %var% [ %num% ] ;|="))
{
variables.addVar(tok->tokAt(4),
tok->tokAt(3)->str() == "*" ? Variables::pointerArray : Variables::referenceArray,
tok->tokAt(8)->str() == "=", false);
tok = tok->tokAt(6); tok = tok->tokAt(6);
} }
else if (Token::Match(tok, "[;{}] const %type% *|& %var% [ %num% ] ;|=") && // const array of pointer or reference of struct or union declaration with possible initialization
(tok->tokAt(2)->isStandardType() || tok->tokAt(2)->str() == "void")) // const struct S * p[10]; const struct T * q[10] = { 0 };
else if (Token::Match(tok, "[;{}] const struct|union %type% *|& %var% [ %num% ] ;|="))
{ {
variables.addVar(tok->tokAt(4), tok->next(), tok->tokAt(8)->str() == "=", true); variables.addVar(tok->tokAt(5),
tok->tokAt(4)->str() == "*" ? Variables::pointerArray : Variables::referenceArray,
tok->tokAt(9)->str() == "=", false);
tok = tok->tokAt(7); tok = tok->tokAt(7);
} }
else if (Token::Match(tok, "delete|return %var%")) else if (Token::Match(tok, "delete|return %var%"))
variables.read(tok->next()->varId()); variables.readAll(tok->next()->varId());
else if (Token::Match(tok, "%var% =")) // assignment
variables.write(tok->varId()); else if (Token::Match(tok, "*| %var% ="))
{
bool pointer = false;
if (tok->str() == "*")
{
pointer = true;
tok = tok->next();
}
unsigned int varid1 = tok->varId();
const Token *start = tok;
tok = tok->tokAt(doAssignment(variables, tok, pointer));
if (pointer)
{
variables.writeAliases(varid1);
variables.read(varid1);
}
else
{
Variables::VariableUsage *var = variables.find(varid1);
if (var && var->_type == Variables::reference)
{
variables.writeAliases(varid1);
variables.read(varid1);
}
else
variables.write(varid1);
}
// checked for chained assignments
if (tok != start && tok->next()->str() == "=")
{
Variables::VariableUsage *var = variables.find(tok->varId());
if (var && var->_type != Variables::reference)
var->_read = true;
tok = tok->previous();
}
}
else if (Token::Match(tok, "%var% [") && Token::Match(tok->next()->link(), "] =")) else if (Token::Match(tok, "%var% [") && Token::Match(tok->next()->link(), "] ="))
variables.write(tok->varId()); {
unsigned int varid = tok->varId();
Variables::VariableUsage *var = variables.find(varid);
else if (Token::Match(tok, "else %var% =")) if (var)
variables.write(tok->next()->varId()); {
if (var->_type == Variables::pointer)
{
variables.read(varid);
variables.writeAliases(varid);
}
else
variables.writeAll(varid);
}
}
else if (Token::Match(tok, ">>|& %var%")) else if (Token::Match(tok, ">>|& %var%"))
variables.use(tok->next()->varId()); // use = read + write variables.use(tok->next()->varId()); // use = read + write
@ -713,16 +1148,16 @@ void CheckOther::functionVariableUsage()
else if ((Token::Match(tok, "[(=&!]") || isOp(tok)) && else if ((Token::Match(tok, "[(=&!]") || isOp(tok)) &&
(Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false"))) (Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false")))
variables.read(tok->next()->varId()); variables.readAll(tok->next()->varId());
else if (Token::Match(tok, "-=|+=|*=|/=|&=|^= %var%") || Token::Match(tok, "|= %var%")) else if (Token::Match(tok, "-=|+=|*=|/=|&=|^= %var%") || Token::Match(tok, "|= %var%"))
variables.modified(tok->next()->varId()); variables.modified(tok->next()->varId());
else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || isOp(tok->next()))) else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || isOp(tok->next())))
variables.read(tok->varId()); variables.readAll(tok->varId());
else if (Token::Match(tok, "; %var% ;")) else if (Token::Match(tok, "; %var% ;"))
variables.read(tok->next()->varId()); variables.readAll(tok->next()->varId());
else if (Token::Match(tok, "++|-- %var%")) else if (Token::Match(tok, "++|-- %var%"))
variables.modified(tok->next()->varId()); variables.modified(tok->next()->varId());
@ -744,7 +1179,7 @@ void CheckOther::functionVariableUsage()
else if (usage._modified & !usage._write) else if (usage._modified & !usage._write)
unassignedVariableError(usage._name, varname); unassignedVariableError(usage._name, varname);
else if (!usage._read && !usage._modified && !usage._aliased) else if (!usage._read && !usage._modified)
unreadVariableError(usage._name, varname); unreadVariableError(usage._name, varname);
else if (!usage._write) else if (!usage._write)

View File

@ -327,23 +327,86 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i(0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" bool i = false;\n" " bool i = false;\n"
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" bool i = true;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" char *i;\n"
" i = fgets();\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
// undefined variables are not reported because they may be classes with constructors
functionVariableUsage("undefined foo()\n"
"{\n"
" undefined i = 0;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i = undefined;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" int * i = 0;\n" " int * i = 0;\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" void * i = 0;\n" " void * i = 0;\n"
"}\n"); "}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" const void * i = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" struct S * i = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" const struct S * i = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" undefined * i = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i = 0;\n"
" int j = i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'j' is assigned a value that is never used\n", errout.str());
} }
void localvar2() void localvar2()
@ -361,6 +424,77 @@ private:
" return i;\n" " return i;\n"
"}\n"); "}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str()); ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
// undefined variables are not reported because they may be classes with constructors
functionVariableUsage("undefined foo()\n"
"{\n"
" undefined i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("undefined *foo()\n"
"{\n"
" undefined * i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
functionVariableUsage("int *foo()\n"
"{\n"
" int * i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
functionVariableUsage("const int *foo()\n"
"{\n"
" const int * i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
functionVariableUsage("struct S *foo()\n"
"{\n"
" struct S * i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
functionVariableUsage("const struct S *foo()\n"
"{\n"
" const struct S * i;\n"
" return i;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'i' is not assigned a value\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" f(a[0]);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" f(a[0], 0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" f(0, a[0]);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" f(0, a[0], 0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
} }
void localvar3() void localvar3()
@ -383,6 +517,13 @@ private:
" f(i);\n" " f(i);\n"
"}\n"); "}\n");
ASSERT_EQUALS(std::string(""), errout.str()); ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i = 0;\n"
" f(&i);\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
} }
void localvar5() void localvar5()
@ -397,6 +538,14 @@ private:
void localvar6() void localvar6()
{ {
functionVariableUsage("void foo()\n"
"{\n"
" int b[10];\n"
" for (int i=0;i<10;++i)\n"
" b[i] = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" int a = 0;\n" " int a = 0;\n"
@ -471,6 +620,12 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" const struct A * i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" int * i[2];\n" " int * i[2];\n"
@ -495,6 +650,42 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str()); ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" struct A * i[2];\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" const struct A * i[2];\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i = 0;\n"
" int &j = i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n"
"[test.cpp:4]: (style) Variable 'j' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i;\n"
" int &j = i;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Unused variable: i\n"
"[test.cpp:4]: (style) Variable 'j' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i;\n"
" int &j = i;\n"
" j = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" A * i;\n" " A * i;\n"
@ -523,26 +714,6 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int i(0);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" char *i;\n"
" i = fgets();\n"
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'i' is assigned a value that is never used\n", errout.str());
ASSERT_EQUALS("", errout.str()); // current value - catch changes
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" char *i;\n" " char *i;\n"
@ -573,6 +744,28 @@ private:
" p[i] = 0;\n" " p[i] = 0;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int x;\n"
" a[0] = 0;\n"
" x = a[0];\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'x' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a, b, c;\n"
" a = b = c = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n", errout.str());
functionVariableUsage("int * foo()\n"
"{\n"
" return &undefined[0];\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void localvar9() void localvar9()
@ -654,8 +847,7 @@ private:
" }\n" " }\n"
" x = a;\n" " x = a;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); // catch changes ASSERT_EQUALS("", errout.str());
TODO_ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'a' is assigned a value that is never used\n", errout.str());
// The variable 'a' is initialized. But the initialized value is // The variable 'a' is initialized. But the initialized value is
// never used. It is only initialized for security reasons. // never used. It is only initialized for security reasons.
@ -671,17 +863,100 @@ private:
" x = a;\n" " x = a;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
} }
void localvaralias() void localvaralias()
{ {
functionVariableUsage("void foo()\n"
"{\n"
" int a;\n"
" int *b = &a;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Unused variable: a\n"
"[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int *b = a;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Unused variable: a\n"
"[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
"{\n" "{\n"
" int a;\n" " int a;\n"
" int *b = &a;\n" " int *b = &a;\n"
" *b = 0;\n" " *b = 0;\n"
"}\n"); "}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a;\n"
" char *b = (char *)&a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a;\n"
" char *b = static_cast<char *>(&a);\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
// a is not a local variable and b is aliased to it (not supported yet)
functionVariableUsage("int a;\n"
"void foo()\n"
"{\n"
" int *b = &a;\n"
"}\n");
TODO_ASSERT_EQUALS(std::string("[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
// a is not a local variable and b is aliased to it (not supported yet)
functionVariableUsage("void foo(int a)\n"
"{\n"
" int *b = &a;\n"
"}\n");
TODO_ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
// a is not a local variable and b is aliased to it (not supported yet)
functionVariableUsage("class A\n"
"{\n"
" int a;\n"
" void foo()\n"
" {\n"
" int *b = &a;\n"
" }\n"
"}\n");
TODO_ASSERT_EQUALS(std::string("[test.cpp:6]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("int a;\n"
"void foo()\n"
"{\n"
" int *b = &a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo(int a)\n"
"{\n"
" int *b = &a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("class A\n"
"{\n"
" int a;\n"
" void foo()\n"
" {\n"
" int *b = &a;\n"
" *b = 0;\n"
" }\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str()); ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo()\n" functionVariableUsage("void foo()\n"
@ -690,8 +965,143 @@ private:
" int *b = a;\n" " int *b = a;\n"
" *b = 0;\n" " *b = 0;\n"
"}\n"); "}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n"), errout.str()); ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
TODO_ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" char *b = (char *)a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" char *b = static_cast<char *>(a);\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("int a[10];\n"
"void foo()\n"
"{\n"
" int *b = a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo(int a[10])\n"
"{\n"
" int *b = a;\n"
" *b = 0;\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("class A\n"
"{\n"
" int a[10];\n"
" void foo()\n"
" {\n"
" int *b = a;\n"
" *b = 0;\n"
" }\n"
"}\n");
ASSERT_EQUALS(std::string(""), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int *b = a;\n"
" int *c = b;\n"
" *c = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int b[10];\n"
" int *c = a;\n"
" int *d = b;\n"
" *d = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Unused variable: a\n"
"[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"
"[test.cpp:5]: (style) Variable 'c' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int b[10];\n"
" int *c = a;\n"
" c = b;\n"
" *c = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Unused variable: a\n"
"[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10];\n"
" int b[10];\n"
" int *c = a;\n"
" c = b;\n"
" *c = 0;\n"
" c = a;\n"
" *c = 0;\n"
"}\n");
ASSERT_EQUALS(std::string("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"
"[test.cpp:4]: (style) Variable 'b' is assigned a value that is never used\n"), errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" b[-10] = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" b[-10] = 0;\n"
" int * c = b - 10;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n"
"[test.cpp:5]: (style) Variable 'c' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" int * c = b - 10;\n"
" x = c[0];\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" int * c = b - 10;\n"
" c[1] = 0;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is assigned a value that is never used\n", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" int * c = b - 10;\n"
" c[1] = c[0];\n"
"}\n");
ASSERT_EQUALS("", errout.str());
functionVariableUsage("void foo()\n"
"{\n"
" int a[10], * b = a + 10;\n"
" int * c = b - 10;\n"
" int d = c[0];\n"
" f(d);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' is not assigned a value\n", errout.str());
} }
void localvarasm() void localvarasm()