static bool astHasAutoResult(const Token *tok) { if (tok->astOperand1() && !astHasAutoResult(tok->astOperand1())) return false; if (tok->astOperand2() && !astHasAutoResult(tok->astOperand2())) return false; if (tok->isOp()) return true; if (tok->isLiteral()) return true; if (tok->isName()) { // TODO: check function calls, struct members, arrays, etc also if (!tok->variable()) return false; if (tok->variable()->isStlType() && !Token::Match(tok->astParent(), "<<|>>")) return true; if (tok->variable()->isClass() || tok->variable()->isPointer() || tok->variable()->isReference()) // TODO: Properly handle pointers/references to classes in symbol database return false; return true; } return false; }
static bool astHasAutoResult(const Token *tok) { if (tok->astOperand1() && !astHasAutoResult(tok->astOperand1())) return false; if (tok->astOperand2() && !astHasAutoResult(tok->astOperand2())) return false; if (tok->isOp()) { if ((tok->str() == "<<" || tok->str() == ">>") && tok->astOperand1()) { const Token* tok2 = tok->astOperand1(); while (tok2 && tok2->str() == "*" && !tok2->astOperand2()) tok2 = tok2->astOperand1(); return tok2 && tok2->variable() && !tok2->variable()->isClass() && !tok2->variable()->isStlType(); // Class or unknown type on LHS: Assume it is a stream } return true; } if (tok->isLiteral()) return true; if (tok->isName()) { // TODO: check function calls, struct members, arrays, etc also if (!tok->variable()) return false; if (tok->variable()->isStlType()) return true; if (tok->variable()->isClass() || tok->variable()->isPointer() || tok->variable()->isReference()) // TODO: Properly handle pointers/references to classes in symbol database return false; return true; } return false; }
void CheckAutoVariables::returnReference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (!scope->function) continue; const Token *tok = scope->function->tokenDef; // have we reached a function that returns a reference? if (tok->previous() && tok->previous()->str() == "&") { for (const Token *tok2 = scope->classStart->next(); tok2 && tok2 != scope->classEnd; tok2 = tok2->next()) { if (tok2->str() != "return") continue; // return.. if (Token::Match(tok2, "return %var% ;")) { // is the returned variable a local variable? if (isAutoVar(tok2->next())) { const Variable *var1 = tok2->next()->variable(); // If reference variable is used, check what it references if (Token::Match(var1->nameToken(), "%var% [=(]")) { const Token *tok3 = var1->nameToken()->tokAt(2); if (!Token::Match(tok3, "%var% [);.]")) continue; // Only report error if variable that is referenced is // a auto variable if (!isAutoVar(tok3)) continue; } // report error.. errorReturnReference(tok2); } } // return reference to temporary.. else if (Token::Match(tok2, "return %var% (") && Token::simpleMatch(tok2->linkAt(2), ") ;")) { if (returnTemporary(tok2->next())) { // report error.. errorReturnTempReference(tok2); } } // Return reference to a literal or the result of a calculation else if (tok2->astOperand1() && (tok2->astOperand1()->isCalculation() || tok2->next()->isLiteral()) && astHasAutoResult(tok2->astOperand1())) { errorReturnTempReference(tok2); } } } } }