void CheckAssert::assertWithSideEffects() { if (!_settings->isEnabled("warning")) return; for (const Token* tok = _tokenizer->list.front(); tok; tok = tok->next()) { if (!Token::simpleMatch(tok, "assert (")) continue; const Token *endTok = tok->next()->link(); for (const Token* tmp = tok->next(); tmp != endTok; tmp = tmp->next()) { checkVariableAssignment(tmp); if (tmp->type() == Token::eFunction) { const Function* f = tmp->function(); if (f->nestedIn->isClassOrStruct() && !f->isStatic() && !f->isConst()) sideEffectInAssertError(tmp, f->name()); // Non-const member function called else { const Scope* scope = f->functionScope; if (!scope) continue; for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) { if (tok2->type() != Token::eAssignmentOp && tok2->type() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); if (!var || var->isLocal() || (var->isArgument() && !var->isReference() && !var->isPointer())) continue; // See ticket #4937. Assigning function arguments not passed by reference is ok. if (var->isArgument() && var->isPointer() && tok2->strAt(-2) != "*") continue; // Pointers need to be dereferenced, otherwise there is no error bool noReturnInScope = true; for (const Token *rt = scope->classStart; rt != scope->classEnd; rt = rt->next()) { if (rt->str() != "return") continue; // find all return statements if (inSameScope(rt, tok2)) { noReturnInScope = false; break; } } if (noReturnInScope) continue; sideEffectInAssertError(tmp, f->name()); break; } } } } tok = endTok; } }
void CheckAssert::assertWithSideEffects() { if (!_settings->isEnabled("warning")) return; const Token *tok = findAssertPattern(_tokenizer->tokens()); const Token *endTok = tok ? tok->next()->link() : NULL; while (tok && endTok) { for (const Token* tmp = tok->tokAt(1); tmp != endTok; tmp = tmp->next()) { checkVariableAssignment(tmp, true); if (tmp->isName() && tmp->type() == Token::eFunction) { const Function* f = tmp->function(); if (f->argCount() == 0 && f->isConst) continue; // functions with non-const references else if (f->argCount() != 0) { for (std::list<Variable>::const_iterator it = f->argumentList.begin(); it != f->argumentList.end(); ++it) { if (it->isConst() || it->isLocal()) continue; else if (it->isReference()) { const Token* next = it->nameToken()->next(); bool isAssigned = checkVariableAssignment(next, false); if (isAssigned) sideEffectInAssertError(tmp, f->name()); continue; } } } // variables in function scope const Scope* scope = f->functionScope; if (!scope) continue; for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) { if (tok2 && tok2->type() != Token::eAssignmentOp && tok2->type() != Token::eIncDecOp) continue; const Variable* var = tok2->previous()->variable(); if (!var) continue; std::vector<const Token*> returnTokens; // find all return statements for (const Token *rt = scope->classStart; rt != scope->classEnd; rt = rt->next()) { if (!Token::Match(rt, "return %any%")) continue; returnTokens.push_back(rt); } bool noReturnInScope = true; for (std::vector<const Token*>::iterator rt = returnTokens.begin(); rt != returnTokens.end(); ++rt) { noReturnInScope &= !inSameScope(*rt, tok2); } if (noReturnInScope) continue; bool isAssigned = checkVariableAssignment(tok2, false); if (isAssigned) sideEffectInAssertError(tmp, f->name()); } } } tok = findAssertPattern(endTok->next()); endTok = tok ? tok->next()->link() : NULL; } }