Add generic reverse valueflow (#2878)
This commit is contained in:
parent
79bdd64689
commit
bd7e915c20
8
Makefile
8
Makefile
|
@ -201,6 +201,7 @@ LIBOBJ = $(libcppdir)/analyzerinfo.o \
|
||||||
$(libcppdir)/platform.o \
|
$(libcppdir)/platform.o \
|
||||||
$(libcppdir)/preprocessor.o \
|
$(libcppdir)/preprocessor.o \
|
||||||
$(libcppdir)/programmemory.o \
|
$(libcppdir)/programmemory.o \
|
||||||
|
$(libcppdir)/reverseanalyzer.o \
|
||||||
$(libcppdir)/settings.o \
|
$(libcppdir)/settings.o \
|
||||||
$(libcppdir)/suppressions.o \
|
$(libcppdir)/suppressions.o \
|
||||||
$(libcppdir)/symboldatabase.o \
|
$(libcppdir)/symboldatabase.o \
|
||||||
|
@ -508,7 +509,7 @@ $(libcppdir)/errortypes.o: lib/errortypes.cpp lib/config.h lib/errortypes.h
|
||||||
$(libcppdir)/exprengine.o: lib/exprengine.cpp lib/astutils.h lib/bughuntingchecks.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.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
|
$(libcppdir)/exprengine.o: lib/exprengine.cpp lib/astutils.h lib/bughuntingchecks.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/exprengine.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.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
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/exprengine.o $(libcppdir)/exprengine.cpp
|
||||||
|
|
||||||
$(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
|
$(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/forwardanalyzer.o $(libcppdir)/forwardanalyzer.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/forwardanalyzer.o $(libcppdir)/forwardanalyzer.cpp
|
||||||
|
|
||||||
$(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson.h externals/tinyxml/tinyxml2.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
|
$(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson.h externals/tinyxml/tinyxml2.h lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h
|
||||||
|
@ -538,6 +539,9 @@ $(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.
|
||||||
$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/programmemory.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h
|
$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/programmemory.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp
|
||||||
|
|
||||||
|
$(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
|
||||||
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/reverseanalyzer.o $(libcppdir)/reverseanalyzer.cpp
|
||||||
|
|
||||||
$(libcppdir)/settings.o: lib/settings.cpp lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h lib/valueflow.h
|
$(libcppdir)/settings.o: lib/settings.cpp lib/config.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h lib/valueflow.h
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/settings.o $(libcppdir)/settings.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/settings.o $(libcppdir)/settings.cpp
|
||||||
|
|
||||||
|
@ -565,7 +569,7 @@ $(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/
|
||||||
$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h
|
$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/utils.o $(libcppdir)/utils.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/utils.o $(libcppdir)/utils.cpp
|
||||||
|
|
||||||
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/astutils.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
|
$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h
|
||||||
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp
|
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/valueflow.o $(libcppdir)/valueflow.cpp
|
||||||
|
|
||||||
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/tinyxml/tinyxml2.h lib/check.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/tinyxml/tinyxml2.h lib/check.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Cppcheck - A tool for static C/C++ code analysis
|
||||||
|
* Copyright (C) 2007-2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef analyzerH
|
||||||
|
#define analyzerH
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class Token;
|
||||||
|
template <class T>
|
||||||
|
class ValuePtr;
|
||||||
|
|
||||||
|
struct Analyzer {
|
||||||
|
struct Action {
|
||||||
|
|
||||||
|
Action() : mFlag(0) {}
|
||||||
|
|
||||||
|
// cppcheck-suppress noExplicitConstructor
|
||||||
|
Action(unsigned int f) : mFlag(f) {}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
None = 0,
|
||||||
|
Read = (1 << 0),
|
||||||
|
Write = (1 << 1),
|
||||||
|
Invalid = (1 << 2),
|
||||||
|
Inconclusive = (1 << 3),
|
||||||
|
Match = (1 << 4),
|
||||||
|
Idempotent = (1 << 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
void set(unsigned int f, bool state = true) { mFlag = state ? mFlag | f : mFlag & ~f; }
|
||||||
|
|
||||||
|
bool get(unsigned int f) const { return ((mFlag & f) != 0); }
|
||||||
|
|
||||||
|
bool isRead() const { return get(Read); }
|
||||||
|
|
||||||
|
bool isWrite() const { return get(Write); }
|
||||||
|
|
||||||
|
bool isInvalid() const { return get(Invalid); }
|
||||||
|
|
||||||
|
bool isInconclusive() const { return get(Inconclusive); }
|
||||||
|
|
||||||
|
bool isNone() const { return mFlag == None; }
|
||||||
|
|
||||||
|
bool isModified() const { return isWrite() || isInvalid(); }
|
||||||
|
|
||||||
|
bool isIdempotent() const { return get(Idempotent); }
|
||||||
|
|
||||||
|
bool matches() const { return get(Match); }
|
||||||
|
|
||||||
|
Action& operator|=(Action a)
|
||||||
|
{
|
||||||
|
set(a.mFlag);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend Action operator|(Action a, Action b)
|
||||||
|
{
|
||||||
|
a |= b;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(Action a, Action b) { return a.mFlag == b.mFlag; }
|
||||||
|
|
||||||
|
friend bool operator!=(Action a, Action b) { return a.mFlag != b.mFlag; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int mFlag;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Direction { Forward, Reverse };
|
||||||
|
|
||||||
|
/// Analyze a token
|
||||||
|
virtual Action analyze(const Token* tok, Direction d) const = 0;
|
||||||
|
/// Update the state of the value
|
||||||
|
virtual void update(Token* tok, Action a, Direction d) = 0;
|
||||||
|
/// Try to evaluate the value of a token(most likely a condition)
|
||||||
|
virtual std::vector<int> evaluate(const Token* tok) const = 0;
|
||||||
|
/// Lower any values to possible
|
||||||
|
virtual bool lowerToPossible() = 0;
|
||||||
|
/// Lower any values to inconclusive
|
||||||
|
virtual bool lowerToInconclusive() = 0;
|
||||||
|
/// If the analysis is unsure whether to update a scope, this will return true if the analysis should bifurcate the scope
|
||||||
|
virtual bool updateScope(const Token* endBlock, bool modified) const = 0;
|
||||||
|
/// If the value is conditional
|
||||||
|
virtual bool isConditional() const = 0;
|
||||||
|
/// The condition that will be assumed during analysis
|
||||||
|
virtual void assume(const Token* tok, bool state, const Token* at = nullptr) = 0;
|
||||||
|
/// Return analyzer for expression at token
|
||||||
|
virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg = "") const = 0;
|
||||||
|
virtual ~Analyzer() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -306,6 +306,18 @@ static bool hasToken(const Token * startTok, const Token * stopTok, const Token
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
||||||
|
static T* previousBeforeAstLeftmostLeafGeneric(T* tok)
|
||||||
|
{
|
||||||
|
T* leftmostLeaf = tok;
|
||||||
|
while (leftmostLeaf && leftmostLeaf->astOperand1())
|
||||||
|
leftmostLeaf = leftmostLeaf->astOperand1();
|
||||||
|
return leftmostLeaf->previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* previousBeforeAstLeftmostLeaf(const Token* tok) { return previousBeforeAstLeftmostLeafGeneric(tok); }
|
||||||
|
Token* previousBeforeAstLeftmostLeaf(Token* tok) { return previousBeforeAstLeftmostLeafGeneric(tok); }
|
||||||
|
|
||||||
template <class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
template <class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
|
||||||
static T* nextAfterAstRightmostLeafGeneric(T* tok)
|
static T* nextAfterAstRightmostLeafGeneric(T* tok)
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,6 +90,9 @@ const Token * astIsVariableComparison(const Token *tok, const std::string &comp,
|
||||||
|
|
||||||
bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown = false);
|
bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknown = false);
|
||||||
|
|
||||||
|
const Token* previousBeforeAstLeftmostLeaf(const Token* tok);
|
||||||
|
Token* previousBeforeAstLeftmostLeaf(Token* tok);
|
||||||
|
|
||||||
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
||||||
Token* nextAfterAstRightmostLeaf(Token* tok);
|
Token* nextAfterAstRightmostLeaf(Token* tok);
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,9 @@ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Set
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Token::Match(tok, "%name% ("))
|
// If its a function pointer then check if its called
|
||||||
|
if (tok->variable() && tok->variable()->isPointer() && Token::Match(tok->variable()->nameToken(), "%name% ) (") &&
|
||||||
|
Token::Match(tok, "%name% ("))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (Token::Match(tok, "%var% = %var% .") &&
|
if (Token::Match(tok, "%var% = %var% .") &&
|
||||||
|
|
|
@ -177,6 +177,9 @@ void CheckSizeof::checkSizeofForPointerSize()
|
||||||
while (Token::Match(variable2, "%var% ::|."))
|
while (Token::Match(variable2, "%var% ::|."))
|
||||||
variable2 = variable2->tokAt(2);
|
variable2 = variable2->tokAt(2);
|
||||||
|
|
||||||
|
if (!variable)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Ensure the variables are in the symbol database
|
// Ensure the variables are in the symbol database
|
||||||
// Also ensure the variables are pointers
|
// Also ensure the variables are pointers
|
||||||
// Only keep variables which are pointers
|
// Only keep variables which are pointers
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
<ClCompile Include="utils.cpp" />
|
<ClCompile Include="utils.cpp" />
|
||||||
<ClCompile Include="valueflow.cpp" />
|
<ClCompile Include="valueflow.cpp" />
|
||||||
<ClCompile Include="forwardanalyzer.cpp" />
|
<ClCompile Include="forwardanalyzer.cpp" />
|
||||||
|
<ClCompile Include="reverseanalyzer.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\externals\simplecpp\simplecpp.h" />
|
<ClInclude Include="..\externals\simplecpp\simplecpp.h" />
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
|
|
||||||
struct ForwardTraversal {
|
struct ForwardTraversal {
|
||||||
enum class Progress { Continue, Break, Skip };
|
enum class Progress { Continue, Break, Skip };
|
||||||
ForwardTraversal(const ValuePtr<ForwardAnalyzer>& analyzer, const Settings* settings)
|
ForwardTraversal(const ValuePtr<Analyzer>& analyzer, const Settings* settings)
|
||||||
: analyzer(analyzer), settings(settings), actions(ForwardAnalyzer::Action::None), analyzeOnly(false)
|
: analyzer(analyzer), settings(settings), actions(Analyzer::Action::None), analyzeOnly(false)
|
||||||
{}
|
{}
|
||||||
ValuePtr<ForwardAnalyzer> analyzer;
|
ValuePtr<Analyzer> analyzer;
|
||||||
const Settings* settings;
|
const Settings* settings;
|
||||||
ForwardAnalyzer::Action actions;
|
Analyzer::Action actions;
|
||||||
bool analyzeOnly;
|
bool analyzeOnly;
|
||||||
|
|
||||||
bool stopUpdates() {
|
bool stopUpdates() {
|
||||||
|
@ -25,12 +25,9 @@ struct ForwardTraversal {
|
||||||
|
|
||||||
std::pair<bool, bool> evalCond(const Token* tok) {
|
std::pair<bool, bool> evalCond(const Token* tok) {
|
||||||
std::vector<int> result = analyzer->evaluate(tok);
|
std::vector<int> result = analyzer->evaluate(tok);
|
||||||
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) {
|
// TODO: We should convert to bool
|
||||||
return x;
|
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) { return x == 1; });
|
||||||
});
|
bool checkElse = std::any_of(result.begin(), result.end(), [](int x) { return x == 0; });
|
||||||
bool checkElse = std::any_of(result.begin(), result.end(), [](int x) {
|
|
||||||
return !x;
|
|
||||||
});
|
|
||||||
return std::make_pair(checkThen, checkElse);
|
return std::make_pair(checkThen, checkElse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,10 +121,10 @@ struct ForwardTraversal {
|
||||||
}
|
}
|
||||||
|
|
||||||
Progress update(Token* tok) {
|
Progress update(Token* tok) {
|
||||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||||
actions |= action;
|
actions |= action;
|
||||||
if (!action.isNone() && !analyzeOnly)
|
if (!action.isNone() && !analyzeOnly)
|
||||||
analyzer->update(tok, action);
|
analyzer->update(tok, action, Analyzer::Direction::Forward);
|
||||||
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
if (action.isInvalid())
|
if (action.isInvalid())
|
||||||
|
@ -153,19 +150,21 @@ struct ForwardTraversal {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T* findRange(T* start, const Token* end, std::function<bool(ForwardAnalyzer::Action)> pred) {
|
T* findRange(T* start, const Token* end, std::function<bool(Analyzer::Action)> pred)
|
||||||
|
{
|
||||||
for (T* tok = start; tok && tok != end; tok = tok->next()) {
|
for (T* tok = start; tok && tok != end; tok = tok->next()) {
|
||||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||||
if (pred(action))
|
if (pred(action))
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ForwardAnalyzer::Action analyzeRecursive(const Token* start) {
|
Analyzer::Action analyzeRecursive(const Token* start)
|
||||||
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
{
|
||||||
std::function<Progress(const Token *)> f = [&](const Token* tok) {
|
Analyzer::Action result = Analyzer::Action::None;
|
||||||
result = analyzer->analyze(tok);
|
std::function<Progress(const Token*)> f = [&](const Token* tok) {
|
||||||
|
result = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||||
if (result.isModified() || result.isInconclusive())
|
if (result.isModified() || result.isInconclusive())
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
return Progress::Continue;
|
return Progress::Continue;
|
||||||
|
@ -174,10 +173,11 @@ struct ForwardTraversal {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ForwardAnalyzer::Action analyzeRange(const Token* start, const Token* end) {
|
Analyzer::Action analyzeRange(const Token* start, const Token* end)
|
||||||
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
{
|
||||||
|
Analyzer::Action result = Analyzer::Action::None;
|
||||||
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
||||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||||
if (action.isModified() || action.isInconclusive())
|
if (action.isModified() || action.isInconclusive())
|
||||||
return action;
|
return action;
|
||||||
result = action;
|
result = action;
|
||||||
|
@ -211,25 +211,25 @@ struct ForwardTraversal {
|
||||||
Inconclusive,
|
Inconclusive,
|
||||||
};
|
};
|
||||||
|
|
||||||
ForwardAnalyzer::Action analyzeScope(const Token* endBlock) {
|
Analyzer::Action analyzeScope(const Token* endBlock) { return analyzeRange(endBlock->link(), endBlock); }
|
||||||
return analyzeRange(endBlock->link(), endBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
ForwardAnalyzer::Action checkScope(Token* endBlock) {
|
Analyzer::Action checkScope(Token* endBlock)
|
||||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
{
|
||||||
|
Analyzer::Action a = analyzeScope(endBlock);
|
||||||
forkScope(endBlock, a.isModified());
|
forkScope(endBlock, a.isModified());
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
ForwardAnalyzer::Action checkScope(const Token* endBlock) {
|
Analyzer::Action checkScope(const Token* endBlock)
|
||||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
{
|
||||||
|
Analyzer::Action a = analyzeScope(endBlock);
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
Progress updateLoop(Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) {
|
Progress updateLoop(Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) {
|
||||||
const bool isDoWhile = precedes(endBlock, condTok);
|
const bool isDoWhile = precedes(endBlock, condTok);
|
||||||
ForwardAnalyzer::Action bodyAnalysis = analyzeScope(endBlock);
|
Analyzer::Action bodyAnalysis = analyzeScope(endBlock);
|
||||||
ForwardAnalyzer::Action allAnalysis = bodyAnalysis;
|
Analyzer::Action allAnalysis = bodyAnalysis;
|
||||||
if (condTok)
|
if (condTok)
|
||||||
allAnalysis |= analyzeRecursive(condTok);
|
allAnalysis |= analyzeRecursive(condTok);
|
||||||
if (initTok)
|
if (initTok)
|
||||||
|
@ -258,7 +258,7 @@ struct ForwardTraversal {
|
||||||
|
|
||||||
forkScope(endBlock, allAnalysis.isModified());
|
forkScope(endBlock, allAnalysis.isModified());
|
||||||
if (bodyAnalysis.isModified()) {
|
if (bodyAnalysis.isModified()) {
|
||||||
Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&ForwardAnalyzer::Action::isModified));
|
Token* writeTok = findRange(endBlock->link(), endBlock, std::mem_fn(&Analyzer::Action::isModified));
|
||||||
const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock);
|
const Token* nextStatement = Token::findmatch(writeTok, ";|}", endBlock);
|
||||||
if (!Token::Match(nextStatement, ";|} break ;"))
|
if (!Token::Match(nextStatement, ";|} break ;"))
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
|
@ -381,8 +381,8 @@ struct ForwardTraversal {
|
||||||
// Check if condition is true or false
|
// Check if condition is true or false
|
||||||
bool checkThen, checkElse;
|
bool checkThen, checkElse;
|
||||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||||
ForwardAnalyzer::Action thenAction = ForwardAnalyzer::Action::None;
|
Analyzer::Action thenAction = Analyzer::Action::None;
|
||||||
ForwardAnalyzer::Action elseAction = ForwardAnalyzer::Action::None;
|
Analyzer::Action elseAction = Analyzer::Action::None;
|
||||||
bool hasElse = Token::simpleMatch(endBlock, "} else {");
|
bool hasElse = Token::simpleMatch(endBlock, "} else {");
|
||||||
bool bail = false;
|
bool bail = false;
|
||||||
|
|
||||||
|
@ -445,7 +445,7 @@ struct ForwardTraversal {
|
||||||
}
|
}
|
||||||
} else if (Token::simpleMatch(tok, "try {")) {
|
} else if (Token::simpleMatch(tok, "try {")) {
|
||||||
Token* endBlock = tok->next()->link();
|
Token* endBlock = tok->next()->link();
|
||||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
Analyzer::Action a = analyzeScope(endBlock);
|
||||||
if (updateRange(tok->next(), endBlock) == Progress::Break)
|
if (updateRange(tok->next(), endBlock) == Progress::Break)
|
||||||
return Progress::Break;
|
return Progress::Break;
|
||||||
if (a.isModified())
|
if (a.isModified())
|
||||||
|
@ -567,12 +567,19 @@ struct ForwardTraversal {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ForwardAnalyzer::Action valueFlowGenericForward(Token* start,
|
Analyzer::Action valueFlowGenericForward(Token* start,
|
||||||
const Token* end,
|
const Token* end,
|
||||||
const ValuePtr<ForwardAnalyzer>& fa,
|
const ValuePtr<Analyzer>& a,
|
||||||
const Settings* settings)
|
const Settings* settings)
|
||||||
{
|
{
|
||||||
ForwardTraversal ft{fa, settings};
|
ForwardTraversal ft{a, settings};
|
||||||
ft.updateRange(start, end);
|
ft.updateRange(start, end);
|
||||||
return ft.actions;
|
return ft.actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Analyzer::Action valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings)
|
||||||
|
{
|
||||||
|
ForwardTraversal ft{a, settings};
|
||||||
|
ft.updateRecursive(start);
|
||||||
|
return ft.actions;
|
||||||
|
}
|
||||||
|
|
|
@ -19,103 +19,18 @@
|
||||||
#ifndef forwardanalyzerH
|
#ifndef forwardanalyzerH
|
||||||
#define forwardanalyzerH
|
#define forwardanalyzerH
|
||||||
|
|
||||||
|
#include "analyzer.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
class Token;
|
class Token;
|
||||||
template <class T> class ValuePtr;
|
template <class T> class ValuePtr;
|
||||||
|
|
||||||
struct ForwardAnalyzer {
|
Analyzer::Action valueFlowGenericForward(Token* start,
|
||||||
struct Action {
|
|
||||||
|
|
||||||
Action() : mFlag(0) {}
|
|
||||||
|
|
||||||
// cppcheck-suppress noExplicitConstructor
|
|
||||||
Action(unsigned int f) : mFlag(f) {}
|
|
||||||
|
|
||||||
enum {
|
|
||||||
None = 0,
|
|
||||||
Read = (1 << 0),
|
|
||||||
Write = (1 << 1),
|
|
||||||
Invalid = (1 << 2),
|
|
||||||
Inconclusive = (1 << 3),
|
|
||||||
};
|
|
||||||
|
|
||||||
void set(unsigned int f, bool state = true) {
|
|
||||||
mFlag = state ? mFlag | f : mFlag & ~f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get(unsigned int f) const {
|
|
||||||
return ((mFlag & f) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRead() const {
|
|
||||||
return get(Read);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWrite() const {
|
|
||||||
return get(Write);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInvalid() const {
|
|
||||||
return get(Invalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInconclusive() const {
|
|
||||||
return get(Inconclusive);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNone() const {
|
|
||||||
return mFlag == None;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isModified() const {
|
|
||||||
return isWrite() || isInvalid();
|
|
||||||
}
|
|
||||||
|
|
||||||
Action& operator|=(Action a) {
|
|
||||||
set(a.mFlag);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Action operator|(Action a, Action b) {
|
|
||||||
a |= b;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator==(Action a, Action b) {
|
|
||||||
return a.mFlag == b.mFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator!=(Action a, Action b) {
|
|
||||||
return a.mFlag != b.mFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
unsigned int mFlag;
|
|
||||||
};
|
|
||||||
/// Analyze a token
|
|
||||||
virtual Action analyze(const Token* tok) const = 0;
|
|
||||||
/// Update the state of the value
|
|
||||||
virtual void update(Token* tok, Action a) = 0;
|
|
||||||
/// Try to evaluate the value of a token(most likely a condition)
|
|
||||||
virtual std::vector<int> evaluate(const Token* tok) const = 0;
|
|
||||||
/// Lower any values to possible
|
|
||||||
virtual bool lowerToPossible() = 0;
|
|
||||||
/// Lower any values to inconclusive
|
|
||||||
virtual bool lowerToInconclusive() = 0;
|
|
||||||
/// If the analysis is unsure whether to update a scope, this will return true if the analysis should bifurcate the scope
|
|
||||||
virtual bool updateScope(const Token* endBlock, bool modified) const = 0;
|
|
||||||
/// If the value is conditional
|
|
||||||
virtual bool isConditional() const = 0;
|
|
||||||
/// The condition that will be assumed during analysis
|
|
||||||
virtual void assume(const Token* tok, bool state, const Token* at = nullptr) = 0;
|
|
||||||
virtual ~ForwardAnalyzer() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
ForwardAnalyzer::Action valueFlowGenericForward(Token* start,
|
|
||||||
const Token* end,
|
const Token* end,
|
||||||
const ValuePtr<ForwardAnalyzer>& fa,
|
const ValuePtr<Analyzer>& a,
|
||||||
const Settings* settings);
|
const Settings* settings);
|
||||||
|
|
||||||
|
Analyzer::Action valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -48,6 +48,7 @@ HEADERS += $${PWD}/analyzerinfo.h \
|
||||||
$${PWD}/platform.h \
|
$${PWD}/platform.h \
|
||||||
$${PWD}/preprocessor.h \
|
$${PWD}/preprocessor.h \
|
||||||
$${PWD}/programmemory.h \
|
$${PWD}/programmemory.h \
|
||||||
|
$${PWD}/reverseanalyzer.h \
|
||||||
$${PWD}/settings.h \
|
$${PWD}/settings.h \
|
||||||
$${PWD}/suppressions.h \
|
$${PWD}/suppressions.h \
|
||||||
$${PWD}/symboldatabase.h \
|
$${PWD}/symboldatabase.h \
|
||||||
|
@ -104,6 +105,7 @@ SOURCES += $${PWD}/analyzerinfo.cpp \
|
||||||
$${PWD}/platform.cpp \
|
$${PWD}/platform.cpp \
|
||||||
$${PWD}/preprocessor.cpp \
|
$${PWD}/preprocessor.cpp \
|
||||||
$${PWD}/programmemory.cpp \
|
$${PWD}/programmemory.cpp \
|
||||||
|
$${PWD}/reverseanalyzer.cpp \
|
||||||
$${PWD}/settings.cpp \
|
$${PWD}/settings.cpp \
|
||||||
$${PWD}/suppressions.cpp \
|
$${PWD}/suppressions.cpp \
|
||||||
$${PWD}/symboldatabase.cpp \
|
$${PWD}/symboldatabase.cpp \
|
||||||
|
|
|
@ -204,6 +204,8 @@ static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok
|
||||||
if (p.first != tok2->next()->varId())
|
if (p.first != tok2->next()->varId())
|
||||||
continue;
|
continue;
|
||||||
const Token *vartok = tok2->tokAt(3);
|
const Token *vartok = tok2->tokAt(3);
|
||||||
|
if (vartok == tok)
|
||||||
|
continue;
|
||||||
pm.setValue(vartok->varId(), p.second);
|
pm.setValue(vartok->varId(), p.second);
|
||||||
setvar = true;
|
setvar = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,286 @@
|
||||||
|
#include "reverseanalyzer.h"
|
||||||
|
#include "analyzer.h"
|
||||||
|
#include "astutils.h"
|
||||||
|
#include "forwardanalyzer.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "symboldatabase.h"
|
||||||
|
#include "token.h"
|
||||||
|
#include "valueptr.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
struct ReverseTraversal {
|
||||||
|
ReverseTraversal(const ValuePtr<Analyzer>& analyzer, const Settings* settings)
|
||||||
|
: analyzer(analyzer), settings(settings)
|
||||||
|
{}
|
||||||
|
ValuePtr<Analyzer> analyzer;
|
||||||
|
const Settings* settings;
|
||||||
|
|
||||||
|
std::pair<bool, bool> evalCond(const Token* tok)
|
||||||
|
{
|
||||||
|
std::vector<int> result = analyzer->evaluate(tok);
|
||||||
|
// TODO: We should convert to bool
|
||||||
|
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) { return x == 1; });
|
||||||
|
bool checkElse = std::any_of(result.begin(), result.end(), [](int x) { return x == 0; });
|
||||||
|
return std::make_pair(checkThen, checkElse);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(Token* tok)
|
||||||
|
{
|
||||||
|
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||||
|
if (!action.isNone())
|
||||||
|
analyzer->update(tok, action, Analyzer::Direction::Reverse);
|
||||||
|
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
||||||
|
return false;
|
||||||
|
if (action.isInvalid())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateRecursive(Token* start)
|
||||||
|
{
|
||||||
|
bool continueB = true;
|
||||||
|
visitAstNodes(start, [&](Token* tok) {
|
||||||
|
continueB &= update(tok);
|
||||||
|
if (continueB)
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
else
|
||||||
|
return ChildrenToVisit::done;
|
||||||
|
});
|
||||||
|
return continueB;
|
||||||
|
}
|
||||||
|
|
||||||
|
Analyzer::Action analyzeRecursive(const Token* start)
|
||||||
|
{
|
||||||
|
Analyzer::Action result = Analyzer::Action::None;
|
||||||
|
visitAstNodes(start, [&](const Token* tok) {
|
||||||
|
result |= analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||||
|
if (result.isModified())
|
||||||
|
return ChildrenToVisit::done;
|
||||||
|
return ChildrenToVisit::op1_and_op2;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Analyzer::Action analyzeRange(const Token* start, const Token* end)
|
||||||
|
{
|
||||||
|
Analyzer::Action result = Analyzer::Action::None;
|
||||||
|
for (const Token* tok = start; tok && tok != end; tok = tok->next()) {
|
||||||
|
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse);
|
||||||
|
if (action.isModified())
|
||||||
|
return action;
|
||||||
|
result |= action;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Token* isDeadCode(Token* tok)
|
||||||
|
{
|
||||||
|
int opSide = 0;
|
||||||
|
for (; tok && tok->astParent(); tok = tok->astParent()) {
|
||||||
|
Token* parent = tok->astParent();
|
||||||
|
if (tok != parent->astOperand2())
|
||||||
|
continue;
|
||||||
|
if (Token::simpleMatch(parent, ":")) {
|
||||||
|
if (astIsLHS(tok))
|
||||||
|
opSide = 1;
|
||||||
|
else if (astIsRHS(tok))
|
||||||
|
opSide = 2;
|
||||||
|
else
|
||||||
|
opSide = 0;
|
||||||
|
}
|
||||||
|
if (!Token::Match(parent, "%oror%|&&|?"))
|
||||||
|
continue;
|
||||||
|
Token* condTok = parent->astOperand1();
|
||||||
|
if (!condTok)
|
||||||
|
continue;
|
||||||
|
bool checkThen, checkElse;
|
||||||
|
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||||
|
|
||||||
|
if (!checkThen && !checkElse) {
|
||||||
|
Analyzer::Action action = analyzeRecursive(condTok);
|
||||||
|
if (action.isRead() || action.isModified())
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent->str() == "?") {
|
||||||
|
if (!checkElse && opSide == 1)
|
||||||
|
return parent;
|
||||||
|
if (!checkThen && opSide == 2)
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
if (!checkThen && parent->str() == "&&")
|
||||||
|
return parent;
|
||||||
|
if (!checkElse && parent->str() == "||")
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void traverse(Token* start)
|
||||||
|
{
|
||||||
|
for (Token* tok = start->previous(); tok; tok = tok->previous()) {
|
||||||
|
if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction ||
|
||||||
|
tok->scope()->type == Scope::ScopeType::eLambda))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok, "return|break|continue"))
|
||||||
|
break;
|
||||||
|
// Evaluate LHS of assignment before RHS
|
||||||
|
if (Token* assignTok = assignExpr(tok)) {
|
||||||
|
Token* assignTop = assignTok;
|
||||||
|
bool continueB = true;
|
||||||
|
while (assignTop->isAssignmentOp()) {
|
||||||
|
if (!Token::Match(assignTop->astOperand1(), "%assign%")) {
|
||||||
|
continueB &= updateRecursive(assignTop->astOperand1());
|
||||||
|
}
|
||||||
|
if (!assignTop->astParent())
|
||||||
|
break;
|
||||||
|
assignTop = assignTop->astParent();
|
||||||
|
}
|
||||||
|
// Is assignment in dead code
|
||||||
|
if (Token* parent = isDeadCode(assignTok)) {
|
||||||
|
tok = parent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Simple assign
|
||||||
|
if (assignTok->astParent() == assignTop || assignTok == assignTop) {
|
||||||
|
Analyzer::Action rhsAction =
|
||||||
|
analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse);
|
||||||
|
Analyzer::Action lhsAction =
|
||||||
|
analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse);
|
||||||
|
// Assignment from
|
||||||
|
if (rhsAction.isRead()) {
|
||||||
|
const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
|
||||||
|
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand1(), info);
|
||||||
|
if (a) {
|
||||||
|
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||||
|
assignTok->astOperand2()->scope()->bodyEnd,
|
||||||
|
a,
|
||||||
|
settings);
|
||||||
|
}
|
||||||
|
// Assignment to
|
||||||
|
} else if (lhsAction.matches()) {
|
||||||
|
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
|
||||||
|
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
|
||||||
|
if (a) {
|
||||||
|
valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()),
|
||||||
|
assignTok->astOperand2()->scope()->bodyEnd,
|
||||||
|
a,
|
||||||
|
settings);
|
||||||
|
valueFlowGenericReverse(assignTok->astOperand1()->previous(), a, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!continueB)
|
||||||
|
break;
|
||||||
|
valueFlowGenericForward(assignTop->astOperand2(), analyzer, settings);
|
||||||
|
tok = previousBeforeAstLeftmostLeaf(assignTop);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tok->str() == "}") {
|
||||||
|
Token* condTok = getCondTokFromEnd(tok);
|
||||||
|
if (!condTok)
|
||||||
|
break;
|
||||||
|
Analyzer::Action condAction = analyzeRecursive(condTok);
|
||||||
|
const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (");
|
||||||
|
// Evaluate condition of for and while loops first
|
||||||
|
if (inLoop) {
|
||||||
|
if (condAction.isModified())
|
||||||
|
break;
|
||||||
|
valueFlowGenericForward(condTok, analyzer, settings);
|
||||||
|
}
|
||||||
|
Token* thenEnd = nullptr;
|
||||||
|
Token* elseEnd = nullptr;
|
||||||
|
const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {");
|
||||||
|
if (hasElse) {
|
||||||
|
elseEnd = tok;
|
||||||
|
thenEnd = tok->link()->tokAt(-2);
|
||||||
|
} else {
|
||||||
|
thenEnd = tok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd);
|
||||||
|
Analyzer::Action elseAction = Analyzer::Action::None;
|
||||||
|
if (hasElse) {
|
||||||
|
elseAction = analyzeRange(tok->link(), tok);
|
||||||
|
}
|
||||||
|
if (thenAction.isModified() && inLoop)
|
||||||
|
break;
|
||||||
|
else if (thenAction.isModified() && !elseAction.isModified())
|
||||||
|
analyzer->assume(condTok, hasElse, condTok);
|
||||||
|
else if (elseAction.isModified() && !thenAction.isModified())
|
||||||
|
analyzer->assume(condTok, !hasElse, condTok);
|
||||||
|
// Bail if one of the branches are read to avoid FPs due to over constraints
|
||||||
|
else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() ||
|
||||||
|
elseAction.isRead())
|
||||||
|
break;
|
||||||
|
if (thenAction.isInvalid() || elseAction.isInvalid())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!thenAction.isModified() && !elseAction.isModified())
|
||||||
|
valueFlowGenericForward(condTok, analyzer, settings);
|
||||||
|
else if (condAction.isRead())
|
||||||
|
break;
|
||||||
|
// If the condition modifies the variable then bail
|
||||||
|
if (condAction.isModified())
|
||||||
|
break;
|
||||||
|
tok = condTok->astTop()->previous();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (tok->str() == "{") {
|
||||||
|
if (tok->previous() &&
|
||||||
|
(Token::simpleMatch(tok->previous(), "do") ||
|
||||||
|
(tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) {
|
||||||
|
Analyzer::Action action = analyzeRange(tok, tok->link());
|
||||||
|
if (action.isModified())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
|
||||||
|
tok = tok->linkAt(-2);
|
||||||
|
if (Token::simpleMatch(tok->previous(), ") {"))
|
||||||
|
tok = tok->previous()->link();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Token* next = isUnevaluated(tok)) {
|
||||||
|
tok = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Token* parent = isDeadCode(tok)) {
|
||||||
|
tok = parent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!update(tok))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token* assignExpr(Token* tok)
|
||||||
|
{
|
||||||
|
while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) {
|
||||||
|
if (tok->astParent()->isAssignmentOp())
|
||||||
|
return tok->astParent();
|
||||||
|
tok = tok->astParent();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token* isUnevaluated(Token* tok)
|
||||||
|
{
|
||||||
|
if (Token::Match(tok, ")|>") && tok->link()) {
|
||||||
|
Token* start = tok->link();
|
||||||
|
if (Token::Match(start->previous(), "sizeof|decltype ("))
|
||||||
|
return start->previous();
|
||||||
|
if (Token::simpleMatch(start, "<"))
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void valueFlowGenericReverse(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings)
|
||||||
|
{
|
||||||
|
ReverseTraversal rt{a, settings};
|
||||||
|
rt.traverse(start);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Cppcheck - A tool for static C/C++ code analysis
|
||||||
|
* Copyright (C) 2007-2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef reverseanalyzerH
|
||||||
|
#define reverseanalyzerH
|
||||||
|
|
||||||
|
struct Analyzer;
|
||||||
|
class Settings;
|
||||||
|
class Token;
|
||||||
|
template <class T>
|
||||||
|
class ValuePtr;
|
||||||
|
|
||||||
|
void valueFlowGenericReverse(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings);
|
||||||
|
|
||||||
|
#endif
|
|
@ -77,6 +77,7 @@
|
||||||
|
|
||||||
#include "valueflow.h"
|
#include "valueflow.h"
|
||||||
|
|
||||||
|
#include "analyzer.h"
|
||||||
#include "astutils.h"
|
#include "astutils.h"
|
||||||
#include "errorlogger.h"
|
#include "errorlogger.h"
|
||||||
#include "forwardanalyzer.h"
|
#include "forwardanalyzer.h"
|
||||||
|
@ -85,6 +86,7 @@
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "programmemory.h"
|
#include "programmemory.h"
|
||||||
|
#include "reverseanalyzer.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "standards.h"
|
#include "standards.h"
|
||||||
#include "symboldatabase.h"
|
#include "symboldatabase.h"
|
||||||
|
@ -1738,7 +1740,7 @@ static void valueFlowGlobalStaticVar(TokenList *tokenList, const Settings *setti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||||
const Token* const endToken,
|
const Token* const endToken,
|
||||||
const Variable* const var,
|
const Variable* const var,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
|
@ -1756,279 +1758,13 @@ static void valueFlowForward(Token* startToken,
|
||||||
ErrorLogger* const errorLogger,
|
ErrorLogger* const errorLogger,
|
||||||
const Settings* settings);
|
const Settings* settings);
|
||||||
|
|
||||||
static void valueFlowReverse(TokenList *tokenlist,
|
static void valueFlowReverse(TokenList* tokenlist,
|
||||||
Token *tok,
|
Token* tok,
|
||||||
const Token * const varToken,
|
const Token* const varToken,
|
||||||
ValueFlow::Value val,
|
ValueFlow::Value val,
|
||||||
ValueFlow::Value val2,
|
ValueFlow::Value val2,
|
||||||
ErrorLogger *errorLogger,
|
ErrorLogger* errorLogger,
|
||||||
const Settings *settings)
|
const Settings* settings);
|
||||||
{
|
|
||||||
const MathLib::bigint num = val.intvalue;
|
|
||||||
const Variable * const var = varToken->variable();
|
|
||||||
if (!var)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int varid = varToken->varId();
|
|
||||||
const Token * const startToken = var->nameToken();
|
|
||||||
|
|
||||||
for (Token *tok2 = tok->previous(); ; tok2 = tok2->previous()) {
|
|
||||||
if (!tok2 || tok2 == startToken ||
|
|
||||||
(tok2->str() == "{" &&
|
|
||||||
(tok2->scope()->type == Scope::ScopeType::eFunction || tok2->scope()->type == Scope::ScopeType::eLambda))) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok2->varId() == varid) {
|
|
||||||
if (tok2->hasKnownValue())
|
|
||||||
break;
|
|
||||||
// bailout: assignment
|
|
||||||
if (Token::Match(tok2->previous(), "!!* %name% =")) {
|
|
||||||
Token* assignTok = const_cast<Token*>(tok2->next()->astOperand2());
|
|
||||||
if (!assignTok->hasKnownValue()) {
|
|
||||||
setTokenValue(assignTok, val, settings);
|
|
||||||
const std::string info = "Assignment from '" + assignTok->expressionString() + "'";
|
|
||||||
val.errorPath.emplace_back(assignTok, info);
|
|
||||||
std::list<ValueFlow::Value> values = {val};
|
|
||||||
if (val2.condition) {
|
|
||||||
val2.errorPath.emplace_back(assignTok, info);
|
|
||||||
setTokenValue(assignTok, val2, settings);
|
|
||||||
values.push_back(val2);
|
|
||||||
}
|
|
||||||
const Token* startForwardToken = nextAfterAstRightmostLeaf(tok2->next());
|
|
||||||
const Token* endForwardToken = tok->scope() ? tok->scope()->bodyEnd : tok;
|
|
||||||
valueFlowForward(const_cast<Token*>(startForwardToken),
|
|
||||||
endForwardToken,
|
|
||||||
assignTok,
|
|
||||||
values,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
settings);
|
|
||||||
// Only reverse analysis supported with variables
|
|
||||||
if (assignTok->varId() > 0)
|
|
||||||
valueFlowReverse(tokenlist, tok2->previous(), assignTok, val, val2, errorLogger, settings);
|
|
||||||
}
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "assignment of " + tok2->str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// increment/decrement
|
|
||||||
int inc = 0;
|
|
||||||
if (Token::Match(tok2->previous(), "[;{}] %name% ++|-- ;"))
|
|
||||||
inc = (tok2->strAt(1)=="++") ? -1 : 1;
|
|
||||||
else if (Token::Match(tok2->tokAt(-2), "[;{}] ++|-- %name% ;"))
|
|
||||||
inc = (tok2->strAt(-1)=="++") ? -1 : 1;
|
|
||||||
else if (Token::Match(tok2->previous(), "++|-- %name%") || Token::Match(tok2, "%name% ++|--")) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "increment/decrement of " + tok2->str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (inc != 0) {
|
|
||||||
val.intvalue += inc;
|
|
||||||
const std::string info(tok2->str() + " is " + std::string(inc==1 ? "decremented" : "incremented") + ", before this " + (inc==1?"decrement":"increment") + " the value is " + val.infoString());
|
|
||||||
val.errorPath.emplace_back(tok2, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// compound assignment
|
|
||||||
if (Token::Match(tok2->previous(), "[;{}] %var% %assign%") && tok2->next()->str() != "=") {
|
|
||||||
const Token * const assignToken = tok2->next();
|
|
||||||
const Token * const rhsToken = assignToken->astOperand2();
|
|
||||||
if (!rhsToken || !rhsToken->hasKnownIntValue()) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "compound assignment, rhs value is not known");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const MathLib::bigint rhsValue = rhsToken->values().front().intvalue;
|
|
||||||
if (assignToken->str() == "+=")
|
|
||||||
val.intvalue -= rhsValue;
|
|
||||||
else if (assignToken->str() == "-=")
|
|
||||||
val.intvalue += rhsValue;
|
|
||||||
else if (assignToken->str() == "*=" && rhsValue != 0)
|
|
||||||
val.intvalue /= rhsValue;
|
|
||||||
else {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "compound assignment " + tok2->str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string info("Compound assignment '" + assignToken->str() + "', before assignment value is " + val.infoString());
|
|
||||||
val.errorPath.emplace_back(tok2, info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bailout: variable is used in rhs in assignment to itself
|
|
||||||
if (bailoutSelfAssignment(tok2)) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "variable " + tok2->str() + " is used in rhs in assignment to itself");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Token::Match(tok2->previous(), "sizeof|.")) {
|
|
||||||
const Token *prev = tok2->previous();
|
|
||||||
while (Token::Match(prev,"%name%|.") && prev->str() != "sizeof")
|
|
||||||
prev = prev->previous();
|
|
||||||
if (prev && prev->str() == "sizeof")
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// assigned by subfunction?
|
|
||||||
bool inconclusive = false;
|
|
||||||
if (isVariableChangedByFunctionCall(tok2, std::max(val.indirect, val2.indirect), settings, &inconclusive)) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "possible assignment of " + tok2->str() + " by subfunction");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Impossible values can't be inconclusive
|
|
||||||
if (val.isImpossible() || val2.isImpossible())
|
|
||||||
break;
|
|
||||||
val.setInconclusive(inconclusive);
|
|
||||||
val2.setInconclusive(inconclusive);
|
|
||||||
|
|
||||||
// skip if variable is conditionally used in ?: expression
|
|
||||||
if (const Token *parent = skipValueInConditionalExpression(tok2)) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
tok2,
|
|
||||||
"no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do-while condition, break in the loop body
|
|
||||||
{
|
|
||||||
const Token *parent = tok2->astParent();
|
|
||||||
while (parent && !Token::simpleMatch(parent->previous(), "while ("))
|
|
||||||
parent = parent->astParent();
|
|
||||||
if (parent && Token::simpleMatch(parent->tokAt(-2), "} while (") && Token::simpleMatch(parent->linkAt(-2)->previous(), "do {")) {
|
|
||||||
bool breakBailout = false;
|
|
||||||
for (const Token *iftok = parent->linkAt(-2); iftok != parent; iftok = iftok->next()) {
|
|
||||||
if (!Token::simpleMatch(iftok, "if ("))
|
|
||||||
continue;
|
|
||||||
if (!Token::simpleMatch(iftok->linkAt(1), ") { break"))
|
|
||||||
continue;
|
|
||||||
ProgramMemory programMemory;
|
|
||||||
programMemory.setIntValue(varid, num);
|
|
||||||
if (conditionIsTrue(iftok->next()->astOperand2(), programMemory)) {
|
|
||||||
breakBailout = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (breakBailout) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
tok2,
|
|
||||||
"no simplification of " + tok2->str() + " in do-while condition since there is a break in the loop body");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTokenValue(tok2, val, settings);
|
|
||||||
if (val2.condition)
|
|
||||||
setTokenValue(tok2,val2, settings);
|
|
||||||
if (tok2 == var->nameToken())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip sizeof etc..
|
|
||||||
if (tok2->str() == ")" && Token::Match(tok2->link()->previous(), "sizeof|typeof|typeid ("))
|
|
||||||
tok2 = tok2->link();
|
|
||||||
|
|
||||||
// goto label
|
|
||||||
if (Token::Match(tok2, "[;{}] %name% :")) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2->next(), "variable " + var->name() + " stopping on goto label");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tok2->str() == "}") {
|
|
||||||
const Token* condTok = getCondTokFromEnd(tok2);
|
|
||||||
// Evaluate condition of for and while loops first
|
|
||||||
if (condTok && condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while (")) {
|
|
||||||
const Token* startTok = nullptr;
|
|
||||||
const Token* endTok = nullptr;
|
|
||||||
std::tie(startTok, endTok) = condTok->findExpressionStartEndTokens();
|
|
||||||
if (!isVariableChanged(startTok, endTok, varid, false, settings, true)) {
|
|
||||||
std::list<ValueFlow::Value> values = {val};
|
|
||||||
if (val2.condition) {
|
|
||||||
values.push_back(val2);
|
|
||||||
}
|
|
||||||
const Token *expr = Token::findmatch(tok2, "%varid%", varid);
|
|
||||||
valueFlowForward(const_cast<Token*>(startTok),
|
|
||||||
endTok,
|
|
||||||
expr,
|
|
||||||
values,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
tokenlist,
|
|
||||||
errorLogger,
|
|
||||||
settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const Token *vartok = Token::findmatch(tok2->link(), "%varid%", tok2, varid);
|
|
||||||
while (Token::Match(vartok, "%name% = %num% ;") && !vartok->tokAt(2)->getValue(num))
|
|
||||||
vartok = Token::findmatch(vartok->next(), "%varid%", tok2, varid);
|
|
||||||
if (vartok) {
|
|
||||||
if (settings->debugwarnings) {
|
|
||||||
std::string errmsg = "variable ";
|
|
||||||
errmsg += var->name() + " ";
|
|
||||||
errmsg += "stopping on }";
|
|
||||||
bailout(tokenlist, errorLogger, tok2, errmsg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
tok2 = tok2->link();
|
|
||||||
if (Token::simpleMatch(tok2->previous(), ") {") && Token::Match(tok2->previous()->link()->previous(), "for|while ("))
|
|
||||||
tok2 = tok2->previous()->link();
|
|
||||||
}
|
|
||||||
} else if (tok2->str() == "{") {
|
|
||||||
// if variable is assigned in loop don't look before the loop
|
|
||||||
if (tok2->previous() &&
|
|
||||||
(Token::simpleMatch(tok2->previous(), "do") ||
|
|
||||||
(tok2->strAt(-1) == ")" && Token::Match(tok2->linkAt(-1)->previous(), "for|while (")))) {
|
|
||||||
|
|
||||||
const Token *start = tok2;
|
|
||||||
const Token *end = start->link();
|
|
||||||
if (isVariableChanged(start,end,varid,var->isGlobal(),settings, tokenlist->isCPP())) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " is assigned in loop. so valueflow analysis bailout when start of loop is reached.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global variable : stop when leaving the function scope
|
|
||||||
if (!var->isLocal()) {
|
|
||||||
if (!Token::Match(tok2->previous(), ")|else|do {"))
|
|
||||||
break;
|
|
||||||
if ((tok2->previous()->str() == ")") &&
|
|
||||||
!Token::Match(tok2->linkAt(-1)->previous(), "if|for|while ("))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (tok2->str() == ";") {
|
|
||||||
const Token *parent = tok2->previous();
|
|
||||||
while (parent && !Token::Match(parent, "return|break|continue|goto"))
|
|
||||||
parent = parent->astParent();
|
|
||||||
// reaching a break/continue/return
|
|
||||||
if (parent) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok2, "variable " + var->name() + " stopping on " + parent->str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->linkAt(1), ") {")) {
|
|
||||||
// bailout: global non-const variables
|
|
||||||
if (!(var->isLocal() || var->isArgument()) && !var->isConst()) {
|
|
||||||
if (settings->debugwarnings)
|
|
||||||
bailout(tokenlist, errorLogger, tok, "global variable " + var->name());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isConditionKnown(const Token* tok, bool then)
|
static bool isConditionKnown(const Token* tok, bool then)
|
||||||
{
|
{
|
||||||
|
@ -2169,6 +1905,18 @@ static void valueFlowAST(Token *tok, nonneg int varid, const ValueFlow::Value &v
|
||||||
valueFlowAST(tok->astOperand2(), varid, value, settings);
|
valueFlowAST(tok->astOperand2(), varid, value, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const std::string& invertAssign(const std::string& assign)
|
||||||
|
{
|
||||||
|
static std::unordered_map<std::string, std::string> lookup = {
|
||||||
|
{"+=", "-="}, {"-=", "+="}, {"*=", "/="}, {"/=", "*="}, {"<<=", ">>="}, {">>=", "<<="}, {"^=", "^="}};
|
||||||
|
static std::string empty = "";
|
||||||
|
auto it = lookup.find(assign);
|
||||||
|
if (it == lookup.end())
|
||||||
|
return empty;
|
||||||
|
else
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign, const ValueFlow::Value &rhsValue)
|
static bool evalAssignment(ValueFlow::Value &lhsValue, const std::string &assign, const ValueFlow::Value &rhsValue)
|
||||||
{
|
{
|
||||||
if (lhsValue.isIntValue()) {
|
if (lhsValue.isIntValue()) {
|
||||||
|
@ -2309,17 +2057,13 @@ struct SelectMapValues {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
struct ValueFlowAnalyzer : Analyzer {
|
||||||
const TokenList* tokenlist;
|
const TokenList* tokenlist;
|
||||||
ProgramMemoryState pms;
|
ProgramMemoryState pms;
|
||||||
|
|
||||||
ValueFlowForwardAnalyzer()
|
ValueFlowAnalyzer() : tokenlist(nullptr), pms() {}
|
||||||
: tokenlist(nullptr), pms()
|
|
||||||
{}
|
|
||||||
|
|
||||||
ValueFlowForwardAnalyzer(const TokenList* t)
|
ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms() {}
|
||||||
: tokenlist(t), pms()
|
|
||||||
{}
|
|
||||||
|
|
||||||
virtual const ValueFlow::Value* getValue(const Token* tok) const = 0;
|
virtual const ValueFlow::Value* getValue(const Token* tok) const = 0;
|
||||||
virtual ValueFlow::Value* getValue(const Token* tok) = 0;
|
virtual ValueFlow::Value* getValue(const Token* tok) = 0;
|
||||||
|
@ -2372,6 +2116,14 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
|
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
|
||||||
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
|
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
|
||||||
return read | Action::Invalid;
|
return read | Action::Invalid;
|
||||||
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
|
// Check if its assigned to the same value
|
||||||
|
if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) &&
|
||||||
|
astIsIntegral(tok->astParent()->astOperand2(), false)) {
|
||||||
|
std::vector<int> result = evaluate(tok->astParent()->astOperand2());
|
||||||
|
if (!result.empty() && value->equalTo(result.front()))
|
||||||
|
return Action::Idempotent;
|
||||||
|
}
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
|
@ -2390,7 +2142,8 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
return Action::None;
|
return Action::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Action isWritable(const Token* tok) const {
|
virtual Action isWritable(const Token* tok, Direction d) const
|
||||||
|
{
|
||||||
const ValueFlow::Value* value = getValue(tok);
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
if (!value)
|
if (!value)
|
||||||
return Action::None;
|
return Action::None;
|
||||||
|
@ -2400,6 +2153,9 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
|
|
||||||
if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
|
if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
|
||||||
parent->astOperand2()->hasKnownValue()) {
|
parent->astOperand2()->hasKnownValue()) {
|
||||||
|
// If the operator is invertible
|
||||||
|
if (d == Direction::Reverse && (parent->str() == "&=" || parent->str() == "|=" || parent->str() == "%="))
|
||||||
|
return Action::None;
|
||||||
const Token* rhs = parent->astOperand2();
|
const Token* rhs = parent->astOperand2();
|
||||||
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
|
const ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
|
||||||
Action a;
|
Action a;
|
||||||
|
@ -2407,8 +2163,12 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
a = Action::Invalid;
|
a = Action::Invalid;
|
||||||
else
|
else
|
||||||
a = Action::Write;
|
a = Action::Write;
|
||||||
if (parent->str() != "=")
|
if (parent->str() != "=") {
|
||||||
a |= Action::Read;
|
a |= Action::Read;
|
||||||
|
} else {
|
||||||
|
if (rhsValue && !value->isImpossible() && value->equalValue(*rhsValue))
|
||||||
|
a = Action::Idempotent;
|
||||||
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2419,7 +2179,16 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
return Action::None;
|
return Action::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void writeValue(ValueFlow::Value* value, const Token* tok) const {
|
static const std::string& getAssign(const Token* tok, Direction d)
|
||||||
|
{
|
||||||
|
if (d == Direction::Forward)
|
||||||
|
return tok->str();
|
||||||
|
else
|
||||||
|
return invertAssign(tok->str());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const
|
||||||
|
{
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
if (!tok->astParent())
|
if (!tok->astParent())
|
||||||
|
@ -2427,7 +2196,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
if (tok->astParent()->isAssignmentOp()) {
|
if (tok->astParent()->isAssignmentOp()) {
|
||||||
// TODO: Check result
|
// TODO: Check result
|
||||||
if (evalAssignment(*value,
|
if (evalAssignment(*value,
|
||||||
tok->astParent()->str(),
|
getAssign(tok->astParent(), d),
|
||||||
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
|
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
|
||||||
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
|
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
|
||||||
value->infoString());
|
value->infoString());
|
||||||
|
@ -2439,30 +2208,33 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
value->intvalue = 0;
|
value->intvalue = 0;
|
||||||
}
|
}
|
||||||
} else if (tok->astParent()->tokType() == Token::eIncDecOp) {
|
} else if (tok->astParent()->tokType() == Token::eIncDecOp) {
|
||||||
const bool inc = tok->astParent()->str() == "++";
|
bool inc = tok->astParent()->str() == "++";
|
||||||
|
std::string opName(inc ? "incremented" : "decremented");
|
||||||
|
if (d == Direction::Reverse)
|
||||||
|
inc = !inc;
|
||||||
value->intvalue += (inc ? 1 : -1);
|
value->intvalue += (inc ? 1 : -1);
|
||||||
const std::string info(tok->str() + " is " + std::string(inc ? "incremented" : "decremented") +
|
const std::string info(tok->str() + " is " + opName + "', new value is " + value->infoString());
|
||||||
"', new value is " + value->infoString());
|
|
||||||
value->errorPath.emplace_back(tok, info);
|
value->errorPath.emplace_back(tok, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Action analyze(const Token* tok) const OVERRIDE {
|
virtual Action analyze(const Token* tok, Direction d) const OVERRIDE
|
||||||
|
{
|
||||||
if (invalid())
|
if (invalid())
|
||||||
return Action::Invalid;
|
return Action::Invalid;
|
||||||
bool inconclusive = false;
|
bool inconclusive = false;
|
||||||
if (match(tok)) {
|
if (match(tok)) {
|
||||||
const Token* parent = tok->astParent();
|
const Token* parent = tok->astParent();
|
||||||
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
if (astIsPointer(tok) && (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0)
|
||||||
return Action::Read;
|
return Action::Read | Action::Match;
|
||||||
|
|
||||||
// Action read = Action::Read;
|
// Action read = Action::Read;
|
||||||
Action w = isWritable(tok);
|
Action w = isWritable(tok, d);
|
||||||
if (w != Action::None)
|
if (w != Action::None)
|
||||||
return w;
|
return w | Action::Match;
|
||||||
|
|
||||||
// Check for modifications by function calls
|
// Check for modifications by function calls
|
||||||
return isModified(tok);
|
return isModified(tok) | Action::Match;
|
||||||
} else if (tok->isUnaryOp("*")) {
|
} else if (tok->isUnaryOp("*")) {
|
||||||
const Token* lifeTok = nullptr;
|
const Token* lifeTok = nullptr;
|
||||||
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
|
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
|
||||||
|
@ -2502,10 +2274,19 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
return {static_cast<int>(tok->values().front().intvalue)};
|
return {static_cast<int>(tok->values().front().intvalue)};
|
||||||
std::vector<int> result;
|
std::vector<int> result;
|
||||||
ProgramMemory pm = pms.get(tok, getProgramState());
|
ProgramMemory pm = pms.get(tok, getProgramState());
|
||||||
|
if (Token::Match(tok, "&&|%oror%")) {
|
||||||
if (conditionIsTrue(tok, pm))
|
if (conditionIsTrue(tok, pm))
|
||||||
result.push_back(1);
|
result.push_back(1);
|
||||||
if (conditionIsFalse(tok, pm))
|
if (conditionIsFalse(tok, pm))
|
||||||
result.push_back(0);
|
result.push_back(0);
|
||||||
|
} else {
|
||||||
|
MathLib::bigint out = 0;
|
||||||
|
bool error = false;
|
||||||
|
execute(tok, &pm, &out, &error);
|
||||||
|
if (!error)
|
||||||
|
result.push_back(out);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2525,32 +2306,37 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||||
makeConditional();
|
makeConditional();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void update(Token* tok, Action a) OVERRIDE {
|
virtual void update(Token* tok, Action a, Direction d) OVERRIDE
|
||||||
|
{
|
||||||
ValueFlow::Value* value = getValue(tok);
|
ValueFlow::Value* value = getValue(tok);
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
if (a.isRead())
|
// Read first when moving forward
|
||||||
|
if (d == Direction::Forward && a.isRead())
|
||||||
setTokenValue(tok, *value, getSettings());
|
setTokenValue(tok, *value, getSettings());
|
||||||
if (a.isInconclusive())
|
if (a.isInconclusive())
|
||||||
lowerToInconclusive();
|
lowerToInconclusive();
|
||||||
if (a.isWrite() && tok->astParent()) {
|
if (a.isWrite() && tok->astParent()) {
|
||||||
writeValue(value, tok);
|
writeValue(value, tok, d);
|
||||||
}
|
}
|
||||||
|
// Read last when moving in reverse
|
||||||
|
if (d == Direction::Reverse && a.isRead())
|
||||||
|
setTokenValue(tok, *value, getSettings());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ValuePtr<Analyzer> reanalyze(Token*, const std::string&) const OVERRIDE { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
ValuePtr<Analyzer> makeAnalyzer(Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist);
|
||||||
|
|
||||||
|
struct SingleValueFlowAnalyzer : ValueFlowAnalyzer {
|
||||||
std::unordered_map<nonneg int, const Variable*> varids;
|
std::unordered_map<nonneg int, const Variable*> varids;
|
||||||
std::unordered_map<nonneg int, const Variable*> aliases;
|
std::unordered_map<nonneg int, const Variable*> aliases;
|
||||||
ValueFlow::Value value;
|
ValueFlow::Value value;
|
||||||
|
|
||||||
SingleValueFlowForwardAnalyzer()
|
SingleValueFlowAnalyzer() : ValueFlowAnalyzer() {}
|
||||||
: ValueFlowForwardAnalyzer()
|
|
||||||
{}
|
|
||||||
|
|
||||||
SingleValueFlowForwardAnalyzer(const ValueFlow::Value& v, const TokenList* t)
|
SingleValueFlowAnalyzer(const ValueFlow::Value& v, const TokenList* t) : ValueFlowAnalyzer(t), value(v) {}
|
||||||
: ValueFlowForwardAnalyzer(t), value(v)
|
|
||||||
{}
|
|
||||||
|
|
||||||
const std::unordered_map<nonneg int, const Variable*>& getVars() const {
|
const std::unordered_map<nonneg int, const Variable*>& getVars() const {
|
||||||
return varids;
|
return varids;
|
||||||
|
@ -2645,17 +2431,26 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg) const OVERRIDE
|
||||||
|
{
|
||||||
|
ValueFlow::Value newValue = value;
|
||||||
|
newValue.errorPath.emplace_back(tok, msg);
|
||||||
|
return makeAnalyzer(tok, newValue, tokenlist);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VariableForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
struct VariableAnalyzer : SingleValueFlowAnalyzer {
|
||||||
const Variable* var;
|
const Variable* var;
|
||||||
|
|
||||||
VariableForwardAnalyzer()
|
VariableAnalyzer() : SingleValueFlowAnalyzer(), var(nullptr) {}
|
||||||
: SingleValueFlowForwardAnalyzer(), var(nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
VariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
|
VariableAnalyzer(const Variable* v,
|
||||||
: SingleValueFlowForwardAnalyzer(val, t), var(v) {
|
const ValueFlow::Value& val,
|
||||||
|
std::vector<const Variable*> paliases,
|
||||||
|
const TokenList* t)
|
||||||
|
: SingleValueFlowAnalyzer(val, t), var(v)
|
||||||
|
{
|
||||||
varids[var->declarationId()] = var;
|
varids[var->declarationId()] = var;
|
||||||
for (const Variable* av:paliases) {
|
for (const Variable* av:paliases) {
|
||||||
if (!av)
|
if (!av)
|
||||||
|
@ -2704,7 +2499,7 @@ static std::vector<const Variable*> getAliasesFromValues(std::list<ValueFlow::Va
|
||||||
return aliases;
|
return aliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||||
const Token* const endToken,
|
const Token* const endToken,
|
||||||
const Variable* const var,
|
const Variable* const var,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
|
@ -2712,15 +2507,15 @@ static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||||
TokenList* const tokenlist,
|
TokenList* const tokenlist,
|
||||||
const Settings* const settings)
|
const Settings* const settings)
|
||||||
{
|
{
|
||||||
ForwardAnalyzer::Action actions;
|
Analyzer::Action actions;
|
||||||
for (ValueFlow::Value& v : values) {
|
for (ValueFlow::Value& v : values) {
|
||||||
VariableForwardAnalyzer a(var, v, aliases, tokenlist);
|
VariableAnalyzer a(var, v, aliases, tokenlist);
|
||||||
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
||||||
}
|
}
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||||
const Token* const endToken,
|
const Token* const endToken,
|
||||||
const Variable* const var,
|
const Variable* const var,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
|
@ -2747,17 +2542,16 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExpressionForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
|
||||||
const Token* expr;
|
const Token* expr;
|
||||||
bool local;
|
bool local;
|
||||||
bool unknown;
|
bool unknown;
|
||||||
|
|
||||||
ExpressionForwardAnalyzer()
|
ExpressionAnalyzer() : SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false) {}
|
||||||
: SingleValueFlowForwardAnalyzer(), expr(nullptr), local(true), unknown(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ExpressionForwardAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
ExpressionAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
||||||
: SingleValueFlowForwardAnalyzer(val, t), expr(e), local(true), unknown(false) {
|
: SingleValueFlowAnalyzer(val, t), expr(e), local(true), unknown(false)
|
||||||
|
{
|
||||||
|
|
||||||
setupExprVarIds();
|
setupExprVarIds();
|
||||||
}
|
}
|
||||||
|
@ -2815,16 +2609,16 @@ struct ExpressionForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowForwardExpression(Token* startToken,
|
static Analyzer::Action valueFlowForwardExpression(Token* startToken,
|
||||||
const Token* endToken,
|
const Token* endToken,
|
||||||
const Token* exprTok,
|
const Token* exprTok,
|
||||||
const std::list<ValueFlow::Value>& values,
|
const std::list<ValueFlow::Value>& values,
|
||||||
const TokenList* const tokenlist,
|
const TokenList* const tokenlist,
|
||||||
const Settings* settings)
|
const Settings* settings)
|
||||||
{
|
{
|
||||||
ForwardAnalyzer::Action actions;
|
Analyzer::Action actions;
|
||||||
for (const ValueFlow::Value& v : values) {
|
for (const ValueFlow::Value& v : values) {
|
||||||
ExpressionForwardAnalyzer a(exprTok, v, tokenlist);
|
ExpressionAnalyzer a(exprTok, v, tokenlist);
|
||||||
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
||||||
}
|
}
|
||||||
return actions;
|
return actions;
|
||||||
|
@ -2891,7 +2685,18 @@ static const Token* solveExprValues(const Token* expr, std::list<ValueFlow::Valu
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowForward(Token* startToken,
|
ValuePtr<Analyzer> makeAnalyzer(Token* exprTok, const ValueFlow::Value& value, const TokenList* tokenlist)
|
||||||
|
{
|
||||||
|
std::list<ValueFlow::Value> values = {value};
|
||||||
|
const Token* expr = solveExprValues(exprTok, values);
|
||||||
|
if (expr->variable()) {
|
||||||
|
return VariableAnalyzer(expr->variable(), value, getAliasesFromValues(values), tokenlist);
|
||||||
|
} else {
|
||||||
|
return ExpressionAnalyzer(expr, value, tokenlist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Analyzer::Action valueFlowForward(Token* startToken,
|
||||||
const Token* endToken,
|
const Token* endToken,
|
||||||
const Token* exprTok,
|
const Token* exprTok,
|
||||||
std::list<ValueFlow::Value> values,
|
std::list<ValueFlow::Value> values,
|
||||||
|
@ -2920,6 +2725,25 @@ static void valueFlowForward(Token* startToken,
|
||||||
valueFlowForward(startToken, endToken, exprTok, std::move(values), tokenlist, settings);
|
valueFlowForward(startToken, endToken, exprTok, std::move(values), tokenlist, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void valueFlowReverse(TokenList* tokenlist,
|
||||||
|
Token* tok,
|
||||||
|
const Token* const varToken,
|
||||||
|
ValueFlow::Value val,
|
||||||
|
ValueFlow::Value val2,
|
||||||
|
ErrorLogger* errorLogger,
|
||||||
|
const Settings* settings)
|
||||||
|
{
|
||||||
|
std::list<ValueFlow::Value> values = {val};
|
||||||
|
if (val2.varId != 0)
|
||||||
|
values.push_back(val2);
|
||||||
|
const Variable* var = varToken->variable();
|
||||||
|
auto aliases = getAliasesFromValues(values);
|
||||||
|
for (ValueFlow::Value& v : values) {
|
||||||
|
VariableAnalyzer a(var, v, aliases, tokenlist);
|
||||||
|
valueFlowGenericReverse(tok, a, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int getArgumentPos(const Variable *var, const Function *f)
|
static int getArgumentPos(const Variable *var, const Function *f)
|
||||||
{
|
{
|
||||||
auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable &v) {
|
auto arg_it = std::find_if(f->argumentList.begin(), f->argumentList.end(), [&](const Variable &v) {
|
||||||
|
@ -5092,16 +4916,15 @@ static void valueFlowForLoop(TokenList *tokenlist, SymbolDatabase* symboldatabas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MultiValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
struct MultiValueFlowAnalyzer : ValueFlowAnalyzer {
|
||||||
std::unordered_map<nonneg int, ValueFlow::Value> values;
|
std::unordered_map<nonneg int, ValueFlow::Value> values;
|
||||||
std::unordered_map<nonneg int, const Variable*> vars;
|
std::unordered_map<nonneg int, const Variable*> vars;
|
||||||
|
|
||||||
MultiValueFlowForwardAnalyzer()
|
MultiValueFlowAnalyzer() : ValueFlowAnalyzer(), values(), vars() {}
|
||||||
: ValueFlowForwardAnalyzer(), values(), vars()
|
|
||||||
{}
|
|
||||||
|
|
||||||
MultiValueFlowForwardAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const TokenList* t)
|
MultiValueFlowAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const TokenList* t)
|
||||||
: ValueFlowForwardAnalyzer(t), values(), vars() {
|
: ValueFlowAnalyzer(t), values(), vars()
|
||||||
|
{
|
||||||
for (const auto& p:args) {
|
for (const auto& p:args) {
|
||||||
values[p.first->declarationId()] = p.second;
|
values[p.first->declarationId()] = p.second;
|
||||||
vars[p.first->declarationId()] = p.first;
|
vars[p.first->declarationId()] = p.first;
|
||||||
|
@ -5285,7 +5108,7 @@ static void valueFlowInjectParameter(TokenList* tokenlist, ErrorLogger* errorLog
|
||||||
}
|
}
|
||||||
if (skip)
|
if (skip)
|
||||||
continue;
|
continue;
|
||||||
MultiValueFlowForwardAnalyzer a(arg, tokenlist);
|
MultiValueFlowAnalyzer a(arg, tokenlist);
|
||||||
valueFlowGenericForward(const_cast<Token*>(functionScope->bodyStart), functionScope->bodyEnd, a, settings);
|
valueFlowGenericForward(const_cast<Token*>(functionScope->bodyStart), functionScope->bodyEnd, a, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5937,21 +5760,26 @@ static void valueFlowContainerReverse(Token *tok, nonneg int containerId, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContainerVariableForwardAnalyzer : VariableForwardAnalyzer {
|
struct ContainerVariableAnalyzer : VariableAnalyzer {
|
||||||
ContainerVariableForwardAnalyzer()
|
ContainerVariableAnalyzer() : VariableAnalyzer() {}
|
||||||
: VariableForwardAnalyzer()
|
|
||||||
{}
|
|
||||||
|
|
||||||
ContainerVariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
|
ContainerVariableAnalyzer(const Variable* v,
|
||||||
: VariableForwardAnalyzer(v, val, std::move(paliases), t) {}
|
const ValueFlow::Value& val,
|
||||||
|
std::vector<const Variable*> paliases,
|
||||||
|
const TokenList* t)
|
||||||
|
: VariableAnalyzer(v, val, std::move(paliases), t)
|
||||||
|
{}
|
||||||
|
|
||||||
virtual bool match(const Token* tok) const OVERRIDE {
|
virtual bool match(const Token* tok) const OVERRIDE {
|
||||||
return tok->varId() == var->declarationId() || (astIsIterator(tok) && isAliasOf(tok, var->declarationId()));
|
return tok->varId() == var->declarationId() || (astIsIterator(tok) && isAliasOf(tok, var->declarationId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Action isWritable(const Token* tok) const OVERRIDE {
|
virtual Action isWritable(const Token* tok, Direction d) const OVERRIDE
|
||||||
|
{
|
||||||
if (astIsIterator(tok))
|
if (astIsIterator(tok))
|
||||||
return Action::None;
|
return Action::None;
|
||||||
|
if (d == Direction::Reverse)
|
||||||
|
return Action::None;
|
||||||
const ValueFlow::Value* value = getValue(tok);
|
const ValueFlow::Value* value = getValue(tok);
|
||||||
if (!value)
|
if (!value)
|
||||||
return Action::None;
|
return Action::None;
|
||||||
|
@ -5977,7 +5805,10 @@ struct ContainerVariableForwardAnalyzer : VariableForwardAnalyzer {
|
||||||
return Action::None;
|
return Action::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void writeValue(ValueFlow::Value* value, const Token* tok) const OVERRIDE {
|
virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const OVERRIDE
|
||||||
|
{
|
||||||
|
if (d == Direction::Reverse)
|
||||||
|
return;
|
||||||
if (!value)
|
if (!value)
|
||||||
return;
|
return;
|
||||||
if (!tok->astParent())
|
if (!tok->astParent())
|
||||||
|
@ -6021,16 +5852,16 @@ struct ContainerVariableForwardAnalyzer : VariableForwardAnalyzer {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static ForwardAnalyzer::Action valueFlowContainerForward(Token* tok,
|
static Analyzer::Action valueFlowContainerForward(Token* tok,
|
||||||
const Token* endToken,
|
const Token* endToken,
|
||||||
const Variable* var,
|
const Variable* var,
|
||||||
ValueFlow::Value value,
|
ValueFlow::Value value,
|
||||||
TokenList* tokenlist)
|
TokenList* tokenlist)
|
||||||
{
|
{
|
||||||
ContainerVariableForwardAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
|
ContainerVariableAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
|
||||||
return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
|
return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
|
||||||
}
|
}
|
||||||
static ForwardAnalyzer::Action valueFlowContainerForward(Token* tok,
|
static Analyzer::Action valueFlowContainerForward(Token* tok,
|
||||||
const Variable* var,
|
const Variable* var,
|
||||||
ValueFlow::Value value,
|
ValueFlow::Value value,
|
||||||
TokenList* tokenlist)
|
TokenList* tokenlist)
|
||||||
|
|
|
@ -25,8 +25,10 @@
|
||||||
#include "mathlib.h"
|
#include "mathlib.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -51,6 +53,14 @@ namespace ValueFlow {
|
||||||
x--;
|
x--;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct equalVisitor {
|
||||||
|
template <class T, class U>
|
||||||
|
void operator()(bool& result, T x, U y) const
|
||||||
|
{
|
||||||
|
result = !(x > y || x < y);
|
||||||
|
}
|
||||||
|
};
|
||||||
class CPPCHECKLIB Value {
|
class CPPCHECKLIB Value {
|
||||||
public:
|
public:
|
||||||
typedef std::pair<const Token *, std::string> ErrorPathItem;
|
typedef std::pair<const Token *, std::string> ErrorPathItem;
|
||||||
|
@ -111,19 +121,20 @@ namespace ValueFlow {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class F>
|
template <class T, class F>
|
||||||
void visitValue(F f) {
|
static void visitValue(T& self, F f)
|
||||||
switch (valueType) {
|
{
|
||||||
|
switch (self.valueType) {
|
||||||
case ValueType::INT:
|
case ValueType::INT:
|
||||||
case ValueType::BUFFER_SIZE:
|
case ValueType::BUFFER_SIZE:
|
||||||
case ValueType::CONTAINER_SIZE:
|
case ValueType::CONTAINER_SIZE:
|
||||||
case ValueType::ITERATOR_START:
|
case ValueType::ITERATOR_START:
|
||||||
case ValueType::ITERATOR_END: {
|
case ValueType::ITERATOR_END: {
|
||||||
f(intvalue);
|
f(self.intvalue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueType::FLOAT: {
|
case ValueType::FLOAT: {
|
||||||
f(floatValue);
|
f(self.floatValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ValueType::UNINIT:
|
case ValueType::UNINIT:
|
||||||
|
@ -151,11 +162,19 @@ namespace ValueFlow {
|
||||||
return !(*this == rhs);
|
return !(*this == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T, REQUIRES("T must be an arithmetic type", std::is_arithmetic<T>)>
|
||||||
|
bool equalTo(const T& x) const
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
visitValue(*this, std::bind(equalVisitor{}, std::ref(result), x, std::placeholders::_1));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void decreaseRange() {
|
void decreaseRange() {
|
||||||
if (bound == Bound::Lower)
|
if (bound == Bound::Lower)
|
||||||
visitValue(increment{});
|
visitValue(*this, increment{});
|
||||||
else if (bound == Bound::Upper)
|
else if (bound == Bound::Upper)
|
||||||
visitValue(decrement{});
|
visitValue(*this, decrement{});
|
||||||
}
|
}
|
||||||
|
|
||||||
void invertBound() {
|
void invertBound() {
|
||||||
|
|
|
@ -3453,6 +3453,20 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct a {\n"
|
||||||
|
" int *b();\n"
|
||||||
|
"};\n"
|
||||||
|
"bool g(a c, a* d) {\n"
|
||||||
|
" a *v, *e = v = &c;\n"
|
||||||
|
" if (!v)\n"
|
||||||
|
" return true;\n"
|
||||||
|
" int *f = v->b();\n"
|
||||||
|
" if (f)\n"
|
||||||
|
" v = nullptr;\n"
|
||||||
|
" if (v == nullptr && e) {}\n"
|
||||||
|
" return d;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void alwaysTrueInfer() {
|
void alwaysTrueInfer() {
|
||||||
|
|
|
@ -101,6 +101,10 @@ private:
|
||||||
TEST_CASE(nullpointer58); // #9807
|
TEST_CASE(nullpointer58); // #9807
|
||||||
TEST_CASE(nullpointer59); // #9897
|
TEST_CASE(nullpointer59); // #9897
|
||||||
TEST_CASE(nullpointer60); // #9842
|
TEST_CASE(nullpointer60); // #9842
|
||||||
|
TEST_CASE(nullpointer61);
|
||||||
|
TEST_CASE(nullpointer62);
|
||||||
|
TEST_CASE(nullpointer63);
|
||||||
|
TEST_CASE(nullpointer64);
|
||||||
TEST_CASE(nullpointer_addressOf); // address of
|
TEST_CASE(nullpointer_addressOf); // address of
|
||||||
TEST_CASE(nullpointerSwitch); // #2626
|
TEST_CASE(nullpointerSwitch); // #2626
|
||||||
TEST_CASE(nullpointer_cast); // #4692
|
TEST_CASE(nullpointer_cast); // #4692
|
||||||
|
@ -692,13 +696,16 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
check("void foo(int *p)\n"
|
check("void foo(int *p, bool x)\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" int var1 = x ? *p : 5;\n"
|
" int var1 = x ? *p : 5;\n"
|
||||||
" if (!p)\n"
|
" if (!p)\n"
|
||||||
" ;\n"
|
" ;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout.str());
|
TODO_ASSERT_EQUALS(
|
||||||
|
"[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n",
|
||||||
|
"",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
// while
|
// while
|
||||||
check("void f(int *p) {\n"
|
check("void f(int *p) {\n"
|
||||||
|
@ -1892,6 +1899,122 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nullpointer61()
|
||||||
|
{
|
||||||
|
check("struct a {\n"
|
||||||
|
" int *e;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct f {\n"
|
||||||
|
" a *g() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void h() {\n"
|
||||||
|
" for (f b;;) {\n"
|
||||||
|
" a *c = b.g();\n"
|
||||||
|
" int *d = c->e;\n"
|
||||||
|
" if (d)\n"
|
||||||
|
" ;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" A* g() const;\n"
|
||||||
|
" A* h() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f(A* a) {\n"
|
||||||
|
" if (!a->h())\n"
|
||||||
|
" return;\n"
|
||||||
|
" const A *b = a;\n"
|
||||||
|
" while (b && !b->h())\n"
|
||||||
|
" b = b->g();\n"
|
||||||
|
" if (!b || b == b->g()->h())\n"
|
||||||
|
" return;\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nullpointer62()
|
||||||
|
{
|
||||||
|
check("struct A {\n"
|
||||||
|
" bool f()() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void a(A *x) {\n"
|
||||||
|
" std::string b = x && x->f() ? \"\" : \"\";\n"
|
||||||
|
" if (x) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" bool f()() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"void a(A *x) {\n"
|
||||||
|
" std::string b = (!x || x->f()) ? \"\" : \"\";\n"
|
||||||
|
" if (x) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" A * aa;\n"
|
||||||
|
"};\n"
|
||||||
|
"void b(A*);\n"
|
||||||
|
"void a(A *x) {\n"
|
||||||
|
" b(x ? x->aa : nullptr);\n"
|
||||||
|
" if (!x) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nullpointer63()
|
||||||
|
{
|
||||||
|
check("struct A {\n"
|
||||||
|
" A* a() const;\n"
|
||||||
|
" A* b() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"A* f(A*);\n"
|
||||||
|
"void g(const A* x) {\n"
|
||||||
|
" A *d = x->a();\n"
|
||||||
|
" d = f(d->b()) ? d->a() : nullptr;\n"
|
||||||
|
" if (d && f(d->b())) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nullpointer64()
|
||||||
|
{
|
||||||
|
check("struct A {\n"
|
||||||
|
" A* f() const;\n"
|
||||||
|
" int g() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"bool a;\n"
|
||||||
|
"bool b(A* c) {\n"
|
||||||
|
" if (c->g() == 0)\n"
|
||||||
|
" ;\n"
|
||||||
|
" A *aq = c;\n"
|
||||||
|
" if (c->g() == 0)\n"
|
||||||
|
" c = c->f();\n"
|
||||||
|
" if (c)\n"
|
||||||
|
" for (A *d = c; d != aq; d = d->f()) {}\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct A {\n"
|
||||||
|
" A* g() const;\n"
|
||||||
|
" A* h() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"bool i(A*);\n"
|
||||||
|
"void f(A* x) {\n"
|
||||||
|
" if (i(x->g())) {\n"
|
||||||
|
" A *y = x->g();\n"
|
||||||
|
" x = x->g()->h();\n"
|
||||||
|
" if (x && x->g()) {\n"
|
||||||
|
" y = x->g()->h();\n"
|
||||||
|
" }\n"
|
||||||
|
" if (!y) {}\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
void nullpointer_addressOf() { // address of
|
void nullpointer_addressOf() { // address of
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" struct X *x = 0;\n"
|
" struct X *x = 0;\n"
|
||||||
|
|
|
@ -1082,7 +1082,7 @@ private:
|
||||||
" if (y == 32) {}"
|
" if (y == 32) {}"
|
||||||
"}\n";
|
"}\n";
|
||||||
ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n"
|
ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n"
|
||||||
"4,Compound assignment '+=', before assignment value is 20\n"
|
"4,Compound assignment '+=', assigned value is 20\n"
|
||||||
"2,Assignment 'x=y', assigned value is 20\n",
|
"2,Assignment 'x=y', assigned value is 20\n",
|
||||||
getErrorPathForX(code, 3U));
|
getErrorPathForX(code, 3U));
|
||||||
|
|
||||||
|
@ -1191,9 +1191,6 @@ private:
|
||||||
" if (x == 4);\n"
|
" if (x == 4);\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 2U, 3));
|
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 += , -= , ...
|
// compound assignment += , -= , ...
|
||||||
code = "void f(int x) {\n"
|
code = "void f(int x) {\n"
|
||||||
|
@ -1222,15 +1219,16 @@ private:
|
||||||
" x /= 5;\n"
|
" x /= 5;\n"
|
||||||
" if (x == 42);\n"
|
" if (x == 42);\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT(tokenValues(code, "x ;").empty());
|
ASSERT_EQUALS(true, testValueOfX(code, 2U, 210));
|
||||||
|
|
||||||
// bailout: assignment
|
// bailout: assignment
|
||||||
bailout("void f(int x) {\n"
|
bailout("void f(int x) {\n"
|
||||||
" x = y;\n"
|
" x = y;\n"
|
||||||
" if (x == 123) {}\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"
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||||
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowReverse bailout: assignment of x\n", errout.str());
|
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
|
void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
|
||||||
|
@ -1345,14 +1343,14 @@ private:
|
||||||
bailout("void f(int x) {\n"
|
bailout("void f(int x) {\n"
|
||||||
" y = ((x<0) ? x : ((x==2)?3:4));\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"
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||||
"[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str());
|
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
bailout("int f(int x) {\n"
|
bailout("int f(int x) {\n"
|
||||||
" int r = x ? 1 / x : 0;\n"
|
" int r = x ? 1 / x : 0;\n"
|
||||||
" if (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"
|
code = "void f(int x) {\n"
|
||||||
" int a =v x;\n"
|
" int a =v x;\n"
|
||||||
|
@ -1410,15 +1408,23 @@ private:
|
||||||
" if (x != 123) { b = x; }\n"
|
" if (x != 123) { b = x; }\n"
|
||||||
" if (x == 123) {}\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"
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||||
"[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str());
|
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable b\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
code = "void f(int x) {\n"
|
code = "void f(int x, bool abc) {\n"
|
||||||
" a = x;\n"
|
" a = x;\n"
|
||||||
" if (abc) { x = 1; }\n" // <- condition must be false if x is 7 in next line
|
" if (abc) { x = 1; }\n" // <- condition must be false if x is 7 in next line
|
||||||
" if (x == 7) { }\n"
|
" if (x == 7) { }\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 2U, 7));
|
ASSERT_EQUALS(true, testValueOfX(code, 2U, 7));
|
||||||
|
|
||||||
|
code = "void f(int x, bool abc) {\n"
|
||||||
|
" a = x;\n"
|
||||||
|
" if (abc) { x = 7; }\n" // <- condition is probably true
|
||||||
|
" if (x == 7) { }\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(false, testValueOfX(code, 2U, 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowBeforeConditionGlobalVariables() {
|
void valueFlowBeforeConditionGlobalVariables() {
|
||||||
|
@ -1451,8 +1457,9 @@ private:
|
||||||
" case 2: if (x==5) {} break;\n"
|
" case 2: if (x==5) {} break;\n"
|
||||||
" };\n"
|
" };\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||||
"[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str());
|
"[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
bailout("void f(int x, int y) {\n"
|
bailout("void f(int x, int y) {\n"
|
||||||
" switch (y) {\n"
|
" switch (y) {\n"
|
||||||
|
@ -1460,8 +1467,9 @@ private:
|
||||||
" case 2: if (x==5) {} break;\n"
|
" case 2: if (x==5) {} break;\n"
|
||||||
" };\n"
|
" };\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||||
"[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str());
|
"[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n",
|
||||||
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowBeforeConditionMacro() {
|
void valueFlowBeforeConditionMacro() {
|
||||||
|
@ -1492,8 +1500,7 @@ private:
|
||||||
" if (x==123){}\n"
|
" if (x==123){}\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
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::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",
|
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
// #5721 - FP
|
// #5721 - FP
|
||||||
|
@ -1507,10 +1514,6 @@ private:
|
||||||
"out:\n"
|
"out:\n"
|
||||||
" if (abc) {}\n"
|
" if (abc) {}\n"
|
||||||
"}\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",
|
|
||||||
errout.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowBeforeConditionForward() {
|
void valueFlowBeforeConditionForward() {
|
||||||
|
|
Loading…
Reference in New Issue