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)/preprocessor.o \
|
||||
$(libcppdir)/programmemory.o \
|
||||
$(libcppdir)/reverseanalyzer.o \
|
||||
$(libcppdir)/settings.o \
|
||||
$(libcppdir)/suppressions.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
|
||||
$(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
|
||||
|
||||
$(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
|
||||
$(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
|
||||
$(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
|
||||
$(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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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*>)>
|
||||
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);
|
||||
|
||||
const Token* previousBeforeAstLeftmostLeaf(const Token* tok);
|
||||
Token* previousBeforeAstLeftmostLeaf(Token* tok);
|
||||
|
||||
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
||||
Token* nextAfterAstRightmostLeaf(Token* tok);
|
||||
|
||||
|
|
|
@ -205,7 +205,9 @@ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Set
|
|||
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;
|
||||
|
||||
if (Token::Match(tok, "%var% = %var% .") &&
|
||||
|
|
|
@ -177,6 +177,9 @@ void CheckSizeof::checkSizeofForPointerSize()
|
|||
while (Token::Match(variable2, "%var% ::|."))
|
||||
variable2 = variable2->tokAt(2);
|
||||
|
||||
if (!variable)
|
||||
continue;
|
||||
|
||||
// Ensure the variables are in the symbol database
|
||||
// Also ensure the variables are pointers
|
||||
// Only keep variables which are pointers
|
||||
|
|
|
@ -101,6 +101,7 @@
|
|||
<ClCompile Include="utils.cpp" />
|
||||
<ClCompile Include="valueflow.cpp" />
|
||||
<ClCompile Include="forwardanalyzer.cpp" />
|
||||
<ClCompile Include="reverseanalyzer.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\externals\simplecpp\simplecpp.h" />
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
|
||||
struct ForwardTraversal {
|
||||
enum class Progress { Continue, Break, Skip };
|
||||
ForwardTraversal(const ValuePtr<ForwardAnalyzer>& analyzer, const Settings* settings)
|
||||
: analyzer(analyzer), settings(settings), actions(ForwardAnalyzer::Action::None), analyzeOnly(false)
|
||||
ForwardTraversal(const ValuePtr<Analyzer>& analyzer, const Settings* settings)
|
||||
: analyzer(analyzer), settings(settings), actions(Analyzer::Action::None), analyzeOnly(false)
|
||||
{}
|
||||
ValuePtr<ForwardAnalyzer> analyzer;
|
||||
ValuePtr<Analyzer> analyzer;
|
||||
const Settings* settings;
|
||||
ForwardAnalyzer::Action actions;
|
||||
Analyzer::Action actions;
|
||||
bool analyzeOnly;
|
||||
|
||||
bool stopUpdates() {
|
||||
|
@ -25,12 +25,9 @@ struct ForwardTraversal {
|
|||
|
||||
std::pair<bool, bool> evalCond(const Token* tok) {
|
||||
std::vector<int> result = analyzer->evaluate(tok);
|
||||
bool checkThen = std::any_of(result.begin(), result.end(), [](int x) {
|
||||
return x;
|
||||
});
|
||||
bool checkElse = std::any_of(result.begin(), result.end(), [](int x) {
|
||||
return !x;
|
||||
});
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -124,10 +121,10 @@ struct ForwardTraversal {
|
|||
}
|
||||
|
||||
Progress update(Token* tok) {
|
||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||
actions |= action;
|
||||
if (!action.isNone() && !analyzeOnly)
|
||||
analyzer->update(tok, action);
|
||||
analyzer->update(tok, action, Analyzer::Direction::Forward);
|
||||
if (action.isInconclusive() && !analyzer->lowerToInconclusive())
|
||||
return Progress::Break;
|
||||
if (action.isInvalid())
|
||||
|
@ -153,19 +150,21 @@ struct ForwardTraversal {
|
|||
}
|
||||
|
||||
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()) {
|
||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||
if (pred(action))
|
||||
return tok;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ForwardAnalyzer::Action analyzeRecursive(const Token* start) {
|
||||
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
||||
Analyzer::Action analyzeRecursive(const Token* start)
|
||||
{
|
||||
Analyzer::Action result = Analyzer::Action::None;
|
||||
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())
|
||||
return Progress::Break;
|
||||
return Progress::Continue;
|
||||
|
@ -174,10 +173,11 @@ struct ForwardTraversal {
|
|||
return result;
|
||||
}
|
||||
|
||||
ForwardAnalyzer::Action analyzeRange(const Token* start, const Token* end) {
|
||||
ForwardAnalyzer::Action result = ForwardAnalyzer::Action::None;
|
||||
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()) {
|
||||
ForwardAnalyzer::Action action = analyzer->analyze(tok);
|
||||
Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward);
|
||||
if (action.isModified() || action.isInconclusive())
|
||||
return action;
|
||||
result = action;
|
||||
|
@ -211,25 +211,25 @@ struct ForwardTraversal {
|
|||
Inconclusive,
|
||||
};
|
||||
|
||||
ForwardAnalyzer::Action analyzeScope(const Token* endBlock) {
|
||||
return analyzeRange(endBlock->link(), endBlock);
|
||||
}
|
||||
Analyzer::Action analyzeScope(const Token* endBlock) { return analyzeRange(endBlock->link(), endBlock); }
|
||||
|
||||
ForwardAnalyzer::Action checkScope(Token* endBlock) {
|
||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
||||
Analyzer::Action checkScope(Token* endBlock)
|
||||
{
|
||||
Analyzer::Action a = analyzeScope(endBlock);
|
||||
forkScope(endBlock, a.isModified());
|
||||
return a;
|
||||
}
|
||||
|
||||
ForwardAnalyzer::Action checkScope(const Token* endBlock) {
|
||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
||||
Analyzer::Action checkScope(const Token* endBlock)
|
||||
{
|
||||
Analyzer::Action a = analyzeScope(endBlock);
|
||||
return a;
|
||||
}
|
||||
|
||||
Progress updateLoop(Token* endBlock, Token* condTok, Token* initTok = nullptr, Token* stepTok = nullptr) {
|
||||
const bool isDoWhile = precedes(endBlock, condTok);
|
||||
ForwardAnalyzer::Action bodyAnalysis = analyzeScope(endBlock);
|
||||
ForwardAnalyzer::Action allAnalysis = bodyAnalysis;
|
||||
Analyzer::Action bodyAnalysis = analyzeScope(endBlock);
|
||||
Analyzer::Action allAnalysis = bodyAnalysis;
|
||||
if (condTok)
|
||||
allAnalysis |= analyzeRecursive(condTok);
|
||||
if (initTok)
|
||||
|
@ -258,7 +258,7 @@ struct ForwardTraversal {
|
|||
|
||||
forkScope(endBlock, allAnalysis.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);
|
||||
if (!Token::Match(nextStatement, ";|} break ;"))
|
||||
return Progress::Break;
|
||||
|
@ -381,8 +381,8 @@ struct ForwardTraversal {
|
|||
// Check if condition is true or false
|
||||
bool checkThen, checkElse;
|
||||
std::tie(checkThen, checkElse) = evalCond(condTok);
|
||||
ForwardAnalyzer::Action thenAction = ForwardAnalyzer::Action::None;
|
||||
ForwardAnalyzer::Action elseAction = ForwardAnalyzer::Action::None;
|
||||
Analyzer::Action thenAction = Analyzer::Action::None;
|
||||
Analyzer::Action elseAction = Analyzer::Action::None;
|
||||
bool hasElse = Token::simpleMatch(endBlock, "} else {");
|
||||
bool bail = false;
|
||||
|
||||
|
@ -445,7 +445,7 @@ struct ForwardTraversal {
|
|||
}
|
||||
} else if (Token::simpleMatch(tok, "try {")) {
|
||||
Token* endBlock = tok->next()->link();
|
||||
ForwardAnalyzer::Action a = analyzeScope(endBlock);
|
||||
Analyzer::Action a = analyzeScope(endBlock);
|
||||
if (updateRange(tok->next(), endBlock) == Progress::Break)
|
||||
return Progress::Break;
|
||||
if (a.isModified())
|
||||
|
@ -567,12 +567,19 @@ struct ForwardTraversal {
|
|||
|
||||
};
|
||||
|
||||
ForwardAnalyzer::Action valueFlowGenericForward(Token* start,
|
||||
Analyzer::Action valueFlowGenericForward(Token* start,
|
||||
const Token* end,
|
||||
const ValuePtr<ForwardAnalyzer>& fa,
|
||||
const ValuePtr<Analyzer>& a,
|
||||
const Settings* settings)
|
||||
{
|
||||
ForwardTraversal ft{fa, settings};
|
||||
ForwardTraversal ft{a, settings};
|
||||
ft.updateRange(start, end);
|
||||
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
|
||||
#define forwardanalyzerH
|
||||
|
||||
#include "analyzer.h"
|
||||
#include <vector>
|
||||
|
||||
class Settings;
|
||||
class Token;
|
||||
template <class T> class ValuePtr;
|
||||
|
||||
struct ForwardAnalyzer {
|
||||
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,
|
||||
Analyzer::Action valueFlowGenericForward(Token* start,
|
||||
const Token* end,
|
||||
const ValuePtr<ForwardAnalyzer>& fa,
|
||||
const ValuePtr<Analyzer>& a,
|
||||
const Settings* settings);
|
||||
|
||||
Analyzer::Action valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>& a, const Settings* settings);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -48,6 +48,7 @@ HEADERS += $${PWD}/analyzerinfo.h \
|
|||
$${PWD}/platform.h \
|
||||
$${PWD}/preprocessor.h \
|
||||
$${PWD}/programmemory.h \
|
||||
$${PWD}/reverseanalyzer.h \
|
||||
$${PWD}/settings.h \
|
||||
$${PWD}/suppressions.h \
|
||||
$${PWD}/symboldatabase.h \
|
||||
|
@ -104,6 +105,7 @@ SOURCES += $${PWD}/analyzerinfo.cpp \
|
|||
$${PWD}/platform.cpp \
|
||||
$${PWD}/preprocessor.cpp \
|
||||
$${PWD}/programmemory.cpp \
|
||||
$${PWD}/reverseanalyzer.cpp \
|
||||
$${PWD}/settings.cpp \
|
||||
$${PWD}/suppressions.cpp \
|
||||
$${PWD}/symboldatabase.cpp \
|
||||
|
|
|
@ -204,6 +204,8 @@ static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok
|
|||
if (p.first != tok2->next()->varId())
|
||||
continue;
|
||||
const Token *vartok = tok2->tokAt(3);
|
||||
if (vartok == tok)
|
||||
continue;
|
||||
pm.setValue(vartok->varId(), p.second);
|
||||
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 "analyzer.h"
|
||||
#include "astutils.h"
|
||||
#include "errorlogger.h"
|
||||
#include "forwardanalyzer.h"
|
||||
|
@ -85,6 +86,7 @@
|
|||
#include "path.h"
|
||||
#include "platform.h"
|
||||
#include "programmemory.h"
|
||||
#include "reverseanalyzer.h"
|
||||
#include "settings.h"
|
||||
#include "standards.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 Variable* const var,
|
||||
std::list<ValueFlow::Value> values,
|
||||
|
@ -1762,273 +1764,7 @@ static void valueFlowReverse(TokenList *tokenlist,
|
|||
ValueFlow::Value val,
|
||||
ValueFlow::Value val2,
|
||||
ErrorLogger* errorLogger,
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const Settings* settings);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (lhsValue.isIntValue()) {
|
||||
|
@ -2309,17 +2057,13 @@ struct SelectMapValues {
|
|||
}
|
||||
};
|
||||
|
||||
struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
||||
struct ValueFlowAnalyzer : Analyzer {
|
||||
const TokenList* tokenlist;
|
||||
ProgramMemoryState pms;
|
||||
|
||||
ValueFlowForwardAnalyzer()
|
||||
: tokenlist(nullptr), pms()
|
||||
{}
|
||||
ValueFlowAnalyzer() : tokenlist(nullptr), pms() {}
|
||||
|
||||
ValueFlowForwardAnalyzer(const TokenList* t)
|
||||
: tokenlist(t), pms()
|
||||
{}
|
||||
ValueFlowAnalyzer(const TokenList* t) : tokenlist(t), pms() {}
|
||||
|
||||
virtual const ValueFlow::Value* getValue(const Token* tok) const = 0;
|
||||
virtual ValueFlow::Value* getValue(const Token* tok) = 0;
|
||||
|
@ -2372,6 +2116,14 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
if (isVariableChanged(tok, getIndirect(tok), getSettings(), isCPP())) {
|
||||
if (Token::Match(tok->astParent(), "*|[|.|++|--"))
|
||||
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 read;
|
||||
|
@ -2390,7 +2142,8 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
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);
|
||||
if (!value)
|
||||
return Action::None;
|
||||
|
@ -2400,6 +2153,9 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
|
||||
if (parent && parent->isAssignmentOp() && astIsLHS(tok) &&
|
||||
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 ValueFlow::Value* rhsValue = getKnownValue(rhs, ValueFlow::Value::ValueType::INT);
|
||||
Action a;
|
||||
|
@ -2407,8 +2163,12 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
a = Action::Invalid;
|
||||
else
|
||||
a = Action::Write;
|
||||
if (parent->str() != "=")
|
||||
if (parent->str() != "=") {
|
||||
a |= Action::Read;
|
||||
} else {
|
||||
if (rhsValue && !value->isImpossible() && value->equalValue(*rhsValue))
|
||||
a = Action::Idempotent;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
|
@ -2419,7 +2179,16 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
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)
|
||||
return;
|
||||
if (!tok->astParent())
|
||||
|
@ -2427,7 +2196,7 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
if (tok->astParent()->isAssignmentOp()) {
|
||||
// TODO: Check result
|
||||
if (evalAssignment(*value,
|
||||
tok->astParent()->str(),
|
||||
getAssign(tok->astParent(), d),
|
||||
*getKnownValue(tok->astParent()->astOperand2(), ValueFlow::Value::ValueType::INT))) {
|
||||
const std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " +
|
||||
value->infoString());
|
||||
|
@ -2439,30 +2208,33 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
value->intvalue = 0;
|
||||
}
|
||||
} 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);
|
||||
const std::string info(tok->str() + " is " + std::string(inc ? "incremented" : "decremented") +
|
||||
"', new value is " + value->infoString());
|
||||
const std::string info(tok->str() + " is " + opName + "', new value is " + value->infoString());
|
||||
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())
|
||||
return Action::Invalid;
|
||||
bool inconclusive = false;
|
||||
if (match(tok)) {
|
||||
const Token* parent = tok->astParent();
|
||||
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 w = isWritable(tok);
|
||||
Action w = isWritable(tok, d);
|
||||
if (w != Action::None)
|
||||
return w;
|
||||
return w | Action::Match;
|
||||
|
||||
// Check for modifications by function calls
|
||||
return isModified(tok);
|
||||
return isModified(tok) | Action::Match;
|
||||
} else if (tok->isUnaryOp("*")) {
|
||||
const Token* lifeTok = nullptr;
|
||||
for (const ValueFlow::Value& v:tok->astOperand1()->values()) {
|
||||
|
@ -2502,10 +2274,19 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
return {static_cast<int>(tok->values().front().intvalue)};
|
||||
std::vector<int> result;
|
||||
ProgramMemory pm = pms.get(tok, getProgramState());
|
||||
if (Token::Match(tok, "&&|%oror%")) {
|
||||
if (conditionIsTrue(tok, pm))
|
||||
result.push_back(1);
|
||||
if (conditionIsFalse(tok, pm))
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2525,32 +2306,37 @@ struct ValueFlowForwardAnalyzer : ForwardAnalyzer {
|
|||
makeConditional();
|
||||
}
|
||||
|
||||
virtual void update(Token* tok, Action a) OVERRIDE {
|
||||
virtual void update(Token* tok, Action a, Direction d) OVERRIDE
|
||||
{
|
||||
ValueFlow::Value* value = getValue(tok);
|
||||
if (!value)
|
||||
return;
|
||||
if (a.isRead())
|
||||
// Read first when moving forward
|
||||
if (d == Direction::Forward && a.isRead())
|
||||
setTokenValue(tok, *value, getSettings());
|
||||
if (a.isInconclusive())
|
||||
lowerToInconclusive();
|
||||
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*> aliases;
|
||||
ValueFlow::Value value;
|
||||
|
||||
SingleValueFlowForwardAnalyzer()
|
||||
: ValueFlowForwardAnalyzer()
|
||||
{}
|
||||
SingleValueFlowAnalyzer() : ValueFlowAnalyzer() {}
|
||||
|
||||
SingleValueFlowForwardAnalyzer(const ValueFlow::Value& v, const TokenList* t)
|
||||
: ValueFlowForwardAnalyzer(t), value(v)
|
||||
{}
|
||||
SingleValueFlowAnalyzer(const ValueFlow::Value& v, const TokenList* t) : ValueFlowAnalyzer(t), value(v) {}
|
||||
|
||||
const std::unordered_map<nonneg int, const Variable*>& getVars() const {
|
||||
return varids;
|
||||
|
@ -2645,17 +2431,26 @@ struct SingleValueFlowForwardAnalyzer : ValueFlowForwardAnalyzer {
|
|||
|
||||
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;
|
||||
|
||||
VariableForwardAnalyzer()
|
||||
: SingleValueFlowForwardAnalyzer(), var(nullptr)
|
||||
{}
|
||||
VariableAnalyzer() : SingleValueFlowAnalyzer(), var(nullptr) {}
|
||||
|
||||
VariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
|
||||
: SingleValueFlowForwardAnalyzer(val, t), var(v) {
|
||||
VariableAnalyzer(const Variable* v,
|
||||
const ValueFlow::Value& val,
|
||||
std::vector<const Variable*> paliases,
|
||||
const TokenList* t)
|
||||
: SingleValueFlowAnalyzer(val, t), var(v)
|
||||
{
|
||||
varids[var->declarationId()] = var;
|
||||
for (const Variable* av:paliases) {
|
||||
if (!av)
|
||||
|
@ -2704,7 +2499,7 @@ static std::vector<const Variable*> getAliasesFromValues(std::list<ValueFlow::Va
|
|||
return aliases;
|
||||
}
|
||||
|
||||
static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||
const Token* const endToken,
|
||||
const Variable* const var,
|
||||
std::list<ValueFlow::Value> values,
|
||||
|
@ -2712,15 +2507,15 @@ static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
|||
TokenList* const tokenlist,
|
||||
const Settings* const settings)
|
||||
{
|
||||
ForwardAnalyzer::Action actions;
|
||||
Analyzer::Action actions;
|
||||
for (ValueFlow::Value& v : values) {
|
||||
VariableForwardAnalyzer a(var, v, aliases, tokenlist);
|
||||
VariableAnalyzer a(var, v, aliases, tokenlist);
|
||||
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
static ForwardAnalyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||
static Analyzer::Action valueFlowForwardVariable(Token* const startToken,
|
||||
const Token* const endToken,
|
||||
const Variable* const var,
|
||||
std::list<ValueFlow::Value> values,
|
||||
|
@ -2747,17 +2542,16 @@ static bool valueFlowForwardVariable(Token* const startToken,
|
|||
return true;
|
||||
}
|
||||
|
||||
struct ExpressionForwardAnalyzer : SingleValueFlowForwardAnalyzer {
|
||||
struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
|
||||
const Token* expr;
|
||||
bool local;
|
||||
bool unknown;
|
||||
|
||||
ExpressionForwardAnalyzer()
|
||||
: SingleValueFlowForwardAnalyzer(), expr(nullptr), local(true), unknown(false)
|
||||
{}
|
||||
ExpressionAnalyzer() : SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false) {}
|
||||
|
||||
ExpressionForwardAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
||||
: SingleValueFlowForwardAnalyzer(val, t), expr(e), local(true), unknown(false) {
|
||||
ExpressionAnalyzer(const Token* e, const ValueFlow::Value& val, const TokenList* t)
|
||||
: SingleValueFlowAnalyzer(val, t), expr(e), local(true), unknown(false)
|
||||
{
|
||||
|
||||
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* exprTok,
|
||||
const std::list<ValueFlow::Value>& values,
|
||||
const TokenList* const tokenlist,
|
||||
const Settings* settings)
|
||||
{
|
||||
ForwardAnalyzer::Action actions;
|
||||
Analyzer::Action actions;
|
||||
for (const ValueFlow::Value& v : values) {
|
||||
ExpressionForwardAnalyzer a(exprTok, v, tokenlist);
|
||||
ExpressionAnalyzer a(exprTok, v, tokenlist);
|
||||
actions |= valueFlowGenericForward(startToken, endToken, a, settings);
|
||||
}
|
||||
return actions;
|
||||
|
@ -2891,7 +2685,18 @@ static const Token* solveExprValues(const Token* expr, std::list<ValueFlow::Valu
|
|||
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* exprTok,
|
||||
std::list<ValueFlow::Value> values,
|
||||
|
@ -2920,6 +2725,25 @@ static void valueFlowForward(Token* startToken,
|
|||
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)
|
||||
{
|
||||
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, const Variable*> vars;
|
||||
|
||||
MultiValueFlowForwardAnalyzer()
|
||||
: ValueFlowForwardAnalyzer(), values(), vars()
|
||||
{}
|
||||
MultiValueFlowAnalyzer() : ValueFlowAnalyzer(), values(), vars() {}
|
||||
|
||||
MultiValueFlowForwardAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const TokenList* t)
|
||||
: ValueFlowForwardAnalyzer(t), values(), vars() {
|
||||
MultiValueFlowAnalyzer(const std::unordered_map<const Variable*, ValueFlow::Value>& args, const TokenList* t)
|
||||
: ValueFlowAnalyzer(t), values(), vars()
|
||||
{
|
||||
for (const auto& p:args) {
|
||||
values[p.first->declarationId()] = p.second;
|
||||
vars[p.first->declarationId()] = p.first;
|
||||
|
@ -5285,7 +5108,7 @@ static void valueFlowInjectParameter(TokenList* tokenlist, ErrorLogger* errorLog
|
|||
}
|
||||
if (skip)
|
||||
continue;
|
||||
MultiValueFlowForwardAnalyzer a(arg, tokenlist);
|
||||
MultiValueFlowAnalyzer a(arg, tokenlist);
|
||||
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 {
|
||||
ContainerVariableForwardAnalyzer()
|
||||
: VariableForwardAnalyzer()
|
||||
{}
|
||||
struct ContainerVariableAnalyzer : VariableAnalyzer {
|
||||
ContainerVariableAnalyzer() : VariableAnalyzer() {}
|
||||
|
||||
ContainerVariableForwardAnalyzer(const Variable* v, const ValueFlow::Value& val, std::vector<const Variable*> paliases, const TokenList* t)
|
||||
: VariableForwardAnalyzer(v, val, std::move(paliases), t) {}
|
||||
ContainerVariableAnalyzer(const Variable* v,
|
||||
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 {
|
||||
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))
|
||||
return Action::None;
|
||||
if (d == Direction::Reverse)
|
||||
return Action::None;
|
||||
const ValueFlow::Value* value = getValue(tok);
|
||||
if (!value)
|
||||
return Action::None;
|
||||
|
@ -5977,7 +5805,10 @@ struct ContainerVariableForwardAnalyzer : VariableForwardAnalyzer {
|
|||
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)
|
||||
return;
|
||||
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 Variable* var,
|
||||
ValueFlow::Value value,
|
||||
TokenList* tokenlist)
|
||||
{
|
||||
ContainerVariableForwardAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
|
||||
ContainerVariableAnalyzer a(var, value, getAliasesFromValues({value}), tokenlist);
|
||||
return valueFlowGenericForward(tok, endToken, a, tokenlist->getSettings());
|
||||
}
|
||||
static ForwardAnalyzer::Action valueFlowContainerForward(Token* tok,
|
||||
static Analyzer::Action valueFlowContainerForward(Token* tok,
|
||||
const Variable* var,
|
||||
ValueFlow::Value value,
|
||||
TokenList* tokenlist)
|
||||
|
|
|
@ -25,8 +25,10 @@
|
|||
#include "mathlib.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
@ -51,6 +53,14 @@ namespace ValueFlow {
|
|||
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 {
|
||||
public:
|
||||
typedef std::pair<const Token *, std::string> ErrorPathItem;
|
||||
|
@ -111,19 +121,20 @@ namespace ValueFlow {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void visitValue(F f) {
|
||||
switch (valueType) {
|
||||
template <class T, class F>
|
||||
static void visitValue(T& self, F f)
|
||||
{
|
||||
switch (self.valueType) {
|
||||
case ValueType::INT:
|
||||
case ValueType::BUFFER_SIZE:
|
||||
case ValueType::CONTAINER_SIZE:
|
||||
case ValueType::ITERATOR_START:
|
||||
case ValueType::ITERATOR_END: {
|
||||
f(intvalue);
|
||||
f(self.intvalue);
|
||||
break;
|
||||
}
|
||||
case ValueType::FLOAT: {
|
||||
f(floatValue);
|
||||
f(self.floatValue);
|
||||
break;
|
||||
}
|
||||
case ValueType::UNINIT:
|
||||
|
@ -151,11 +162,19 @@ namespace ValueFlow {
|
|||
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() {
|
||||
if (bound == Bound::Lower)
|
||||
visitValue(increment{});
|
||||
visitValue(*this, increment{});
|
||||
else if (bound == Bound::Upper)
|
||||
visitValue(decrement{});
|
||||
visitValue(*this, decrement{});
|
||||
}
|
||||
|
||||
void invertBound() {
|
||||
|
|
|
@ -3453,6 +3453,20 @@ private:
|
|||
"}\n");
|
||||
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() {
|
||||
|
|
|
@ -101,6 +101,10 @@ private:
|
|||
TEST_CASE(nullpointer58); // #9807
|
||||
TEST_CASE(nullpointer59); // #9897
|
||||
TEST_CASE(nullpointer60); // #9842
|
||||
TEST_CASE(nullpointer61);
|
||||
TEST_CASE(nullpointer62);
|
||||
TEST_CASE(nullpointer63);
|
||||
TEST_CASE(nullpointer64);
|
||||
TEST_CASE(nullpointer_addressOf); // address of
|
||||
TEST_CASE(nullpointerSwitch); // #2626
|
||||
TEST_CASE(nullpointer_cast); // #4692
|
||||
|
@ -692,13 +696,16 @@ private:
|
|||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("void foo(int *p)\n"
|
||||
check("void foo(int *p, bool x)\n"
|
||||
"{\n"
|
||||
" int var1 = x ? *p : 5;\n"
|
||||
" if (!p)\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
|
||||
check("void f(int *p) {\n"
|
||||
|
@ -1892,6 +1899,122 @@ private:
|
|||
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
|
||||
check("void f() {\n"
|
||||
" struct X *x = 0;\n"
|
||||
|
|
|
@ -1082,7 +1082,7 @@ private:
|
|||
" if (y == 32) {}"
|
||||
"}\n";
|
||||
ASSERT_EQUALS("5,Assuming that condition 'y==32' is not redundant\n"
|
||||
"4,Compound assignment '+=', before assignment value is 20\n"
|
||||
"4,Compound assignment '+=', assigned value is 20\n"
|
||||
"2,Assignment 'x=y', assigned value is 20\n",
|
||||
getErrorPathForX(code, 3U));
|
||||
|
||||
|
@ -1191,9 +1191,6 @@ private:
|
|||
" if (x == 4);\n"
|
||||
"}";
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 2U, 3));
|
||||
ASSERT_EQUALS("4,Assuming that condition 'x==4' is not redundant\n"
|
||||
"3,x is incremented, before this increment the value is 3\n",
|
||||
getErrorPathForX(code, 2U));
|
||||
|
||||
// compound assignment += , -= , ...
|
||||
code = "void f(int x) {\n"
|
||||
|
@ -1222,15 +1219,16 @@ private:
|
|||
" x /= 5;\n"
|
||||
" if (x == 42);\n"
|
||||
"}";
|
||||
ASSERT(tokenValues(code, "x ;").empty());
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 2U, 210));
|
||||
|
||||
// bailout: assignment
|
||||
bailout("void f(int x) {\n"
|
||||
" x = y;\n"
|
||||
" if (x == 123) {}\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n"
|
||||
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowReverse bailout: assignment of x\n", errout.str());
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by &&
|
||||
|
@ -1345,14 +1343,14 @@ private:
|
|||
bailout("void f(int x) {\n"
|
||||
" y = ((x<0) ? x : ((x==2)?3:4));\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n"
|
||||
"[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str());
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n",
|
||||
errout.str());
|
||||
|
||||
bailout("int f(int x) {\n"
|
||||
" int r = x ? 1 / x : 0;\n"
|
||||
" if (x == 0) {}\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str());
|
||||
|
||||
code = "void f(int x) {\n"
|
||||
" int a =v x;\n"
|
||||
|
@ -1410,15 +1408,23 @@ private:
|
|||
" if (x != 123) { b = x; }\n"
|
||||
" if (x == 123) {}\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable b\n"
|
||||
"[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str());
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[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"
|
||||
" if (abc) { x = 1; }\n" // <- condition must be false if x is 7 in next line
|
||||
" if (x == 7) { }\n"
|
||||
"}";
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 2U, 7));
|
||||
|
||||
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() {
|
||||
|
@ -1451,8 +1457,9 @@ private:
|
|||
" case 2: if (x==5) {} break;\n"
|
||||
" };\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
|
||||
"[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str());
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[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"
|
||||
" switch (y) {\n"
|
||||
|
@ -1460,8 +1467,9 @@ private:
|
|||
" case 2: if (x==5) {} break;\n"
|
||||
" };\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
|
||||
"[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str());
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void valueFlowBeforeConditionMacro() {
|
||||
|
@ -1492,8 +1500,7 @@ private:
|
|||
" if (x==123){}\n"
|
||||
"}");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n"
|
||||
"[test.cpp:4]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable x stopping on goto label\n",
|
||||
"[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n",
|
||||
errout.str());
|
||||
|
||||
// #5721 - FP
|
||||
|
@ -1507,10 +1514,6 @@ private:
|
|||
"out:\n"
|
||||
" if (abc) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:2]: (debug) valueflow.cpp:1035:valueFlowReverse bailout: assignment of abc\n"
|
||||
"[test.cpp:8]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable abc stopping on goto label\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void valueFlowBeforeConditionForward() {
|
||||
|
|
Loading…
Reference in New Issue