Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ce44d2f
Replace with forward expression
pfultz2 Jul 12, 2021
644fed7
Check for const expression
pfultz2 Jul 13, 2021
9d46112
Fix issue with uknown in alias
pfultz2 Jul 14, 2021
2b67272
Check for expression that depend on this
pfultz2 Jul 14, 2021
6261342
Just use expression id
pfultz2 Jul 14, 2021
c2ebc78
Check if this variable is modified
pfultz2 Jul 14, 2021
5076e91
Improve checking for side effects
pfultz2 Jul 14, 2021
0abf67a
Dont set values for literals
pfultz2 Jul 14, 2021
4985604
Assume library function dont modify user-global variables
pfultz2 Jul 14, 2021
8d6b7d9
Switch to use container expression
pfultz2 Jul 14, 2021
144cff1
Fix check for single boolean expression
pfultz2 Jul 15, 2021
425d7c7
Update the tests to check for multiple values
pfultz2 Jul 15, 2021
d948015
Remove variable analyzer
pfultz2 Jul 16, 2021
56521b1
Remove unused functions
pfultz2 Jul 16, 2021
2c52100
Format
pfultz2 Jul 16, 2021
abb242f
Use update method
pfultz2 Jul 16, 2021
3e21935
Add spaces
pfultz2 Jul 16, 2021
9dfdeb0
Format
pfultz2 Jul 16, 2021
940c162
Fix null pointer deref
pfultz2 Jul 16, 2021
f00ca2c
Fix possible null pointer deref
pfultz2 Jul 16, 2021
314317c
Add const
pfultz2 Jul 16, 2021
c477e6a
Add test for non-const
pfultz2 Jul 16, 2021
1c0868a
Add test for overloaded const
pfultz2 Jul 16, 2021
661ee2e
Fix test with non-const method
pfultz2 Jul 16, 2021
faa3982
Fix test cases
pfultz2 Jul 16, 2021
1d67319
Format
pfultz2 Jul 16, 2021
cf5c90e
Merge branch 'main' into remove-variable-analyzer
pfultz2 Jul 16, 2021
a8c33c4
Merge branch 'main' into remove-variable-analyzer
pfultz2 Jul 16, 2021
0f2f2d6
Improve const check on unknown functions
pfultz2 Jul 16, 2021
778ca51
Dont treat unknown nullary functions as no side effects
pfultz2 Jul 16, 2021
04b40a2
Assume this pointer cant be modified
pfultz2 Jul 17, 2021
6830f72
Fix null pointer deref
pfultz2 Jul 17, 2021
1333ed7
Merge branch 'main' into remove-variable-analyzer
pfultz2 Jul 18, 2021
7f1739f
Fix FPs
pfultz2 Jul 18, 2021
3a9a5d3
Format
pfultz2 Jul 18, 2021
6710583
Run dmake
pfultz2 Jul 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ $(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/library.h lib/mathlib.h lib/reverseanalyzer.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h
$(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/astutils.h 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/summaries.h lib/suppressions.h lib/timer.h lib/utils.h lib/valueflow.h
Expand Down
1 change: 1 addition & 0 deletions cfg/std.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7717,6 +7717,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
<!-- template< class T > typename std::remove_reference<T>::type&& move( T&& t ) noexcept; // (since C++11) -->
<!-- template< class T > constexpr typename std::remove_reference<T>::type&& move( T&& t ) noexcept; // (until C++14) -->
<function name="std::move">
<pure/>
<noreturn>false</noreturn>
<use-retval/>
<arg nr="1">
Expand Down
1 change: 0 additions & 1 deletion cmake/compileroptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Wcast-qual) # Cast for removing type qualifiers
add_compile_options(-Wno-deprecated-declarations)
add_compile_options(-Wfloat-equal) # Floating values used in equality comparisons
add_compile_options(-Wmissing-declarations) # If a global function is defined without a previous declaration
add_compile_options(-Wmissing-format-attribute) #
Expand Down
7 changes: 7 additions & 0 deletions lib/analyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ struct Analyzer {
{}
Action action;
Terminate terminate;

void update(Result rhs)
{
if (terminate == Terminate::None)
terminate = rhs.terminate;
action |= rhs.action;
}
};

enum class Direction { Forward, Reverse };
Expand Down
121 changes: 106 additions & 15 deletions lib/astutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,10 @@ static bool hasToken(const Token * startTok, const Token * stopTok, const Token
template <class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
static T* previousBeforeAstLeftmostLeafGeneric(T* tok)
{
if (!tok)
return nullptr;
T* leftmostLeaf = tok;
while (leftmostLeaf && leftmostLeaf->astOperand1())
while (leftmostLeaf->astOperand1())
leftmostLeaf = leftmostLeaf->astOperand1();
return leftmostLeaf->previous();
}
Expand Down Expand Up @@ -1392,14 +1394,96 @@ bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * cons
return false;
}

static bool functionModifiesArguments(const Function* f)
{
return std::any_of(f->argumentList.begin(), f->argumentList.end(), [](const Variable& var) {
if (var.isReference() || var.isPointer())
return !var.isConst();
return true;
});
}

bool isConstFunctionCall(const Token* ftok, const Library& library)
{
if (!Token::Match(ftok, "%name% ("))
return false;
if (const Function* f = ftok->function()) {
if (f->isAttributePure() || f->isAttributeConst())
return true;
if (Function::returnsVoid(f))
return false;
// Any modified arguments
if (functionModifiesArguments(f))
return false;
// Member function call
if (Token::simpleMatch(ftok->previous(), ".")) {
if (f->isConst())
return true;
// Check for const overloaded function that just return the const version
if (!Function::returnsConst(f)) {
std::vector<const Function*> fs = f->getOverloadedFunctions();
if (std::any_of(fs.begin(), fs.end(), [&](const Function* g) {
if (f == g)
return false;
if (f->argumentList.size() != g->argumentList.size())
return false;
if (functionModifiesArguments(g))
return false;
if (g->isConst() && Function::returnsConst(g))
return true;
return false;
}))
return true;
}
return false;
} else if (f->argumentList.empty()) {
// TODO: Check for constexpr
return false;
}
} else if (const Library::Function* f = library.getFunction(ftok)) {
if (f->ispure)
return true;
for (auto&& p : f->argumentChecks) {
const Library::ArgumentChecks& ac = p.second;
if (ac.direction != Library::ArgumentChecks::Direction::DIR_IN)
return false;
}
if (Token::simpleMatch(ftok->previous(), ".")) {
if (!f->isconst)
return false;
} else if (f->argumentChecks.empty()) {
return false;
}
} else {
bool memberFunction = Token::Match(ftok->previous(), ". %name% (");
bool constMember = !memberFunction;
if (Token::Match(ftok->tokAt(-2), "%var% . %name% (")) {
const Variable* var = ftok->tokAt(-2)->variable();
if (var)
constMember = var->isConst();
}
// TODO: Only check const on lvalues
std::vector<const Token*> args = getArguments(ftok);
if (memberFunction && args.empty())
return false;
return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) {
const Variable* var = tok->variable();
if (var)
return var->isConst();
return false;
});
}
return true;
}

bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp)
{
if (!tok)
return true;
if (tok->variable() && tok->variable()->isVolatile())
return false;
if (tok->isName() && tok->next()->str() == "(") {
if (!tok->function() && !Token::Match(tok->previous(), ".|::") && !library.isFunctionConst(tok->str(), pure))
return false;
else if (tok->function() && !tok->function()->isConst())
if (!isConstFunctionCall(tok, library))
return false;
}
if (tok->tokType() == Token::eIncDecOp)
Expand Down Expand Up @@ -1880,6 +1964,9 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings *settings,
// Member function call
if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) {
const Variable * var = tok->variable();
// Member function cannot change what `this` points to
if (indirect == 0 && astIsPointer(tok))
return false;
bool isConst = var && var->isConst();
if (!isConst) {
const ValueType * valueType = var->valueType();
Expand Down Expand Up @@ -2092,24 +2179,28 @@ bool isVariablesChanged(const Token* start,
return false;
}

bool isThisChanged(const Token* tok, int indirect, const Settings* settings, bool cpp)
{
if (Token::Match(tok->previous(), "%name% (")) {
if (tok->previous()->function()) {
return (!tok->previous()->function()->isConst());
} else if (!tok->previous()->isKeyword()) {
return true;
}
}
if (isVariableChanged(tok, indirect, settings, cpp))
return true;
return false;
}

bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp)
{
if (!precedes(start, end))
return false;
for (const Token* tok = start; tok != end; tok = tok->next()) {
if (!exprDependsOnThis(tok))
continue;
if (Token::Match(tok->previous(), "%name% (")) {
if (tok->previous()->function()) {
if (!tok->previous()->function()->isConst())
return true;
else
continue;
} else if (!tok->previous()->isKeyword()) {
return true;
}
}
if (isVariableChanged(tok, indirect, settings, cpp))
if (isThisChanged(tok, indirect, settings, cpp))
return true;
}
return false;
Expand Down
3 changes: 3 additions & 0 deletions lib/astutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token

bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr);

bool isConstFunctionCall(const Token* ftok, const Library& library);

bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp);

