From 59a8944e309670a6532e470f56df7450d9841584 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Sat, 1 Jul 2023 02:43:57 -0500 Subject: [PATCH] Fix 11656: FP: containerOutOfBounds std::array (#5210) --- lib/programmemory.cpp | 640 ++++++++++++++++++++++-------------------- test/teststl.cpp | 20 ++ 2 files changed, 361 insertions(+), 299 deletions(-) diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp index 70e0d1ec4..a6db19e64 100644 --- a/lib/programmemory.cpp +++ b/lib/programmemory.cpp @@ -1185,324 +1185,366 @@ static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name) return it->second; } -static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const Settings* settings) -{ - ValueFlow::Value unknown = ValueFlow::Value::unknown(); - const ValueFlow::Value* value = nullptr; - if (!expr) - return unknown; - if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") - return expr->values().front(); - if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || - (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { - return *value; - } - if (expr->isNumber()) { - if (MathLib::isFloat(expr->str())) - return unknown; - MathLib::bigint i = MathLib::toLongNumber(expr->str()); - if (i < 0 && astIsUnsigned(expr)) - return unknown; - return ValueFlow::Value{i}; - } - if (expr->isBoolean()) - return ValueFlow::Value{ expr->str() == "true" }; - if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { - const Token* containerTok = expr->tokAt(-2)->astOperand1(); - const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); - if (yield == Library::Container::Yield::SIZE) { - ValueFlow::Value v = execute(containerTok, pm); - if (!v.isContainerSizeValue()) - return unknown; - v.valueType = ValueFlow::Value::ValueType::INT; - return v; - } - if (yield == Library::Container::Yield::EMPTY) { - ValueFlow::Value v = execute(containerTok, pm); - if (!v.isContainerSizeValue()) - return unknown; - if (v.isImpossible() && v.intvalue == 0) - return ValueFlow::Value{0}; - if (!v.isImpossible()) - return ValueFlow::Value{v.intvalue == 0}; - } - } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && expr->astOperand1()->exprId() > 0) { - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - if (rhs.isUninitValue()) - return unknown; - if (expr->str() != "=") { - if (!pm.hasValue(expr->astOperand1()->exprId())) - return unknown; - ValueFlow::Value& lhs = pm.at(expr->astOperand1()->exprId()); - rhs = evaluate(removeAssign(expr->str()), lhs, rhs); - if (lhs.isIntValue()) - ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); - else if (lhs.isFloatValue()) - ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); - else - return unknown; - return lhs; - } - pm.setValue(expr->astOperand1(), rhs); - return rhs; - } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue()) - return unknown; - if (isFalse(lhs)) - return lhs; - if (isTrue(lhs)) - return execute(expr->astOperand2(), pm); - return unknown; - } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue() || lhs.isImpossible()) - return unknown; - if (isTrue(lhs)) - return lhs; - if (isFalse(lhs)) - return execute(expr->astOperand2(), pm); - return unknown; - } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { - execute(expr->astOperand1(), pm); - return execute(expr->astOperand2(), pm); - } else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { - if (!pm.hasValue(expr->astOperand1()->exprId())) - return unknown; - ValueFlow::Value& lhs = pm.at(expr->astOperand1()->exprId()); - if (!lhs.isIntValue()) - return unknown; - // overflow - if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) - return unknown; +struct Executor { + ProgramMemory* pm = nullptr; + const Settings* settings = nullptr; + int fdepth = 4; - if (expr->str() == "++") - lhs.intvalue++; - else - lhs.intvalue--; - return lhs; - } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { - const Token *tokvalue = nullptr; - if (!pm.getTokValue(expr->astOperand1()->exprId(), &tokvalue)) { - auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(), - expr->astOperand1()->values().cend(), - std::mem_fn(&ValueFlow::Value::isTokValue)); - if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { + explicit Executor(ProgramMemory* pm = nullptr, const Settings* settings = nullptr) : pm(pm), settings(settings) {} + + ValueFlow::Value executeImpl(const Token* expr) + { + ValueFlow::Value unknown = ValueFlow::Value::unknown(); + const ValueFlow::Value* value = nullptr; + if (!expr) + return unknown; + if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") + return expr->values().front(); + if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { + return *value; + } + if (expr->isNumber()) { + if (MathLib::isFloat(expr->str())) + return unknown; + MathLib::bigint i = MathLib::toLongNumber(expr->str()); + if (i < 0 && astIsUnsigned(expr)) + return unknown; + return ValueFlow::Value{i}; + } + if (expr->isBoolean()) + return ValueFlow::Value{expr->str() == "true"}; + if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { + const Token* containerTok = expr->tokAt(-2)->astOperand1(); + const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); + if (yield == Library::Container::Yield::SIZE) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown; + v.valueType = ValueFlow::Value::ValueType::INT; + return v; + } + if (yield == Library::Container::Yield::EMPTY) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown; + if (v.isImpossible() && v.intvalue == 0) + return ValueFlow::Value{0}; + if (!v.isImpossible()) + return ValueFlow::Value{v.intvalue == 0}; + } + } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && + expr->astOperand1()->exprId() > 0) { + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (rhs.isUninitValue()) + return unknown; + if (expr->str() != "=") { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return unknown; + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + rhs = evaluate(removeAssign(expr->str()), lhs, rhs); + if (lhs.isIntValue()) + ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); + else if (lhs.isFloatValue()) + ValueFlow::Value::visitValue(rhs, + std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); + else + return unknown; + return lhs; + } + pm->setValue(expr->astOperand1(), rhs); + return rhs; + } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue()) + return unknown; + if (isFalse(lhs)) + return lhs; + if (isTrue(lhs)) + return execute(expr->astOperand2()); + return unknown; + } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue() || lhs.isImpossible()) + return unknown; + if (isTrue(lhs)) + return lhs; + if (isFalse(lhs)) + return execute(expr->astOperand2()); + return unknown; + } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { + execute(expr->astOperand1()); + return execute(expr->astOperand2()); + } else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return unknown; + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + if (!lhs.isIntValue()) + return unknown; + // overflow + if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) + return unknown; + + if (expr->str() == "++") + lhs.intvalue++; + else + lhs.intvalue--; + return lhs; + } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { + const Token* tokvalue = nullptr; + if (!pm->getTokValue(expr->astOperand1()->exprId(), &tokvalue)) { + auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(), + expr->astOperand1()->values().cend(), + std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { + return unknown; + } + tokvalue = tokvalue_it->tokvalue; + } + if (!tokvalue || !tokvalue->isLiteral()) { return unknown; } - tokvalue = tokvalue_it->tokvalue; - } - if (!tokvalue || !tokvalue->isLiteral()) { - return unknown; - } - const std::string strValue = tokvalue->strValue(); - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - if (!rhs.isIntValue()) - return unknown; - const MathLib::bigint index = rhs.intvalue; - if (index >= 0 && index < strValue.size()) - return ValueFlow::Value{strValue[index]}; - if (index == strValue.size()) - return ValueFlow::Value{}; - } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - ValueFlow::Value rhs = execute(expr->astOperand2(), pm); - ValueFlow::Value r = unknown; - if (!lhs.isUninitValue() && !rhs.isUninitValue()) - r = evaluate(expr->str(), lhs, rhs); - if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { - if (rhs.isIntValue()) { - std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); - if (!result.empty() && result.front().isKnown()) - return result.front(); + const std::string strValue = tokvalue->strValue(); + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (!rhs.isIntValue()) + return unknown; + const MathLib::bigint index = rhs.intvalue; + if (index >= 0 && index < strValue.size()) + return ValueFlow::Value{strValue[index]}; + if (index == strValue.size()) + return ValueFlow::Value{}; + } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + ValueFlow::Value rhs = execute(expr->astOperand2()); + ValueFlow::Value r = unknown; + if (!lhs.isUninitValue() && !rhs.isUninitValue()) + r = evaluate(expr->str(), lhs, rhs); + if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { + if (rhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {rhs}); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + if (lhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values()); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + return unknown; } - if (lhs.isIntValue()) { - std::vector result = - infer(ValueFlow::makeIntegralInferModel(), expr->str(), {lhs}, expr->astOperand2()->values()); - if (!result.empty() && result.front().isKnown()) - return result.front(); - } - return unknown; + return r; } - return r; + // Unary ops + else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue()) + return unknown; + if (expr->str() == "!") { + if (isTrue(lhs)) { + lhs.intvalue = 0; + } else if (isFalse(lhs)) { + lhs.intvalue = 1; + } else { + return unknown; + } + lhs.setPossible(); + lhs.bound = ValueFlow::Value::Bound::Point; + } + if (expr->str() == "-") + lhs.intvalue = -lhs.intvalue; + return lhs; + } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value cond = execute(expr->astOperand1()); + if (!cond.isIntValue()) + return unknown; + const Token* child = expr->astOperand2(); + if (isFalse(cond)) + return execute(child->astOperand2()); + if (isTrue(cond)) + return execute(child->astOperand1()); + + return unknown; + } else if (expr->str() == "(" && expr->isCast()) { + if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) + return execute(expr->astOperand2()); + return execute(expr->astOperand1()); + } + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) { + ValueFlow::Value result = pm->at(expr->exprId()); + if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { + result.intvalue = !result.intvalue; + result.setKnown(); + } + return result; + } + + if (Token::Match(expr->previous(), ">|%name% {|(")) { + const Token* ftok = expr->previous(); + const Function* f = ftok->function(); + ValueFlow::Value result = unknown; + if (settings && expr->str() == "(") { + std::vector tokArgs = getArguments(expr); + std::vector args(tokArgs.size()); + std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { + return execute(tok); + }); + if (f) { + if (fdepth >= 0) { + ProgramMemory functionState; + for (std::size_t i = 0; i < args.size(); ++i) { + const Variable* const arg = f->getArgumentVar(i); + if (!arg) + return unknown; + functionState.setValue(arg->nameToken(), args[i]); + } + Executor ex = *this; + ex.pm = &functionState; + ex.fdepth--; + auto r = ex.execute(f->functionScope); + if (!r.empty()) + result = r.front(); + // TODO: Track values changed by reference + } + } else { + BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); + if (lf) + return lf(args); + const std::string& returnValue = settings->library.returnValue(ftok); + if (!returnValue.empty()) { + std::unordered_map arg_map; + int argn = 0; + for (const ValueFlow::Value& v : args) { + if (!v.isUninitValue()) + arg_map[argn] = v; + argn++; + } + return evaluateLibraryFunction(arg_map, returnValue, settings); + } + } + } + // Check if function modifies argument + visitAstNodes(expr->astOperand2(), [&](const Token* child) { + if (child->exprId() > 0 && pm->hasValue(child->exprId())) { + ValueFlow::Value& v = pm->at(child->exprId()); + if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { + if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings)) + v = unknown; + } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { + if (isVariableChanged(child, v.indirect, settings, true)) + v = unknown; + } + } + return ChildrenToVisit::op1_and_op2; + }); + return result; + } + + return unknown; } - // Unary ops - else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { - ValueFlow::Value lhs = execute(expr->astOperand1(), pm); - if (!lhs.isIntValue()) + static const ValueFlow::Value* getImpossibleValue(const Token* tok) + { + if (!tok) + return nullptr; + std::vector values; + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isImpossible()) + continue; + if (v.isContainerSizeValue() || v.isIntValue()) { + values.push_back(std::addressof(v)); + } + } + auto it = + std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) { + return x->intvalue < y->intvalue; + }); + if (it == values.end()) + return nullptr; + return *it; + } + + ValueFlow::Value execute(const Token* expr) + { + ValueFlow::Value v = executeImpl(expr); + if (!v.isUninitValue()) + return v; + if (!expr) + return v; + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) + return pm->at(expr->exprId()); + if (const ValueFlow::Value* value = getImpossibleValue(expr)) + return *value; + return v; + } + + std::vector execute(const Scope* scope) + { + static const std::vector unknown = {ValueFlow::Value::unknown()}; + if (!scope) return unknown; - if (expr->str() == "!") { - if (isTrue(lhs)) { - lhs.intvalue = 0; - } else if (isFalse(lhs)) { - lhs.intvalue = 1; + if (!scope->bodyStart) + return unknown; + for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { + const Token* top = tok->astTop(); + if (!top) + return unknown; + + if (Token::simpleMatch(top, "return") && top->astOperand1()) + return {execute(top->astOperand1())}; + + if (Token::Match(top, "%op%")) { + if (execute(top).isUninitValue()) + return unknown; + const Token* next = nextAfterAstRightmostLeaf(top); + if (!next) + return unknown; + tok = next; + } else if (Token::simpleMatch(top->previous(), "if (")) { + const Token* condTok = top->astOperand2(); + ValueFlow::Value v = execute(condTok); + if (!v.isIntValue()) + return unknown; + const Token* thenStart = top->link()->next(); + const Token* next = thenStart->link(); + const Token* elseStart = nullptr; + if (Token::simpleMatch(thenStart->link(), "} else {")) { + elseStart = thenStart->link()->tokAt(2); + next = elseStart->link(); + } + std::vector result; + if (isTrue(v)) { + result = execute(thenStart->scope()); + } else if (isFalse(v)) { + if (elseStart) + result = execute(elseStart->scope()); + } else { + return unknown; + } + if (!result.empty()) + return result; + tok = next; } else { return unknown; } - lhs.setPossible(); - lhs.bound = ValueFlow::Value::Bound::Point; } - if (expr->str() == "-") - lhs.intvalue = -lhs.intvalue; - return lhs; - } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { - ValueFlow::Value cond = execute(expr->astOperand1(), pm); - if (!cond.isIntValue()) - return unknown; - const Token* child = expr->astOperand2(); - if (isFalse(cond)) - return execute(child->astOperand2(), pm); - if (isTrue(cond)) - return execute(child->astOperand1(), pm); - - return unknown; - } else if (expr->str() == "(" && expr->isCast()) { - if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) - return execute(expr->astOperand2(), pm); - return execute(expr->astOperand1(), pm); + return {}; } - if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) { - ValueFlow::Value result = pm.at(expr->exprId()); - if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { - result.intvalue = !result.intvalue; - result.setKnown(); - } - return result; - } - - if (Token::Match(expr->previous(), ">|%name% {|(")) { - const Token* ftok = expr->previous(); - const Function* f = ftok->function(); - // TODO: Evaluate inline functions as well - if (!f && settings && expr->str() == "(") { - std::vector tokArgs = getArguments(expr); - std::vector args(tokArgs.size()); - std::transform(tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { - return execute(tok, pm, settings); - }); - BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); - if (lf) - return lf(args); - const std::string& returnValue = settings->library.returnValue(ftok); - if (!returnValue.empty()) { - std::unordered_map arg_map; - int argn = 0; - for (const ValueFlow::Value& result : args) { - if (!result.isUninitValue()) - arg_map[argn] = result; - argn++; - } - return evaluateLibraryFunction(arg_map, returnValue, settings); - } - } - // Check if function modifies argument - visitAstNodes(expr->astOperand2(), [&](const Token* child) { - if (child->exprId() > 0 && pm.hasValue(child->exprId())) { - ValueFlow::Value& v = pm.at(child->exprId()); - if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { - if (ValueFlow::isContainerSizeChanged(child, v.indirect, settings)) - v = unknown; - } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { - if (isVariableChanged(child, v.indirect, settings, true)) - v = unknown; - } - } - return ChildrenToVisit::op1_and_op2; - }); - } - - return unknown; -} - -static const ValueFlow::Value* getImpossibleValue(const Token* tok) -{ - if (!tok) - return nullptr; - std::vector values; - for (const ValueFlow::Value& v : tok->values()) { - if (!v.isImpossible()) - continue; - if (v.isContainerSizeValue() || v.isIntValue()) { - values.push_back(std::addressof(v)); - } - } - auto it = std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) { - return x->intvalue < y->intvalue; - }); - if (it == values.end()) - return nullptr; - return *it; -} +}; static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings) { - ValueFlow::Value v = executeImpl(expr, pm, settings); - if (!v.isUninitValue()) - return v; - if (!expr) - return v; - if (expr->exprId() > 0 && pm.hasValue(expr->exprId())) - return pm.at(expr->exprId()); - if (const ValueFlow::Value* value = getImpossibleValue(expr)) - return *value; - return v; + Executor ex{&pm, settings}; + return ex.execute(expr); } std::vector execute(const Scope* scope, ProgramMemory& pm, const Settings* settings) { - static const std::vector unknown = {ValueFlow::Value::unknown()}; - if (!scope) - return unknown; - if (!scope->bodyStart) - return unknown; - for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { - const Token* top = tok->astTop(); - if (!top) - return unknown; - - if (Token::simpleMatch(top, "return") && top->astOperand1()) - return {execute(top->astOperand1(), pm, settings)}; - - if (Token::Match(top, "%op%")) { - if (execute(top, pm, settings).isUninitValue()) - return unknown; - const Token* next = nextAfterAstRightmostLeaf(top); - if (!next) - return unknown; - tok = next; - } else if (Token::simpleMatch(top->previous(), "if (")) { - const Token* condTok = top->astOperand2(); - ValueFlow::Value v = execute(condTok, pm, settings); - if (!v.isIntValue()) - return unknown; - const Token* thenStart = top->link()->next(); - const Token* next = thenStart->link(); - const Token* elseStart = nullptr; - if (Token::simpleMatch(thenStart->link(), "} else {")) { - elseStart = thenStart->link()->tokAt(2); - next = elseStart->link(); - } - std::vector result; - if (isTrue(v)) { - result = execute(thenStart->scope(), pm, settings); - } else if (isFalse(v)) { - if (elseStart) - result = execute(elseStart->scope(), pm, settings); - } else { - return unknown; - } - if (!result.empty()) - return result; - tok = next; - } else { - return unknown; - } - } - return {}; + Executor ex{&pm, settings}; + return ex.execute(scope); } ValueFlow::Value evaluateLibraryFunction(const std::unordered_map& args, diff --git a/test/teststl.cpp b/test/teststl.cpp index c43191248..77dc8db6b 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -888,6 +888,26 @@ private: " auto x = v->back();\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + checkNormal("template \n" + "struct Foo {\n" + " std::array items = {0};\n" + " T maxCount = count;\n" + " explicit Foo(const T& maxValue = (std::numeric_limits::max)()) : maxCount(maxValue) {}\n" + " bool Set(const uint8_t idx) {\n" + " if (CheckBounds(idx) && items[idx] < maxCount) {\n" + " items[idx] += 1;\n" + " return true;\n" + " }\n" + " return false;\n" + " }\n" + " static bool CheckBounds(const uint8_t idx) { return idx < count; }\n" + "};\n" + "void f() {\n" + " Foo x;\n" + " if (x.Set(42U)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void outOfBoundsSymbolic()