Skip to content

Commit 59a1c1a

Browse files
authored
Refactor: Remove variable analyzer (danmar#3339)
1 parent 59c797c commit 59a1c1a

File tree

13 files changed

+403
-372
lines changed

13 files changed

+403
-372
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ $(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.
545545
$(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
546546
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/programmemory.o $(libcppdir)/programmemory.cpp
547547

548-
$(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
548+
$(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
549549
$(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o $(libcppdir)/reverseanalyzer.o $(libcppdir)/reverseanalyzer.cpp
550550

551551
$(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

cfg/std.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7717,6 +7717,7 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
77177717
<!-- template< class T > typename std::remove_reference<T>::type&& move( T&& t ) noexcept; // (since C++11) -->
77187718
<!-- template< class T > constexpr typename std::remove_reference<T>::type&& move( T&& t ) noexcept; // (until C++14) -->
77197719
<function name="std::move">
7720+
<pure/>
77207721
<noreturn>false</noreturn>
77217722
<use-retval/>
77227723
<arg nr="1">

cmake/compileroptions.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"
2626
add_compile_options(-Wall)
2727
add_compile_options(-Wextra)
2828
add_compile_options(-Wcast-qual) # Cast for removing type qualifiers
29-
add_compile_options(-Wno-deprecated-declarations)
3029
add_compile_options(-Wfloat-equal) # Floating values used in equality comparisons
3130
add_compile_options(-Wmissing-declarations) # If a global function is defined without a previous declaration
3231
add_compile_options(-Wmissing-format-attribute) #

lib/analyzer.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ struct Analyzer {
119119
{}
120120
Action action;
121121
Terminate terminate;
122+
123+
void update(Result rhs)
124+
{
125+
if (terminate == Terminate::None)
126+
terminate = rhs.terminate;
127+
action |= rhs.action;
128+
}
122129
};
123130

124131
enum class Direction { Forward, Reverse };

lib/astutils.cpp

Lines changed: 106 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -347,8 +347,10 @@ static bool hasToken(const Token * startTok, const Token * stopTok, const Token
347347
template <class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*>)>
348348
static T* previousBeforeAstLeftmostLeafGeneric(T* tok)
349349
{
350+
if (!tok)
351+
return nullptr;
350352
T* leftmostLeaf = tok;
351-
while (leftmostLeaf && leftmostLeaf->astOperand1())
353+
while (leftmostLeaf->astOperand1())
352354
leftmostLeaf = leftmostLeaf->astOperand1();
353355
return leftmostLeaf->previous();
354356
}
@@ -1392,14 +1394,96 @@ bool isOppositeExpression(bool cpp, const Token * const tok1, const Token * cons
13921394
return false;
13931395
}
13941396

1397+
static bool functionModifiesArguments(const Function* f)
1398+
{
1399+
return std::any_of(f->argumentList.begin(), f->argumentList.end(), [](const Variable& var) {
1400+
if (var.isReference() || var.isPointer())
1401+
return !var.isConst();
1402+
return true;
1403+
});
1404+
}
1405+
1406+
bool isConstFunctionCall(const Token* ftok, const Library& library)
1407+
{
1408+
if (!Token::Match(ftok, "%name% ("))
1409+
return false;
1410+
if (const Function* f = ftok->function()) {
1411+
if (f->isAttributePure() || f->isAttributeConst())
1412+
return true;
1413+
if (Function::returnsVoid(f))
1414+
return false;
1415+
// Any modified arguments
1416+
if (functionModifiesArguments(f))
1417+
return false;
1418+
// Member function call
1419+
if (Token::simpleMatch(ftok->previous(), ".")) {
1420+
if (f->isConst())
1421+
return true;
1422+
// Check for const overloaded function that just return the const version
1423+
if (!Function::returnsConst(f)) {
1424+
std::vector<const Function*> fs = f->getOverloadedFunctions();
1425+
if (std::any_of(fs.begin(), fs.end(), [&](const Function* g) {
1426+
if (f == g)
1427+
return false;
1428+
if (f->argumentList.size() != g->argumentList.size())
1429+
return false;
1430+
if (functionModifiesArguments(g))
1431+
return false;
1432+
if (g->isConst() && Function::returnsConst(g))
1433+
return true;
1434+
return false;
1435+
}))
1436+
return true;
1437+
}
1438+
return false;
1439+
} else if (f->argumentList.empty()) {
1440+
// TODO: Check for constexpr
1441+
return false;
1442+
}
1443+
} else if (const Library::Function* f = library.getFunction(ftok)) {
1444+
if (f->ispure)
1445+
return true;
1446+
for (auto&& p : f->argumentChecks) {
1447+
const Library::ArgumentChecks& ac = p.second;
1448+
if (ac.direction != Library::ArgumentChecks::Direction::DIR_IN)
1449+
return false;
1450+
}
1451+
if (Token::simpleMatch(ftok->previous(), ".")) {
1452+
if (!f->isconst)
1453+
return false;
1454+
} else if (f->argumentChecks.empty()) {
1455+
return false;
1456+
}
1457+
} else {
1458+
bool memberFunction = Token::Match(ftok->previous(), ". %name% (");
1459+
bool constMember = !memberFunction;
1460+
if (Token::Match(ftok->tokAt(-2), "%var% . %name% (")) {
1461+
const Variable* var = ftok->tokAt(-2)->variable();
1462+
if (var)
1463+
constMember = var->isConst();
1464+
}
1465+
// TODO: Only check const on lvalues
1466+
std::vector<const Token*> args = getArguments(ftok);
1467+
if (memberFunction && args.empty())
1468+
return false;
1469+
return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) {
1470+
const Variable* var = tok->variable();
1471+
if (var)
1472+
return var->isConst();
1473+
return false;
1474+
});
1475+
}
1476+
return true;
1477+
}
1478+
13951479
bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp)
13961480
{
13971481
if (!tok)
13981482
return true;
1483+
if (tok->variable() && tok->variable()->isVolatile())
1484+
return false;
13991485
if (tok->isName() && tok->next()->str() == "(") {
1400-
if (!tok->function() && !Token::Match(tok->previous(), ".|::") && !library.isFunctionConst(tok->str(), pure))
1401-
return false;
1402-
else if (tok->function() && !tok->function()->isConst())
1486+
if (!isConstFunctionCall(tok, library))
14031487
return false;
14041488
}
14051489
if (tok->tokType() == Token::eIncDecOp)
@@ -1880,6 +1964,9 @@ bool isVariableChanged(const Token *tok, int indirect, const Settings *settings,
18801964
// Member function call
18811965
if (tok->variable() && Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && tok2->astParent()->astOperand1() == tok2) {
18821966
const Variable * var = tok->variable();
1967+
// Member function cannot change what `this` points to
1968+
if (indirect == 0 && astIsPointer(tok))
1969+
return false;
18831970
bool isConst = var && var->isConst();
18841971
if (!isConst) {
18851972
const ValueType * valueType = var->valueType();
@@ -2092,24 +2179,28 @@ bool isVariablesChanged(const Token* start,
20922179
return false;
20932180
}
20942181

2182+
bool isThisChanged(const Token* tok, int indirect, const Settings* settings, bool cpp)
2183+
{
2184+
if (Token::Match(tok->previous(), "%name% (")) {
2185+
if (tok->previous()->function()) {
2186+
return (!tok->previous()->function()->isConst());
2187+
} else if (!tok->previous()->isKeyword()) {
2188+
return true;
2189+
}
2190+
}
2191+
if (isVariableChanged(tok, indirect, settings, cpp))
2192+
return true;
2193+
return false;
2194+
}
2195+
20952196
bool isThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings, bool cpp)
20962197
{
20972198
if (!precedes(start, end))
20982199
return false;
20992200
for (const Token* tok = start; tok != end; tok = tok->next()) {
21002201
if (!exprDependsOnThis(tok))
21012202
continue;
2102-
if (Token::Match(tok->previous(), "%name% (")) {
2103-
if (tok->previous()->function()) {
2104-
if (!tok->previous()->function()->isConst())
2105-
return true;
2106-
else
2107-
continue;
2108-
} else if (!tok->previous()->isKeyword()) {
2109-
return true;
2110-
}
2111-
}
2112-
if (isVariableChanged(tok, indirect, settings, cpp))
2203+
if (isThisChanged(tok, indirect, settings, cpp))
21132204
return true;
21142205
}
21152206
return false;

lib/astutils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token
171171

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

174+
bool isConstFunctionCall(const Token* ftok, const Library& library);
175+
174176
bool isConstExpression(const Token *tok, const Library& library, bool pure, bool cpp);
175177

176178
bool isWithoutSideEffects(bool cpp, const Token* tok);
@@ -226,6 +228,7 @@ bool isVariablesChanged(const Token* start,
226228
const Settings* settings,
227229
bool cpp);
228230

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

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

lib/checkcondition.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,15 +441,15 @@ void CheckCondition::duplicateCondition()
441441
if (scope.type != Scope::eIf)
442442
continue;
443443

444-
const Token *cond1 = scope.classDef->next()->astOperand2();
444+
const Token* tok2 = scope.classDef->next();
445+
if (!tok2)
446+
continue;
447+
const Token* cond1 = tok2->astOperand2();
445448
if (!cond1)
446449
continue;
447450
if (cond1->hasKnownIntValue())
448451
continue;
449452

450-
const Token *tok2 = scope.classDef->next();
451-
if (!tok2)
452-
continue;
453453
tok2 = tok2->link();
454454
if (!Token::simpleMatch(tok2, ") {"))
455455
continue;

lib/reverseanalyzer.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "astutils.h"
44
#include "errortypes.h"
55
#include "forwardanalyzer.h"
6+
#include "settings.h"
67
#include "symboldatabase.h"
78
#include "token.h"
89
#include "valueptr.h"
@@ -171,7 +172,8 @@ struct ReverseTraversal {
171172
settings);
172173
}
173174
// Assignment to
174-
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownValue()) {
175+
} else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownValue() &&
176+
isConstExpression(assignTok->astOperand2(), settings->library, true, true)) {
175177
const std::string info = "Assignment to '" + assignTok->expressionString() + "'";
176178
ValuePtr<Analyzer> a = analyzer->reanalyze(assignTok->astOperand2(), info);
177179
if (a) {

lib/symboldatabase.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3943,6 +3943,29 @@ bool Function::isImplicitlyVirtual(bool defaultVal) const
39433943
return defaultVal; //If we can't see all the bases classes then we can't say conclusively
39443944
}
39453945

3946+
std::vector<const Function*> Function::getOverloadedFunctions() const
3947+
{
3948+
std::vector<const Function*> result;
3949+
const Scope* scope = nestedIn;
3950+
3951+
while (scope) {
3952+
const bool isMemberFunction = scope->isClassOrStruct() && !isStatic();
3953+
for (std::multimap<std::string, const Function*>::const_iterator it = scope->functionMap.find(tokenDef->str());
3954+
it != scope->functionMap.end() && it->first == tokenDef->str();
3955+
++it) {
3956+
const Function* func = it->second;
3957+
if (isMemberFunction == func->isStatic())
3958+
continue;
3959+
result.push_back(func);
3960+
}
3961+
if (isMemberFunction)
3962+
break;
3963+
scope = scope->nestedIn;
3964+
}
3965+
3966+
return result;
3967+
}
3968+
39463969
const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const
39473970
{
39483971
if (foundAllBaseClasses)

lib/symboldatabase.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,8 @@ class CPPCHECKLIB Function {
769769
/** @brief check if this function is virtual in the base classes */
770770
bool isImplicitlyVirtual(bool defaultVal = false) const;
771771

772+
std::vector<const Function*> getOverloadedFunctions() const;
773+
772774
/** @brief get function in base class that is overridden */
773775
const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const;
774776

0 commit comments

Comments
 (0)