bool isWithoutSideEffects(bool cpp, const Token* tok);
Expand Down Expand Up @@ -226,6 +228,7 @@ bool isVariablesChanged(const Token* start,
const Settings* settings,
bool cpp);

bool isThisChanged(const Token* tok, int indirect, const Settings* settings, bool cpp);
bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp);

const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, bool cpp, int depth = 20);
Expand Down
8 changes: 4 additions & 4 deletions lib/checkcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,15 +441,15 @@ void CheckCondition::duplicateCondition()
if (scope.type != Scope::eIf)
continue;

const Token *cond1 = scope.classDef->next()->astOperand2();
const Token* tok2 = scope.classDef->next();
if (!tok2)
continue;
const Token* cond1 = tok2->astOperand2();
if (!cond1)
continue;
if (cond1->hasKnownIntValue())
continue;

const Token *tok2 = scope.classDef->next();
if (!tok2)
continue;
tok2 = tok2->link();
if (!Token::simpleMatch(tok2, ") {"))
continue;
Expand Down
4 changes: 3 additions & 1 deletion lib/reverseanalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "astutils.h"
#include "errortypes.h"
#include "forwardanalyzer.h"
#include "settings.h"
#include "symboldatabase.h"
#include "token.h"
#include "valueptr.h"
Expand Down Expand Up @@ -171,7 +172,8 @@ struct ReverseTraversal {
settings);
}
// Assignment to
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownValue()) {
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownValue() &&
isConstExpression(assignTok->astOperand2(), settings->library, true, true)) {
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
if (a) {
Expand Down
23 changes: 23 additions & 0 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3943,6 +3943,29 @@ bool Function::isImplicitlyVirtual(bool defaultVal) const
return defaultVal; //If we can't see all the bases classes then we can't say conclusively
}

std::vector<const Function*> Function::getOverloadedFunctions() const
{
std::vector<const Function*> result;
const Scope* scope = nestedIn;

while (scope) {
const bool isMemberFunction = scope->isClassOrStruct() && !isStatic();
for (std::multimap<std::string, const Function*>::const_iterator it = scope->functionMap.find(tokenDef->str());
it != scope->functionMap.end() && it->first == tokenDef->str();
++it) {
const Function* func = it->second;
if (isMemberFunction == func->isStatic())
continue;
result.push_back(func);
}
if (isMemberFunction)
break;
scope = scope->nestedIn;
}

return result;
}

const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const
{
if (foundAllBaseClasses)
Expand Down
2 changes: 2 additions & 0 deletions lib/symboldatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ class CPPCHECKLIB Function {
/** @brief check if this function is virtual in the base classes */
bool isImplicitlyVirtual(bool defaultVal = false) const;

std::vector<const Function*> getOverloadedFunctions() const;

/** @brief get function in base class that is overridden */
const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const;

Expand Down
Loading