//--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables, bool insideLoop) { Variables::ScopeGuard scopeGuard=variables.newScope(insideLoop); // Find declarations if the scope is executable.. if (scope->isExecutable()) { // Find declarations for (std::list<Variable>::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { if (i->isThrow() || i->isExtern()) continue; 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()) type = Variables::array; else if (i->isReference()) type = Variables::reference; else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") type = Variables::pointerPointer; else if (i->isPointer()) type = Variables::pointer; else if (_tokenizer->isC() || i->typeEndToken()->isStandardType() || isRecordTypeWithoutSideEffects(i->type()) || (i->isStlType() && !Token::Match(i->typeStartToken()->tokAt(2), "lock_guard|unique_lock|shared_ptr|unique_ptr|auto_ptr|shared_lock"))) type = Variables::standard; if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) continue; const Token* defValTok = i->nameToken()->next(); for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=") { variables.addVar(&*i, type, true); break; } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { variables.addVar(&*i, type, i->isStatic()); break; } } if (i->isArray() && i->isClass()) // Array of class/struct members. Initialized by ctor. variables.write(i->declarationId(), i->nameToken()); if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read. variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken()); if (defValTok && defValTok->str() == "=") { if (defValTok->next() && defValTok->next()->str() == "{") { for (const Token* tok = defValTok; tok && tok != defValTok->linkAt(1); tok = tok->next()) if (tok->varId()) // Variables used to initialize the array read. variables.read(tok->varId(), i->nameToken()); } else doAssignment(variables, i->nameToken(), false, scope); } } } // Check variable usage 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()) { if (tok->str() == "for" || tok->str() == "while" || tok->str() == "do") { for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classDef == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables, true); // Scan child scope tok = (*i)->classStart->link(); break; } } if (!tok) break; } if (tok->str() == "{" && tok != scope->classStart && !tok->previous()->varId()) { for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables, false); // Scan child scope tok = tok->link(); break; } } if (!tok) break; } if (Token::Match(tok, "asm ( %str% )")) { variables.clear(); break; } if (Token::simpleMatch(tok, "goto")) { // https://sourceforge.net/apps/trac/cppcheck/ticket/4447 variables.clear(); break; } // bailout when for_each is used if (Token::Match(tok, "%name% (") && Token::simpleMatch(tok->linkAt(1), ") {") && !Token::Match(tok, "if|for|while|switch")) { // does the name contain "for_each" or "foreach"? std::string nameTok; nameTok.resize(tok->str().size()); std::transform(tok->str().begin(), tok->str().end(), nameTok.begin(), ::tolower); if (nameTok.find("foreach") != std::string::npos || nameTok.find("for_each") != std::string::npos) { // 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()); } } } } // C++11 std::for_each // No warning should be written if a variable is first read and // then written in the body. else if (Token::simpleMatch(tok, "for_each (") && Token::simpleMatch(tok->linkAt(1), ") ;")) { 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; if (!Token::simpleMatch(body->next(),"=")) readvar.insert(body->varId()); else if (readvar.find(body->varId()) != readvar.end()) variables.erase(body->varId()); } } } else if (Token::Match(tok->previous(), "[;{}]")) { for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) { const Variable *var = tok2->variable(); if (var && var->nameToken() == tok2) { // Declaration: Skip 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); } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector tok = tok->previous(); break; } } else if (Token::Match(tok2, "[;({=]")) break; } } // 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% )") || Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { unsigned int varid = 0; if (tok->str() != "delete") { varid = tok->tokAt(2)->varId(); tok = tok->tokAt(3); } else if (tok->strAt(1) == "[") { varid = tok->tokAt(3)->varId(); tok = tok->tokAt(3); } else { varid = tok->next()->varId(); tok = tok->next(); } Variables::VariableUsage *var = variables.find(varid); if (var && !var->_allocateMemory) { variables.readAll(varid, tok); } } else if (Token::Match(tok, "return|throw")) { for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) variables.readAll(tok2->varId(), tok); else if (tok2->str() == ";") break; } } else if (Token::Match(tok->tokAt(-2), "while|if") && tok->strAt(1) == "=" && tok->varId() && tok->varId() == tok->tokAt(2)->varId()) { variables.use(tok->tokAt(2)->varId(), tok); } // assignment else if (Token::Match(tok, "*| ++|--| %name% ++|--| =") || Token::Match(tok, "*| ( const| %type% *| ) %name% =")) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %name% =")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (tok->type() == Token::eIncDecOp) { pre = true; tok = tok->next(); } if (tok->next()->type() == Token::eIncDecOp) post = true; const unsigned int varid1 = tok->varId(); const Token * const start = tok; tok = doAssignment(variables, tok, dereference, scope); if (pre || post) variables.use(varid1, tok); if (dereference) { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::array) variables.write(varid1, tok); variables.writeAliases(varid1, tok); variables.read(varid1, tok); } else { Variables::VariableUsage *var = variables.find(varid1); if (var && start->strAt(-1) == ",") { variables.use(varid1, tok); } else if (var && var->_type == Variables::reference) { variables.writeAliases(varid1, tok); variables.read(varid1, tok); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->_type == Variables::pointer && 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")) { bool allocate = true; if (start->strAt(2) == "new") { const Token *type = start->tokAt(3); // skip nothrow if (Token::simpleMatch(type, "( nothrow )") || Token::simpleMatch(type, "( std :: nothrow )")) type = type->link()->next(); // is it a user defined type? if (!type->isStandardType()) { const Variable *variable = start->variable(); if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) allocate = false; } } if (allocate) variables.allocateMemory(varid1, tok); else variables.write(varid1, tok); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.use(varid1, tok); } else { variables.write(varid1, tok); } 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); } } const Token * const equal = skipBracketsAndMembers(tok->next()); // checked for chained assignments if (tok != start && equal && equal->str() == "=") { unsigned int varId = tok->varId(); Variables::VariableUsage *var = variables.find(varId); if (var && var->_type != Variables::reference) { variables.read(varId,tok); } tok = tok->previous(); } } // assignment else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || (Token::simpleMatch(tok, "* (") && Token::simpleMatch(tok->next()->link(), ") ="))) { if (tok->str() == "*") { tok = tok->tokAt(2); if (tok->str() == "(") tok = tok->link()->next(); } unsigned int varid = tok->varId(); 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 && 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")) { variables.allocateMemory(varid, tok); } else if (var->_type == Variables::pointer || var->_type == Variables::reference) { variables.read(varid, tok); variables.writeAliases(varid, tok); } else if (var->_type == Variables::pointerArray) { tok = doAssignment(variables, tok, false, scope); } else variables.writeAll(varid, tok); } } else if (_tokenizer->isCPP() && Token::Match(tok, "[;{}] %var% <<")) { variables.erase(tok->next()->varId()); } else if (Token::Match(tok, "& %var%")) { if (tok->astOperand2()) { // bitop variables.read(tok->next()->varId(), tok); } else // addressof variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, ">>|>>= %name%")) { if (_tokenizer->isC() || (tok->previous()->variable() && tok->previous()->variable()->typeEndToken()->isStandardType() && tok->astOperand1() && tok->astOperand1()->str() != ">>")) variables.read(tok->next()->varId(), tok); else variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) { variables.read(tok->varId(), tok); } // function parameter else if (Token::Match(tok, "[(,] %var% [")) { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") { variables.use(tok->next()->varId(), tok); // use = read + write } else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) { variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write } // function else if (Token::Match(tok, "%var% (")) { variables.read(tok->varId(), tok); } else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) { variables.read(tok->varId(), tok); } else if (tok->varId() && Token::Match(tok, "%var% .")) { variables.use(tok->varId(), tok); // use = read + write } else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=") { variables.readAll(tok->next()->varId(), tok); } else if (tok->varId() && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) { variables.readAll(tok->varId(), tok); } else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) { variables.readAll(tok->varId(), tok); } // ++|-- else if (tok->next() && tok->next()->type() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) { if (tok->next()->astParent()) variables.use(tok->next()->astOperand1()->varId(), tok); else variables.modified(tok->next()->astOperand1()->varId(), tok); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { if (tok2->strAt(1) == "=") variables.write(tok2->varId(), tok); else if (tok2->next()->isAssignmentOp()) variables.use(tok2->varId(), tok); else variables.read(tok2->varId(), tok); } } } } }
//--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) { if (scope->type == Scope::eClass || scope->type == Scope::eUnion || scope->type == Scope::eStruct) return; // Find declarations for (std::list<Variable>::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { 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()) type = Variables::array; else if (i->nameToken()->previous()->str() == "&") type = Variables::reference; else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") type = Variables::pointerPointer; else if (i->nameToken()->previous()->str() == "*" || Token::Match(i->nameToken()->tokAt(-2), "* %type%")) type = Variables::pointer; else if (i->typeEndToken()->isStandardType() || isRecordTypeWithoutSideEffects(*i) || Token::simpleMatch(i->nameToken()->tokAt(-3), "std :: string")) type = Variables::standard; if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) continue; const Token* defValTok = i->nameToken()->next(); for (; defValTok; defValTok = defValTok->next()) { if (defValTok->str() == "[") defValTok = defValTok->link(); else if (defValTok->str() == "(" || defValTok->str() == "=") { variables.addVar(i->nameToken(), type, scope, true); break; } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { variables.addVar(i->nameToken(), type, scope, i->isStatic()); break; } } if (i->isArray() && Token::Match(i->nameToken(), "%var% [ %var% ]")) // Array index variable read. variables.read(i->nameToken()->tokAt(2)->varId()); if (defValTok && defValTok->str() == "=") { if (defValTok->next() && defValTok->next()->str() == "{") { for (const Token* tok = defValTok; tok && tok != defValTok->linkAt(1); tok = tok->next()) if (Token::Match(tok, "%var%")) // Variables used to initialize the array read. variables.read(tok->varId()); } else doAssignment(variables, i->nameToken(), false, scope); } else if (Token::Match(defValTok, "( %var% )")) // Variables used to initialize the variable read. variables.readAll(defValTok->next()->varId()); // ReadAll? } // Check variable usage for (const Token *tok = scope->classDef->next(); tok && tok != scope->classEnd; tok = tok->next()) { if (tok->str() == "for" || tok->str() == "catch") { for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classDef == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables); // Scan child scope tok = (*i)->classStart->link(); break; } } if (!tok) break; } if (tok->str() == "{") { for (std::list<Scope*>::const_iterator i = scope->nestedList.begin(); i != scope->nestedList.end(); ++i) { if ((*i)->classStart == tok) { // Find associated scope checkFunctionVariableUsage_iterateScopes(*i, variables); // Scan child scope tok = tok->link(); break; } } if (!tok) break; } if (Token::Match(tok, "asm ( %str% )")) { variables.clear(); break; } if (Token::Match(tok, "%type% const| *|&| const| *| const| %var% [;=[(]") && tok->str() != "return" && tok->str() != "throw") { // Declaration: Skip tok = tok->next(); while (Token::Match(tok, "const|*|&")) tok = tok->next(); tok = Token::findmatch(tok, "[;=[(]"); if (tok && Token::Match(tok, "( %var% )")) // Simple initialization through copy ctor tok = tok->next(); else if (tok && Token::Match(tok, "= %var% ;")) // Simple initialization tok = tok->next(); if (!tok) break; } // 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% )") || Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")) { unsigned int varid = 0; if (tok->str() != "delete") { varid = tok->tokAt(2)->varId(); tok = tok->tokAt(3); } else if (tok->strAt(1) == "[") { varid = tok->tokAt(3)->varId(); tok = tok->tokAt(3); } else { varid = tok->next()->varId(); tok = tok->next(); } Variables::VariableUsage *var = variables.find(varid); if (var && !var->_allocateMemory) { variables.readAll(varid); } } else if (Token::Match(tok, "return|throw %var%")) { for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { if (tok2->varId()) variables.readAll(tok2->varId()); else if (tok2->str() == ";") break; } } // assignment else if (!Token::Match(tok->tokAt(-2), "[;{}.] %var% (") && (Token::Match(tok, "*| (| ++|--| %var% ++|--| )| =") || Token::Match(tok, "*| ( const| %type% *| ) %var% ="))) { bool dereference = false; bool pre = false; bool post = false; if (tok->str() == "*") { dereference = true; tok = tok->next(); } if (Token::Match(tok, "( const| %type% *| ) %var% =")) tok = tok->link()->next(); else if (tok->str() == "(") tok = tok->next(); if (Token::Match(tok, "++|--")) { pre = true; tok = tok->next(); } if (Token::Match(tok->next(), "++|--")) post = true; const unsigned int varid1 = tok->varId(); const Token *start = tok; tok = tok->tokAt(doAssignment(variables, tok, dereference, scope)); if (pre || post) variables.use(varid1); if (dereference) { Variables::VariableUsage *var = variables.find(varid1); if (var && var->_type == Variables::array) variables.write(varid1); 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); } // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable else if (var && var->_type == Variables::pointer && Token::Match(start, "%var% = new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { bool allocate = true; if (start->strAt(2) == "new") { const Token *type = start->tokAt(3); // skip nothrow if (Token::simpleMatch(type, "( nothrow )") || Token::simpleMatch(type, "( std :: nothrow )")) type = type->link()->next(); // is it a user defined type? if (!type->isStandardType()) { const Variable* variable = _tokenizer->getSymbolDatabase()->getVariableFromVarId(start->varId()); if (!variable || !isRecordTypeWithoutSideEffects(*variable)) allocate = false; } } if (allocate) variables.allocateMemory(varid1); else variables.write(varid1); } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { variables.use(varid1); } else { variables.write(varid1); } Variables::VariableUsage *var2 = variables.find(tok->varId()); if (var2) { if (var2->_type == Variables::reference) { variables.writeAliases(tok->varId()); variables.read(tok->varId()); } else if (tok->varId() != varid1 && Token::Match(tok, "%var% .")) variables.read(tok->varId()); else if (tok->varId() != varid1 && var2->_type == Variables::standard && tok->strAt(-1) != "&") variables.use(tok->varId()); } } const Token *equal = skipBrackets(tok->next()); // checked for chained assignments if (tok != start && equal && equal->str() == "=") { Variables::VariableUsage *var = variables.find(tok->varId()); if (var && var->_type != Variables::reference) var->_read = true; tok = tok->previous(); } } // assignment else if (Token::Match(tok, "%var% [") && Token::simpleMatch(skipBrackets(tok->next()), "=")) { unsigned int varid = tok->varId(); 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 && Token::Match(skipBrackets(tok->next()), "= new|malloc|calloc|g_malloc|kmalloc|vmalloc")) { variables.allocateMemory(varid); } else if (var->_type == Variables::pointer || var->_type == Variables::reference) { variables.read(varid); variables.writeAliases(varid); } else variables.writeAll(varid); } } else if (Token::Match(tok, ">>|& %var%")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) variables.read(tok->varId()); // function parameter else if (Token::Match(tok, "[(,] %var% [")) variables.use(tok->next()->varId()); // use = read + write else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") { variables.use(tok->next()->varId()); // use = read + write } else if (Token::Match(tok, "[(,] (") && Token::Match(tok->next()->link(), ") %var% [,)]")) variables.use(tok->next()->link()->next()->varId()); // use = read + write // function else if (Token::Match(tok, "%var% (")) { variables.read(tok->varId()); if (Token::Match(tok->tokAt(2), "%var% =")) variables.read(tok->tokAt(2)->varId()); } else if (Token::Match(tok, "[{,] %var% [,}]")) variables.read(tok->next()->varId()); else if (Token::Match(tok, "%var% .")) variables.use(tok->varId()); // use = read + write else if ((Token::Match(tok, "[(=&!]") || tok->isExtendedOp()) && (Token::Match(tok->next(), "%var%") && !Token::Match(tok->next(), "true|false|new")) && tok->strAt(2) != "=") variables.readAll(tok->next()->varId()); else if (Token::Match(tok, "%var%") && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) variables.readAll(tok->varId()); else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) variables.readAll(tok->varId()); else if (Token::Match(tok, "++|-- %var%")) { if (!Token::Match(tok->previous(), "[;{}:]")) variables.use(tok->next()->varId()); else variables.modified(tok->next()->varId()); } else if (Token::Match(tok, "%var% ++|--")) { if (!Token::Match(tok->previous(), "[;{}:]")) variables.use(tok->varId()); else variables.modified(tok->varId()); } else if (tok->isAssignmentOp()) { for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId()) { variables.read(tok2->varId()); if (tok2->next()->isAssignmentOp()) variables.write(tok2->varId()); } } } } }