Add generic reverse valueflow (#2878)

This commit is contained in:
Paul Fultz II 2020-11-10 09:00:55 -06:00 committed by GitHub
parent 79bdd64689
commit bd7e915c20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 909 additions and 542 deletions

View File

@ -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

110
lib/analyzer.h Normal file
View File

@ -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

View File

@ -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)
{ {

View File

@ -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);

View File

@ -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% .") &&

View File

@ -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

View File

@ -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" />

View File

@ -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; {
Analyzer::Action result = Analyzer::Action::None;
std::function<Progress(const Token*)> f = [&](const Token* tok) { std::function<Progress(const Token*)> f = [&](const Token* tok) {
result = analyzer->analyze(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;
}

View File

@ -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

View File

@ -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 \

View File

@ -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;
} }

286
lib/reverseanalyzer.cpp Normal file
View File

@ -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);
}

30
lib/reverseanalyzer.h Normal file
View File

@ -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

View File

@ -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,
@ -1762,273 +1764,7 @@ static void valueFlowReverse(TokenList *tokenlist,
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)

View File

@ -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() {

View File

@ -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() {

View File

@ -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"

View File

@ -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() {