| 2220 | } |
| 2221 | |
| 2222 | bool SymbolDatabase::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, std::list<const Function*> checkedFuncs) const |
| 2223 | { |
| 2224 | // no body to analyze |
| 2225 | if (!func.hasBody()) { |
| 2226 | return false; |
| 2227 | } |
| 2228 | |
| 2229 | for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) { |
| 2230 | const Variable* argVar = argsToken->variable(); |
| 2231 | if (argVar && argVar->isGlobal()) { |
| 2232 | return false; // TODO: analyze global variable usage |
| 2233 | } |
| 2234 | } |
| 2235 | |
| 2236 | bool sideEffectReturnFound = false; |
| 2237 | std::set<const Variable*> pointersToGlobals; |
| 2238 | for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; |
| 2239 | bodyToken = bodyToken->next()) { |
| 2240 | // check variable inside function body |
| 2241 | const Variable* bodyVariable = bodyToken->variable(); |
| 2242 | if (bodyVariable) { |
| 2243 | if (!isVariableWithoutSideEffects(*bodyVariable)) { |
| 2244 | return false; |
| 2245 | } |
| 2246 | // check if global variable is changed |
| 2247 | if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) { |
| 2248 | const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer(); |
| 2249 | if (isVariableChanged(bodyToken, indirect, mSettings)) { |
| 2250 | return false; |
| 2251 | } |
| 2252 | // check if pointer to global variable assigned to another variable (another_var = &global_var) |
| 2253 | if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) { |
| 2254 | const Token* assigned_var_token = bodyToken->tokAt(-3); |
| 2255 | if (assigned_var_token && assigned_var_token->variable()) { |
| 2256 | pointersToGlobals.insert(assigned_var_token->variable()); |
| 2257 | } |
| 2258 | } |
| 2259 | } |
| 2260 | } |
| 2261 | |
| 2262 | // check nested function |
| 2263 | const Function* bodyFunction = bodyToken->function(); |
| 2264 | if (bodyFunction) { |
| 2265 | if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found |
| 2266 | continue; |
| 2267 | } |
| 2268 | checkedFuncs.push_back(bodyFunction); |
| 2269 | if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) { |
| 2270 | return false; |
| 2271 | } |
| 2272 | } |
| 2273 | |
| 2274 | // check returned value |
| 2275 | if (Token::simpleMatch(bodyToken, "return")) { |
| 2276 | const Token* returnValueToken = bodyToken->next(); |
| 2277 | // TODO: handle complex return expressions |
| 2278 | if (!Token::simpleMatch(returnValueToken->next(), ";")) { |
| 2279 | sideEffectReturnFound = true; |
nothing calls this directly
no test coverage detected