diff --git a/Makefile b/Makefile
index f482051b8fa..46352622bfe 100644
--- a/Makefile
+++ b/Makefile
@@ -243,6 +243,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/platform.o \
$(libcppdir)/preprocessor.o \
$(libcppdir)/programmemory.o \
+ $(libcppdir)/programmemoryfast.o \
$(libcppdir)/reverseanalyzer.o \
$(libcppdir)/settings.o \
$(libcppdir)/summaries.o \
@@ -252,6 +253,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \
$(libcppdir)/token.o \
$(libcppdir)/tokenlist.o \
$(libcppdir)/utils.o \
+ $(libcppdir)/valueflowfast.o \
$(libcppdir)/vfvalue.o
EXTOBJ = externals/simplecpp/simplecpp.o \
@@ -338,6 +340,7 @@ TESTOBJ = test/fixture.o \
test/testutils.o \
test/testvaarg.o \
test/testvalueflow.o \
+ test/testvalueflowfast.o \
test/testvarid.o
.PHONY: run-dmake tags
@@ -459,7 +462,7 @@ validateRules:
###### Build
-$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
+$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueflowfast.h lib/valueptr.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp
$(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h
@@ -618,6 +621,9 @@ $(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.
$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/addoninfo.h lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/programmemory.cpp
+$(libcppdir)/programmemoryfast.o: lib/programmemoryfast.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/programmemoryfast.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/programmemoryfast.cpp
+
$(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueptr.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/reverseanalyzer.cpp
@@ -645,6 +651,9 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/
$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp
+$(libcppdir)/valueflowfast.o: lib/valueflowfast.cpp lib/addoninfo.h lib/astutils.h lib/calculate.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemoryfast.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueflowfast.h lib/vfvalue.h
+ $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflowfast.cpp
+
$(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vfvalue.cpp
@@ -888,6 +897,9 @@ test/testvaarg.o: test/testvaarg.cpp lib/addoninfo.h lib/check.h lib/checkvaarg.
test/testvalueflow.o: test/testvalueflow.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvalueflow.cpp
+test/testvalueflowfast.o: test/testvalueflowfast.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h test/fixture.h test/helpers.h
+ $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvalueflowfast.cpp
+
test/testvarid.o: test/testvarid.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvarid.cpp
diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp
index 503d3f7ff32..a2b3e1d2d95 100644
--- a/cli/cmdlineparser.cpp
+++ b/cli/cmdlineparser.cpp
@@ -475,7 +475,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
else if (std::strncmp(argv[i], "--check-level=", 14) == 0) {
Settings::CheckLevel level = Settings::CheckLevel::normal;
const std::string level_s(argv[i] + 14);
- if (level_s == "normal")
+ if (level_s == "fast")
+ level = Settings::CheckLevel::fast;
+ else if (level_s == "normal")
level = Settings::CheckLevel::normal;
else if (level_s == "exhaustive")
level = Settings::CheckLevel::exhaustive;
diff --git a/lib/checkother.cpp b/lib/checkother.cpp
index 0f9f90415d4..abf732fb7cf 100644
--- a/lib/checkother.cpp
+++ b/lib/checkother.cpp
@@ -3585,7 +3585,7 @@ void CheckOther::checkShadowVariables()
continue;
if (scope.type == Scope::eFunction && scope.className == var.name())
continue;
- if (functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && functionScope->function->isStatic() &&
+ if (functionScope && functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && functionScope->function->isStatic() &&
shadowed->variable() && !shadowed->variable()->isLocal())
continue;
shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function");
diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj
index e53ce4d856b..94ee1f1769a 100644
--- a/lib/cppcheck.vcxproj
+++ b/lib/cppcheck.vcxproj
@@ -78,6 +78,7 @@
+
@@ -87,6 +88,7 @@
+
@@ -149,6 +151,7 @@
+
@@ -164,6 +167,7 @@
+
diff --git a/lib/lib.pri b/lib/lib.pri
index 5125fb684a0..c81464f0c30 100644
--- a/lib/lib.pri
+++ b/lib/lib.pri
@@ -60,6 +60,7 @@ HEADERS += $${PWD}/addoninfo.h \
$${PWD}/precompiled.h \
$${PWD}/preprocessor.h \
$${PWD}/programmemory.h \
+ $${PWD}/programmemoryfast.h \
$${PWD}/reverseanalyzer.h \
$${PWD}/settings.h \
$${PWD}/smallvector.h \
@@ -75,6 +76,7 @@ HEADERS += $${PWD}/addoninfo.h \
$${PWD}/tokenrange.h \
$${PWD}/utils.h \
$${PWD}/valueflow.h \
+ $${PWD}/valueflowfast.h \
$${PWD}/valueptr.h \
$${PWD}/version.h \
$${PWD}/vfvalue.h \
@@ -133,6 +135,7 @@ SOURCES += $${PWD}/valueflow.cpp \
$${PWD}/platform.cpp \
$${PWD}/preprocessor.cpp \
$${PWD}/programmemory.cpp \
+ $${PWD}/programmemoryfast.cpp \
$${PWD}/reverseanalyzer.cpp \
$${PWD}/settings.cpp \
$${PWD}/summaries.cpp \
@@ -142,4 +145,5 @@ SOURCES += $${PWD}/valueflow.cpp \
$${PWD}/token.cpp \
$${PWD}/tokenlist.cpp \
$${PWD}/utils.cpp \
+ $${PWD}/valueflowfast.cpp \
$${PWD}/vfvalue.cpp
diff --git a/lib/programmemoryfast.cpp b/lib/programmemoryfast.cpp
new file mode 100644
index 00000000000..a598049808b
--- /dev/null
+++ b/lib/programmemoryfast.cpp
@@ -0,0 +1,423 @@
+
+#include "programmemoryfast.h"
+#include "token.h"
+#include "astutils.h"
+#include "symboldatabase.h"
+#include
+
+void ProgramMemoryFast::setValue(nonneg int varid, const ValueFlow::Value &value)
+{
+ values[varid] = value;
+}
+
+bool ProgramMemoryFast::getIntValue(nonneg int varid, MathLib::bigint* result) const
+{
+ const std::map::const_iterator it = values.find(varid);
+ const bool found = it != values.end() && it->second.isIntValue();
+ if (found)
+ *result = it->second.intvalue;
+ return found;
+}
+
+void ProgramMemoryFast::setIntValue(nonneg int varid, MathLib::bigint value)
+{
+ values[varid] = ValueFlow::Value(value);
+}
+
+bool ProgramMemoryFast::getTokValue(nonneg int varid, const Token** result) const
+{
+ const std::map::const_iterator it = values.find(varid);
+ const bool found = it != values.end() && it->second.isTokValue();
+ if (found)
+ *result = it->second.tokvalue;
+ return found;
+}
+
+bool ProgramMemoryFast::hasValue(nonneg int varid)
+{
+ return values.find(varid) != values.end();
+}
+
+void ProgramMemoryFast::swap(ProgramMemoryFast &pm)
+{
+ values.swap(pm.values);
+}
+
+void ProgramMemoryFast::clear()
+{
+ values.clear();
+}
+
+bool ProgramMemoryFast::empty() const
+{
+ return values.empty();
+}
+
+void ProgramMemoryFast::replace(const ProgramMemoryFast &pm)
+{
+ for (auto&& p:pm.values)
+ values[p.first] = p.second;
+}
+
+void ProgramMemoryFast::insert(const ProgramMemoryFast &pm)
+{
+ for (auto&& p:pm.values)
+ values.insert(p);
+}
+
+
+bool conditionIsFalse(const Token *condition, const ProgramMemoryFast &programMemory)
+{
+ if (!condition)
+ return false;
+ if (condition->str() == "&&") {
+ return conditionIsFalse(condition->astOperand1(), programMemory) ||
+ conditionIsFalse(condition->astOperand2(), programMemory);
+ }
+ ProgramMemoryFast progmem(programMemory);
+ MathLib::bigint result = 0;
+ bool error = false;
+ execute(condition, &progmem, &result, &error);
+ return !error && result == 0;
+}
+
+bool conditionIsTrue(const Token *condition, const ProgramMemoryFast &programMemory)
+{
+ if (!condition)
+ return false;
+ if (condition->str() == "||") {
+ return conditionIsTrue(condition->astOperand1(), programMemory) ||
+ conditionIsTrue(condition->astOperand2(), programMemory);
+ }
+ ProgramMemoryFast progmem(programMemory);
+ bool error = false;
+ MathLib::bigint result = 0;
+ execute(condition, &progmem, &result, &error);
+ return !error && result == 1;
+}
+
+static void programMemoryParseCondition(ProgramMemoryFast& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then)
+{
+ if (Token::Match(tok, "==|>=|<=|<|>|!=")) {
+ if (then && !Token::Match(tok, "==|>=|<="))
+ return;
+ if (!then && !Token::Match(tok, "<|>|!="))
+ return;
+ ValueFlow::Value truevalue;
+ ValueFlow::Value falsevalue;
+ const Token* vartok = parseCompareInt(tok, truevalue, falsevalue);
+ if (!vartok)
+ return;
+ if (vartok->varId() == 0)
+ return;
+ if (!truevalue.isIntValue())
+ return;
+ if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true))
+ return;
+ pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue);
+ } else if (Token::Match(tok, "%var%")) {
+ if (tok->varId() == 0)
+ return;
+ if (then && !astIsPointer(tok) && !astIsBool(tok))
+ return;
+ if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true))
+ return;
+ pm.setIntValue(tok->varId(), then);
+ } else if (Token::simpleMatch(tok, "!")) {
+ programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then);
+ } else if (then && Token::simpleMatch(tok, "&&")) {
+ programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
+ programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
+ } else if (!then && Token::simpleMatch(tok, "||")) {
+ programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then);
+ programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then);
+ }
+}
+
+static void fillProgramMemoryFromConditions(ProgramMemoryFast& pm, const Scope* scope, const Token* endTok, const Settings* settings)
+{
+ if (!scope)
+ return;
+ if (!scope->isLocal())
+ return;
+ assert(scope != scope->nestedIn);
+ fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings);
+ if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) {
+ const Token * bodyStart = scope->bodyStart;
+ if (scope->type == Scope::eElse) {
+ if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {"))
+ return;
+ bodyStart = bodyStart->linkAt(-2);
+ }
+ const Token * condEndTok = bodyStart->previous();
+ if (!Token::simpleMatch(condEndTok, ") {"))
+ return;
+ const Token * condStartTok = condEndTok->link();
+ if (!condStartTok)
+ return;
+ if (!Token::Match(condStartTok->previous(), "if|while ("))
+ return;
+ const Token * condTok = condStartTok->astOperand2();
+ programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse);
+ }
+}
+
+static void fillProgramMemoryFromConditions(ProgramMemoryFast& pm, const Token* tok, const Settings* settings)
+{
+ fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings);
+}
+
+static void fillProgramMemoryFromAssignments(ProgramMemoryFast& pm, const Token* tok, const ProgramMemoryFast& state, std::unordered_map vars)
+{
+ int indentlevel = 0;
+ for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) {
+ bool setvar = false;
+ if (Token::Match(tok2, "[;{}] %var% = %var% ;")) {
+ for (auto&& p:vars) {
+ if (p.first != tok2->next()->varId())
+ continue;
+ const Token *vartok = tok2->tokAt(3);
+ pm.setValue(vartok->varId(), p.second);
+ setvar = true;
+ }
+ }
+ if (!setvar && (Token::Match(tok2, ";|{|}|%type% %var% =") ||
+ Token::Match(tok2, "[;{}] const| %type% %var% ("))) {
+ const Token *vartok = tok2->next();
+ while (vartok->next()->isName())
+ vartok = vartok->next();
+ if (!pm.hasValue(vartok->varId())) {
+ MathLib::bigint result = 0;
+ bool error = false;
+ execute(vartok->next()->astOperand2(), &pm, &result, &error);
+ if (!error)
+ pm.setIntValue(vartok->varId(), result);
+ }
+ }
+
+ if (tok2->str() == "{") {
+ if (indentlevel <= 0)
+ break;
+ --indentlevel;
+ }
+ if (tok2->str() == "}") {
+ const Token *cond = tok2->link();
+ cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr;
+ if (cond && conditionIsFalse(cond->astOperand2(), state))
+ tok2 = cond->previous();
+ else if (cond && conditionIsTrue(cond->astOperand2(), state)) {
+ ++indentlevel;
+ continue;
+ } else
+ break;
+ }
+ }
+}
+
+ProgramMemoryFast getProgramMemoryFast(const Token *tok, nonneg int varid, const ValueFlow::Value &value)
+{
+ ProgramMemoryFast programMemory;
+ if (value.tokvalue)
+ fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr);
+ if (value.condition)
+ fillProgramMemoryFromConditions(programMemory, value.condition, nullptr);
+ programMemory.setValue(varid, value);
+ if (value.varId)
+ programMemory.setIntValue(value.varId, value.varvalue);
+ const ProgramMemoryFast state = programMemory;
+ fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}});
+ return programMemory;
+}
+
+void execute(const Token *expr,
+ ProgramMemoryFast * const programMemory,
+ MathLib::bigint *result,
+ bool *error)
+{
+ if (!expr)
+ *error = true;
+
+ else if (expr->hasKnownIntValue()) {
+ *result = expr->values().front().intvalue;
+ }
+
+ else if (expr->isNumber()) {
+ *result = MathLib::toBigNumber(expr->str());
+ if (MathLib::isFloat(expr->str()))
+ *error = true;
+ }
+
+ else if (expr->varId() > 0) {
+ if (!programMemory->getIntValue(expr->varId(), result))
+ *error = true;
+ }
+
+ else if (expr->isComparisonOp()) {
+ MathLib::bigint result1(0), result2(0);
+ execute(expr->astOperand1(), programMemory, &result1, error);
+ execute(expr->astOperand2(), programMemory, &result2, error);
+ if (expr->str() == "<")
+ *result = result1 < result2;
+ else if (expr->str() == "<=")
+ *result = result1 <= result2;
+ else if (expr->str() == ">")
+ *result = result1 > result2;
+ else if (expr->str() == ">=")
+ *result = result1 >= result2;
+ else if (expr->str() == "==")
+ *result = result1 == result2;
+ else if (expr->str() == "!=")
+ *result = result1 != result2;
+ }
+
+ else if (expr->isAssignmentOp()) {
+ execute(expr->astOperand2(), programMemory, result, error);
+ if (!expr->astOperand1() || !expr->astOperand1()->varId())
+ *error = true;
+ if (*error)
+ return;
+
+ if (expr->str() == "=") {
+ programMemory->setIntValue(expr->astOperand1()->varId(), *result);
+ return;
+ }
+
+ long long intValue;
+ if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) {
+ *error = true;
+ return;
+ }
+ if (expr->str() == "+=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result);
+ else if (expr->str() == "-=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result);
+ else if (expr->str() == "*=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result);
+ else if (expr->str() == "/=" && *result != 0)
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result);
+ else if (expr->str() == "%=" && *result != 0)
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result);
+ else if (expr->str() == "&=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result);
+ else if (expr->str() == "|=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result);
+ else if (expr->str() == "^=")
+ programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result);
+ }
+
+ else if (Token::Match(expr, "++|--")) {
+ if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U)
+ *error = true;
+ else {
+ long long intValue;
+ if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue))
+ *error = true;
+ else {
+ if (intValue == 0 &&
+ expr->str() == "--" &&
+ expr->astOperand1()->variable() &&
+ expr->astOperand1()->variable()->typeStartToken()->isUnsigned())
+ *error = true; // overflow
+ *result = intValue + (expr->str() == "++" ? 1 : -1);
+ programMemory->setIntValue(expr->astOperand1()->varId(), *result);
+ }
+ }
+ }
+
+ else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) {
+ MathLib::bigint result1(0), result2(0);
+ execute(expr->astOperand1(), programMemory, &result1, error);
+ execute(expr->astOperand2(), programMemory, &result2, error);
+ if (expr->str() == "+")
+ *result = result1 + result2;
+ else if (expr->str() == "-")
+ *result = result1 - result2;
+ else if (expr->str() == "*") {
+ if (result2 && (result1 > std::numeric_limits::max()/result2))
+ *error = true;
+ else
+ *result = result1 * result2;
+ } else if (result2 == 0)
+ *error = true;
+ else if (expr->str() == "/")
+ *result = result1 / result2;
+ else if (expr->str() == "%")
+ *result = result1 % result2;
+ else if (expr->str() == "<<") {
+ if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB
+ *error= true;
+ } else {
+ *result = result1 << result2;
+ }
+ } else if (expr->str() == ">>") {
+ if (result2 < 0) { // don't perform UB
+ *error=true;
+ } else {
+ *result = result1 >> result2;
+ }
+ }
+ }
+
+ else if (expr->str() == "&&") {
+ bool error1 = false;
+ execute(expr->astOperand1(), programMemory, result, &error1);
+ if (!error1 && *result == 0)
+ *result = 0;
+ else {
+ bool error2 = false;
+ execute(expr->astOperand2(), programMemory, result, &error2);
+ if (error1 && error2)
+ *error = true;
+ if (error2)
+ *result = 1;
+ else
+ *result = !!*result;
+ }
+ }
+
+ else if (expr->str() == "||") {
+ execute(expr->astOperand1(), programMemory, result, error);
+ if (*result == 0 && *error == false)
+ execute(expr->astOperand2(), programMemory, result, error);
+ }
+
+ else if (expr->str() == "!") {
+ execute(expr->astOperand1(), programMemory, result, error);
+ *result = !(*result);
+ }
+
+ else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) {
+ execute(expr->astOperand1(), programMemory, result, error);
+ execute(expr->astOperand2(), programMemory, result, error);
+ }
+
+ else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) {
+ const Token *tokvalue = nullptr;
+ if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) {
+ auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(),
+ expr->astOperand1()->values().end(),
+ std::mem_fn(&ValueFlow::Value::isTokValue));
+ if (tokvalue_it == expr->astOperand1()->values().end()) {
+ *error = true;
+ return;
+ }
+ tokvalue = tokvalue_it->tokvalue;
+ }
+ if (!tokvalue || !tokvalue->isLiteral()) {
+ *error = true;
+ return;
+ }
+ const std::string strValue = tokvalue->strValue();
+ MathLib::bigint index = 0;
+ execute(expr->astOperand2(), programMemory, &index, error);
+ if (index >= 0 && index < strValue.size())
+ *result = strValue[index];
+ else if (index == strValue.size())
+ *result = 0;
+ else
+ *error = true;
+ }
+
+ else
+ *error = true;
+}
diff --git a/lib/programmemoryfast.h b/lib/programmemoryfast.h
new file mode 100644
index 00000000000..35de06ebb8d
--- /dev/null
+++ b/lib/programmemoryfast.h
@@ -0,0 +1,59 @@
+#ifndef GUARD_PROGRAMMEMORYFAST_H
+#define GUARD_PROGRAMMEMORYFAST_H
+
+#include "config.h"
+#include "utils.h"
+#include "valueflow.h"
+#include "mathlib.h"
+#include
diff --git a/test/testvalueflowfast.cpp b/test/testvalueflowfast.cpp
new file mode 100644
index 00000000000..3265a88ef08
--- /dev/null
+++ b/test/testvalueflowfast.cpp
@@ -0,0 +1,4431 @@
+/*
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2019 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "library.h"
+#include "platform.h"
+#include "settings.h"
+#include "token.h"
+#include "tokenize.h"
+#include "valueflow.h"
+
+#include "fixture.h"
+#include "helpers.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class TestValueFlowFast : public TestFixture {
+public:
+ TestValueFlowFast() : TestFixture("TestValueFlowFast") {}
+
+private:
+ /*const*/ Settings settings = settingsBuilder().library("std.cfg").checkLevelFast().build();
+
+ void run() override {
+ TEST_CASE(valueFlowNumber);
+ TEST_CASE(valueFlowString);
+ TEST_CASE(valueFlowPointerAlias);
+ // FIXME TEST_CASE(valueFlowLifetime);
+ TEST_CASE(valueFlowArrayElement);
+ TEST_CASE(valueFlowMove);
+
+ TEST_CASE(valueFlowBitAnd);
+ TEST_CASE(valueFlowRightShift);
+
+ TEST_CASE(valueFlowCalculations);
+ TEST_CASE(valueFlowSizeof);
+
+ // FIXME TEST_CASE(valueFlowErrorPath);
+
+ TEST_CASE(valueFlowBeforeCondition);
+ TEST_CASE(valueFlowBeforeConditionAndAndOrOrGuard);
+ // FIXME TEST_CASE(valueFlowBeforeConditionAssignIncDec);
+ TEST_CASE(valueFlowBeforeConditionFunctionCall);
+ TEST_CASE(valueFlowBeforeConditionGlobalVariables);
+ // FIXME TEST_CASE(valueFlowBeforeConditionGoto);
+ // FIXME TEST_CASE(valueFlowBeforeConditionIfElse);
+ TEST_CASE(valueFlowBeforeConditionLoop);
+ // FIXME TEST_CASE(valueFlowBeforeConditionMacro);
+ TEST_CASE(valueFlowBeforeConditionSizeof);
+ // FIXME TEST_CASE(valueFlowBeforeConditionSwitch);
+ // FIXME TEST_CASE(valueFlowBeforeConditionTernaryOp);
+ TEST_CASE(valueFlowBeforeConditionForward);
+
+ TEST_CASE(valueFlowAfterAssign);
+ TEST_CASE(valueFlowAfterCondition);
+ // FIXME TEST_CASE(valueFlowAfterConditionExpr);
+ TEST_CASE(valueFlowAfterConditionSeveralNot);
+ TEST_CASE(valueFlowForwardCompoundAssign);
+ TEST_CASE(valueFlowForwardCorrelatedVariables);
+ TEST_CASE(valueFlowForwardModifiedVariables);
+ TEST_CASE(valueFlowForwardFunction);
+ TEST_CASE(valueFlowForwardTernary);
+ TEST_CASE(valueFlowForwardLambda);
+
+ // FIXME TEST_CASE(valueFlowFwdAnalysis);
+
+ TEST_CASE(valueFlowSwitchVariable);
+
+ // FIXME TEST_CASE(valueFlowForLoop);
+ TEST_CASE(valueFlowSubFunction);
+ // FIXME TEST_CASE(valueFlowFunctionReturn);
+
+ TEST_CASE(valueFlowFunctionDefaultParameter);
+
+ // FIXME TEST_CASE(knownValue);
+
+ TEST_CASE(valueFlowSizeofForwardDeclaredEnum);
+
+ TEST_CASE(valueFlowGlobalVar);
+
+ TEST_CASE(valueFlowGlobalConstVar);
+
+ TEST_CASE(valueFlowGlobalStaticVar);
+
+ TEST_CASE(valueFlowInlineAssembly);
+
+ TEST_CASE(valueFlowSameExpression);
+
+ // FIXME TEST_CASE(valueFlowUninit);
+
+ // FIXME TEST_CASE(valueFlowTerminatingCond);
+
+ TEST_CASE(valueFlowContainerSize);
+
+ TEST_CASE(valueFlowDynamicBufferSize);
+
+ // FIXME TEST_CASE(valueFlowSafeFunctionParameterValues);
+ TEST_CASE(valueFlowUnknownFunctionReturn);
+
+ // FIXME TEST_CASE(valueFlowPointerAliasDeref);
+
+ TEST_CASE(valueFlowCrashIncompleteCode);
+
+ TEST_CASE(valueFlowCrash);
+ }
+
+ static bool isNotTokValue(const ValueFlow::Value &val) {
+ return !val.isTokValue();
+ }
+
+#define testValueOfXKnown(...) testValueOfXKnown_(__FILE__, __LINE__, __VA_ARGS__)
+ bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, int value) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
+ if (val.isSymbolicValue())
+ return false;
+ if (val.isKnown() && val.intvalue == value)
+ return true;
+ return false;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfXKnown_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& val) {
+ if (!val.isSymbolicValue())
+ return false;
+ if (val.isKnown() && val.intvalue == value && val.tokvalue->expressionString() == expr)
+ return true;
+ return false;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+#define getErrorPathForX(code, linenr) getErrorPathForX_(code, linenr, __FILE__, __LINE__)
+ std::string getErrorPathForX_(const char code[], unsigned int linenr, const char* file, int line) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() != "x" || tok->linenr() != linenr)
+ continue;
+
+ std::ostringstream ostr;
+ for (const ValueFlow::Value &v : tok->values()) {
+ for (const ValueFlow::Value::ErrorPathItem &ep : v.errorPath) {
+ const Token *eptok = ep.first;
+ const std::string &msg = ep.second;
+ ostr << eptok->linenr() << ',' << msg << '\n';
+ }
+ }
+ return ostr.str();
+ }
+
+ return "";
+ }
+
+#define testValueOfX(...) testValueOfX_(__FILE__, __LINE__, __VA_ARGS__)
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, const Settings *s = nullptr) {
+ const Settings *settings1 = s ? s : &settings;
+
+ // Tokenize..
+ Tokenizer tokenizer(*settings1, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.isIntValue() && !v.isImpossible() && v.intvalue == value;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, const std::string& expr, int value)
+ {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.isSymbolicValue() && !v.isImpossible() && v.intvalue == value && v.tokvalue->expressionString() == expr;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, double value, double diff) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.isFloatValue() && !v.isImpossible() && v.floatValue >= value - diff && v.floatValue <= value + diff;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, const char value[], ValueFlow::Value::ValueType type) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ const std::size_t len = strlen(value);
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.valueType == type && Token::simpleMatch(v.tokvalue, value, len);
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, int value, ValueFlow::Value::ValueType type) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.valueType == type && v.intvalue == value;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testValueOfX_(const char* file, int line, const char code[], unsigned int linenr, ValueFlow::Value::MoveKind moveKind) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ if (std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) {
+ return v.isMovedValue() && v.moveKind == moveKind;
+ }))
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool testConditionalValueOfX(const char code[], unsigned int linenr, int value) {
+ // Tokenize..
+ Tokenizer tokenizer(settings, this);
+ std::istringstream istr(code);
+ tokenizer.tokenize(istr, "test.cpp");
+
+ for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) {
+ if (tok->str() == "x" && tok->linenr() == linenr) {
+ for (const ValueFlow::Value &v : tok->values()) {
+ if (v.isIntValue() && v.intvalue == value && v.condition)
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+#define bailout(CODE) bailout_(__FILE__, __LINE__, CODE)
+ void bailout_(const char* file, int line, const char code[]) {
+ const Settings s = settingsBuilder().debugwarnings().exhaustive().build();
+ errout.str("");
+
+ std::vector files(1, "test.cpp");
+ Tokenizer tokenizer(s, this);
+ PreprocessorHelper::preprocess(code, files, tokenizer);
+
+ // Tokenize..
+ ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line);
+ }
+
+ std::list tokenValues(const char code[], const char tokstr[], const Settings *s = nullptr) {
+ Tokenizer tokenizer(s ? *s : settings, this);
+ std::istringstream istr(code);
+ errout.str("");
+ tokenizer.tokenize(istr, "test.cpp");
+ const Token *tok = Token::findmatch(tokenizer.tokens(), tokstr);
+ return tok ? tok->values() : std::list();
+ }
+
+ ValueFlow::Value valueOfTok(const char code[], const char tokstr[]) {
+ std::list values = tokenValues(code, tokstr);
+ return values.size() == 1U && !values.front().isTokValue() ? values.front() : ValueFlow::Value();
+ }
+
+ void valueFlowNumber() {
+ ASSERT_EQUALS(123, valueOfTok("x=123;", "123").intvalue);
+ ASSERT_EQUALS_DOUBLE(192.0, valueOfTok("x=0x0.3p10;", "0x0.3p10").floatValue, 1e-5); // 3 * 16^-1 * 2^10 = 192
+ ASSERT(std::fabs(valueOfTok("x=0.5;", "0.5").floatValue - 0.5f) < 0.1f);
+ ASSERT_EQUALS(10, valueOfTok("enum {A=10,B=15}; x=A+0;", "+").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("x=false;", "false").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("x=true;", "true").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("x(NULL);", "NULL").intvalue);
+ ASSERT_EQUALS((int)('a'), valueOfTok("x='a';", "'a'").intvalue);
+ ASSERT_EQUALS((int)('\n'), valueOfTok("x='\\n';", "'\\n'").intvalue);
+ // TODO ASSERT_EQUALS(0xFFFFFFFF00000000, valueOfTok("x=0xFFFFFFFF00000000;","18446744069414584320U").intvalue); // #7701
+
+ // scope
+ {
+ const char code[] = "namespace N { enum E {e0,e1}; }\n"
+ "void foo() { x = N::e1; }";
+ ASSERT_EQUALS(1, valueOfTok(code, "::").intvalue);
+ }
+ }
+
+ void valueFlowString() {
+ const char *code;
+
+ // valueFlowAfterAssign
+ code = "const char * f() {\n"
+ " static const char *x;\n"
+ " if (a) x = \"123\";\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4, "\"123\"", ValueFlow::Value::ValueType::TOK));
+
+ // valueFlowSubFunction
+ code = "void dostuff(const char *x) {\n"
+ " f(x);\n"
+ "}\n"
+ "\n"
+ "void test() { dostuff(\"abc\"); }";
+ ASSERT_EQUALS(true, testValueOfX(code, 2, "\"abc\"", ValueFlow::Value::ValueType::TOK));
+ }
+
+ void valueFlowPointerAlias() {
+ const char *code;
+ std::list values;
+
+ code = "const char * f() {\n"
+ " static const char *x;\n"
+ " static char ret[10];\n"
+ " if (a) x = &ret[0];\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]", ValueFlow::Value::ValueType::TOK));
+
+ // dead pointer
+ code = "void f() {\n"
+ " int *x;\n"
+ " if (cond) { int i; x = &i; }\n"
+ " *x = 0;\n" // <- x can point at i
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4, "& i", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n"
+ " struct X *x;\n"
+ " x = &x[1];\n"
+ "}";
+ values = tokenValues(code, "&");
+ values.remove_if(&isNotTokValue);
+ ASSERT_EQUALS(true, values.empty());
+
+ values = tokenValues(code, "x [");
+ values.remove_if(&isNotTokValue);
+ ASSERT_EQUALS(true, values.empty());
+ }
+
+ void valueFlowLifetime() {
+ const char *code;
+
+ code = "void f() {\n"
+ " int a = 1;\n"
+ " auto x = [&]() { return a + 1; };\n"
+ " auto b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4, "a + 1", ValueFlow::Value::ValueType::LIFETIME));
+
+ code = "void f() {\n"
+ " int a = 1;\n"
+ " auto x = [=]() { return a + 1; };\n"
+ " auto b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4, "a ;", ValueFlow::Value::ValueType::LIFETIME));
+
+ code = "void f(int v) {\n"
+ " int a = v;\n"
+ " int * p = &a;\n"
+ " auto x = [=]() { return p + 1; };\n"
+ " auto b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 5, "a ;", ValueFlow::Value::ValueType::LIFETIME));
+
+ code = "void f() {\n"
+ " std::vector v;\n"
+ " auto x = v.begin();\n"
+ " auto it = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4, "v . begin", ValueFlow::Value::ValueType::LIFETIME));
+ }
+
+ void valueFlowArrayElement() {
+ const char *code;
+
+ code = "void f() {\n"
+ " const int x[] = {43,23,12};\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, "{ 43 , 23 , 12 }", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n"
+ " const char x[] = \"abcd\";\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n"
+ " char x[32] = \"abcd\";\n"
+ " return x;\n"
+ "}";
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, "\"abcd\"", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n"
+ " int a[10];\n"
+ " int *x = a;\n" // <- a value is a
+ " *x = 0;\n" // .. => x value is a
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4, "a", ValueFlow::Value::ValueType::TOK));
+
+ code = "char f() {\n"
+ " const char *x = \"abcd\";\n"
+ " return x[0];\n"
+ "}";
+ ASSERT_EQUALS((int)('a'), valueOfTok(code, "[").intvalue);
+
+ code = "char f() {\n"
+ " const char *x = \"\";\n"
+ " return x[0];\n"
+ "}";
+ ASSERT_EQUALS(0, valueOfTok(code, "[").intvalue);
+ }
+
+ void valueFlowMove() {
+ const char *code;
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::move(x));\n"
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::forward(x));\n"
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::ForwardedVariable));
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::move(x).getA());\n" // Only parts of x might be moved out
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::forward(x).getA());\n" // Only parts of x might be moved out
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::ForwardedVariable));
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::move(x));\n"
+ " x.clear();\n"
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f() {\n"
+ " X x;\n"
+ " g(std::move(x));\n"
+ " y=x->y;\n"
+ " z=x->z;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f(int i) {\n"
+ " X x;\n"
+ " z = g(std::move(x));\n"
+ " y = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f(int i) {\n"
+ " X x;\n"
+ " y = g(std::move(x), \n"
+ " x.size());\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f(int i) {\n"
+ " X x;\n"
+ " x = g(std::move(x));\n"
+ " y = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "A f(int i) {\n"
+ " X x;\n"
+ " if (i)"
+ " return g(std::move(x));\n"
+ " return h(std::move(x));\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "struct X {\n"
+ "};\n"
+ "struct Data {\n"
+ " template\n"
+ " void foo(Fun f) {}\n"
+ "};\n"
+ "Data g(X value) { return Data(); }\n"
+ "void f() {\n"
+ " X x;\n"
+ " g(std::move(x)).foo([=](int value) mutable {;});\n"
+ " X y=x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 11U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "void f(int x) {\n"
+ " g(std::move(x));\n"
+ " y=x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, ValueFlow::Value::MoveKind::MovedVariable));
+ }
+
+ void valueFlowCalculations() {
+ const char *code;
+
+ // Different operators
+ ASSERT_EQUALS(5, valueOfTok("3 + (a ? b : 2);", "+").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("3 - (a ? b : 2);", "-").intvalue);
+ ASSERT_EQUALS(6, valueOfTok("3 * (a ? b : 2);", "*").intvalue);
+ ASSERT_EQUALS(6, valueOfTok("13 / (a ? b : 2);", "/").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("13 % (a ? b : 2);", "%").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("3 == (a ? b : 2);", "==").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("3 != (a ? b : 2);", "!=").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("3 > (a ? b : 2);", ">").intvalue);
+ ASSERT_EQUALS(1, valueOfTok("3 >= (a ? b : 2);", ">=").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("3 < (a ? b : 2);", "<").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("3 <= (a ? b : 2);", "<=").intvalue);
+
+ ASSERT_EQUALS(1, valueOfTok("(UNKNOWN_TYPE)1;","(").intvalue);
+ ASSERT(tokenValues("(UNKNOWN_TYPE)1000;","(").empty()); // don't know if there is truncation, sign extension
+ ASSERT_EQUALS(255, valueOfTok("(unsigned char)~0;", "(").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("(int)0;", "(").intvalue);
+ ASSERT_EQUALS(3, valueOfTok("(int)(1+2);", "(").intvalue);
+ ASSERT_EQUALS(0, valueOfTok("(UNKNOWN_TYPE*)0;","(").intvalue);
+ ASSERT_EQUALS(100, valueOfTok("(int)100.0;", "(").intvalue);
+ ASSERT_EQUALS(10, valueOfTok("x = static_cast(10);", "( 10 )").intvalue);
+
+ // Don't calculate if there is UB
+ ASSERT(tokenValues(";-1<<10;","<<").empty());
+ ASSERT(tokenValues(";10<<-1;","<<").empty());
+ ASSERT(tokenValues(";10<<64;","<<").empty());
+ ASSERT(tokenValues(";-1>>10;",">>").empty());
+ ASSERT(tokenValues(";10>>-1;",">>").empty());
+ ASSERT(tokenValues(";10>>64;",">>").empty());
+
+ // calculation using 1,2 variables/values
+ code = "void f(int x) {\n"
+ " a = x+456;\n"
+ " if (x==123) {}"
+ "}";
+ ASSERT_EQUALS(579, valueOfTok(code, "+").intvalue);
+
+ code = "void f(int x, int y) {\n"
+ " a = x+y;\n"
+ " if (x==123 || y==456) {}"
+ "}";
+ ASSERT_EQUALS(0, valueOfTok(code, "+").intvalue);
+
+ code = "void f(int x) {\n"
+ " a = x+x;\n"
+ " if (x==123) {}"
+ "}";
+ ASSERT_EQUALS(246, valueOfTok(code, "+").intvalue);
+
+ code = "void f(int x, int y) {\n"
+ " a = x*x;\n"
+ " if (x==2) {}\n"
+ " if (x==4) {}\n"
+ "}";
+ std::list values = tokenValues(code,"*");
+ ASSERT_EQUALS(2U, values.size());
+ ASSERT_EQUALS(4, values.front().intvalue);
+ ASSERT_EQUALS(16, values.back().intvalue);
+
+ code = "void f(int x) {\n"
+ " if (x == 3) {}\n"
+ " a = x * (1 - x - 1);\n"
+ "}";
+ ASSERT_EQUALS(-9, valueOfTok(code, "*").intvalue);
+
+ // addition of different variables with known values
+ code = "int f(int x) {\n"
+ " int a = 1;\n"
+ " while (x!=3) { x+=a; }\n"
+ " return x/a;\n"
+ "}\n";
+ ASSERT_EQUALS(3, valueOfTok(code, "/").intvalue);
+
+ // ? :
+ code = "x = y ? 2 : 3;\n";
+ values = tokenValues(code,"?");
+ ASSERT_EQUALS(2U, values.size());
+ ASSERT_EQUALS(2, values.front().intvalue);
+ ASSERT_EQUALS(3, values.back().intvalue);
+
+ code = "void f(int a) { x = a ? 2 : 3; }\n";
+ values = tokenValues(code,"?");
+ ASSERT_EQUALS(2U, values.size());
+ ASSERT_EQUALS(2, values.front().intvalue);
+ ASSERT_EQUALS(3, values.back().intvalue);
+
+ code = "x = (2<5) ? 2 : 3;\n";
+ values = tokenValues(code, "?");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(2, values.front().intvalue);
+
+ code = "x = 123 ? : 456;\n";
+ values = tokenValues(code, "?");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(123, values.empty() ? 0 : values.front().intvalue);
+
+ // ~
+ code = "x = ~0U;";
+ settings.platform.set(Platform::Native); // ensure platform is native
+ values = tokenValues(code,"~");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(~0U, values.back().intvalue);
+
+ // !
+ code = "void f(int x) {\n"
+ " a = !x;\n"
+ " if (x==0) {}\n"
+ "}";
+ values = tokenValues(code,"!");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(1, values.back().intvalue);
+
+ // unary minus
+ code = "void f(int x) {\n"
+ " a = -x;\n"
+ " if (x==10) {}\n"
+ "}";
+ values = tokenValues(code,"-");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(-10, values.back().intvalue);
+
+ // Logical and
+ code = "void f(bool b) {\n"
+ " bool x = false && b;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(bool b) {\n"
+ " bool x = b && false;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(bool b) {\n"
+ " bool x = true && b;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1));
+
+ code = "void f(bool b) {\n"
+ " bool x = b && true;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1));
+
+ // Logical or
+ code = "void f(bool b) {\n"
+ " bool x = true || b;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1));
+
+ code = "void f(bool b) {\n"
+ " bool x = b || true;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1));
+
+ code = "void f(bool b) {\n"
+ " bool x = false || b;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "void f(bool b) {\n"
+ " bool x = b || false;\n"
+ " bool a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "bool f() {\n"
+ " bool a = (4 == 3);\n"
+ " bool b = (3 == 3);\n"
+ " return a || b;\n"
+ "}\n";
+ values = tokenValues(code, "%oror%");
+ ASSERT_EQUALS(1, values.size());
+ if (!values.empty()) {
+ ASSERT_EQUALS(true, values.front().isIntValue());
+ ASSERT_EQUALS(true, values.front().isKnown());
+ ASSERT_EQUALS(1, values.front().intvalue);
+ }
+
+ // function call => calculation
+ code = "void f(int x, int y) {\n"
+ " a = x + y;\n"
+ "}\n"
+ "void callf() {\n"
+ " f(1,1);\n"
+ " f(10,10);\n"
+ "}";
+ values = tokenValues(code, "+");
+ ASSERT_EQUALS(true, values.empty());
+ if (!values.empty()) {
+ /* todo.. */
+ ASSERT_EQUALS(2U, values.size());
+ ASSERT_EQUALS(2, values.front().intvalue);
+ ASSERT_EQUALS(22, values.back().intvalue);
+ }
+
+ // Comparison of string
+ values = tokenValues("f(\"xyz\" == \"xyz\");", "=="); // implementation defined
+ ASSERT_EQUALS(0U, values.size()); // <- no value
+
+ values = tokenValues("f(\"xyz\" == 0);", "==");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(0, values.front().intvalue);
+
+ values = tokenValues("f(0 == \"xyz\");", "==");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(0, values.front().intvalue);
+
+ values = tokenValues("f(\"xyz\" != 0);", "!=");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(1, values.front().intvalue);
+
+ values = tokenValues("f(0 != \"xyz\");", "!=");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(1, values.front().intvalue);
+ }
+
+ void valueFlowSizeof() {
+ const char *code;
+ std::list values;
+
+ // array size
+ code = "void f() {\n"
+ " char a[10];"
+ " x = sizeof(*a);\n"
+ "}";
+ values = tokenValues(code,"( *");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(1, values.back().intvalue);
+
+#define CHECK(A, B) \
+ code = "void f() {\n" \
+ " x = sizeof(" A ");\n" \
+ "}"; \
+ values = tokenValues(code,"( " A " )"); \
+ ASSERT_EQUALS(1U, values.size()); \
+ ASSERT_EQUALS(B, values.back().intvalue);
+
+ // standard types
+ CHECK("void *", settings.platform.sizeof_pointer);
+ CHECK("char", 1U);
+ CHECK("short", settings.platform.sizeof_short);
+ CHECK("int", settings.platform.sizeof_int);
+ CHECK("long", settings.platform.sizeof_long);
+ CHECK("wchar_t", settings.platform.sizeof_wchar_t);
+
+ // string/char literals
+ CHECK("\"asdf\"", 5);
+ CHECK("L\"asdf\"", 5 * settings.platform.sizeof_wchar_t);
+ CHECK("u8\"asdf\"", 5); // char8_t
+ CHECK("u\"asdf\"", 5 * 2); // char16_t
+ CHECK("U\"asdf\"", 5 * 4); // char32_t
+ CHECK("'a'", 1U);
+ CHECK("'ab'", settings.platform.sizeof_int);
+ CHECK("L'a'", settings.platform.sizeof_wchar_t);
+ CHECK("u8'a'", 1U); // char8_t
+ CHECK("u'a'", 2U); // char16_t
+ CHECK("U'a'", 4U); // char32_t
+#undef CHECK
+
+ // array size
+ code = "void f() {\n"
+ " struct S *a[10];"
+ " x = sizeof(a) / sizeof(a[0]);\n"
+ "}";
+ values = tokenValues(code,"/");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(10, values.back().intvalue);
+
+#define CHECK(A, B, C, D) \
+ code = "enum " A " E " B " { E0, E1 };\n" \
+ "void f() {\n" \
+ " x = sizeof(" C ");\n" \
+ "}"; \
+ values = tokenValues(code,"( " C " )"); \
+ ASSERT_EQUALS(1U, values.size()); \
+ ASSERT_EQUALS(D, values.back().intvalue);
+
+ // enums
+ CHECK("", "", "E", settings.platform.sizeof_int);
+
+ // typed enums
+ CHECK("", ": char", "E", 1U);
+ CHECK("", ": signed char", "E", 1U);
+ CHECK("", ": unsigned char", "E", 1U);
+ CHECK("", ": short", "E", settings.platform.sizeof_short);
+ CHECK("", ": signed short", "E", settings.platform.sizeof_short);
+ CHECK("", ": unsigned short", "E", settings.platform.sizeof_short);
+ CHECK("", ": int", "E", settings.platform.sizeof_int);
+ CHECK("", ": signed int", "E", settings.platform.sizeof_int);
+ CHECK("", ": unsigned int", "E", settings.platform.sizeof_int);
+ CHECK("", ": long", "E", settings.platform.sizeof_long);
+ CHECK("", ": signed long", "E", settings.platform.sizeof_long);
+ CHECK("", ": unsigned long", "E", settings.platform.sizeof_long);
+ CHECK("", ": long long", "E", settings.platform.sizeof_long_long);
+ CHECK("", ": signed long long", "E", settings.platform.sizeof_long_long);
+ CHECK("", ": unsigned long long", "E", settings.platform.sizeof_long_long);
+ CHECK("", ": wchar_t", "E", settings.platform.sizeof_wchar_t);
+ CHECK("", ": size_t", "E", settings.platform.sizeof_size_t);
+
+ // enumerators
+ CHECK("", "", "E0", settings.platform.sizeof_int);
+
+ // typed enumerators
+ CHECK("", ": char", "E0", 1U);
+ CHECK("", ": signed char", "E0", 1U);
+ CHECK("", ": unsigned char", "E0", 1U);
+ CHECK("", ": short", "E0", settings.platform.sizeof_short);
+ CHECK("", ": signed short", "E0", settings.platform.sizeof_short);
+ CHECK("", ": unsigned short", "E0", settings.platform.sizeof_short);
+ CHECK("", ": int", "E0", settings.platform.sizeof_int);
+ CHECK("", ": signed int", "E0", settings.platform.sizeof_int);
+ CHECK("", ": unsigned int", "E0", settings.platform.sizeof_int);
+ CHECK("", ": long", "E0", settings.platform.sizeof_long);
+ CHECK("", ": signed long", "E0", settings.platform.sizeof_long);
+ CHECK("", ": unsigned long", "E0", settings.platform.sizeof_long);
+ CHECK("", ": long long", "E0", settings.platform.sizeof_long_long);
+ CHECK("", ": signed long long", "E0", settings.platform.sizeof_long_long);
+ CHECK("", ": unsigned long long", "E0", settings.platform.sizeof_long_long);
+ CHECK("", ": wchar_t", "E0", settings.platform.sizeof_wchar_t);
+ CHECK("", ": size_t", "E0", settings.platform.sizeof_size_t);
+
+ // class typed enumerators
+ CHECK("class", ": char", "E :: E0", 1U);
+ CHECK("class", ": signed char", "E :: E0", 1U);
+ CHECK("class", ": unsigned char", "E :: E0", 1U);
+ CHECK("class", ": short", "E :: E0", settings.platform.sizeof_short);
+ CHECK("class", ": signed short", "E :: E0", settings.platform.sizeof_short);
+ CHECK("class", ": unsigned short", "E :: E0", settings.platform.sizeof_short);
+ CHECK("class", ": int", "E :: E0", settings.platform.sizeof_int);
+ CHECK("class", ": signed int", "E :: E0", settings.platform.sizeof_int);
+ CHECK("class", ": unsigned int", "E :: E0", settings.platform.sizeof_int);
+ CHECK("class", ": long", "E :: E0", settings.platform.sizeof_long);
+ CHECK("class", ": signed long", "E :: E0", settings.platform.sizeof_long);
+ CHECK("class", ": unsigned long", "E :: E0", settings.platform.sizeof_long);
+ CHECK("class", ": long long", "E :: E0", settings.platform.sizeof_long_long);
+ CHECK("class", ": signed long long", "E :: E0", settings.platform.sizeof_long_long);
+ CHECK("class", ": unsigned long long", "E :: E0", settings.platform.sizeof_long_long);
+ CHECK("class", ": wchar_t", "E :: E0", settings.platform.sizeof_wchar_t);
+ CHECK("class", ": size_t", "E :: E0", settings.platform.sizeof_size_t);
+#undef CHECK
+
+#define CHECK(A, B) \
+ code = "enum E " A " { E0, E1 };\n" \
+ "void f() {\n" \
+ " E arrE[] = { E0, E1 };\n" \
+ " x = sizeof(arrE);\n" \
+ "}"; \
+ values = tokenValues(code,"( arrE )"); \
+ ASSERT_EQUALS(1U, values.size()); \
+ ASSERT_EQUALS(B * 2U, values.back().intvalue);
+
+ // enum array
+ CHECK("", settings.platform.sizeof_int);
+
+ // typed enum array
+ CHECK(": char", 1U);
+ CHECK(": signed char", 1U);
+ CHECK(": unsigned char", 1U);
+ CHECK(": short", settings.platform.sizeof_short);
+ CHECK(": signed short", settings.platform.sizeof_short);
+ CHECK(": unsigned short", settings.platform.sizeof_short);
+ CHECK(": int", settings.platform.sizeof_int);
+ CHECK(": signed int", settings.platform.sizeof_int);
+ CHECK(": unsigned int", settings.platform.sizeof_int);
+ CHECK(": long", settings.platform.sizeof_long);
+ CHECK(": signed long", settings.platform.sizeof_long);
+ CHECK(": unsigned long", settings.platform.sizeof_long);
+ CHECK(": long long", settings.platform.sizeof_long_long);
+ CHECK(": signed long long", settings.platform.sizeof_long_long);
+ CHECK(": unsigned long long", settings.platform.sizeof_long_long);
+ CHECK(": wchar_t", settings.platform.sizeof_wchar_t);
+ CHECK(": size_t", settings.platform.sizeof_size_t);
+#undef CHECK
+
+#define CHECK(A, B) \
+ code = "enum class E " A " { E0, E1 };\n" \
+ "void f() {\n" \
+ " E arrE[] = { E::E0, E::E1 };\n" \
+ " x = sizeof(arrE);\n" \
+ "}"; \
+ values = tokenValues(code,"( arrE )"); \
+ ASSERT_EQUALS(1U, values.size()); \
+ ASSERT_EQUALS(B * 2U, values.back().intvalue);
+
+ // enum array
+ CHECK("", settings.platform.sizeof_int);
+
+ // typed enum array
+ CHECK(": char", 1U);
+ CHECK(": signed char", 1U);
+ CHECK(": unsigned char", 1U);
+ CHECK(": short", settings.platform.sizeof_short);
+ CHECK(": signed short", settings.platform.sizeof_short);
+ CHECK(": unsigned short", settings.platform.sizeof_short);
+ CHECK(": int", settings.platform.sizeof_int);
+ CHECK(": signed int", settings.platform.sizeof_int);
+ CHECK(": unsigned int", settings.platform.sizeof_int);
+ CHECK(": long", settings.platform.sizeof_long);
+ CHECK(": signed long", settings.platform.sizeof_long);
+ CHECK(": unsigned long", settings.platform.sizeof_long);
+ CHECK(": long long", settings.platform.sizeof_long_long);
+ CHECK(": signed long long", settings.platform.sizeof_long_long);
+ CHECK(": unsigned long long", settings.platform.sizeof_long_long);
+ CHECK(": wchar_t", settings.platform.sizeof_wchar_t);
+ CHECK(": size_t", settings.platform.sizeof_size_t);
+#undef CHECK
+
+ code = "uint16_t arr[10];\n"
+ "x = sizeof(arr);";
+ values = tokenValues(code,"( arr )");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(10 * sizeof(std::uint16_t), values.back().intvalue);
+ }
+
+ void valueFlowErrorPath() {
+ const char *code;
+
+ code = "void f() {\n"
+ " int x = 53;\n"
+ " a = x;\n"
+ "}\n";
+ ASSERT_EQUALS("2,Assignment 'x=53', assigned value is 53\n",
+ getErrorPathForX(code, 3U));
+
+ code = "void f(int y) {\n"
+ " int x = y;\n"
+ " a = x;\n"
+ " y += 12;\n"
+ " if (y == 32) {}"
+ "}\n";
+ ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n"
+ "4,Compound assignment '+=', before assignment value is 20\n"
+ "2,Assignment 'x=y', assigned value is 20\n",
+ getErrorPathForX(code, 3U));
+
+ code = "void f1(int x) {\n"
+ " a = x;\n"
+ "}\n"
+ "void f2() {\n"
+ " int x = 3;\n"
+ " f1(x+1);\n"
+ "}\n";
+ ASSERT_EQUALS("5,Assignment 'x=3', assigned value is 3\n"
+ "6,Calling function 'f1', 1st argument 'x+1' value is 4\n",
+ getErrorPathForX(code, 2U));
+
+ code = "void f(int a) {\n"
+ " int x;\n"
+ " for (x = a; x < 50; x++) {}\n"
+ " b = x;\n"
+ "}\n";
+ ASSERT_EQUALS("3,After for loop, x has value 50\n",
+ getErrorPathForX(code, 4U));
+ }
+
+ void valueFlowBeforeCondition() {
+ const char *code;
+
+ code = "void f(int x) {\n"
+ " int a = x;\n"
+ " if (x == 123) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
+
+ code = "void f(unsigned int x) {\n"
+ " int a = x;\n"
+ " if (x >= 1) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 1));
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "void f(unsigned int x) {\n"
+ " int a = x;\n"
+ " if (x > 0) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "void f(unsigned int x) {\n"
+ " int a = x;\n"
+ " if (x > 1) {}\n" // not zero => don't consider > condition
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 1));
+
+ code = "void f(int x) {\n" // not unsigned => don't consider > condition
+ " int a = x;\n"
+ " if (x > 0) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
+
+ code = "void f(int *x) {\n"
+ " *x = 100;\n"
+ " if (x) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "extern const int x;\n"
+ "void f() {\n"
+ " int a = x;\n"
+ " if (x == 123) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ // after loop
+ code = "void f(struct X *x) {\n"
+ " do {\n"
+ " if (!x)\n"
+ " break;\n"
+ " } while (x->a);\n"
+ " if (x) {}\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+ }
+
+ void valueFlowBeforeConditionAssignIncDec() { // assignment / increment
+ const char *code;
+
+ code = "void f(int x) {\n"
+ " x = 2 + x;\n"
+ " if (x == 65);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));
+
+ code = "void f(int x) {\n"
+ " x = y = 2 + x;\n"
+ " if (x == 65);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 65));
+
+ code = "void f(int x) {\n"
+ " a[x++] = 0;\n"
+ " if (x == 5);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 5));
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " x++;\n"
+ " if (x == 4);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 3));
+ ASSERT_EQUALS("4,Assuming that condition 'x==4' is not redundant\n"
+ "3,x is incremented, before this increment the value is 3\n",
+ getErrorPathForX(code, 2U));
+
+ // compound assignment += , -= , ...
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " x += 2;\n"
+ " if (x == 4);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 2));
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " x -= 2;\n"
+ " if (x == 4);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 6));
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " x *= 2;\n"
+ " if (x == 42);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 21));
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " x /= 5;\n"
+ " if (x == 42);\n"
+ "}";
+ ASSERT(tokenValues(code, "x ;").empty());
+
+ // bailout: assignment
+ bailout("void f(int x) {\n"
+ " x = y;\n"
+ " if (x == 123) {}\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n"
+ "[test.cpp:2]: (debug) valueflow.cpp::valueFlowReverse bailout: assignment of x\n", errout.str());
+ }
+
+ void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
+ const char *code;
+
+ code = "void f(int x) {\n"
+ " if (!x || \n" // <- x can be 0
+ " a/x) {}\n" // <- x can't be 0
+ " if (x==0) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "void f(int *x) {\n"
+ " ((x=ret())&&\n"
+ " (*x==0));\n" // <- x is not 0
+ " if (x==0) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "void f(int *x) {\n"
+ " int a = (x && *x == '1');\n"
+ " int b = a ? atoi(x) : 0;\n" // <- x is not 0
+ " if (x==0) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+ }
+
+ void valueFlowBeforeConditionFunctionCall() { // function calls
+ const char *code;
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " setx(x);\n"
+ " if (x == 1) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX((std::string("void setx(int x);")+code).c_str(), 2U, 1));
+ ASSERT_EQUALS(false, testValueOfX((std::string("void setx(int &x);")+code).c_str(), 2U, 1));
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 1));
+
+ code = "void f(char* x) {\n"
+ " strcpy(x,\"abc\");\n"
+ " if (x) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "void addNewFunction(Scope**scope, const Token**tok);\n"
+ "void f(Scope *x) {\n"
+ " x->functionList.back();\n"
+ " addNewFunction(&x,&tok);\n" // address-of, x can be changed by subfunction
+ " if (x) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+ }
+
+ void valueFlowBeforeConditionLoop() { // while, for, do-while
+ const char *code;
+
+ code = "void f(int x) {\n" // loop condition, x is not assigned inside loop => use condition
+ " a = x;\n" // x can be 37
+ " while (x == 37) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 37));
+
+ code = "void f(int x) {\n" // loop condition, x is assigned inside loop => don't use condition
+ " a = x;\n" // don't assume that x can be 37
+ " while (x != 37) { x++; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 37));
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " for (; x!=1; x++) { }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 1));
+
+ code = "void f(menu *x) {\n"
+ " a = x->parent;\n"
+ " for (i=0;(i<10) && (x!=0); i++) { x = x->next; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
+
+ code = "void f(int x) {\n" // condition inside loop, x is NOT assigned inside loop => use condition
+ " a = x;\n"
+ " do {\n"
+ " if (x==76) {}\n"
+ " } while (1);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 76));
+
+ code = "void f(int x) {\n" // conditions inside loop, x is assigned inside do-while => don't use condition
+ " a = x;\n"
+ " do {\n"
+ " if (x!=76) { x=do_something(); }\n"
+ " } while (1);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 76));
+
+ code = "void f(X x) {\n" // conditions inside loop, x is assigned inside do-while => don't use condition
+ " a = x;\n"
+ " for (i=1;i<=count;i++) {\n"
+ " BUGON(x==0)\n"
+ " x = x.next;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
+ }
+
+ void valueFlowBeforeConditionTernaryOp() { // bailout: ?:
+ const char *code;
+
+ bailout("void f(int x) {\n"
+ " y = ((x<0) ? x : ((x==2)?3:4));\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n"
+ "[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str());
+
+ bailout("int f(int x) {\n"
+ " int r = x ? 1 / x : 0;\n"
+ " if (x == 0) {}\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str());
+
+ code = "void f(int x) {\n"
+ " int a =v x;\n"
+ " a = b ? x/2 : 20/x;\n"
+ " if (x == 123) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
+
+ code = "void f(const s *x) {\n"
+ " x->a = 0;\n"
+ " if (x ? x->a : 0) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "void f(int x, int y) {\n"
+ " a = x;\n"
+ " if (y){}\n"
+ " if (x==123){}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 123));
+ }
+
+ void valueFlowBeforeConditionSizeof() { // skip sizeof
+ const char *code;
+
+ code = "void f(int *x) {\n"
+ " sizeof(x[0]);\n"
+ " if (x==63){}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 63));
+
+ code = "void f(int *x) {\n"
+ " char a[sizeof x.y];\n"
+ " if (x==0){}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
+ }
+
+ void valueFlowBeforeConditionIfElse() { // bailout: if/else/etc
+ const char *code;
+
+ code = "void f(X * x) {\n"
+ " a = x;\n"
+ " if ((x != NULL) &&\n"
+ " (a(x->name, html)) &&\n"
+ " (a(x->name, body))) {}\n"
+ " if (x != NULL) { }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+
+ bailout("void f(int x) {\n"
+ " if (x != 123) { b = x; }\n"
+ " if (x == 123) {}\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable b\n"
+ "[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str());
+
+ code = "void f(int x) {\n"
+ " a = x;\n"
+ " if (abc) { x = 1; }\n" // <- condition must be false if x is 7 in next line
+ " if (x == 7) { }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 7));
+ }
+
+ void valueFlowBeforeConditionGlobalVariables() {
+ const char *code;
+
+ // handle global variables
+ code = "int x;\n"
+ "void f() {\n"
+ " int a = x;\n"
+ " if (x == 123) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code,3,123));
+
+ // bailout when there is function call
+ code = "class Fred { int x; void clear(); void f(); };\n"
+ "void Fred::f() {\n"
+ " int a = x;\n"
+ " clear();\n" // <- x might be assigned
+ " if (x == 234) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code,3,234));
+ }
+
+ void valueFlowBeforeConditionSwitch() {
+ // bailout: switch
+ // TODO : handle switch/goto more intelligently
+ bailout("void f(int x, int y) {\n"
+ " switch (y) {\n"
+ " case 1: a=x; break;\n"
+ " case 2: if (x==5) {} break;\n"
+ " };\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
+ "[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str());
+
+ bailout("void f(int x, int y) {\n"
+ " switch (y) {\n"
+ " case 1: a=x; return 1;\n"
+ " case 2: if (x==5) {} break;\n"
+ " };\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
+ "[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str());
+ }
+
+ void valueFlowBeforeConditionMacro() {
+ // bailout: condition is a expanded macro
+ bailout("#define M if (x==123) {}\n"
+ "void f(int x) {\n"
+ " a = x;\n"
+ " M;\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
+ "[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str());
+
+ bailout("#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0)\n" // #8349
+ "void f(int *x) {\n"
+ " a = x;\n"
+ " FREE(x);\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
+ "[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str());
+ }
+
+ void valueFlowBeforeConditionGoto() {
+ // bailout: goto label (TODO: handle gotos more intelligently)
+ bailout("void f(int x) {\n"
+ " if (x == 123) { goto out; }\n"
+ " a=x;\n" // <- x is not 123
+ "out:"
+ " if (x==123){}\n"
+ "}");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS(
+ "[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
+ "[test.cpp:4]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable x stopping on goto label\n"
+ "[test.cpp:2]: (debug) valueflow.cpp:1813:valueFlowForwardVariable bailout: variable x. noreturn conditional scope.\n",
+ errout.str());
+
+ // #5721 - FP
+ bailout("static void f(int rc) {\n"
+ " ABC* abc = getabc();\n"
+ " if (!abc) { goto out };\n"
+ "\n"
+ " abc->majortype = 0;\n"
+ " if (FAILED(rc)) {}\n"
+ "\n"
+ "out:\n"
+ " if (abc) {}\n"
+ "}\n");
+ ASSERT_EQUALS_WITHOUT_LINENUMBERS(
+ "[test.cpp:2]: (debug) valueflow.cpp:1035:valueFlowReverse bailout: assignment of abc\n"
+ "[test.cpp:8]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable abc stopping on goto label\n"
+ "[test.cpp:3]: (debug) valueflow.cpp:1813:valueFlowForwardVariable bailout: variable abc. noreturn conditional scope.\n",
+ errout.str());
+ }
+
+ void valueFlowBeforeConditionForward() {
+ const char* code;
+
+ code = "void f(int a) {\n"
+ " int x = a;\n"
+ " if (a == 123) {}\n"
+ " int b = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+ code = "void f(int a) {\n"
+ " int x = a;\n"
+ " if (a != 123) {}\n"
+ " int b = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+ }
+
+ void valueFlowAfterAssign() {
+ const char *code;
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f() {\n"
+ " bool x = 32;\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1));
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " a = sizeof(x);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " a = 2 + x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f() {\n"
+ " int x = 9;\n"
+ " --x;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 9));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 8));
+ ASSERT_EQUALS("2,Assignment 'x=9', assigned value is 9\n"
+ "3,x is decremented', new value is 8\n",
+ getErrorPathForX(code, 4U));
+
+ code = "void x() {\n"
+ " int x = value ? 6 : 0;\n"
+ " x =\n"
+ " 1 + x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 7));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " y = x += z;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " static int x = 2;\n"
+ " x++;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 2));
+
+ code = "void f() {\n"
+ " static int x = 2;\n"
+ " a >> x;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 2));
+
+ code = "void f() {\n"
+ " static int x = 0;\n"
+ " if (x==0) x = getX();\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ // truncation
+ code = "int f() {\n"
+ " int x = 1.5;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1));
+
+ code = "int f() {\n"
+ " unsigned char x = 0x123;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0x23));
+
+ code = "int f() {\n"
+ " signed char x = 0xfe;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, -2));
+
+ // function
+ code = "void f() {\n"
+ " char *x = 0;\n"
+ " int success = getx((char**)&x);\n"
+ " if (success) x[0] = 0;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " char *x = 0;\n"
+ " getx(reinterpret_cast(&x));\n"
+ " *x = 0;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ // lambda
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " Q q = [&]() {\n"
+ " if (x > 0) {}\n"
+ " x++;\n"
+ " };\n"
+ " dosomething(q);\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " dostuff([&]() {\n"
+ " if (x > 0) {}\n"
+ " x++;\n"
+ " });\n"
+ " dosomething(q);\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "int f() {\n"
+ " int x = 1;\n"
+ " dostuff([&]() {\n"
+ " x = y;\n"
+ " });\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 1));
+
+ // ?:
+ code = "void f() {\n"
+ " int x = 8;\n"
+ " a = ((x > 10) ?\n"
+ " x : 0);\n" // <- x is not 8
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 8));
+
+ code = "void f() {\n" // #6973
+ " char *x = \"\";\n"
+ " a = ((x[0] == 'U') ?\n"
+ " x[1] : 0);\n" // <- x is not ""
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, "\"\"", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n" // #6973
+ " char *x = getenv (\"LC_ALL\");\n"
+ " if (x == NULL)\n"
+ " x = \"\";\n"
+ "\n"
+ " if ( (x[0] == 'U') &&\n" // x can be ""
+ " (x[1] ?\n" // x can't be ""
+ " x[3] :\n" // x can't be ""
+ " x[2] ))\n" // x can't be ""
+ " {}\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 6U, "\"\"", ValueFlow::Value::ValueType::TOK));
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, "\"\"", ValueFlow::Value::ValueType::TOK));
+ ASSERT_EQUALS(false, testValueOfX(code, 8U, "\"\"", ValueFlow::Value::ValueType::TOK));
+ ASSERT_EQUALS(false, testValueOfX(code, 9U, "\"\"", ValueFlow::Value::ValueType::TOK));
+
+ code = "void f() {\n" // #7599
+ " t *x = 0;\n"
+ " y = (a ? 1 : x\n" // <- x is 0
+ " && x->y ? 1 : 2);" // <- x is not 0
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n" // #7599
+ " t *x = 0;\n"
+ " y = (a ? 1 : !x\n" // <- x is 0
+ " || x->y ? 1 : 2);" // <- x is not 0
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ // if/else
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " if (condition) return;\n"
+ " a = 2 + x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+ code = "void f() {\n"
+ " int x = 1;\n"
+ " if (condition) x = 2;\n"
+ " a = 2 + x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 1));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 2));
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " if (condition1) x = 456;\n"
+ " if (condition2) x = 789;\n"
+ " a = 2 + x;\n" // <- either assignment "x=123" is redundant or x can be 123 here.
+ "}";
+ // ??? ASSERT_EQUALS(true, testValueOfX(code, 5U, 123));
+
+ code = "void f(int a) {\n"
+ " int x = 123;\n"
+ " if (a > 1)\n"
+ " ++x;\n"
+ " else\n"
+ " ++x;\n"
+ " return 2 + x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+
+ code = "void f() {\n"
+ " int x = 1;\n"
+ " if (condition1) x = 2;\n"
+ " else return;\n"
+ " a = 2 + x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 1));
+
+ code = "void f(){\n"
+ " int x = 0;\n"
+ " if (a>=0) { x = getx(); }\n"
+ " if (x==0) { return; }\n"
+ " return 123 / x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+
+ code = "void f() {\n"
+ " X *x = getx();\n"
+ " if(0) { x = 0; }\n"
+ " else { x->y = 1; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n" // #6239
+ " int x = 4;\n"
+ " if(1) { x = 0; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 4));
+
+ code = "void f() {\n"
+ " int x = 32;\n"
+ " if (x>=32) return;\n"
+ " a[x]=0;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 32));
+
+ code = "void f() {\n"
+ " int x = 32;\n"
+ " if (x>=32) {\n"
+ " a[x] = 0;\n" // <- should have possible value 32
+ " return;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 32));
+
+ code = "void f() {\n"
+ " int x = 33;\n"
+ " if (x==33) goto fail;\n"
+ " a[x]=0;\n"
+ "fail:\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 33));
+
+ code = "void f() {\n"
+ " int x = 32;\n"
+ " if (a==1) { z=x+12; }\n"
+ " if (a==2) { z=x+32; }\n"
+ " z = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 32));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 32));
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 32));
+
+ code = "void f() {\n" // #5656 - FP
+ " int x = 0;\n"
+ " if (!x) {\n"
+ " x = getx();\n"
+ " }\n"
+ " y = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 0));
+
+ code = "void f(int y) {\n" // alias
+ " int x = y;\n"
+ " if (y == 54) {}\n"
+ " else { a = x; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 54));
+
+ code = "void f () {\n"
+ " ST * x = g_pST;\n"
+ " if (x->y == 0) {\n"
+ " x = NULL;\n"
+ " return 1;\n"
+ " }\n"
+ " a = x->y;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 0));
+
+ code = "void f () {\n"
+ " ST * x = g_pST;\n"
+ " if (x->y == 0) {\n"
+ " x = NULL;\n"
+ " goto label;\n"
+ " }\n"
+ " a = x->y;\n"
+ "label:\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 0));
+
+ code = "void f() {\n" // #5752 - FP
+ " int *x = 0;\n"
+ " if (x && *x == 123) {\n"
+ " getx(*x);\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " if (!x) {}\n"
+ " else { y = x; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n" // #6118 - FP
+ " int x = 0;\n"
+ " x = x & 0x1;\n"
+ " if (x == 0) { x = 2; }\n"
+ " y = 42 / x;\n" // <- x is 2
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 2));
+
+ code = "void f() {\n" // #6118 - FN
+ " int x = 0;\n"
+ " x = x & 0x1;\n"
+ " if (x == 0) { x += 2; }\n"
+ " y = 42 / x;\n" // <- x is 2
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 2));
+
+ code = "void f(int mode) {\n"
+ " struct ABC *x;\n"
+ "\n"
+ " if (mode) { x = &y; }\n"
+ " else { x = NULL; }\n"
+ "\n"
+ " if (!x) exit(1);\n"
+ "\n"
+ " a = x->a;\n" // <- x can't be 0
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 9U, 0));
+
+ code = "void f(int i) {\n"
+ " bool x = false;\n"
+ " if (i == 0) { x = true; }\n"
+ " else if (x && i == 1) {}\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 0));
+
+ code = "void f(int i) {\n"
+ " bool x = false;\n"
+ " while(i > 0) {\n"
+ " i++;\n"
+ " if (i == 0) { x = true; }\n"
+ " else if (x && i == 1) {}\n"
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 0));
+
+ // multivariables
+ code = "void f(int a) {\n"
+ " int x = a;\n"
+ " if (a!=132) { b = x; }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 132));
+
+ code = "void f(int a) {\n"
+ " int x = a;\n"
+ " b = x;\n" // <- line 3
+ " if (a!=132) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 132));
+
+ code = "void f() {\n"
+ " int a;\n"
+ " if (n) { a = n; }\n"
+ " else { a = 0; }\n"
+ " int x = a;\n"
+ " if (a > 0) { a = b / x; }\n" // <- line 6
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 0)); // x is not 0 at line 6
+
+ code = "void f(int x1) {\n" // #6086
+ " int x = x1;\n"
+ " if (x1 >= 3) {\n"
+ " return;\n"
+ " }\n"
+ " a = x;\n" // <- x is not 3
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 3));
+
+ code = "int f(int *x) {\n" // #5980
+ " if (!x) {\n"
+ " switch (i) {\n"
+ " default:\n"
+ " throw std::runtime_error(msg);\n"
+ " };\n"
+ " }\n"
+ " return *x;\n" // <- x is not 0
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 8U, 0));
+
+ code = "void f(int a) {\n" // #6826
+ " int x = a ? a : 87;\n"
+ " if (a && x) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 87));
+
+ code = "void f() {\n"
+ " int first=-1, x=0;\n"
+ " do {\n"
+ " if (first >= 0) { a = x; }\n" // <- x is not 0
+ " first++; x=3;\n"
+ " } while (1);\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ // pointer/reference to x
+ code = "int f(void) {\n"
+ " int x = 2;\n"
+ " int *px = &x;\n"
+ " for (int i = 0; i < 1; i++) {\n"
+ " *px = 1;\n"
+ " }\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 2));
+
+ code = "int f(void) {\n"
+ " int x = 5;\n"
+ " int &rx = x;\n"
+ " for (int i = 0; i < 1; i++) {\n"
+ " rx = 1;\n"
+ " }\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 5));
+
+ // break
+ code = "void f() {\n"
+ " for (;;) {\n"
+ " int x = 1;\n"
+ " if (!abc()) {\n"
+ " x = 2;\n"
+ " break;\n"
+ " }\n"
+ " a = x;\n" // <- line 8
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 8U, 2)); // x is not 2 at line 8
+
+ code = "void f() {\n"
+ " int x;\n"
+ " switch (ab) {\n"
+ " case A: x = 12; break;\n"
+ " case B: x = 34; break;\n"
+ " }\n"
+ " v = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 7U, 12));
+ ASSERT_EQUALS(true, testValueOfX(code, 7U, 34));
+
+ code = "void f() {\n" // #5981
+ " int x;\n"
+ " switch (ab) {\n"
+ " case A: x = 12; break;\n"
+ " case B: x = 34; break;\n"
+ " }\n"
+ " switch (ab) {\n"
+ " case A: v = x; break;\n" // <- x is not 34
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 8U, 34));
+
+ // while/for
+ code = "void f() {\n" // #6138
+ " ENTRY *x = 0;\n"
+ " while (x = get()) {\n"
+ " set(x->value);\n" // <- x is not 0
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f(const int *buf) {\n"
+ " int x = 0;\n"
+ " for (int i = 0; i < 10; i++) {\n"
+ " if (buf[i] == 123) {\n"
+ " x = i;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " a = x;\n" // <- x can be 0
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9
+
+ code = "void f(const int *buf) {\n"
+ " int x = 111;\n"
+ " bool found = false;\n"
+ " for (int i = 0; i < 10; i++) {\n"
+ " if (buf[i] == 123) {\n"
+ " x = i;\n"
+ " found = true;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " if (found)\n"
+ " a = x;\n" // <- x can't be 111
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 12U, 111)); // x can not be 111 at line 9
+
+ code = "void f(const int *buf) {\n"
+ " int x = 0;\n"
+ " for (int i = 0; i < 10; i++) {\n"
+ " if (buf[i] == 123) {\n"
+ " x = i;\n"
+ " ;\n" // <- no break
+ " }\n"
+ " }\n"
+ " a = x;\n" // <- x can't be 0
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 9U, 0)); // x can't be 0 at line 9
+
+ code = "void f(const int *buf) {\n"
+ " int x = 0;\n"
+ " while (++i < 10) {\n"
+ " if (buf[i] == 123) {\n"
+ " x = i;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " a = x;\n" // <- x can be 0
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 9U, 0)); // x can be 0 at line 9
+
+ code = "void f(const int a[]) {\n" // #6616
+ " const int *x = 0;\n"
+ " for (int i = 0; i < 10; i = *x) {\n" // <- x is not 0
+ " x = a[i];\n"
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ // alias
+ code = "void f() {\n" // #7778
+ " int x = 0;\n"
+ " int *p = &x;\n"
+ " x = 3;\n"
+ " *p = 2;\n"
+ " a = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 3));
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 6U, 2));
+ }
+
+ void valueFlowAfterCondition() {
+ const char *code;
+ // in if
+ code = "void f(int x) {\n"
+ " if (x == 123) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x != 123) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x > 123) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 124));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x < 123) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 122));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ // ----
+
+ code = "void f(int x) {\n"
+ " if (123 < x) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 124));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (123 > x) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 122));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ // in else
+ code = "void f(int x) {\n"
+ " if (x == 123) {}\n"
+ " else a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x != 123) {}\n"
+ " else a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ // after if
+ code = "void f(int x) {\n"
+ " if (x == 10) {\n"
+ " x++;\n"
+ " }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 10));
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 5U, 11));
+
+ // !
+ code = "void f(int x) {\n"
+ " if (!x) { a = x; }\n"
+ " else a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 0));
+
+ code = "void f(int x, int y) {\n"
+ " if (!(x&&y)) { return; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "void f(int x) {\n"
+ " if (!x) { { throw new string(); }; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "void f(int x) {\n"
+ " if (x != 123) { throw " "; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x != 123) { }\n"
+ " else { throw " "; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+ code = "void f(int x) {\n"
+ " if (x == 123) { }\n"
+ " else { throw " "; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+
+ code = "void f(int x) {\n"
+ " if (x < 123) { }\n"
+ " else { a = x; }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x < 123) { throw \"\"; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x < 123) { }\n"
+ " else { throw \"\"; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 122));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x > 123) { }\n"
+ " else { a = x; }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x > 123) { throw \"\"; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x > 123) { }\n"
+ " else { throw \"\"; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 124));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+
+ code = "void f(int x) {\n"
+ " if (x < 123) { return; }\n"
+ " else { return; }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 124));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+
+ // if (var)
+ code = "void f(int x) {\n"
+ " if (x) { a = x; }\n" // <- x is not 0
+ " else { b = x; }\n" // <- x is 0
+ " c = x;\n" // <- x might be 0
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 2U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 0));
+
+ // After while
+ code = "void f(int x) {\n"
+ " while (x != 3) {}\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 3));
+
+ code = "void f(int x) {\n"
+ " while (11 != (x = dostuff())) {}\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 11));
+
+ code = "void f(int x) {\n"
+ " while (11 != (x = dostuff()) && y) {}\n"
+ " a = x;\n"
+ "}";
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 11));
+
+ code = "void f(int x) {\n"
+ " while (x = dostuff()) {}\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(const Token *x) {\n" // #5866
+ " x = x->next();\n"
+ " while (x) { x = x->next(); }\n"
+ " if (x->str()) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 0));
+
+ code = "void f(const Token *x) {\n"
+ " while (0 != (x = x->next)) {}\n"
+ " x->ab = 0;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(const Token* x) {\n"
+ " while (0 != (x = x->next)) {}\n"
+ " if (x->str) {\n" // <- possible value 0
+ " x = y;\n" // <- this caused some problem
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ // conditional code after if/else/while
+ code = "void f(int x) {\n"
+ " if (x == 2) {}\n"
+ " if (x > 0)\n"
+ " a = x;\n" // <- TODO, x can be 2
+ " else\n"
+ " b = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 2));
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 2));
+
+ // condition with 2nd variable
+ code = "void f(int x) {\n"
+ " int y = 0;\n"
+ " if (x == 7) { y = 1; }\n"
+ " if (!y)\n"
+ " a = x;\n" // <- x can not be 7 here
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 7));
+
+ code = "void f(struct X *x) {\n"
+ " bool b = TRUE;\n"
+ " if(x) { }\n"
+ " else\n"
+ " b = FALSE;\n"
+ " if (b)\n"
+ " abc(x->value);\n" // <- x is not 0
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 0));
+
+ // In condition, after && and ||
+ code = "void f(int x) {\n"
+ " a = (x != 3 ||\n"
+ " x);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 3));
+
+ code = "void f(int x) {\n"
+ " a = (x == 4 &&\n"
+ " x);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 4));
+
+ // protected usage with &&
+ code = "void f(const Token* x) {\n"
+ " if (x) {}\n"
+ " for (; x && \n"
+ " x->str() != y; x = x->next()) {}\n"
+ "}";
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f(const Token* x) {\n"
+ " if (x) {}\n"
+ " if (x && \n"
+ " x->str() != y) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ // return
+ code = "void f(int x) {\n" // #6024
+ " if (x == 5) {\n"
+ " if (z) return; else return;\n"
+ " }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 5));
+
+ code = "void f(int x) {\n" // #6730
+ " if (x == 5) {\n"
+ " if (z) continue; else throw e;\n"
+ " }\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 5));
+
+ // TODO: float
+ code = "void f(float x) {\n"
+ " if (x == 0.5) {}\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ // aliased variable
+ code = "void f() {\n"
+ " int x = 1;\n"
+ " int *data = &x;\n"
+ " if (!x) {\n"
+ " calc(data);\n"
+ " a = x;\n" // <- x might be changed by calc
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 0));
+
+ code = "int* g();\n"
+ "int f() {\n"
+ " int * x;\n"
+ " x = g();\n"
+ " if (x) { printf(\"\"); }\n"
+ " return *x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 6U, 0));
+
+ code = "int g(int);\n"
+ "void f(int x) {\n"
+ " if (x!=0) {}\n"
+ " else if (g(x)) {\n"
+ " a = x;\n" // <- no value
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 0));
+ }
+
+ void valueFlowAfterConditionExpr() {
+ const char* code;
+
+ code = "void f(int* p) {\n"
+ " if (p[0] == 123) {\n"
+ " int x = p[0];\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+ code = "void f(int y) {\n"
+ " if (y+1 == 123) {\n"
+ " int x = y+1;\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+ code = "void f(int y) {\n"
+ " if (y+1 == 123) {\n"
+ " int x = y+2;\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 124));
+
+ code = "void f(int y, int z) {\n"
+ " if (y+z == 123) {\n"
+ " int x = y+z;\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
+
+ code = "void f(int y, int z) {\n"
+ " if (y+z == 123) {\n"
+ " y++;\n"
+ " int x = y+z;\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 123));
+
+ code = "void f(int y) {\n"
+ " if (y++ == 123) {\n"
+ " int x = y++;\n"
+ " int a = x;\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 124));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 125));
+ }
+
+ void valueFlowAfterConditionSeveralNot() {
+ const char *code;
+
+ code = "int f(int x, int y) {\n"
+ " if (x!=0) {}\n"
+ " return y/x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "int f(int x, int y) {\n"
+ " if (!!(x != 0)) {\n"
+ " return y/x;\n"
+ "}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ code = "int f(int x, int y) {\n"
+ " if (!!!(x != 0)) {\n"
+ " return y/x;\n"
+ "}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "int f(int x, int y) {\n"
+ " if (!!!!(x != 0)) {\n"
+ " return y/x;\n"
+ "}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+ }
+
+ void valueFlowForwardCompoundAssign() {
+ const char *code;
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " x += 43;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 166));
+ ASSERT_EQUALS("2,Assignment 'x=123', assigned value is 123\n"
+ "3,Compound assignment '+=', assigned value is 166\n",
+ getErrorPathForX(code, 4U));
+
+ code = "void f() {\n"
+ " int x = 123;\n"
+ " x /= 0;\n" // don't crash when evaluating x/=0
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
+
+ code = "void f() {\n"
+ " float x = 123.45;\n"
+ " x += 67;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 123.45F + 67, 0.01F));
+ }
+
+ void valueFlowForwardCorrelatedVariables() {
+ const char *code;
+
+ code = "void f(int x = 0) {\n"
+ " bool zero(x==0);\n"
+ " if (zero) a = x;\n" // <- x is 0
+ " else b = x;\n" // <- x is not 0
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+ }
+
+ void valueFlowForwardModifiedVariables() {
+ const char *code;
+
+ code = "void f(bool b) {\n"
+ " int x = 0;\n"
+ " if (b) x = 1;\n"
+ " else b = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0));
+
+ code = "void f(int i) {\n"
+ " int x = 0;\n"
+ " if (i == 0) \n"
+ " x = 1;\n"
+ " else if (!x && i == 1) \n"
+ " int b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 0));
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, 0));
+ }
+
+ void valueFlowForwardFunction() {
+ const char *code;
+
+ code = "class C {\n"
+ "public:\n"
+ " C(int &i);\n" // non-const argument => might be changed
+ "};\n"
+ "int f() {\n"
+ " int x=1;\n"
+ " C c(x);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 8U, 1));
+
+ code = "class C {\n"
+ "public:\n"
+ " C(const int &i);\n" // const argument => is not changed
+ "};\n"
+ "int f() {\n"
+ " int x=1;\n"
+ " C c(x);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 8U, 1));
+
+ code = "int f(int *);\n"
+ "int g() {\n"
+ " const int a = 1;\n"
+ " int x = 11;\n"
+ " c = (a && f(&x));\n"
+ " if (x == 42) {}\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 11));
+ }
+
+ void valueFlowForwardTernary() {
+ const char *code;
+
+ code = "int f() {\n"
+ " int x=5;\n"
+ " a = b ? init1(&x) : init2(&x);\n"
+ " return 1 + x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 5));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 5));
+
+ code = "int f(int *p) {\n" // #9008 - gcc ternary ?:
+ " if (p) return;\n"
+ " x = *p ? : 1;\n" // <- no explicit expr0
+ "}";
+ testValueOfX(code, 1U, 0); // do not crash
+
+ code = "void f(int a) {\n" // #8784
+ " int x = 13;\n"
+ " if (a == 1) x = 26;\n"
+ " return a == 1 ? x : 0;\n" // <- x is 26
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 13));
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 4U, 26));
+ }
+
+ void valueFlowForwardLambda() {
+ const char *code;
+
+ code = "void f() {\n"
+ " int x=1;\n"
+ " auto f = [&](){ a=x; }\n" // x is not 1
+ " x = 2;\n"
+ " f();\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1));
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 2));
+
+ code = "void f() {\n"
+ " int x=3;\n"
+ " auto f = [&](){ a=x; }\n" // todo: x is 3
+ " f();\n"
+ "}";
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 3U, 3));
+ }
+
+ void valueFlowBitAnd() {
+ const char *code;
+
+ code = "int f(int a) {\n"
+ " int x = a & 0x80;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code,3U,0));
+ ASSERT_EQUALS(true, testValueOfX(code,3U,0x80));
+
+ code = "int f(int a) {\n"
+ " int x = a & 0x80 ? 1 : 2;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code,3U,0));
+ ASSERT_EQUALS(false, testValueOfX(code,3U,0x80));
+
+ code = "int f() {\n"
+ " int x = (19 - 3) & 15;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code,3U,0));
+ ASSERT_EQUALS(false, testValueOfX(code,3U,16));
+ }
+
+ void valueFlowRightShift() {
+ const char *code;
+ /* Set some temporary fixed values to simplify testing */
+ /*const*/ Settings s = settings;
+ s.platform.int_bit = 32;
+ s.platform.long_bit = 64;
+ s.platform.long_long_bit = MathLib::bigint_bits * 2;
+
+ code = "int f(int a) {\n"
+ " int x = (a & 0xff) >> 16;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code,3U,0,&s));
+
+ code = "int f(unsigned int a) {\n"
+ " int x = (a % 123) >> 16;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code,3U,0,&s));
+
+ code = "int f(int y) {\n"
+ " int x = (y & 0xFFFFFFF) >> 31;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(int y) {\n"
+ " int x = (y & 0xFFFFFFF) >> 32;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(short y) {\n"
+ " int x = (y & 0xFFFFFF) >> 31;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(short y) {\n"
+ " int x = (y & 0xFFFFFF) >> 32;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 63;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 64;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 63;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 64;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 121;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+
+ code = "int f(long long y) {\n"
+ " int x = (y & 0xFFFFFF) >> 128;\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 3u, 0, &s));
+ }
+
+ void valueFlowFwdAnalysis() {
+ const char *code;
+ std::list values;
+
+ code = "void f() {\n"
+ " struct Foo foo;\n"
+ " foo.x = 1;\n"
+ " x = 0 + foo.x;\n" // <- foo.x is 1
+ "}";
+ values = tokenValues(code, "+");
+ ASSERT_EQUALS(1U, values.size());
+ ASSERT_EQUALS(true, values.front().isKnown());
+ ASSERT_EQUALS(true, values.front().isIntValue());
+ ASSERT_EQUALS(1, values.front().intvalue);
+
+ code = "void f() {\n"
+ " S s;\n"
+ " s.x = 1;\n"
+ " int y = 10;\n"
+ " while (s.x < y)\n" // s.x does not have known value
+ " s.x++;\n"
+ "}";
+ values = tokenValues(code, "<");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void f() {\n"
+ " S s;\n"
+ " s.x = 37;\n"
+ " int y = 10;\n"
+ " while (s.x < y)\n" // s.x has a known value
+ " y--;\n"
+ "}";
+ values = tokenValues(code, ". x <");
+ ASSERT(values.size() == 1 &&
+ values.front().isKnown() &&
+ values.front().isIntValue() &&
+ values.front().intvalue == 37);
+
+ code = "void f() {\n"
+ " Hints hints;\n"
+ " hints.x = 1;\n"
+ " if (foo)\n"
+ " hints.x = 2;\n"
+ " x = 0 + foo.x;\n" // <- foo.x is possible 1, possible 2
+ "}";
+ values = tokenValues(code, "+");
+ TODO_ASSERT_EQUALS(2U, 0U, values.size()); // should be 2
+
+ // FP: Condition '*b>0' is always true
+ code = "bool dostuff(const char *x, const char *y);\n"
+ "void fun(char *s, int *b) {\n"
+ " for (int i = 0; i < 42; ++i) {\n"
+ " if (dostuff(s, \"1\")) {\n"
+ " *b = 1;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " if (*b > 0) {\n" // *b does not have known value
+ " }\n"
+ "}";
+ values = tokenValues(code, ">");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void foo() {\n"
+ " struct ISO_PVD_s pvd;\n"
+ " pvd.descr_type = 0xff;\n"
+ " do {\n"
+ " if (pvd.descr_type == 0xff) {}\n"
+ " dostuff(&pvd);\n"
+ " } while (condition)\n"
+ "}";
+ values = tokenValues(code, "==");
+ ASSERT_EQUALS(true, values.empty());
+
+ // for loops
+ code = "struct S { int x; };\n" // #9036
+ "void foo(struct S s) {\n"
+ " for (s.x = 0; s.x < 127; s.x++) {}\n"
+ "}";
+ values = tokenValues(code, "<"); // TODO: comparison can be true or false
+ ASSERT_EQUALS(true, values.empty());
+ }
+
+ void valueFlowSwitchVariable() {
+ const char *code;
+ code = "void f(int x) {\n"
+ " a = x - 1;\n" // <- x can be 14
+ " switch (x) {\n"
+ " case 14: a=x+2; break;\n" // <- x is 14
+ " };\n"
+ " a = x;\n" // <- x can be 14
+ "}";
+ ASSERT_EQUALS(true, testConditionalValueOfX(code, 2U, 14));
+ TODO_ASSERT_EQUALS(true, false, testConditionalValueOfX(code, 4U, 14));
+ TODO_ASSERT_EQUALS(true, false, testConditionalValueOfX(code, 6U, 14));
+
+ ValueFlow::Value value1 = valueOfTok(code, "-");
+ ASSERT_EQUALS(13, value1.intvalue);
+ ASSERT(!value1.isKnown());
+
+ ValueFlow::Value value2 = valueOfTok(code, "+");
+ TODO_ASSERT_EQUALS(16, 0, value2.intvalue);
+ TODO_ASSERT_EQUALS(true, false, value2.isKnown());
+ }
+
+ void valueFlowForLoop() {
+ const char *code;
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 10; x++)\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (x = 2; x < 1; x++)\n"
+ " a[x] = 0;\n" // <- not 2
+ " b = x;\n" // 2
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 2));
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 2));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (x = 2; x < 1; ++x)\n"
+ " a[x] = 0;\n" // <- not 2
+ " b = x;\n" // 2
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 2));
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 2));
+
+ code = "enum AB {A,B};\n" // enum => handled by valueForLoop2
+ "void f() {\n"
+ " int x;\n"
+ " for (x = 1; x < B; ++x)\n"
+ " a[x] = 0;\n" // <- not 1
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 1));
+
+ code = "void f(int a) {\n"
+ " for (int x = a; x < 10; x++)\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 5; x += 2)\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 4));
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 10; x = x + 2)\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 8));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 10));
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 10; x = x / 0)\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0)); // don't crash
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 10; x++)\n"
+ " x<4 ?\n"
+ " a[x] : 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 9));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 9));
+
+ code = "void f() {\n"
+ " for (int x = 0; x < 10; x++)\n"
+ " x==0 ?\n"
+ " 0 : a[x];\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n" // #5223
+ " for (int x = 0; x < 300 && x < 18; x++)\n"
+ " x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 17));
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 299));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (int i = 0; x = bar[i]; i++)\n"
+ " x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " const char abc[] = \"abc\";\n"
+ " int x;\n"
+ " for (x = 0; abc[x] != '\\0'; x++) {}\n"
+ " a[x] = 0;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 3));
+
+ code = "void f() {\n" // #5939
+ " int x;\n"
+ " for (int x = 0; (x = do_something()) != 0;)\n"
+ " x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (int x = 0; x < 10 && y = do_something();)\n"
+ " x;\n"
+ "}";
+ TODO_ASSERT_EQUALS(true, false, testValueOfX(code, 4U, 0));
+
+ code = "void f() {\n"
+ " int x,y;\n"
+ " for (x = 0, y = 0; x < 10, y < 10; x++, y++)\n" // usage of ,
+ " x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 0));
+
+ code = "void foo(double recoveredX) {\n"
+ " for (double x = 1e-18; x < 1e40; x *= 1.9) {\n"
+ " double relativeError = (x - recoveredX) / x;\n"
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 0));
+
+ // Ticket #7139
+ // "<<" in third expression of for
+ code = "void f(void) {\n"
+ " int bit, x;\n"
+ " for (bit = 1, x = 0; bit < 128; bit = bit << 1, x++) {\n"
+ " z = x;\n" // <- known value [0..6]
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 6));
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 7));
+
+ // &&
+ code = "void foo() {\n"
+ " for (int x = 0; x < 10; x++) {\n"
+ " if (x > 1\n"
+ " && x) {}" // <- x is not 0
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 9));
+
+ code = "void foo() {\n"
+ " for (int x = 0; x < 10; x++) {\n"
+ " if (x < value\n"
+ " && x) {}" // <- maybe x is not 9
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 9));
+
+ // ||
+ code = "void foo() {\n"
+ " for (int x = 0; x < 10; x++) {\n"
+ " if (x == 0\n"
+ " || x) {}" // <- x is not 0
+ " }\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 9));
+
+ // After loop
+ code = "void foo() {\n"
+ " int x;\n"
+ " for (x = 0; x < 10; x++) {}\n"
+ " a = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 10));
+
+ code = "void foo() {\n"
+ " int x;\n"
+ " for (x = 0; 2 * x < 20; x++) {}\n"
+ " a = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 10));
+
+ code = "void foo() {\n" // related with #887
+ " int x;\n"
+ " for (x = 0; x < 20; x++) {}\n"
+ " a = x++;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 20));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (x = 0; x < 5; x++) {}\n"
+ " if (x == 5) {\n"
+ " abort();\n"
+ " }\n"
+ " a = x;\n" // <- x can't be 5
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 7U, 5));
+
+ code = "void f() {\n"
+ " int x;\n"
+ " for (x = 0; x < 5; x++) {}\n"
+ " if (x < 5) {}\n"
+ " else return;\n"
+ " a = x;\n" // <- x can't be 5
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 5));
+
+ // assert after for loop..
+ code = "static void f() {\n"
+ " int x;\n"
+ " int ctls[10];\n"
+ " for (x = 0; x <= 10; x++) {\n"
+ " if (cond)\n"
+ " break;\n"
+ " }\n"
+ " assert(x <= 10);\n"
+ " ctls[x] = 123;\n" // <- x can't be 11
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 9U, 11));
+
+ // hang
+ code = "void f() {\n"
+ " for(int i = 0; i < 20; i++)\n"
+ " n = (int)(i < 10 || abs(negWander) < abs(negTravel));\n"
+ "}";
+ testValueOfX(code,0,0); // <- don't hang
+
+ // conditional code in loop
+ code = "void f(int mask) {\n" // #6000
+ " for (int x = 10; x < 14; x++) {\n"
+ " int bit = mask & (1 << i);\n"
+ " if (bit) {\n"
+ " if (bit == (1 << 10)) {}\n"
+ " else { a = x; }\n" // <- x is not 10
+ " }\n"
+ " }\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 6U, 10));
+
+ // #7886 - valueFlowForLoop must be called after valueFlowAfterAssign
+ code = "void f() {\n"
+ " int sz = 4;\n"
+ " int x,y;\n"
+ " for(x=0,y=0; x < sz && y < 10; x++)\n"
+ " a = x;\n" // <- max value is 3
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 3));
+ }
+
+ void valueFlowSubFunction() {
+ const char *code;
+
+ code = "int f(int size) {\n"
+ " int x = 0;\n"
+ " if(size>16) {\n"
+ " x = size;\n"
+ " int a = x;\n"
+ " }\n"
+ " return x;\n"
+ "}\n"
+ "void g(){\n"
+ " f(42);\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 17));
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 42));
+ ASSERT_EQUALS(true, testValueOfX(code, 7U, 0));
+ ASSERT_EQUALS(true, testValueOfX(code, 7U, 17));
+ ASSERT_EQUALS(true, testValueOfX(code, 7U, 42));
+ }
+ void valueFlowFunctionReturn() {
+ const char *code;
+
+ code = "int f1(int x) {\n"
+ " return x+1;\n"
+ "}\n"
+ "void f2() {\n"
+ " x = 10 - f1(2);\n"
+ "}";
+ ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue);
+ ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown());
+
+ code = "int add(int x, int y) {\n"
+ " return x+y;\n"
+ "}\n"
+ "void f2() {\n"
+ " x = 1 * add(10+1,4);\n"
+ "}";
+ ASSERT_EQUALS(15, valueOfTok(code, "*").intvalue);
+ ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
+
+ code = "int one() { return 1; }\n"
+ "void f() { x = 1 * one(); }";
+ ASSERT_EQUALS(1, valueOfTok(code, "*").intvalue);
+ ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
+
+ code = "int add(int x, int y) {\n"
+ " return x+y;\n"
+ "}\n"
+ "void f2() {\n"
+ " x = 1 * add(1,add(2,3));\n"
+ "}";
+ ASSERT_EQUALS(6, valueOfTok(code, "*").intvalue);
+ ASSERT_EQUALS(true, valueOfTok(code, "*").isKnown());
+
+ code = "int f(int i, X x) {\n"
+ " if (i)\n"
+ " return g(std::move(x));\n"
+ " g(x);\n"
+ " return 0;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 4U, ValueFlow::Value::MoveKind::MovedVariable));
+
+ code = "class A\n"
+ "{\n"
+ " int f1(int x) {\n"
+ " return x+1;\n"
+ " }\n"
+ " void f2() {\n"
+ " x = 10 - f1(2);\n"
+ " }\n"
+ "};";
+ ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue);
+ ASSERT_EQUALS(true, valueOfTok(code, "-").isKnown());
+
+ code = "class A\n"
+ "{\n"
+ " virtual int f1(int x) {\n"
+ " return x+1;\n"
+ " }\n"
+ " void f2() {\n"
+ " x = 10 - f1(2);\n"
+ " }\n"
+ "};";
+ ASSERT_EQUALS(7, valueOfTok(code, "-").intvalue);
+ ASSERT_EQUALS(false, valueOfTok(code, "-").isKnown());
+ }
+
+ void valueFlowFunctionDefaultParameter() {
+ const char *code;
+
+ code = "class continuous_src_time {\n"
+ " continuous_src_time(std::complex f, double st = 0.0, double et = infinity) {}\n"
+ "};";
+ testValueOfX(code, 2U, 2); // Don't crash (#6494)
+ }
+
+ bool isNotKnownValues(const char code[], const char str[]) {
+ for (const ValueFlow::Value &v : tokenValues(code, str)) {
+ if (v.isKnown())
+ return false;
+ }
+ return true;
+ }
+
+ void knownValue() {
+ const char *code;
+ ValueFlow::Value value;
+
+ ASSERT(valueOfTok("x = 1;", "1").isKnown());
+
+ // after assignment
+ code = "void f() {\n"
+ " int x = 1;\n"
+ " return x + 2;\n" // <- known value
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(3, value.intvalue);
+ ASSERT(value.isKnown());
+
+ {
+ code = "void f() {\n"
+ " int x = 15;\n"
+ " if (x == 15) { x += 7; }\n" // <- condition is true
+ "}";
+ value = valueOfTok(code, "==");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "int f() {\n"
+ " int a = 0, x = 0;\n"
+ " a = index();\n"
+ " if (a != 0)\n"
+ " x = next();\n"
+ " return x + 1;\n"
+ "}\n";
+ value = valueOfTok(code, "+");
+ ASSERT(value.isPossible());
+ }
+
+ code = "void f() {\n"
+ " int x;\n"
+ " if (ab) { x = 7; }\n"
+ " return x + 2;\n" // <- possible value
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(9, value.intvalue);
+ ASSERT(value.isPossible());
+
+ code = "void f(int c) {\n"
+ " int x = 0;\n"
+ " if (c) {} else { x++; }\n"
+ " return x + 2;\n" // <- possible value
+ "}";
+ ASSERT(isNotKnownValues(code, "+"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(&x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(0 ? ptr : &x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(unknown ? ptr : &x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " fred.dostuff(x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void dostuff(int x);\n"
+ "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ value = valueOfTok(code, "<");
+ ASSERT_EQUALS(0, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "void dostuff(int & x);\n"
+ "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void dostuff(const int & x);\n"
+ "void f() {\n"
+ " int x = 0;\n"
+ " dostuff(x);\n"
+ " if (x < 0) {}\n"
+ "}\n";
+ value = valueOfTok(code, "<");
+ ASSERT_EQUALS(0, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " do {\n"
+ " if (x < 0) {}\n"
+ " fred.dostuff(x);\n"
+ " } while (abc);\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "int x;\n"
+ "void f() {\n"
+ " x = 4;\n"
+ " while (1) {\n"
+ " a = x+2;\n"
+ " dostuff();\n"
+ " }\n"
+ "}";
+ ASSERT(isNotKnownValues(code, "+"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " if (y) { dostuff(x); }\n"
+ " if (!x) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "!"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " MACRO( v, { if (y) { x++; } } );\n"
+ " if (!x) {}\n"
+ "}\n";
+ ASSERT(isNotKnownValues(code, "!"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " for (int i = 0; i < 10; i++) {\n"
+ " if (cond) {\n"
+ " x = 1;\n"
+ " break;\n"
+ " }\n"
+ " }\n"
+ " if (!x) {}\n" // <- possible value
+ "}";
+ ASSERT(isNotKnownValues(code, "!"));
+
+ code = "void f() {\n" // #8356
+ " bool b = false;\n"
+ " for(int x = 3; !b && x < 10; x++) {\n" // <- b has known value
+ " for(int y = 4; !b && y < 20; y++) {}\n"
+ " }\n"
+ "}";
+ value = valueOfTok(code, "!");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " switch (state) {\n"
+ " case 1:\n"
+ " x = 1;\n"
+ " break;\n"
+ " }\n"
+ " if (!x) {}\n" // <- possible value
+ "}";
+ ASSERT(isNotKnownValues(code, "!"));
+
+ code = "void f() {\n" // #7049
+ " int x = 0;\n"
+ " switch (a) {\n"
+ " case 1:\n"
+ " x = 1;\n"
+ " case 2:\n"
+ " if (!x) {}\n" // <- possible value
+ " }\n"
+ "}";
+ ASSERT(isNotKnownValues(code, "!"));
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " while (!x) {\n" // <- possible value
+ " scanf(\"%d\", &x);\n"
+ " }\n"
+ "}";
+ value = valueOfTok(code, "!");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isPossible());
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ " do { } while (++x < 12);\n" // <- possible value
+ "}";
+ ASSERT(isNotKnownValues(code, "<"));
+
+ code = "void f() {\n"
+ " static int x = 0;\n"
+ " return x + 1;\n" // <- known value
+ "}\n";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "void f() {\n"
+ " int x = 0;\n"
+ "a:\n"
+ " a = x + 1;\n" // <- possible value
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isPossible());
+
+ // in conditional code
+ code = "void f(int x) {\n"
+ " if (!x) {\n"
+ " a = x+1;\n" // <- known value
+ " }\n"
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "void f(int x) {\n"
+ " if (a && 4==x && y) {\n"
+ " a = x+12;\n" // <- known value
+ " }\n"
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(16, value.intvalue);
+ ASSERT(value.isKnown());
+
+ // after condition
+ code = "int f(int x) {\n"
+ " if (x == 4) {}\n"
+ " return x + 1;\n" // <- possible value
+ "}";
+ value = valueOfTok(code, "+");
+ ASSERT_EQUALS(5, value.intvalue);
+ ASSERT(value.isPossible());
+
+ code = "int f(int x) {\n"
+ " if (x < 2) {}\n"
+ " else if (x >= 2) {}\n" // <- known value
+ "}";
+ value = valueOfTok(code, ">=");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT(value.isKnown());
+
+ code = "int f(int x) {\n"
+ " if (x < 2) {}\n"
+ " else if (x > 2) {}\n" // <- possible value
+ "}";
+ ASSERT(isNotKnownValues(code, ">"));
+
+ // known and possible value
+ code = "void f() {\n"
+ " int x = 1;\n"
+ " int y = 2 + x;\n" // <- known value, don't care about condition
+ " if (x == 2) {}\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1)); // value of x can be 1
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 2)); // value of x can't be 2
+
+ // calculation with known result
+ code = "int f(int x) { a = x & 0; }"; // <- & is 0
+ value = valueOfTok(code, "&");
+ ASSERT_EQUALS(0, value.intvalue);
+ ASSERT(value.isKnown());
+
+ // template parameters are not known
+ code = "template void f() { a = X; }\n"
+ "f<1>();";
+ value = valueOfTok(code, "1");
+ ASSERT_EQUALS(1, value.intvalue);
+ ASSERT_EQUALS(false, value.isKnown());
+ }
+
+ void valueFlowSizeofForwardDeclaredEnum() {
+ const char *code = "enum E; sz=sizeof(E);";
+ valueOfTok(code, "="); // Don't crash (#7775)
+ }
+
+ void valueFlowGlobalVar() {
+ const char *code;
+
+ code = "int x;\n"
+ "void f() {\n"
+ " x = 4;\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 4));
+
+ code = "int x;\n"
+ "void f() {\n"
+ " if (x == 4) {}\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 4));
+
+ code = "int x;\n"
+ "void f() {\n"
+ " x = 42;\n"
+ " unknownFunction();\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 42));
+ }
+
+ void valueFlowGlobalConstVar() {
+ const char* code;
+
+ code = "const int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 321));
+
+ code = "void f(const int x = 1) {\n"
+ " int a = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 2U, 1));
+
+ code = "volatile const int x = 42;\n"
+ "void f(){ int a = x; }\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 2U, 42));
+
+ code = "static const int x = 42;\n"
+ "void f(){ int a = x; }\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 2U, 42));
+ }
+
+ void valueFlowGlobalStaticVar() {
+ const char *code;
+
+ code = "static int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 321));
+
+ code = "static int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}"
+ "void other() { x=a; }\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 321));
+
+ code = "static int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}"
+ "void other() { p = &x; }\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 321));
+
+ code = "static int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}"
+ "void other() { x++; }\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 321));
+
+ code = "static int x = 321;\n"
+ "void f() {\n"
+ " a = x;\n"
+ "}"
+ "void other() { foo(x); }\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 321));
+
+ code = "static int x = 1;\n" // compound assignment
+ "void f() {\n"
+ " a = x;\n"
+ "}"
+ "void other() { x += b; }\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1));
+ }
+
+ void valueFlowInlineAssembly() {
+ const char* code = "void f() {\n"
+ " int x = 42;\n"
+ " asm(\"\");\n"
+ " a = x;\n"
+ "}";
+ ASSERT_EQUALS(false, testValueOfX(code, 5U, 42));
+ }
+
+ void valueFlowSameExpression() {
+ const char* code;
+
+ code = "void f(int a) {\n"
+ " bool x = a == a;\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 1));
+
+ code = "void f(int a) {\n"
+ " bool x = a != a;\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(int a) {\n"
+ " int x = a - a;\n"
+ " int b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 0));
+
+ code = "void f(float a) {\n"
+ " bool x = a == a;\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfX(code, 3U, 1));
+ }
+
+ void valueFlowUninit() {
+ const char* code;
+ std::list values;
+
+ code = "void f() {\n"
+ " int x;\n"
+ " switch (x) {}\n"
+ "}";
+ values = tokenValues(code, "x )");
+ ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue());
+
+ code = "void f() {\n"
+ " const C *c;\n"
+ " if (c->x() == 4) {}\n"
+ "}";
+ values = tokenValues(code, "c .");
+ ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue());
+
+ code = "void f() {\n"
+ " C *c;\n"
+ " if (c->x() == 4) {}\n"
+ "}";
+ values = tokenValues(code, "c .");
+ ASSERT_EQUALS(true, values.size()==1U && values.front().isUninitValue());
+
+ code = "void f() {\n"
+ " int **x;\n"
+ " y += 10;\n"
+ " x = dostuff(sizeof(*x)*y);\n"
+ "}";
+ ASSERT_EQUALS(0U, tokenValues(code, "x )").size());
+
+ // #8036
+ code = "void foo() {\n"
+ " int x;\n"
+ " f(x=3), return x+3;\n"
+ "}";
+ ASSERT_EQUALS(0U, tokenValues(code, "x +").size());
+
+ // #8195
+ code = "void foo(std::istream &is) {\n"
+ " int x;\n"
+ " if (is >> x) {\n"
+ " a = x;\n"
+ " }\n"
+ "}";
+ values = tokenValues(code, "x ; }");
+ ASSERT_EQUALS(true, values.empty());
+
+ // return (#8173)
+ code = "int repeat() {\n"
+ " const char *n;\n"
+ " return((n=42) && *n == 'A');\n"
+ "}";
+ values = tokenValues(code, "n ==");
+ ASSERT_EQUALS(true, values.size() != 1U || !values.front().isUninitValue());
+
+ // #8233
+ code = "void foo() {\n"
+ " int x;\n"
+ " int y = 1;\n"
+ " if (y>1)\n"
+ " x = 1;\n"
+ " else\n"
+ " x = 1;\n"
+ " if (x>1) {}\n"
+ "}";
+ values = tokenValues(code, "x >");
+ ASSERT_EQUALS(true, values.size() == 1U && values.front().isIntValue());
+
+ // #8348 - noreturn else
+ code = "int test_input_int(int a, int b) {\n"
+ " int x;\n"
+ " if (a == 1)\n"
+ " x = b;\n"
+ " else\n"
+ " abort();\n"
+ " a = x + 1;\n"
+ "}\n";
+ values = tokenValues(code, "x +");
+ ASSERT_EQUALS(true, values.empty());
+
+ // #8494 - overloaded operator &
+ code = "void f() {\n"
+ " int x;\n"
+ " a & x;\n"
+ "}";
+ values = tokenValues(code, "x ; }");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void b(bool d, bool e) {\n"
+ " int c;\n"
+ " if (d)\n"
+ " c = 0;\n"
+ " if (e)\n"
+ " goto;\n"
+ " c++;\n"
+ "}\n";
+ values = tokenValues(code, "c ++ ; }");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void b(bool d, bool e) {\n"
+ " int c;\n"
+ " if (d)\n"
+ " c = 0;\n"
+ " if (e)\n"
+ " return;\n"
+ " c++;\n"
+ "}\n";
+ values = tokenValues(code, "c ++ ; }");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void b(bool d, bool e) {\n"
+ " int c;\n"
+ " if (d)\n"
+ " c = 0;\n"
+ " if (e)\n"
+ " exit();\n"
+ " c++;\n"
+ "}\n";
+ values = tokenValues(code, "c ++ ; }");
+ ASSERT_EQUALS(true, values.empty());
+
+ code = "void b(bool d, bool e) {\n"
+ " int c;\n"
+ " if (d)\n"
+ " c = 0;\n"
+ " else if (e)\n"
+ " c = 0;\n"
+ " c++;\n"
+ "}\n";
+ values = tokenValues(code, "c ++ ; }");
+ ASSERT_EQUALS(true, values.size() == 2);
+ ASSERT_EQUALS(true, values.front().isUninitValue() || values.back().isUninitValue());
+ ASSERT_EQUALS(true, values.front().isPossible() || values.back().isPossible());
+ ASSERT_EQUALS(true, values.front().intvalue == 0 || values.back().intvalue == 0);
+
+ code = "void b(bool d, bool e) {\n"
+ " int c;\n"
+ " if (d)\n"
+ " c = 0;\n"
+ " else if (!d)\n"
+ " c = 0;\n"
+ " c++;\n"
+ "}\n";
+ values = tokenValues(code, "c ++ ; }");
+ ASSERT_EQUALS(true, values.size() == 1);
+ // TODO: Value should be known
+ ASSERT_EQUALS(true, values.back().isPossible());
+ ASSERT_EQUALS(true, values.back().intvalue == 0);
+
+ code = "void f() {\n" // sqlite
+ " int szHdr;\n"
+ " idx = (A<0x80) ? (szHdr = 0) : dostuff(A, (int *)&(szHdr));\n"
+ " d = szHdr;\n" // szHdr can be 0.
+ "}";
+ values = tokenValues(code, "szHdr ; }");
+ TODO_ASSERT_EQUALS(1, 0, values.size());
+ if (values.size() == 1) {
+ ASSERT_EQUALS(false, values.front().isUninitValue());
+ }
+
+ code = "void f () {\n"
+ " int szHdr;\n"
+ " idx = ((aKey<0x80) ? ((szHdr)=aKey), 1 : sqlite3GetVarint32(&(szHdr)));\n"
+ " d = szHdr;\n"
+ "}";
+ values = tokenValues(code, "szHdr ; }");
+ ASSERT_EQUALS(0, values.size());
+ }
+
+ void valueFlowTerminatingCond() {
+ const char* code;
+
+ // opposite condition
+ code = "void f(int i, int j) {\n"
+ " if (i == j) return;\n"
+ " if(i != j) {}\n"
+ "}\n";
+ ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1);
+
+ code = "void f(int i, int j) {\n"
+ " if (i == j) return;\n"
+ " i++;\n"
+ " if (i != j) {}\n"
+ "}\n";
+ ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1);
+
+ code = "void f(int i, int j, bool a) {\n"
+ " if (a) {\n"
+ " if (i == j) return;\n"
+ " }\n"
+ " if (i != j) {}\n"
+ "}\n";
+ ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1);
+
+ code = "void f(int i, int j, bool a) {\n"
+ " if (i != j) {}\n"
+ " if (i == j) return; \n"
+ "}\n";
+ ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1);
+
+ // same expression
+ code = "void f(int i, int j) {\n"
+ " if (i != j) return;\n"
+ " bool x = (i != j);\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0));
+
+ code = "void f(int i, int j) {\n"
+ " if (i != j) return;\n"
+ " i++;\n"
+ " bool x = (i != j);\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 0));
+
+ code = "void f(int i, int j, bool a) {\n"
+ " if (a) {\n"
+ " if (i != j) return;\n"
+ " }\n"
+ " bool x = (i != j);\n"
+ " bool b = x;\n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 6U, 0));
+
+ code = "void f(int i, int j, bool a) {\n"
+ " bool x = (i != j);\n"
+ " bool b = x;\n"
+ " if (i != j) return; \n"
+ "}\n";
+ ASSERT_EQUALS(false, testValueOfXKnown(code, 3U, 0));
+
+ code = "void f(int i, int j, bool b) {\n"
+ " if (i == j) { if(b) return; }\n"
+ " if(i != j) {}\n"
+ "}\n";
+ ASSERT_EQUALS(false, valueOfTok(code, "!=").intvalue == 1);
+
+ code = "void foo()\n" // #8924
+ "{\n"
+ " if ( this->FileIndex >= 0 )\n"
+ " return;\n"
+ "\n"
+ " this->FileIndex = 1 ;\n"
+ " if ( this->FileIndex < 0 ) {}\n"
+ "}";
+ ASSERT_EQUALS(false, valueOfTok(code, "<").intvalue == 1);
+ }
+
+ static std::string isPossibleContainerSizeValue(const std::list &values, MathLib::bigint i) {
+ if (values.size() != 1)
+ return "values.size():" + std::to_string(values.size());
+ if (!values.front().isContainerSizeValue())
+ return "ContainerSizeValue";
+ if (!values.front().isPossible())
+ return "Possible";
+ if (values.front().intvalue != i)
+ return "intvalue:" + std::to_string(values.front().intvalue);
+ return "";
+ }
+
+ static std::string isImpossibleContainerSizeValue(const std::list& values, MathLib::bigint i) {
+ if (values.size() != 1)
+ return "values.size():" + std::to_string(values.size());
+ if (!values.front().isContainerSizeValue())
+ return "ContainerSizeValue";
+ if (!values.front().isImpossible())
+ return "Impossible";
+ if (values.front().intvalue != i)
+ return "intvalue:" + std::to_string(values.front().intvalue);
+ return "";
+ }
+
+ static std::string isKnownContainerSizeValue(const std::list &values, MathLib::bigint i) {
+ if (values.size() != 1)
+ return "values.size():" + std::to_string(values.size());
+ if (!values.front().isContainerSizeValue())
+ return "ContainerSizeValue";
+ if (!values.front().isKnown())
+ return "Known";
+ if (values.front().intvalue != i)
+ return "intvalue:" + std::to_string(values.front().intvalue);
+ return "";
+ }
+
+ void valueFlowContainerSize() {
+ const char *code;
+
+ // condition
+ code = "void f(const std::list &ints) {\n"
+ " if (!static_cast(ints.empty()))\n"
+ " ints.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "ints . front").empty());
+
+ // valueFlowContainerReverse
+ code = "void f(const std::list &ints) {\n"
+ " ints.front();\n" // <- container can be empty
+ " if (ints.empty()) {}\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f(const std::list &ints) {\n"
+ " ints.front();\n" // <- container can be empty
+ " if (ints.size()==0) {}\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f(std::list ints) {\n"
+ " ints.front();\n" // <- no container size
+ " ints.pop_back();\n"
+ " if (ints.empty()) {}\n"
+ "}";
+ ASSERT(tokenValues(code, "ints . front").empty());
+
+ code = "void f(std::vector v) {\n"
+ " v[10] = 0;\n" // <- container size can be 10
+ " if (v.size() == 10) {}\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "v ["), 10));
+
+ code = "void f(std::vector params) {\n"
+ " switch(x) {\n"
+ " case CMD_RESPONSE:\n"
+ " if(y) { break; }\n"
+ " params[2];\n" // <- container use
+ " break;\n"
+ " case CMD_DELETE:\n"
+ " if (params.size() < 2) { }\n" // <- condition
+ " break;\n"
+ " }\n"
+ "}";
+ ASSERT(tokenValues(code, "params [ 2 ]").empty());
+
+ // valueFlowContainerForward
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.empty()) {}\n"
+ " ints.front();\n" // <- container can be empty
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.empty()) { continue; }\n"
+ " ints.front();\n" // <- no container size
+ "}";
+ ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.empty()) { ints.push_back(0); }\n"
+ " ints.front();\n" // <- container is not empty
+ "}";
+ ASSERT(tokenValues(code, "ints . front").empty());
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.empty()) {\n"
+ " ints.front();\n" // <- container is empty
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.size() == 3) {\n"
+ " ints.front();\n" // <- container size is 3
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 3));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.size() <= 3) {\n"
+ " ints.front();\n" // <- container size is 3
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.size() >= 3) {\n"
+ " ints.front();\n" // <- container size is 3
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 3));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.size() < 3) {\n"
+ " ints.front();\n" // <- container size is 2
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 2));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.size() > 3) {\n"
+ " ints.front();\n" // <- container size is 4
+ " }\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "ints . front"), 4));
+
+ code = "void f(const std::list &ints) {\n"
+ " if (ints.empty() == false) {\n"
+ " ints.front();\n" // <- container is not empty
+ " }\n"
+ "}";
+ ASSERT(tokenValues(code, "ints . front").empty());
+
+ code = "void f(const std::vector &v) {\n"
+ " if (v.empty()) {}\n"
+ " if (!v.empty() && v[10]==0) {}\n" // <- no container size for 'v[10]'
+ "}";
+ ASSERT(tokenValues(code, "v [").empty());
+
+ code = "void f() {\n"
+ " std::list ints;\n" // No value => ints is empty
+ " ints.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 0));
+
+ code = "void f() {\n"
+ " std::array ints;\n" // Array size is 10
+ " ints.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "ints . front"), 10));
+
+ code = "void f() {\n"
+ " std::string s;\n"
+ " cin >> s;\n"
+ " s[0];\n"
+ "}";
+ ASSERT(tokenValues(code, "s [").empty());
+
+ code = "void f() {\n"
+ " std::string s = \"abc\";\n" // size of s is 3
+ " s.size();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 3));
+
+ code = "void f() {\n"
+ " std::string s=\"abc\";\n" // size of s is 3
+ " s += unknown;\n"
+ " s.size();\n"
+ "}";
+ ASSERT(tokenValues(code, "s . size").empty());
+
+ code = "void f() {\n"
+ " std::string s=\"abc\";\n" // size of s is 3
+ " s += \"def\";\n" // size of s => 6
+ " s.size();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s . size"), 6));
+
+ code = "void f(std::string s) {\n"
+ " if (s == \"hello\")\n"
+ " s[40] = c;\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "s ["), 5));
+
+ code = "void f(std::string s) {\n"
+ " s[40] = c;\n"
+ " if (s == \"hello\") {}\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5));
+
+ code = "void f(std::string s) {\n"
+ " if (s != \"hello\") {}\n"
+ " s[40] = c;\n"
+ "}";
+ ASSERT_EQUALS("", isPossibleContainerSizeValue(tokenValues(code, "s ["), 5));
+
+ code = "void f(std::string s) {\n"
+ " if (s != \"hello\")\n"
+ " s[40] = c;\n"
+ "}";
+ ASSERT_EQUALS("", isImpossibleContainerSizeValue(tokenValues(code, "s ["), 5));
+
+ // valueFlowContainerForward, loop
+ code = "void f() {\n"
+ " std::stack links;\n"
+ " while (!links.empty() || indentlevel)\n"
+ " links.push(tok);\n"
+ "}";
+ ASSERT(tokenValues(code, "links . empty").empty());
+
+ // valueFlowContainerForward, function call
+ code = "void f() {\n"
+ " std::list x;\n"
+ " f(x);\n"
+ " x.front();\n" // <- unknown container size
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void f() {\n" // #8689
+ " std::list x;\n"
+ " f(x);\n"
+ " x.front();\n" // <- unknown container size
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void g(std::list&);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void g(std::list*);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(&x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void g(std::list* const);\n" // #9434
+ "void f() {\n"
+ " std::list x;\n"
+ " g(&x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void g(const std::list&);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(std::list);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(int&);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x[0]);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(int&);\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x.back());\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(std::list&) {}\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(std::list& y) { y.push_back(1); }\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void g(std::list*) {}\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(&x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void g(std::list* y) { y->push_back(1); }\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(&x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void h(std::list&);\n"
+ "void g(std::list& y) { h(y); }\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void h(const std::list&);\n"
+ "void g(std::list& y) { h(y); }\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "x . front"), 0));
+
+ code = "void h(const std::list&);\n"
+ "void g(std::list& y) { h(y); y.push_back(1); }\n"
+ "void f() {\n"
+ " std::list x;\n"
+ " g(x);\n"
+ " x.front();\n"
+ "}";
+ ASSERT(tokenValues(code, "x . front").empty());
+
+ code = "void f(std::vector ints) {\n" // #8697
+ " if (ints.empty())\n"
+ " abort() << 123;\n"
+ " ints[0] = 0;\n"
+ "}";
+ ASSERT(tokenValues(code, "ints [").empty());
+
+ code = "struct A {\n" // forward, nested function call, #9424
+ " double getMessage( std::vector *message );\n"
+ "};\n"
+ "\n"
+ "struct B {\n"
+ " A *a;\n"
+ " double getMessage( std::vector *message ) { return a->getMessage( message ); }\n"
+ "};\n"
+ "\n"
+ "void foo(B *ptr) {\n"
+ " std::vector v;\n"
+ " ptr->getMessage (&v);\n"
+ " if (v.size () > 0) {}\n" // <- v has unknown size!
+ "}";
+ ASSERT_EQUALS(0U, tokenValues(code, "v . size ( )").size());
+
+ // if
+ code = "bool f(std::vector&) {\n" // #9532
+ " return false;\n"
+ "}\n"
+ "int g() {\n"
+ " std::vector v;\n"
+ " if (f(v) || v.empty())\n"
+ " return 0;\n"
+ " return v[0];\n"
+ "}\n";
+ ASSERT_EQUALS(0U, tokenValues(code, "v [ 0 ]").size());
+
+ // container size => yields
+ code = "void f() {\n"
+ " std::string s = \"abcd\";\n"
+ " s.size();\n"
+ "}";
+ ASSERT_EQUALS(4, tokenValues(code, "( ) ;").front().intvalue);
+
+ code = "void f() {\n"
+ " std::string s;\n"
+ " s.empty();\n"
+ "}";
+ ASSERT_EQUALS(1, tokenValues(code, "( ) ;").front().intvalue);
+
+ // Calculations
+ code = "void f() {\n"
+ " std::string s = \"abcd\";\n"
+ " x = s + s;\n"
+ "}";
+ ASSERT_EQUALS("", isKnownContainerSizeValue(tokenValues(code, "+"), 8));
+ }
+
+ void valueFlowDynamicBufferSize() {
+ const char *code;
+
+ const Settings settingsOld = settings;
+ settings = settingsBuilder(settings).library("posix.cfg").library("bsd.cfg").build();
+
+ code = "void* f() {\n"
+ " void* x = malloc(10);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 10, ValueFlow::Value::ValueType::BUFFER_SIZE));
+
+ code = "void* f() {\n"
+ " void* x = calloc(4, 5);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 3U, 20, ValueFlow::Value::ValueType::BUFFER_SIZE));
+
+ code = "void* f() {\n"
+ " const char* y = \"abcd\";\n"
+ " const char* x = strdup(y);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 5, ValueFlow::Value::ValueType::BUFFER_SIZE));
+
+ code = "void* f() {\n"
+ " void* y = malloc(10);\n"
+ " void* x = realloc(y, 20);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 20, ValueFlow::Value::ValueType::BUFFER_SIZE));
+
+ code = "void* f() {\n"
+ " void* y = calloc(10, 4);\n"
+ " void* x = reallocarray(y, 20, 5);\n"
+ " return x;\n"
+ "}";
+ ASSERT_EQUALS(true, testValueOfX(code, 4U, 100, ValueFlow::Value::ValueType::BUFFER_SIZE));
+
+ settings = settingsOld;
+ }
+
+ void valueFlowSafeFunctionParameterValues() {
+ const char *code;
+ std::list values;
+ Settings s = settings;
+ s.safeChecks.classes = s.safeChecks.externalFunctions = s.safeChecks.internalFunctions = true;
+
+ code = "short f(short x) {\n"
+ " return x + 0;\n"
+ "}";
+ values = tokenValues(code, "+", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT_EQUALS(-0x8000, values.front().intvalue);
+ ASSERT_EQUALS(0x7fff, values.back().intvalue);
+
+ code = "short f(std::string x) {\n"
+ " return x[10];\n"
+ "}";
+ values = tokenValues(code, "x [", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT_EQUALS(0, values.front().intvalue);
+ ASSERT_EQUALS(1000000, values.back().intvalue);
+
+ code = "int f(float x) {\n"
+ " return x;\n"
+ "}";
+ values = tokenValues(code, "x ;", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT(values.front().floatValue < -1E20);
+ ASSERT(values.back().floatValue > 1E20);
+
+ code = "short f(__cppcheck_low__(0) __cppcheck_high__(100) short x) {\n"
+ " return x + 0;\n"
+ "}";
+ values = tokenValues(code, "+", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT_EQUALS(0, values.front().intvalue);
+ ASSERT_EQUALS(100, values.back().intvalue);
+
+ code = "unsigned short f(unsigned short x) [[expects: x <= 100]] {\n"
+ " return x + 0;\n"
+ "}";
+ values = tokenValues(code, "+", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT_EQUALS(0, values.front().intvalue);
+ ASSERT_EQUALS(100, values.back().intvalue);
+ }
+
+
+ void valueFlowUnknownFunctionReturn() {
+ const char *code;
+ std::list values;
+ Settings s = settings;
+ s.checkUnknownFunctionReturn.insert("rand");
+
+ code = "x = rand();";
+ values = tokenValues(code, "(", &s);
+ ASSERT_EQUALS(2, values.size());
+ ASSERT_EQUALS(INT_MIN, values.front().intvalue);
+ ASSERT_EQUALS(INT_MAX, values.back().intvalue);
+ }
+
+ void valueFlowPointerAliasDeref() {
+ const char* code;
+
+ code = "int f() {\n"
+ " int a = 123;\n"
+ " int *p = &a;\n"
+ " int x = *p;\n"
+ " return x;\n"
+ "}\n";
+ ASSERT_EQUALS(true, testValueOfX(code, 5U, 123));
+ }
+
+ void valueFlowCrashIncompleteCode() {
+ const char* code;
+
+ code = "void SlopeFloor::setAttr(const Value &val) {\n"
+ " int x = val;\n"
+ " if (x >= -1)\n"
+ " state = x;\n"
+ "}\n";
+ valueOfTok(code, "=");
+
+ code = "void a() {\n"
+ " auto b = [b = 0] {\n"
+ " if (b) {\n"
+ " }\n"
+ " };\n"
+ "}\n";
+ valueOfTok(code, "0");
+ }
+
+ void valueFlowCrash() {
+ const char* code;
+
+ code = "void f(int x) {\n"
+ " if (0 * (x > 2)) {}\n"
+ "}\n";
+ valueOfTok(code, "x");
+ }
+};
+
+REGISTER_TEST(TestValueFlowFast)
diff --git a/tools/compare-normal-exhaustive.py b/tools/compare-normal-exhaustive.py
index 7139748331f..3b44c95201d 100755
--- a/tools/compare-normal-exhaustive.py
+++ b/tools/compare-normal-exhaustive.py
@@ -36,6 +36,8 @@ def format_float(a, b=1):
parser.add_argument('--work-path', '--work-path=', default=__work_path, type=str, help='Working directory for reference repo')
args = parser.parse_args()
+ check_levels = ('normal', 'exhaustive')
+
print(args)
if not lib.check_requirements():
@@ -50,8 +52,8 @@ def format_float(a, b=1):
result_file = os.path.join(work_path, args.o)
(f, ext) = os.path.splitext(result_file)
timing_file = f + '_timing' + ext
- normal_results = f + '_normal' + ext
- exhaustive_results = f + '_exhaustive' + ext
+ normal_results = f + '_' + check_levels[0] + ext
+ exhaustive_results = f + '_' + check_levels[1] + ext
if os.path.exists(result_file):
os.remove(result_file)
@@ -131,31 +133,31 @@ def format_float(a, b=1):
debug_warnings = False
libraries = lib.library_includes.get_libraries(source_path)
- c, errout, info, time_normal, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level='normal')
+ c, errout, info, time_normal, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level=check_levels[0])
if c < 0:
if c == -101 and 'error: could not find or open any of the paths given.' in errout:
# No sourcefile found (for example only headers present)
print('Error: 101')
elif c == lib.RETURN_CODE_TIMEOUT:
- print('Normal check level timed out!')
+ print(check_levels[0] + ' check level timed out!')
normal_timeout = True
continue # we don't want to compare timeouts
else:
- print('Normal check level crashed!')
+ print(check_levels[0] + ' check level crashed!')
normal_crashed = True
results_to_diff.append(errout)
- c, errout, info, time_exhaustive, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level='exhaustive')
+ c, errout, info, time_exhaustive, cppcheck_options, timing_info = lib.scan_package(cppcheck_path, source_path, libraries, enable=enable, debug_warnings=debug_warnings, check_level=check_levels[1])
if c < 0:
if c == -101 and 'error: could not find or open any of the paths given.' in errout:
# No sourcefile found (for example only headers present)
print('Error: 101')
elif c == lib.RETURN_CODE_TIMEOUT:
- print('Exhaustive check level timed out!')
+ print(check_levels[1] + ' check level timed out!')
exhaustive_timeout = True
continue # we don't want to compare timeouts
else:
- print('Exhaustive check level crashed!')
+ print(check_levels[1] + ' check level crashed!')
exhaustive_crashed = True
results_to_diff.append(errout)
@@ -164,9 +166,9 @@ def format_float(a, b=1):
if normal_crashed and exhaustive_crashed:
who = 'Both'
elif normal_crashed:
- who = 'Normal'
+ who = check_levels[0]
else:
- who = 'Exhaustive'
+ who = check_levels[1]
crashes.append(package + ' ' + who)
if normal_timeout or exhaustive_timeout:
@@ -174,14 +176,14 @@ def format_float(a, b=1):
if normal_timeout and exhaustive_timeout:
who = 'Both'
elif normal_timeout:
- who = 'Normal'
+ who = check_levels[0]
else:
- who = 'Exhaustive'
+ who = check_levels[1]
timeouts.append(package + ' ' + who)
with open(result_file, 'a') as myfile:
myfile.write(package + '\n')
- diff = lib.diff_results('normal', results_to_diff[0], 'exhaustive', results_to_diff[1])
+ diff = lib.diff_results(check_levels[0], results_to_diff[0], check_levels[1], results_to_diff[1])
if not normal_crashed and not exhaustive_crashed and diff != '':
myfile.write('libraries:' + ','.join(libraries) +'\n')
myfile.write('diff:\n' + diff + '\n')