2011-08-19 20:35:25 +02:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2018-03-31 20:59:09 +02:00
|
|
|
* Copyright (C) 2007-2018 Cppcheck team.
|
2011-08-19 20:35:25 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "checkunusedvar.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
|
|
|
|
#include "errorlogger.h"
|
|
|
|
#include "settings.h"
|
2011-08-19 20:35:25 +02:00
|
|
|
#include "symboldatabase.h"
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "token.h"
|
|
|
|
#include "tokenize.h"
|
|
|
|
#include "valueflow.h"
|
|
|
|
|
2012-12-17 18:19:05 +01:00
|
|
|
#include <algorithm>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <cctype>
|
|
|
|
#include <cstddef>
|
|
|
|
#include <list>
|
|
|
|
#include <set>
|
2013-04-01 12:41:14 +02:00
|
|
|
#include <utility>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <vector>
|
2011-08-19 20:35:25 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// Register this check class (by creating a static instance of it)
|
2011-10-13 20:53:06 +02:00
|
|
|
namespace {
|
|
|
|
CheckUnusedVar instance;
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2016-07-16 21:21:24 +02:00
|
|
|
static const struct CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable')
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
static const struct CWE CWE665(665U); // Improper Initialization
|
2016-07-15 15:49:21 +02:00
|
|
|
|
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
/**
|
|
|
|
* @brief This class is used create a list of variables within a function.
|
|
|
|
*/
|
2011-10-13 20:53:06 +02:00
|
|
|
class Variables {
|
2011-08-19 20:35:25 +02:00
|
|
|
public:
|
2011-12-18 20:15:41 +01:00
|
|
|
enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none };
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
/** Store information about variable usage */
|
2011-10-13 20:53:06 +02:00
|
|
|
class VariableUsage {
|
2011-08-19 20:35:25 +02:00
|
|
|
public:
|
2014-11-13 21:39:14 +01:00
|
|
|
explicit VariableUsage(const Variable *var = nullptr,
|
|
|
|
VariableType type = standard,
|
|
|
|
bool read = false,
|
|
|
|
bool write = false,
|
|
|
|
bool modified = false,
|
|
|
|
bool allocateMemory = false) :
|
2012-09-04 14:53:24 +02:00
|
|
|
_var(var),
|
2017-08-09 20:00:26 +02:00
|
|
|
_lastAccess(var ? var->nameToken() : nullptr),
|
2012-05-14 20:46:23 +02:00
|
|
|
_type(type),
|
2011-08-19 20:35:25 +02:00
|
|
|
_read(read),
|
|
|
|
_write(write),
|
|
|
|
_modified(modified),
|
2014-11-20 14:20:09 +01:00
|
|
|
_allocateMemory(allocateMemory) {
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** variable is used.. set both read+write */
|
2014-11-20 14:20:09 +01:00
|
|
|
void use(std::list<std::set<unsigned int> > & varReadInScope) {
|
2013-07-20 12:31:04 +02:00
|
|
|
varReadInScope.back().insert(_var->declarationId());
|
2011-08-19 20:35:25 +02:00
|
|
|
_read = true;
|
|
|
|
_write = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** is variable unused? */
|
2014-11-20 14:20:09 +01:00
|
|
|
bool unused() const {
|
2017-10-08 07:54:39 +02:00
|
|
|
return (!_read && !_write);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-05-14 20:46:23 +02:00
|
|
|
std::set<unsigned int> _aliases;
|
|
|
|
std::set<const Scope*> _assignments;
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
const Variable* _var;
|
|
|
|
const Token* _lastAccess;
|
2012-05-14 20:46:23 +02:00
|
|
|
VariableType _type;
|
2011-08-19 20:35:25 +02:00
|
|
|
bool _read;
|
|
|
|
bool _write;
|
|
|
|
bool _modified; // read/modify/write
|
|
|
|
bool _allocateMemory;
|
|
|
|
};
|
|
|
|
|
2012-12-07 01:48:27 +01:00
|
|
|
class ScopeGuard {
|
2012-12-04 21:39:51 +01:00
|
|
|
public:
|
|
|
|
ScopeGuard(Variables & guarded,
|
|
|
|
bool insideLoop)
|
|
|
|
:_guarded(guarded),
|
2014-11-20 14:20:09 +01:00
|
|
|
_insideLoop(insideLoop) {
|
2012-12-04 21:39:51 +01:00
|
|
|
_guarded.enterScope();
|
|
|
|
}
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
~ScopeGuard() {
|
2012-12-04 21:39:51 +01:00
|
|
|
_guarded.leaveScope(_insideLoop);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2014-11-20 20:49:05 +01:00
|
|
|
/** No implementation */
|
2015-06-28 18:07:31 +02:00
|
|
|
ScopeGuard();
|
2014-11-20 20:49:05 +01:00
|
|
|
ScopeGuard& operator=(const ScopeGuard &);
|
|
|
|
|
2012-12-04 21:39:51 +01:00
|
|
|
Variables & _guarded;
|
|
|
|
bool _insideLoop;
|
|
|
|
};
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
void clear() {
|
2011-08-19 20:35:25 +02:00
|
|
|
_varUsage.clear();
|
|
|
|
}
|
2014-11-20 14:20:09 +01:00
|
|
|
const std::map<unsigned int, VariableUsage> &varUsage() const {
|
2011-08-19 20:35:25 +02:00
|
|
|
return _varUsage;
|
|
|
|
}
|
2012-09-04 14:53:24 +02:00
|
|
|
void addVar(const Variable *var, VariableType type, bool write_);
|
|
|
|
void allocateMemory(unsigned int varid, const Token* tok);
|
|
|
|
void read(unsigned int varid, const Token* tok);
|
|
|
|
void readAliases(unsigned int varid, const Token* tok);
|
|
|
|
void readAll(unsigned int varid, const Token* tok);
|
|
|
|
void write(unsigned int varid, const Token* tok);
|
|
|
|
void writeAliases(unsigned int varid, const Token* tok);
|
|
|
|
void writeAll(unsigned int varid, const Token* tok);
|
|
|
|
void use(unsigned int varid, const Token* tok);
|
|
|
|
void modified(unsigned int varid, const Token* tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *find(unsigned int varid);
|
|
|
|
void alias(unsigned int varid1, unsigned int varid2, bool replace);
|
2014-11-20 14:20:09 +01:00
|
|
|
void erase(unsigned int varid) {
|
2011-08-19 20:35:25 +02:00
|
|
|
_varUsage.erase(varid);
|
|
|
|
}
|
|
|
|
void eraseAliases(unsigned int varid);
|
|
|
|
void eraseAll(unsigned int varid);
|
|
|
|
void clearAliases(unsigned int varid);
|
|
|
|
|
2014-11-20 14:20:09 +01:00
|
|
|
ScopeGuard newScope(bool insideLoop) {
|
2012-12-04 21:39:51 +01:00
|
|
|
return ScopeGuard(*this, insideLoop);
|
|
|
|
}
|
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
private:
|
2012-12-04 21:39:51 +01:00
|
|
|
void enterScope();
|
|
|
|
void leaveScope(bool insideLoop);
|
|
|
|
|
2012-12-05 20:18:14 +01:00
|
|
|
std::map<unsigned int, VariableUsage> _varUsage;
|
|
|
|
std::list<std::set<unsigned int> > _varAddedInScope;
|
|
|
|
std::list<std::set<unsigned int> > _varReadInScope;
|
2011-08-19 20:35:25 +02:00
|
|
|
};
|
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
/**
|
|
|
|
* Alias the 2 given variables. Either replace the existing aliases if
|
|
|
|
* they exist or merge them. You would replace an existing alias when this
|
|
|
|
* assignment is in the same scope as the previous assignment. You might
|
|
|
|
* merge the aliases when this assignment is in a different scope from the
|
|
|
|
* previous assignment depending on the relationship of the 2 scopes.
|
|
|
|
*/
|
|
|
|
void Variables::alias(unsigned int varid1, unsigned int varid2, bool replace)
|
|
|
|
{
|
|
|
|
VariableUsage *var1 = find(varid1);
|
|
|
|
VariableUsage *var2 = find(varid2);
|
|
|
|
|
2014-08-14 16:10:12 +02:00
|
|
|
if (!var1 || !var2)
|
|
|
|
return;
|
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
// alias to self
|
2011-10-13 20:53:06 +02:00
|
|
|
if (varid1 == varid2) {
|
2014-08-14 16:10:12 +02:00
|
|
|
var1->use(_varReadInScope);
|
2011-08-19 20:35:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (replace) {
|
2011-08-19 20:35:25 +02:00
|
|
|
// remove var1 from all aliases
|
2015-06-16 22:45:33 +02:00
|
|
|
for (std::set<unsigned int>::const_iterator i = var1->_aliases.begin(); i != var1->_aliases.end(); ++i) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *temp = find(*i);
|
|
|
|
|
|
|
|
if (temp)
|
2013-07-20 12:31:04 +02:00
|
|
|
temp->_aliases.erase(var1->_var->declarationId());
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove all aliases from var1
|
|
|
|
var1->_aliases.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// var1 gets all var2s aliases
|
2015-06-16 22:45:33 +02:00
|
|
|
for (std::set<unsigned int>::const_iterator i = var2->_aliases.begin(); i != var2->_aliases.end(); ++i) {
|
2011-08-19 20:35:25 +02:00
|
|
|
if (*i != varid1)
|
|
|
|
var1->_aliases.insert(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// var2 is an alias of var1
|
|
|
|
var2->_aliases.insert(varid1);
|
|
|
|
var1->_aliases.insert(varid2);
|
|
|
|
|
2012-12-04 21:39:51 +01:00
|
|
|
if (var2->_type == Variables::pointer) {
|
|
|
|
_varReadInScope.back().insert(varid2);
|
2011-08-19 20:35:25 +02:00
|
|
|
var2->_read = true;
|
2012-12-04 21:39:51 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Variables::clearAliases(unsigned int varid)
|
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2011-08-19 20:35:25 +02:00
|
|
|
// remove usage from all aliases
|
2015-06-16 22:45:33 +02:00
|
|
|
std::set<unsigned int>::const_iterator i;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
for (i = usage->_aliases.begin(); i != usage->_aliases.end(); ++i) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *temp = find(*i);
|
|
|
|
|
|
|
|
if (temp)
|
2013-07-20 12:31:04 +02:00
|
|
|
temp->_aliases.erase(usage->_var->declarationId());
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// remove all aliases from usage
|
|
|
|
usage->_aliases.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Variables::eraseAliases(unsigned int varid)
|
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2015-06-16 22:45:33 +02:00
|
|
|
std::set<unsigned int>::const_iterator aliases;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases)
|
|
|
|
erase(*aliases);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Variables::eraseAll(unsigned int varid)
|
|
|
|
{
|
|
|
|
eraseAliases(varid);
|
|
|
|
erase(varid);
|
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::addVar(const Variable *var,
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableType type,
|
|
|
|
bool write_)
|
|
|
|
{
|
2013-07-20 12:31:04 +02:00
|
|
|
if (var->declarationId() > 0) {
|
|
|
|
_varAddedInScope.back().insert(var->declarationId());
|
|
|
|
_varUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false)));
|
2012-12-04 21:39:51 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::allocateMemory(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (usage) {
|
2011-08-19 20:35:25 +02:00
|
|
|
usage->_allocateMemory = true;
|
2012-09-04 14:53:24 +02:00
|
|
|
usage->_lastAccess = tok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::read(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (usage) {
|
2012-12-04 21:39:51 +01:00
|
|
|
_varReadInScope.back().insert(varid);
|
2011-08-19 20:35:25 +02:00
|
|
|
usage->_read = true;
|
2012-12-04 21:39:51 +01:00
|
|
|
if (tok)
|
|
|
|
usage->_lastAccess = tok;
|
2012-09-04 14:53:24 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::readAliases(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2011-08-19 20:35:25 +02:00
|
|
|
std::set<unsigned int>::iterator aliases;
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *aliased = find(*aliases);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (aliased) {
|
2012-12-04 21:39:51 +01:00
|
|
|
_varReadInScope.back().insert(*aliases);
|
2011-08-19 20:35:25 +02:00
|
|
|
aliased->_read = true;
|
2012-09-04 14:53:24 +02:00
|
|
|
aliased->_lastAccess = tok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::readAll(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
2012-09-11 14:24:12 +02:00
|
|
|
read(varid, tok);
|
|
|
|
readAliases(varid, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::write(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2012-09-02 18:50:17 +02:00
|
|
|
if (usage) {
|
2011-08-19 20:35:25 +02:00
|
|
|
usage->_write = true;
|
2012-09-30 17:22:35 +02:00
|
|
|
if (!usage->_var->isStatic() && !Token::simpleMatch(tok->next(), "= 0 ;"))
|
2012-09-11 14:14:35 +02:00
|
|
|
usage->_read = false;
|
2012-09-04 14:53:24 +02:00
|
|
|
usage->_lastAccess = tok;
|
2012-09-02 18:50:17 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::writeAliases(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2015-06-16 22:45:33 +02:00
|
|
|
std::set<unsigned int>::const_iterator aliases;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *aliased = find(*aliases);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (aliased) {
|
2011-08-19 20:35:25 +02:00
|
|
|
aliased->_write = true;
|
2012-09-04 14:53:24 +02:00
|
|
|
aliased->_lastAccess = tok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::writeAll(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
2012-09-11 14:24:12 +02:00
|
|
|
write(varid, tok);
|
|
|
|
writeAliases(varid, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::use(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2012-12-04 21:39:51 +01:00
|
|
|
usage->use(_varReadInScope);
|
2012-09-04 14:53:24 +02:00
|
|
|
usage->_lastAccess = tok;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2015-06-16 22:45:33 +02:00
|
|
|
std::set<unsigned int>::const_iterator aliases;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *aliased = find(*aliases);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (aliased) {
|
2012-12-04 21:39:51 +01:00
|
|
|
aliased->use(_varReadInScope);
|
2012-09-04 14:53:24 +02:00
|
|
|
aliased->_lastAccess = tok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
void Variables::modified(unsigned int varid, const Token* tok)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
VariableUsage *usage = find(varid);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (usage) {
|
2016-10-10 21:27:40 +02:00
|
|
|
if (!usage->_var->isStatic())
|
|
|
|
usage->_read = false;
|
2011-08-19 20:35:25 +02:00
|
|
|
usage->_modified = true;
|
2012-09-04 14:53:24 +02:00
|
|
|
usage->_lastAccess = tok;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2015-06-16 22:45:33 +02:00
|
|
|
std::set<unsigned int>::const_iterator aliases;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
for (aliases = usage->_aliases.begin(); aliases != usage->_aliases.end(); ++aliases) {
|
2011-08-19 20:35:25 +02:00
|
|
|
VariableUsage *aliased = find(*aliases);
|
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
if (aliased) {
|
2011-08-19 20:35:25 +02:00
|
|
|
aliased->_modified = true;
|
2012-09-04 14:53:24 +02:00
|
|
|
aliased->_lastAccess = tok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Variables::VariableUsage *Variables::find(unsigned int varid)
|
|
|
|
{
|
2011-10-13 20:53:06 +02:00
|
|
|
if (varid) {
|
2012-12-05 20:18:14 +01:00
|
|
|
std::map<unsigned int, VariableUsage>::iterator i = _varUsage.find(varid);
|
2011-08-19 20:35:25 +02:00
|
|
|
if (i != _varUsage.end())
|
|
|
|
return &i->second;
|
|
|
|
}
|
2017-08-09 20:00:26 +02:00
|
|
|
return nullptr;
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-12-04 21:39:51 +01:00
|
|
|
void Variables::enterScope()
|
|
|
|
{
|
2012-12-05 20:18:14 +01:00
|
|
|
_varAddedInScope.push_back(std::set<unsigned int>());
|
|
|
|
_varReadInScope.push_back(std::set<unsigned int>());
|
2012-12-04 21:39:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Variables::leaveScope(bool insideLoop)
|
|
|
|
{
|
|
|
|
if (insideLoop) {
|
|
|
|
// read variables are read again in subsequent run through loop
|
2012-12-05 20:18:14 +01:00
|
|
|
std::set<unsigned int> const & currentVarReadInScope = _varReadInScope.back();
|
|
|
|
for (std::set<unsigned int>::const_iterator readIter = currentVarReadInScope.begin();
|
2012-12-04 21:39:51 +01:00
|
|
|
readIter != currentVarReadInScope.end();
|
2012-12-07 01:48:27 +01:00
|
|
|
++readIter) {
|
2014-02-15 08:46:28 +01:00
|
|
|
read(*readIter, nullptr);
|
2012-12-04 21:39:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-05 20:18:14 +01:00
|
|
|
std::list<std::set<unsigned int> >::reverse_iterator reverseReadIter = _varReadInScope.rbegin();
|
2012-12-04 21:39:51 +01:00
|
|
|
++reverseReadIter;
|
2012-12-07 01:48:27 +01:00
|
|
|
if (reverseReadIter != _varReadInScope.rend()) {
|
2012-12-04 21:39:51 +01:00
|
|
|
// Transfer read variables into previous scope
|
|
|
|
|
2012-12-05 20:18:14 +01:00
|
|
|
std::set<unsigned int> const & currentVarAddedInScope = _varAddedInScope.back();
|
|
|
|
std::set<unsigned int> & currentVarReadInScope = _varReadInScope.back();
|
|
|
|
for (std::set<unsigned int>::const_iterator addedIter = currentVarAddedInScope.begin();
|
2012-12-04 21:39:51 +01:00
|
|
|
addedIter != currentVarAddedInScope.end();
|
2012-12-07 01:48:27 +01:00
|
|
|
++addedIter) {
|
2012-12-04 21:39:51 +01:00
|
|
|
currentVarReadInScope.erase(*addedIter);
|
|
|
|
}
|
2012-12-05 20:18:14 +01:00
|
|
|
std::set<unsigned int> & previousVarReadInScope = *reverseReadIter;
|
2012-12-04 21:39:51 +01:00
|
|
|
previousVarReadInScope.insert(currentVarReadInScope.begin(),
|
|
|
|
currentVarReadInScope.end());
|
|
|
|
}
|
|
|
|
_varReadInScope.pop_back();
|
|
|
|
_varAddedInScope.pop_back();
|
|
|
|
}
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
|
|
|
// a = a + b;
|
2017-09-26 23:51:04 +02:00
|
|
|
if (Token::Match(tok, "%var% = %var% !!;")) {
|
|
|
|
const Token* rhsVarTok = tok->tokAt(2);
|
|
|
|
if (tok->varId() == rhsVarTok->varId()) {
|
|
|
|
return rhsVarTok;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2015-12-31 01:15:49 +01:00
|
|
|
if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=")
|
|
|
|
return tok->next();
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
const Token* const tokOld = tok;
|
2011-12-18 20:15:41 +01:00
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
// check for aliased variable
|
|
|
|
const unsigned int varid1 = tok->varId();
|
|
|
|
Variables::VariableUsage *var1 = variables.find(varid1);
|
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (var1) {
|
2012-02-29 20:57:48 +01:00
|
|
|
// jump behind '='
|
|
|
|
tok = tok->next();
|
2015-12-31 01:15:49 +01:00
|
|
|
while (!tok->isAssignmentOp()) {
|
2012-08-22 19:47:46 +02:00
|
|
|
if (tok->varId())
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(tok->varId(), tok);
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
2012-08-22 19:47:46 +02:00
|
|
|
}
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
|
2013-02-20 17:43:16 +01:00
|
|
|
if (Token::Match(tok, "( const| struct|union| %type% * ) ( ("))
|
|
|
|
tok = tok->link()->next();
|
|
|
|
|
|
|
|
if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]"))
|
|
|
|
tok = tok->next();
|
|
|
|
|
2015-01-31 10:50:39 +01:00
|
|
|
if (Token::Match(tok, "(| &| %name%") ||
|
2016-11-26 17:08:36 +01:00
|
|
|
(Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) {
|
2011-08-19 20:35:25 +02:00
|
|
|
bool addressOf = false;
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
if (Token::Match(tok, "%var% ."))
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->varId(), tok); // use = read + write
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// check for C style cast
|
2012-02-29 20:57:48 +01:00
|
|
|
if (tok->str() == "(") {
|
|
|
|
tok = tok->next();
|
|
|
|
if (tok->str() == "const")
|
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
if (Token::Match(tok, "struct|union"))
|
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2013-03-04 19:13:49 +01:00
|
|
|
while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")"))
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
if (tok->str() == "&") {
|
2011-08-19 20:35:25 +02:00
|
|
|
addressOf = true;
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
} else if (tok->str() == "(") {
|
|
|
|
tok = tok->next();
|
|
|
|
if (tok->str() == "&") {
|
2011-08-19 20:35:25 +02:00
|
|
|
addressOf = true;
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
}
|
2013-09-03 17:02:46 +02:00
|
|
|
} else if (Token::Match(tok, "%cop% %var%")) {
|
|
|
|
variables.read(tok->next()->varId(), tok);
|
2012-02-29 20:57:48 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// check for C++ style cast
|
2012-02-29 20:57:48 +01:00
|
|
|
else if (tok->str().find("cast") != std::string::npos &&
|
|
|
|
tok->strAt(1) == "<") {
|
|
|
|
tok = tok->tokAt(2);
|
|
|
|
if (tok->str() == "const")
|
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
if (Token::Match(tok, "struct|union"))
|
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
if (tok->str() == "*")
|
|
|
|
tok = tok->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->tokAt(2);
|
2016-01-31 10:25:09 +01:00
|
|
|
if (!tok)
|
|
|
|
return tokOld;
|
2012-02-29 20:57:48 +01:00
|
|
|
if (tok->str() == "&") {
|
2011-08-19 20:35:25 +02:00
|
|
|
addressOf = true;
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
// no cast, no ?
|
2015-01-31 10:50:39 +01:00
|
|
|
else if (!Token::Match(tok, "%name% ?")) {
|
2012-02-29 20:57:48 +01:00
|
|
|
if (tok->str() == "&") {
|
2011-08-19 20:35:25 +02:00
|
|
|
addressOf = true;
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = tok->next();
|
|
|
|
} else if (tok->str() == "new")
|
|
|
|
return tokOld;
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if variable is local
|
2012-02-29 20:57:48 +01:00
|
|
|
unsigned int varid2 = tok->varId();
|
2015-06-03 17:17:53 +02:00
|
|
|
const Variables::VariableUsage* var2 = variables.find(varid2);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-10-13 20:53:06 +02:00
|
|
|
if (var2) { // local variable (alias or read it)
|
2012-06-24 16:54:37 +02:00
|
|
|
if (var1->_type == Variables::pointer || var1->_type == Variables::pointerArray) {
|
2011-08-19 20:35:25 +02:00
|
|
|
if (dereference)
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(varid2, tok);
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2011-08-19 20:35:25 +02:00
|
|
|
if (addressOf ||
|
|
|
|
var2->_type == Variables::array ||
|
2011-10-13 20:53:06 +02:00
|
|
|
var2->_type == Variables::pointer) {
|
2012-02-29 20:57:48 +01:00
|
|
|
bool replace = true;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-06-24 16:54:37 +02:00
|
|
|
// pointerArray => don't replace
|
|
|
|
if (var1->_type == Variables::pointerArray)
|
|
|
|
replace = false;
|
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
// check if variable declared in same scope
|
2012-09-04 14:53:24 +02:00
|
|
|
else if (scope == var1->_var->scope())
|
2011-08-19 20:35:25 +02:00
|
|
|
replace = true;
|
|
|
|
|
|
|
|
// not in same scope as declaration
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2011-08-19 20:35:25 +02:00
|
|
|
// no other assignment in this scope
|
2012-04-28 15:43:42 +02:00
|
|
|
if (var1->_assignments.find(scope) == var1->_assignments.end() ||
|
|
|
|
scope->type == Scope::eSwitch) {
|
2011-08-19 20:35:25 +02:00
|
|
|
// nothing to replace
|
|
|
|
if (var1->_assignments.empty())
|
|
|
|
replace = false;
|
|
|
|
|
|
|
|
// this variable has previous assignments
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2011-08-19 20:35:25 +02:00
|
|
|
/**
|
|
|
|
* @todo determine if existing aliases should be replaced or merged
|
|
|
|
*/
|
|
|
|
|
|
|
|
replace = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// assignment in this scope
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2012-02-29 20:57:48 +01:00
|
|
|
// replace when only one other assignment, merge them otherwise
|
|
|
|
replace = (var1->_assignments.size() == 1);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
variables.alias(varid1, varid2, replace);
|
2012-02-29 20:57:48 +01:00
|
|
|
} else if (tok->strAt(1) == "?") {
|
2011-08-19 20:35:25 +02:00
|
|
|
if (var2->_type == Variables::reference)
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAliases(varid2, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
else
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(varid2, tok);
|
2012-08-22 19:47:46 +02:00
|
|
|
} else {
|
2012-12-28 18:18:36 +01:00
|
|
|
variables.readAll(varid2, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
2011-10-13 20:53:06 +02:00
|
|
|
} else if (var1->_type == Variables::reference) {
|
2011-08-19 20:35:25 +02:00
|
|
|
variables.alias(varid1, varid2, true);
|
2011-10-13 20:53:06 +02:00
|
|
|
} else {
|
2012-12-28 18:18:36 +01:00
|
|
|
if ((var2->_type == Variables::pointer || var2->_type == Variables::pointerArray) && tok->strAt(1) == "[")
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAliases(varid2, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(varid2, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2011-10-13 20:53:06 +02:00
|
|
|
} else { // not a local variable (or an unsupported local variable)
|
|
|
|
if (var1->_type == Variables::pointer && !dereference) {
|
2011-08-19 20:35:25 +02:00
|
|
|
// check if variable declaration is in this scope
|
2017-07-09 11:19:00 +02:00
|
|
|
if (var1->_var->scope() == scope) {
|
|
|
|
// If variable is used in RHS then "use" variable
|
|
|
|
for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) {
|
|
|
|
if (rhs->varId() == varid1) {
|
|
|
|
variables.use(varid1, tok);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
variables.clearAliases(varid1);
|
2017-07-09 11:19:00 +02:00
|
|
|
} else {
|
2011-08-19 20:35:25 +02:00
|
|
|
// no other assignment in this scope
|
2011-12-18 20:15:41 +01:00
|
|
|
if (var1->_assignments.find(scope) == var1->_assignments.end()) {
|
2011-08-19 20:35:25 +02:00
|
|
|
/**
|
|
|
|
* @todo determine if existing aliases should be discarded
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
// this assignment replaces the last assignment in this scope
|
2011-10-13 20:53:06 +02:00
|
|
|
else {
|
2011-08-19 20:35:25 +02:00
|
|
|
// aliased variables in a larger scope are not supported
|
|
|
|
// remove all aliases
|
|
|
|
variables.clearAliases(varid1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-29 20:57:48 +01:00
|
|
|
} else
|
|
|
|
tok = tokOld;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
var1->_assignments.insert(scope);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for alias to struct member
|
|
|
|
// char c[10]; a.b = c;
|
2015-01-31 10:50:39 +01:00
|
|
|
else if (Token::Match(tok->tokAt(-2), "%name% .")) {
|
2017-09-26 23:51:04 +02:00
|
|
|
const Token *rhsVarTok = tok->tokAt(2);
|
|
|
|
if (rhsVarTok && rhsVarTok->varId()) {
|
|
|
|
const unsigned int varid2 = rhsVarTok->varId();
|
2015-06-03 17:17:53 +02:00
|
|
|
const Variables::VariableUsage *var2 = variables.find(varid2);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// struct member aliased to local variable
|
|
|
|
if (var2 && (var2->_type == Variables::array ||
|
2011-10-13 20:53:06 +02:00
|
|
|
var2->_type == Variables::pointer)) {
|
2011-08-19 20:35:25 +02:00
|
|
|
// erase aliased variable and all variables that alias it
|
|
|
|
// to prevent false positives
|
|
|
|
variables.eraseAll(varid2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-20 06:38:53 +01:00
|
|
|
// Possible pointer alias
|
2015-01-31 10:50:39 +01:00
|
|
|
else if (Token::Match(tok, "%name% = %name% ;")) {
|
2013-03-20 06:38:53 +01:00
|
|
|
const unsigned int varid2 = tok->tokAt(2)->varId();
|
2015-06-03 17:17:53 +02:00
|
|
|
const Variables::VariableUsage *var2 = variables.find(varid2);
|
2013-03-20 06:38:53 +01:00
|
|
|
if (var2 && (var2->_type == Variables::array ||
|
|
|
|
var2->_type == Variables::pointer)) {
|
|
|
|
variables.use(varid2,tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
return tok;
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
static bool isPartOfClassStructUnion(const Token* tok)
|
|
|
|
{
|
|
|
|
for (; tok; tok = tok->previous()) {
|
|
|
|
if (tok->str() == "}" || tok->str() == ")")
|
|
|
|
tok = tok->link();
|
|
|
|
else if (tok->str() == "(")
|
2013-08-07 16:27:37 +02:00
|
|
|
return (false);
|
2011-12-18 20:15:41 +01:00
|
|
|
else if (tok->str() == "{") {
|
2013-08-07 16:27:37 +02:00
|
|
|
return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union");
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-12-26 08:12:23 +01:00
|
|
|
// Skip [ .. ]
|
|
|
|
static const Token * skipBrackets(const Token *tok)
|
|
|
|
{
|
|
|
|
while (tok && tok->str() == "[")
|
|
|
|
tok = tok->link()->next();
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-26 08:29:02 +01:00
|
|
|
// Skip [ .. ] . x
|
|
|
|
static const Token * skipBracketsAndMembers(const Token *tok)
|
|
|
|
{
|
|
|
|
while (tok) {
|
|
|
|
if (tok->str() == "[")
|
|
|
|
tok = tok->link()->next();
|
2015-01-31 10:50:39 +01:00
|
|
|
else if (Token::Match(tok, ". %name%"))
|
2012-02-26 08:29:02 +01:00
|
|
|
tok = tok->tokAt(2);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
2017-02-28 22:04:05 +01:00
|
|
|
static void useFunctionArgs(const Token *tok, Variables& variables)
|
|
|
|
{
|
|
|
|
// TODO: Match function args to see if they are const or not. Assume that const data is not written.
|
|
|
|
if (!tok)
|
|
|
|
return;
|
2017-07-01 22:45:51 +02:00
|
|
|
if (tok->str() == ",") {
|
|
|
|
useFunctionArgs(tok->astOperand1(), variables);
|
|
|
|
useFunctionArgs(tok->astOperand2(), variables);
|
|
|
|
} else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) {
|
2017-02-28 22:04:05 +01:00
|
|
|
useFunctionArgs(tok->astOperand1(), variables);
|
|
|
|
useFunctionArgs(tok->astOperand2(), variables);
|
|
|
|
} else if (tok->variable() && tok->variable()->isArray()) {
|
|
|
|
variables.use(tok->varId(), tok);
|
|
|
|
}
|
|
|
|
}
|
2012-02-26 08:29:02 +01:00
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Usage of function variables
|
|
|
|
//---------------------------------------------------------------------------
|
2012-12-04 21:39:51 +01:00
|
|
|
void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables, bool insideLoop)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
2012-12-04 21:39:51 +01:00
|
|
|
Variables::ScopeGuard scopeGuard=variables.newScope(insideLoop);
|
|
|
|
|
2012-02-25 12:56:33 +01:00
|
|
|
// Find declarations if the scope is executable..
|
2012-09-04 14:53:24 +02:00
|
|
|
if (scope->isExecutable()) {
|
2012-02-25 12:56:33 +01:00
|
|
|
// Find declarations
|
|
|
|
for (std::list<Variable>::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) {
|
2012-06-28 17:22:56 +02:00
|
|
|
if (i->isThrow() || i->isExtern())
|
2012-03-26 21:19:42 +02:00
|
|
|
continue;
|
2012-02-25 12:56:33 +01:00
|
|
|
Variables::VariableType type = Variables::none;
|
|
|
|
if (i->isArray() && (i->nameToken()->previous()->str() == "*" || i->nameToken()->strAt(-2) == "*"))
|
|
|
|
type = Variables::pointerArray;
|
|
|
|
else if (i->isArray() && i->nameToken()->previous()->str() == "&")
|
|
|
|
type = Variables::referenceArray;
|
|
|
|
else if (i->isArray())
|
2017-06-03 15:31:29 +02:00
|
|
|
type = (i->dimensions().size() == 1U) ? Variables::array : Variables::pointerArray;
|
2012-02-25 12:56:33 +01:00
|
|
|
else if (i->isReference())
|
|
|
|
type = Variables::reference;
|
|
|
|
else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*")
|
|
|
|
type = Variables::pointerPointer;
|
2015-08-27 23:56:26 +02:00
|
|
|
else if (i->isPointerToArray())
|
|
|
|
type = Variables::pointerPointer;
|
2012-02-25 12:56:33 +01:00
|
|
|
else if (i->isPointer())
|
|
|
|
type = Variables::pointer;
|
2013-01-07 20:28:43 +01:00
|
|
|
else if (_tokenizer->isC() ||
|
|
|
|
i->typeEndToken()->isStandardType() ||
|
2013-03-05 15:28:40 +01:00
|
|
|
isRecordTypeWithoutSideEffects(i->type()) ||
|
2014-01-30 05:26:48 +01:00
|
|
|
(i->isStlType() &&
|
2014-12-07 15:32:09 +01:00
|
|
|
!Token::Match(i->typeStartToken()->tokAt(2), "lock_guard|unique_lock|shared_ptr|unique_ptr|auto_ptr|shared_lock")))
|
2012-02-25 12:56:33 +01:00
|
|
|
type = Variables::standard;
|
|
|
|
if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken()))
|
|
|
|
continue;
|
|
|
|
const Token* defValTok = i->nameToken()->next();
|
2016-01-30 20:59:32 +01:00
|
|
|
if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list.
|
|
|
|
defValTok = defValTok->linkAt(1)->next();
|
2012-02-25 12:56:33 +01:00
|
|
|
for (; defValTok; defValTok = defValTok->next()) {
|
|
|
|
if (defValTok->str() == "[")
|
|
|
|
defValTok = defValTok->link();
|
2015-10-27 14:46:58 +01:00
|
|
|
else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.addVar(&*i, type, true);
|
2012-02-25 12:56:33 +01:00
|
|
|
break;
|
|
|
|
} else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.addVar(&*i, type, i->isStatic());
|
2012-02-25 12:56:33 +01:00
|
|
|
break;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2012-03-20 19:00:16 +01:00
|
|
|
if (i->isArray() && i->isClass()) // Array of class/struct members. Initialized by ctor.
|
2013-07-20 12:31:04 +02:00
|
|
|
variables.write(i->declarationId(), i->nameToken());
|
2015-01-31 10:50:39 +01:00
|
|
|
if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read.
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken());
|
2012-02-25 12:56:33 +01:00
|
|
|
|
2017-01-22 10:16:40 +01:00
|
|
|
if (defValTok && defValTok->next()) {
|
|
|
|
// simple assignment "var = 123"
|
|
|
|
if (defValTok->str() == "=" && defValTok->next()->str() != "{") {
|
2012-02-25 12:56:33 +01:00
|
|
|
doAssignment(variables, i->nameToken(), false, scope);
|
2017-01-22 10:16:40 +01:00
|
|
|
} else {
|
|
|
|
// could be "var = {...}" OR "var{...}" (since C++11)
|
2017-07-23 23:32:14 +02:00
|
|
|
const Token* tokBraceStart = nullptr;
|
|
|
|
if (Token::simpleMatch(defValTok, "= {")) {
|
2017-01-22 10:16:40 +01:00
|
|
|
// "var = {...}"
|
|
|
|
tokBraceStart = defValTok->next();
|
|
|
|
} else if (defValTok->str() == "{") {
|
|
|
|
// "var{...}"
|
|
|
|
tokBraceStart = defValTok;
|
|
|
|
}
|
|
|
|
if (tokBraceStart) {
|
|
|
|
for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) {
|
|
|
|
if (tok->varId()) {
|
|
|
|
// Variables used to initialize the array read.
|
|
|
|
variables.read(tok->varId(), i->nameToken());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-05 15:33:57 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// Check variable usage
|
2014-08-03 19:13:37 +02:00
|
|
|
const Token *tok;
|
|
|
|
if (scope->type == Scope::eFunction)
|
|
|
|
tok = scope->classStart->next();
|
|
|
|
else
|
|
|
|
tok = scope->classDef->next();
|
|
|
|
for (; tok && tok != scope->classEnd; tok = tok->next()) {
|
2012-11-28 06:11:33 +01:00
|
|
|
if (tok->str() == "for" || tok->str() == "while" || tok->str() == "do") {
|
2011-12-18 20:15:41 +01:00
|
|
|
for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) {
|
|
|
|
if ((*i)->classDef == tok) { // Find associated scope
|
2012-12-04 21:39:51 +01:00
|
|
|
checkFunctionVariableUsage_iterateScopes(*i, variables, true); // Scan child scope
|
2011-12-18 20:15:41 +01:00
|
|
|
tok = (*i)->classStart->link();
|
|
|
|
break;
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
if (!tok)
|
|
|
|
break;
|
|
|
|
}
|
2014-08-05 15:33:57 +02:00
|
|
|
if (tok->str() == "{" && tok != scope->classStart && !tok->previous()->varId()) {
|
2011-12-18 20:15:41 +01:00
|
|
|
for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) {
|
|
|
|
if ((*i)->classStart == tok) { // Find associated scope
|
2012-12-04 21:39:51 +01:00
|
|
|
checkFunctionVariableUsage_iterateScopes(*i, variables, false); // Scan child scope
|
2011-12-18 20:15:41 +01:00
|
|
|
tok = tok->link();
|
|
|
|
break;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
if (!tok)
|
|
|
|
break;
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
if (Token::Match(tok, "asm ( %str% )")) {
|
|
|
|
variables.clear();
|
|
|
|
break;
|
|
|
|
}
|
2016-07-07 18:27:31 +02:00
|
|
|
if (Token::Match(tok, "goto|break")) { // #4447
|
2013-03-28 06:44:37 +01:00
|
|
|
variables.clear();
|
|
|
|
break;
|
|
|
|
}
|
2016-01-31 12:39:43 +01:00
|
|
|
|
|
|
|
// templates
|
2018-03-29 22:00:04 +02:00
|
|
|
if (tok->isName() && endsWith(tok->str(), '>')) {
|
2016-01-31 09:11:52 +01:00
|
|
|
// TODO: This is a quick fix to handle when constants are used
|
|
|
|
// as template parameters. Try to handle this better, perhaps
|
|
|
|
// only remove constants.
|
2016-01-30 16:49:39 +01:00
|
|
|
variables.clear();
|
|
|
|
}
|
2013-03-28 06:44:37 +01:00
|
|
|
|
2012-12-17 18:19:05 +01:00
|
|
|
// bailout when for_each is used
|
2015-01-31 10:50:39 +01:00
|
|
|
if (Token::Match(tok, "%name% (") && Token::simpleMatch(tok->linkAt(1), ") {") && !Token::Match(tok, "if|for|while|switch")) {
|
2012-12-17 18:19:05 +01:00
|
|
|
// does the name contain "for_each" or "foreach"?
|
2013-03-12 16:11:18 +01:00
|
|
|
std::string nameTok;
|
|
|
|
nameTok.resize(tok->str().size());
|
|
|
|
std::transform(tok->str().begin(), tok->str().end(), nameTok.begin(), ::tolower);
|
2012-12-27 11:51:12 +01:00
|
|
|
if (nameTok.find("foreach") != std::string::npos || nameTok.find("for_each") != std::string::npos) {
|
2012-12-17 18:19:05 +01:00
|
|
|
// bailout all variables in the body that are used more than once.
|
|
|
|
// TODO: there is no need to bailout if variable is only read or only written
|
|
|
|
std::set<unsigned int> varid;
|
|
|
|
const Token * const endTok = tok->linkAt(1)->linkAt(1);
|
|
|
|
for (const Token *tok2 = endTok->link(); tok2 && tok2 != endTok; tok2 = tok2->next()) {
|
|
|
|
if (tok2->varId()) {
|
|
|
|
if (varid.find(tok2->varId()) == varid.end())
|
|
|
|
varid.insert(tok2->varId());
|
|
|
|
else
|
|
|
|
variables.erase(tok2->varId());
|
|
|
|
}
|
|
|
|
}
|
2012-12-17 17:07:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-26 15:22:28 +02:00
|
|
|
// C++11 std::for_each
|
|
|
|
// No warning should be written if a variable is first read and
|
|
|
|
// then written in the body.
|
2015-06-29 21:17:15 +02:00
|
|
|
else if (_tokenizer->isCPP() && Token::simpleMatch(tok, "for_each (") && Token::simpleMatch(tok->linkAt(1), ") ;")) {
|
2013-10-26 15:22:28 +02:00
|
|
|
const Token *end = tok->linkAt(1);
|
|
|
|
if (end->previous()->str() == "}") {
|
|
|
|
std::set<unsigned int> readvar;
|
|
|
|
for (const Token *body = end->linkAt(-1); body != end; body = body->next()) {
|
|
|
|
if (body->varId() == 0U)
|
|
|
|
continue;
|
2014-03-14 16:26:37 +01:00
|
|
|
if (!Token::simpleMatch(body->next(),"="))
|
2013-10-26 15:22:28 +02:00
|
|
|
readvar.insert(body->varId());
|
|
|
|
else if (readvar.find(body->varId()) != readvar.end())
|
|
|
|
variables.erase(body->varId());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-02 00:17:35 +02:00
|
|
|
else if (Token::Match(tok->previous(), "[;{}]")) {
|
2012-07-24 20:47:29 +02:00
|
|
|
for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) {
|
|
|
|
if (tok2->varId()) {
|
2016-01-31 12:39:43 +01:00
|
|
|
// Is this a variable declaration?
|
2013-02-06 06:39:58 +01:00
|
|
|
const Variable *var = tok2->variable();
|
2016-01-31 12:39:43 +01:00
|
|
|
if (!var || var->nameToken() != tok2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Mark template parameters used in declaration as use..
|
|
|
|
if (tok2->strAt(-1) == ">") {
|
|
|
|
for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) {
|
|
|
|
if (tok3->varId() > 0U)
|
|
|
|
variables.use(tok3->varId(), tok3);
|
|
|
|
}
|
2012-07-24 20:47:29 +02:00
|
|
|
}
|
2016-01-31 12:39:43 +01:00
|
|
|
|
|
|
|
// Skip variable declaration..
|
|
|
|
tok = tok2->next();
|
|
|
|
if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor
|
|
|
|
tok = tok->next();
|
|
|
|
else if (Token::Match(tok, "= %var% ;")) { // Simple initialization
|
|
|
|
tok = tok->next();
|
|
|
|
if (!var->isReference())
|
|
|
|
variables.read(tok->varId(), tok);
|
2017-07-23 23:32:14 +02:00
|
|
|
} else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) {
|
|
|
|
const Token * const rhs1 = skipBrackets(tok)->next();
|
|
|
|
for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) {
|
|
|
|
if (rhs->varId())
|
|
|
|
variables.readAll(rhs->varId(), rhs);
|
|
|
|
}
|
2016-01-31 12:39:43 +01:00
|
|
|
} else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector
|
|
|
|
tok = tok->previous();
|
|
|
|
break;
|
2012-07-24 20:47:29 +02:00
|
|
|
} else if (Token::Match(tok2, "[;({=]"))
|
2012-02-19 15:25:46 +01:00
|
|
|
break;
|
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
|
|
|
// Freeing memory (not considered "using" the pointer if it was also allocated in this function)
|
|
|
|
if (Token::Match(tok, "free|g_free|kfree|vfree ( %var% )") ||
|
2015-06-29 21:17:15 +02:00
|
|
|
(_tokenizer->isCPP() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) {
|
2011-12-18 20:15:41 +01:00
|
|
|
unsigned int varid = 0;
|
|
|
|
if (tok->str() != "delete") {
|
2017-09-26 23:51:04 +02:00
|
|
|
const Token *varTok = tok->tokAt(2);
|
|
|
|
varid = varTok->varId();
|
|
|
|
tok = varTok->next();
|
2011-12-18 20:15:41 +01:00
|
|
|
} else if (tok->strAt(1) == "[") {
|
2017-09-26 23:51:04 +02:00
|
|
|
const Token *varTok = tok->tokAt(3);
|
|
|
|
varid = varTok->varId();
|
|
|
|
tok = varTok;
|
2011-12-18 20:15:41 +01:00
|
|
|
} else {
|
|
|
|
varid = tok->next()->varId();
|
2011-08-19 20:35:25 +02:00
|
|
|
tok = tok->next();
|
|
|
|
}
|
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
Variables::VariableUsage *var = variables.find(varid);
|
|
|
|
if (var && !var->_allocateMemory) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAll(varid, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-03-20 19:00:16 +01:00
|
|
|
else if (Token::Match(tok, "return|throw")) {
|
2012-01-08 08:44:18 +01:00
|
|
|
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
|
|
|
|
if (tok2->varId())
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAll(tok2->varId(), tok);
|
2012-01-08 08:44:18 +01:00
|
|
|
else if (tok2->str() == ";")
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// assignment
|
2015-12-31 01:15:49 +01:00
|
|
|
else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") ||
|
|
|
|
Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) {
|
2011-12-18 20:15:41 +01:00
|
|
|
bool dereference = false;
|
|
|
|
bool pre = false;
|
|
|
|
bool post = false;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
if (tok->str() == "*") {
|
|
|
|
dereference = true;
|
2011-08-19 20:35:25 +02:00
|
|
|
tok = tok->next();
|
|
|
|
}
|
|
|
|
|
2015-12-31 01:15:49 +01:00
|
|
|
if (Token::Match(tok, "( const| %type% *| ) %name% %assign%"))
|
2011-12-18 20:15:41 +01:00
|
|
|
tok = tok->link()->next();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
else if (tok->str() == "(")
|
2011-08-19 20:35:25 +02:00
|
|
|
tok = tok->next();
|
|
|
|
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->tokType() == Token::eIncDecOp) {
|
2011-12-18 20:15:41 +01:00
|
|
|
pre = true;
|
2011-08-19 20:35:25 +02:00
|
|
|
tok = tok->next();
|
|
|
|
}
|
|
|
|
|
2015-08-14 20:46:13 +02:00
|
|
|
if (tok->next()->tokType() == Token::eIncDecOp)
|
2011-12-18 20:15:41 +01:00
|
|
|
post = true;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
const unsigned int varid1 = tok->varId();
|
2012-11-15 08:36:43 +01:00
|
|
|
const Token * const start = tok;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-01-01 12:14:18 +01:00
|
|
|
// assignment in while head..
|
|
|
|
bool inwhile = false;
|
|
|
|
{
|
|
|
|
const Token *parent = tok->astParent();
|
|
|
|
while (parent) {
|
2017-08-24 17:10:33 +02:00
|
|
|
if (Token::simpleMatch(parent->previous(), "while (")) {
|
2016-01-01 12:14:18 +01:00
|
|
|
inwhile = true;
|
2017-08-24 17:10:33 +02:00
|
|
|
break;
|
|
|
|
}
|
2016-01-01 12:14:18 +01:00
|
|
|
parent = parent->astParent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 20:57:48 +01:00
|
|
|
tok = doAssignment(variables, tok, dereference, scope);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2015-12-31 01:15:49 +01:00
|
|
|
if (tok && tok->isAssignmentOp() && tok->str() != "=") {
|
|
|
|
variables.use(varid1, tok);
|
2015-12-31 15:30:33 +01:00
|
|
|
if (Token::Match(tok, "%assign% %name%")) {
|
2015-12-31 01:15:49 +01:00
|
|
|
tok = tok->next();
|
2015-12-31 15:30:33 +01:00
|
|
|
variables.read(tok->varId(), tok);
|
|
|
|
}
|
2015-12-31 01:15:49 +01:00
|
|
|
}
|
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
if (pre || post)
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(varid1, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
if (dereference) {
|
|
|
|
Variables::VariableUsage *var = variables.find(varid1);
|
|
|
|
if (var && var->_type == Variables::array)
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.write(varid1, tok);
|
|
|
|
variables.writeAliases(varid1, tok);
|
|
|
|
variables.read(varid1, tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
} else {
|
|
|
|
Variables::VariableUsage *var = variables.find(varid1);
|
2016-01-01 12:14:18 +01:00
|
|
|
if (var && (inwhile || start->strAt(-1) == ",")) {
|
2012-11-15 08:36:43 +01:00
|
|
|
variables.use(varid1, tok);
|
|
|
|
} else if (var && var->_type == Variables::reference) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.writeAliases(varid1, tok);
|
|
|
|
variables.read(varid1, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
// Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
|
|
|
|
else if (var && var->_type == Variables::pointer &&
|
2015-01-31 10:50:39 +01:00
|
|
|
Token::Match(start, "%name% = new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf")) {
|
2011-12-18 20:15:41 +01:00
|
|
|
bool allocate = true;
|
|
|
|
|
|
|
|
if (start->strAt(2) == "new") {
|
|
|
|
const Token *type = start->tokAt(3);
|
|
|
|
|
|
|
|
// skip nothrow
|
2015-06-29 21:17:15 +02:00
|
|
|
if (_tokenizer->isCPP() && (Token::simpleMatch(type, "( nothrow )") ||
|
2015-07-01 07:50:13 +02:00
|
|
|
Token::simpleMatch(type, "( std :: nothrow )")))
|
2011-12-18 20:15:41 +01:00
|
|
|
type = type->link()->next();
|
|
|
|
|
|
|
|
// is it a user defined type?
|
|
|
|
if (!type->isStandardType()) {
|
2013-02-06 06:39:58 +01:00
|
|
|
const Variable *variable = start->variable();
|
2013-03-05 15:28:40 +01:00
|
|
|
if (!variable || !isRecordTypeWithoutSideEffects(variable->type()))
|
2011-12-18 20:15:41 +01:00
|
|
|
allocate = false;
|
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
if (allocate)
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.allocateMemory(varid1, tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
else
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.write(varid1, tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
} else if (varid1 && Token::Match(tok, "%varid% .", varid1)) {
|
2017-08-01 14:56:53 +02:00
|
|
|
variables.read(varid1, tok);
|
|
|
|
variables.write(varid1, start);
|
2016-10-18 21:44:02 +02:00
|
|
|
} else if (var &&
|
|
|
|
var->_type == Variables::pointer &&
|
|
|
|
Token::Match(tok, "%name% ;") &&
|
|
|
|
tok->varId() == 0 &&
|
|
|
|
tok->hasKnownIntValue() &&
|
2017-03-27 18:48:34 +02:00
|
|
|
tok->values().front().intvalue == 0) {
|
2016-10-18 21:44:02 +02:00
|
|
|
variables.use(varid1, tok);
|
2011-10-13 20:53:06 +02:00
|
|
|
} else {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.write(varid1, tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2015-12-31 01:15:49 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2015-12-31 01:15:49 +01:00
|
|
|
Variables::VariableUsage *var2 = variables.find(tok->varId());
|
|
|
|
if (var2) {
|
|
|
|
if (var2->_type == Variables::reference) {
|
|
|
|
variables.writeAliases(tok->varId(), tok);
|
|
|
|
variables.read(tok->varId(), tok);
|
|
|
|
} else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|["))
|
|
|
|
variables.read(tok->varId(), tok);
|
|
|
|
else if (tok->varId() != varid1 &&
|
|
|
|
var2->_type == Variables::standard &&
|
|
|
|
tok->strAt(-1) != "&")
|
|
|
|
variables.use(tok->varId(), tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2012-02-26 08:29:02 +01:00
|
|
|
const Token * const equal = skipBracketsAndMembers(tok->next());
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// checked for chained assignments
|
2011-12-26 08:12:23 +01:00
|
|
|
if (tok != start && equal && equal->str() == "=") {
|
2015-06-04 17:45:12 +02:00
|
|
|
const unsigned int varId = tok->varId();
|
2012-12-04 21:39:51 +01:00
|
|
|
Variables::VariableUsage *var = variables.find(varId);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-12-04 21:39:51 +01:00
|
|
|
if (var && var->_type != Variables::reference) {
|
|
|
|
variables.read(varId,tok);
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
tok = tok->previous();
|
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// assignment
|
2015-01-31 10:50:39 +01:00
|
|
|
else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) ||
|
2012-08-25 13:07:33 +02:00
|
|
|
(Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) {
|
2016-05-17 16:03:55 +02:00
|
|
|
const Token *eq = tok;
|
|
|
|
while (eq && !eq->isAssignmentOp())
|
|
|
|
eq = eq->astParent();
|
|
|
|
|
|
|
|
const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U;
|
|
|
|
|
2012-08-25 13:07:33 +02:00
|
|
|
if (tok->str() == "*") {
|
|
|
|
tok = tok->tokAt(2);
|
|
|
|
if (tok->str() == "(")
|
|
|
|
tok = tok->link()->next();
|
|
|
|
}
|
|
|
|
|
2015-06-04 17:45:12 +02:00
|
|
|
const unsigned int varid = tok->varId();
|
2011-12-18 20:15:41 +01:00
|
|
|
const Variables::VariableUsage *var = variables.find(varid);
|
|
|
|
|
|
|
|
if (var) {
|
|
|
|
// Consider allocating memory separately because allocating/freeing alone does not constitute using the variable
|
|
|
|
if (var->_type == Variables::pointer &&
|
2012-02-29 20:57:48 +01:00
|
|
|
Token::Match(skipBrackets(tok->next()), "= new|malloc|calloc|kmalloc|kzalloc|kcalloc|strdup|strndup|vmalloc|g_new0|g_try_new|g_new|g_malloc|g_malloc0|g_try_malloc|g_try_malloc0|g_strdup|g_strndup|g_strdup_printf")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.allocateMemory(varid, tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
} else if (var->_type == Variables::pointer || var->_type == Variables::reference) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(varid, tok);
|
|
|
|
variables.writeAliases(varid, tok);
|
2012-06-24 16:54:37 +02:00
|
|
|
} else if (var->_type == Variables::pointerArray) {
|
2016-05-17 16:03:55 +02:00
|
|
|
tok = doAssignment(variables, tok, deref, scope);
|
2011-12-18 20:15:41 +01:00
|
|
|
} else
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.writeAll(varid, tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-12-18 19:02:30 +01:00
|
|
|
else if (_tokenizer->isCPP() && Token::Match(tok, "[;{}] %var% <<")) {
|
|
|
|
variables.erase(tok->next()->varId());
|
|
|
|
}
|
|
|
|
|
2012-03-20 19:00:16 +01:00
|
|
|
else if (Token::Match(tok, "& %var%")) {
|
2014-07-02 00:17:35 +02:00
|
|
|
if (tok->astOperand2()) { // bitop
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(tok->next()->varId(), tok);
|
2012-03-20 19:00:16 +01:00
|
|
|
} else // addressof
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->next()->varId(), tok); // use = read + write
|
2015-01-31 10:50:39 +01:00
|
|
|
} else if (Token::Match(tok, ">>|>>= %name%")) {
|
2014-07-31 23:14:44 +02:00
|
|
|
if (_tokenizer->isC() || (tok->previous()->variable() && tok->previous()->variable()->typeEndToken()->isStandardType() && tok->astOperand1() && tok->astOperand1()->str() != ">>"))
|
2013-08-27 15:46:51 +02:00
|
|
|
variables.read(tok->next()->varId(), tok);
|
|
|
|
else
|
|
|
|
variables.use(tok->next()->varId(), tok); // use = read + write
|
2012-09-04 13:06:04 +02:00
|
|
|
} else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(tok->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
|
|
|
|
// function parameter
|
2012-09-04 13:06:04 +02:00
|
|
|
else if (Token::Match(tok, "[(,] %var% [")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->next()->varId(), tok); // use = read + write
|
2012-09-04 13:06:04 +02:00
|
|
|
} else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->next()->varId(), tok); // use = read + write
|
2016-09-03 20:38:36 +02:00
|
|
|
} else if (Token::Match(tok, "[(,] & %var% [,)]")) {
|
|
|
|
variables.eraseAll(tok->tokAt(2)->varId());
|
2011-12-18 20:15:41 +01:00
|
|
|
} else if (Token::Match(tok, "[(,] (") &&
|
2012-09-04 13:06:04 +02:00
|
|
|
Token::Match(tok->next()->link(), ") %var% [,)]")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write
|
2016-08-02 08:50:04 +02:00
|
|
|
} else if (Token::Match(tok, "[(,] *| %var% =")) {
|
|
|
|
tok = tok->next();
|
|
|
|
if (tok->str() == "*")
|
|
|
|
tok = tok->next();
|
|
|
|
variables.use(tok->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
|
|
|
|
// function
|
2017-02-28 22:04:05 +01:00
|
|
|
else if (Token::Match(tok, "%name% (")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(tok->varId(), tok);
|
2017-02-28 22:04:05 +01:00
|
|
|
useFunctionArgs(tok->next()->astOperand2(), variables);
|
2016-09-03 20:38:36 +02:00
|
|
|
} else if (Token::Match(tok, "std :: ref ( %var% )")) {
|
|
|
|
variables.eraseAll(tok->tokAt(4)->varId());
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2014-09-30 12:39:27 +02:00
|
|
|
else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) {
|
|
|
|
variables.read(tok->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2014-07-02 00:17:35 +02:00
|
|
|
else if (tok->varId() && Token::Match(tok, "%var% .")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.use(tok->varId(), tok); // use = read + write
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2017-07-01 22:45:51 +02:00
|
|
|
else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) {
|
2017-07-01 11:31:51 +02:00
|
|
|
if (tok->astOperand1())
|
|
|
|
variables.use(tok->astOperand1()->varId(), tok->astOperand1());
|
|
|
|
if (tok->astOperand2())
|
|
|
|
variables.use(tok->astOperand2()->varId(), tok->astOperand2());
|
|
|
|
}
|
|
|
|
|
2014-07-02 00:17:35 +02:00
|
|
|
else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=") {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAll(tok->next()->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2013-03-01 12:42:04 +01:00
|
|
|
else if (tok->varId() && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) {
|
2016-01-01 16:04:13 +01:00
|
|
|
if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") &&
|
|
|
|
!(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference()))
|
|
|
|
variables.use(tok->varId(), tok);
|
|
|
|
else
|
|
|
|
variables.readAll(tok->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-09-04 13:06:04 +02:00
|
|
|
else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) {
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.readAll(tok->varId(), tok);
|
2012-09-04 13:06:04 +02:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2014-07-02 00:17:35 +02:00
|
|
|
// ++|--
|
2015-08-14 20:46:13 +02:00
|
|
|
else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) {
|
2014-07-02 00:17:35 +02:00
|
|
|
if (tok->next()->astParent())
|
|
|
|
variables.use(tok->next()->astOperand1()->varId(), tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
else
|
2014-07-02 00:17:35 +02:00
|
|
|
variables.modified(tok->next()->astOperand1()->varId(), tok);
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
else if (tok->isAssignmentOp()) {
|
|
|
|
for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
|
|
|
|
if (tok2->varId()) {
|
2014-08-31 19:46:30 +02:00
|
|
|
if (tok2->strAt(1) == "=")
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.write(tok2->varId(), tok);
|
2015-06-04 17:45:12 +02:00
|
|
|
else if (tok2->next() && tok2->next()->isAssignmentOp())
|
2014-08-31 19:46:30 +02:00
|
|
|
variables.use(tok2->varId(), tok);
|
2012-08-10 12:36:08 +02:00
|
|
|
else
|
2012-09-04 14:53:24 +02:00
|
|
|
variables.read(tok2->varId(), tok);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
2011-12-18 20:15:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
void CheckUnusedVar::checkFunctionVariableUsage()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::STYLE))
|
2011-12-18 20:15:41 +01:00
|
|
|
return;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// Parse all executing scopes..
|
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2012-11-16 06:50:49 +01:00
|
|
|
// only check functions
|
|
|
|
const std::size_t functions = symbolDatabase->functionScopes.size();
|
|
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
|
|
const Scope * scope = symbolDatabase->functionScopes[i];
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-05-26 18:07:56 +02:00
|
|
|
// Bailout when there are lambdas or inline functions
|
|
|
|
// TODO: Handle lambdas and inline functions properly
|
|
|
|
if (scope->hasInlineOrLambdaFunction())
|
|
|
|
continue;
|
|
|
|
|
2011-12-18 20:15:41 +01:00
|
|
|
// varId, usage {read, write, modified}
|
|
|
|
Variables variables;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2015-01-17 16:28:39 +01:00
|
|
|
checkFunctionVariableUsage_iterateScopes(scope, variables, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
|
|
|
|
// Check usage of all variables in the current scope..
|
2012-12-05 20:18:14 +01:00
|
|
|
for (std::map<unsigned int, Variables::VariableUsage>::const_iterator it = variables.varUsage().begin();
|
|
|
|
it != variables.varUsage().end();
|
|
|
|
++it) {
|
2011-08-19 20:35:25 +02:00
|
|
|
const Variables::VariableUsage &usage = it->second;
|
|
|
|
|
|
|
|
// variable has been marked as unused so ignore it
|
2014-08-06 11:13:58 +02:00
|
|
|
if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed())
|
2011-08-19 20:35:25 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// skip things that are only partially implemented to prevent false positives
|
|
|
|
if (usage._type == Variables::pointerPointer ||
|
|
|
|
usage._type == Variables::pointerArray ||
|
|
|
|
usage._type == Variables::referenceArray)
|
|
|
|
continue;
|
|
|
|
|
2014-09-16 11:34:16 +02:00
|
|
|
const std::string &varname = usage._var->name();
|
|
|
|
const Variable* var = symbolDatabase->getVariableFromVarId(it->first);
|
|
|
|
|
2011-08-19 20:35:25 +02:00
|
|
|
// variable has had memory allocated for it, but hasn't done
|
|
|
|
// anything with that memory other than, perhaps, freeing it
|
|
|
|
if (usage.unused() && !usage._modified && usage._allocateMemory)
|
2012-09-04 14:53:24 +02:00
|
|
|
allocatedButUnusedVariableError(usage._lastAccess, varname);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// variable has not been written, read, or modified
|
|
|
|
else if (usage.unused() && !usage._modified)
|
2012-09-04 14:53:24 +02:00
|
|
|
unusedVariableError(usage._var->nameToken(), varname);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// variable has not been written but has been modified
|
2015-01-14 22:43:23 +01:00
|
|
|
else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType())
|
2012-09-04 14:53:24 +02:00
|
|
|
unassignedVariableError(usage._var->nameToken(), varname);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// variable has been written but not read
|
2016-10-10 21:27:40 +02:00
|
|
|
else if (!usage._read)
|
|
|
|
unreadVariableError(usage._lastAccess, varname, usage._modified);
|
2011-08-19 20:35:25 +02:00
|
|
|
|
|
|
|
// variable has been read but not written
|
2015-01-18 13:02:09 +01:00
|
|
|
else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type()))
|
2012-09-04 14:53:24 +02:00
|
|
|
unassignedVariableError(usage._var->nameToken(), varname);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
2016-07-15 15:49:21 +02:00
|
|
|
reportError(tok, Severity::style, "unusedVariable", "Unused variable: " + varname, CWE563, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
reportError(tok, Severity::style, "unusedAllocatedMemory", "Variable '" + varname + "' is allocated memory that is never used.", CWE563, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
2016-10-10 21:27:40 +02:00
|
|
|
void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
2016-10-10 21:27:40 +02:00
|
|
|
if (modified)
|
|
|
|
reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is modified but its new value is never used.", CWE563, false);
|
|
|
|
else
|
|
|
|
reportError(tok, Severity::style, "unreadVariable", "Variable '" + varname + "' is assigned a value that is never used.", CWE563, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname)
|
|
|
|
{
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
reportError(tok, Severity::style, "unassignedVariable", "Variable '" + varname + "' is not assigned a value.", CWE665, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Check that all struct members are used
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void CheckUnusedVar::checkStructMemberUsage()
|
|
|
|
{
|
2017-04-11 11:49:09 +02:00
|
|
|
if (!_settings->isEnabled(Settings::STYLE))
|
2011-08-19 20:35:25 +02:00
|
|
|
return;
|
|
|
|
|
2016-05-24 23:10:39 +02:00
|
|
|
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
for (std::list<Scope>::const_iterator scope = symbolDatabase->scopeList.cbegin(); scope != symbolDatabase->scopeList.cend(); ++scope) {
|
|
|
|
if (scope->type != Scope::eStruct && scope->type != Scope::eUnion)
|
2011-08-19 20:35:25 +02:00
|
|
|
continue;
|
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
if (scope->classStart->fileIndex() != 0 || scope->className.empty())
|
|
|
|
continue;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-09-05 17:27:12 +02:00
|
|
|
// Packed struct => possibly used by lowlevel code. Struct members might be required by hardware.
|
|
|
|
if (scope->classEnd->isAttributePacked())
|
|
|
|
continue;
|
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
// Bail out if struct/union contains any functions
|
|
|
|
if (!scope->functionList.empty())
|
|
|
|
continue;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
// bail out if struct is inherited
|
|
|
|
bool bailout = false;
|
|
|
|
for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.cbegin(); i != symbolDatabase->scopeList.cend(); ++i) {
|
|
|
|
if (i->definedType) {
|
|
|
|
for (size_t j = 0; j < i->definedType->derivedFrom.size(); j++) {
|
|
|
|
if (i->definedType->derivedFrom[j].type == scope->definedType) {
|
|
|
|
bailout = true;
|
|
|
|
break;
|
2012-11-18 15:24:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-25 11:13:31 +02:00
|
|
|
}
|
|
|
|
if (bailout)
|
|
|
|
continue;
|
2015-09-22 15:38:49 +02:00
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
// bail out for extern/global struct
|
|
|
|
for (size_t i = 0; i < symbolDatabase->getVariableListSize(); i++) {
|
|
|
|
const Variable* var = symbolDatabase->getVariableFromVarId(i);
|
|
|
|
if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope->className) {
|
|
|
|
bailout = true;
|
|
|
|
break;
|
2013-01-07 19:20:15 +01:00
|
|
|
}
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2016-05-25 11:13:31 +02:00
|
|
|
if (bailout)
|
|
|
|
continue;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
// Bail out if some data is casted to struct..
|
|
|
|
const std::string castPattern("( struct| " + scope->className + " * ) & %name% [");
|
|
|
|
if (Token::findmatch(scope->classEnd, castPattern.c_str()))
|
|
|
|
continue;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2017-11-12 22:33:17 +01:00
|
|
|
// (struct S){..}
|
|
|
|
const std::string initPattern("( struct| " + scope->className + " ) {");
|
|
|
|
if (Token::findmatch(scope->classEnd, initPattern.c_str()))
|
|
|
|
continue;
|
|
|
|
|
2016-08-02 18:50:04 +02:00
|
|
|
// Bail out if struct is used in sizeof..
|
|
|
|
for (const Token *tok = scope->classEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) {
|
2016-08-02 19:23:45 +02:00
|
|
|
tok = tok->tokAt(2);
|
|
|
|
if (Token::Match(tok, ("struct| " + scope->className).c_str())) {
|
2016-08-02 18:50:04 +02:00
|
|
|
bailout = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bailout)
|
|
|
|
continue;
|
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
// Try to prevent false positives when struct members are not used directly.
|
|
|
|
if (Token::findmatch(scope->classEnd, (scope->className + " %type%| *").c_str()))
|
|
|
|
continue;
|
2015-08-15 20:24:26 +02:00
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
for (std::list<Variable>::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) {
|
|
|
|
// declaring a POD member variable?
|
|
|
|
if (!var->typeStartToken()->isStandardType() && !var->isPointer())
|
2011-08-19 20:35:25 +02:00
|
|
|
continue;
|
|
|
|
|
2015-09-24 18:29:08 +02:00
|
|
|
// Check if the struct member variable is used anywhere in the file
|
2016-05-25 11:13:31 +02:00
|
|
|
if (Token::findsimplematch(_tokenizer->tokens(), (". " + var->name()).c_str()))
|
2015-12-16 14:51:50 +01:00
|
|
|
continue;
|
2011-08-19 20:35:25 +02:00
|
|
|
|
2016-05-25 11:13:31 +02:00
|
|
|
unusedStructMemberError(var->nameToken(), scope->className, var->name(), scope->type == Scope::eUnion);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-22 16:38:23 +02:00
|
|
|
void CheckUnusedVar::unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, bool isUnion)
|
2011-08-19 20:35:25 +02:00
|
|
|
{
|
2015-09-22 16:38:23 +02:00
|
|
|
const char* prefix = isUnion ? "union member '" : "struct member '";
|
CWE mapping of useAutoPointerMalloc, uselessCallsCompare, uselessCallsSwap, uselessCallsSubstr, uselessCallsEmpty, uselessCallsRemove, derefInvalidIterator, reademptycontainer, multiplySizeof, divideSizeof, stringLiteralWrite, incorrectStringCompare, literalWithCharPtrCompare, charLiteralWithCharPtrCompare, incorrectStringBooleanError, staticStringCompare, stringCompare, signConversion, truncLongCastAssignment, truncLongCastReturn, unusedFunction, unusedVariable, unusedAllocatedMemory, unreadVariable, unassignedVariable, unusedStructMember, postfixOperator, va_start_wrongParameter (#824)
Add an optional extended description…
2016-09-03 00:31:35 +02:00
|
|
|
reportError(tok, Severity::style, "unusedStructMember", std::string(prefix) + structname + "::" + varname + "' is never used.", CWE563, false);
|
2011-08-19 20:35:25 +02:00
|
|
|
}
|
2013-04-01 12:41:14 +02:00
|
|
|
|
|
|
|
bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
|
|
|
{
|
|
|
|
// a type that has no side effects (no constructors and no members with constructors)
|
|
|
|
/** @todo false negative: check constructors for side effects */
|
|
|
|
|
2015-06-29 21:17:15 +02:00
|
|
|
const std::pair<std::map<const Type *,bool>::iterator,bool> found=isRecordTypeWithoutSideEffectsMap.insert(
|
2014-01-24 06:10:19 +01:00
|
|
|
std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions
|
2013-04-01 12:41:14 +02:00
|
|
|
bool & withoutSideEffects=found.first->second;
|
|
|
|
if (!found.second)
|
|
|
|
return withoutSideEffects;
|
|
|
|
|
|
|
|
if (type && type->classScope && type->classScope->numConstructors == 0 &&
|
|
|
|
(type->classScope->varlist.empty() || type->needInitialization == Type::True)) {
|
|
|
|
for (std::vector<Type::BaseInfo>::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) {
|
|
|
|
if (!isRecordTypeWithoutSideEffects(i->type)) {
|
|
|
|
withoutSideEffects=false;
|
|
|
|
return withoutSideEffects;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
withoutSideEffects=true;
|
|
|
|
return withoutSideEffects;
|
|
|
|
}
|
|
|
|
|
|
|
|
withoutSideEffects=false; // unknown types are assumed to have side effects
|
|
|
|
return withoutSideEffects;
|
|
|
|
}
|
2014-02-01 22:40:35 +01:00
|
|
|
|
|
|
|
bool CheckUnusedVar::isEmptyType(const Type* type)
|
|
|
|
{
|
|
|
|
// a type that has no variables and no constructor
|
|
|
|
|
2015-06-29 21:17:15 +02:00
|
|
|
const std::pair<std::map<const Type *,bool>::iterator,bool> found=isEmptyTypeMap.insert(
|
2014-02-01 22:40:35 +01:00
|
|
|
std::pair<const Type *,bool>(type,false));
|
|
|
|
bool & emptyType=found.first->second;
|
|
|
|
if (!found.second)
|
|
|
|
return emptyType;
|
|
|
|
|
|
|
|
if (type && type->classScope && type->classScope->numConstructors == 0 &&
|
|
|
|
(type->classScope->varlist.empty())) {
|
|
|
|
for (std::vector<Type::BaseInfo>::const_iterator i = type->derivedFrom.begin(); i != type->derivedFrom.end(); ++i) {
|
|
|
|
if (!isEmptyType(i->type)) {
|
|
|
|
emptyType=false;
|
|
|
|
return emptyType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
emptyType=true;
|
|
|
|
return emptyType;
|
|
|
|
}
|
|
|
|
|
|
|
|
emptyType=false; // unknown types are assumed to be nonempty
|
|
|
|
return emptyType;
|
|
|
|
}
|