parent
381361629e
commit
215124461e
|
@ -2291,11 +2291,37 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
|
||||||
auto getFuncTok = [](const Token* tok) -> const Token* {
|
auto getFuncTok = [](const Token* tok) -> const Token* {
|
||||||
if (Token::simpleMatch(tok, "this"))
|
if (Token::simpleMatch(tok, "this"))
|
||||||
tok = getFuncTokFromThis(tok);
|
tok = getFuncTokFromThis(tok);
|
||||||
if ((Token::Match(tok, "%name% (|{") || Token::simpleMatch(tok->astParent(), "return {")) && !tok->isStandardType() && !tok->isKeyword())
|
bool isReturn = false;
|
||||||
|
if ((Token::Match(tok, "%name% (|{") || (isReturn = Token::simpleMatch(tok->astParent(), "return {"))) && !tok->isStandardType() && !tok->isKeyword()) {
|
||||||
|
if (isReturn)
|
||||||
|
tok = tok->astParent();
|
||||||
return tok;
|
return tok;
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto checkFuncCall = [this, &memberAccessed](const Token* funcTok, const Scope* scope) {
|
||||||
|
if (isMemberFunc(scope, funcTok) && (funcTok->strAt(-1) != "." || Token::simpleMatch(funcTok->tokAt(-2), "this ."))) {
|
||||||
|
if (!isConstMemberFunc(scope, funcTok))
|
||||||
|
return false;
|
||||||
|
memberAccessed = true;
|
||||||
|
}
|
||||||
|
// Member variable given as parameter
|
||||||
|
const Token *lpar = funcTok->next();
|
||||||
|
if (Token::simpleMatch(lpar, "( ) ("))
|
||||||
|
lpar = lpar->tokAt(2);
|
||||||
|
for (const Token* tok = lpar->next(); tok && tok != funcTok->next()->link(); tok = tok->next()) {
|
||||||
|
if (tok->str() == "(")
|
||||||
|
tok = tok->link();
|
||||||
|
else if ((tok->isName() && isMemberVar(scope, tok)) || (tok->isUnaryOp("&") && (tok = tok->astOperand1()))) {
|
||||||
|
const Variable* var = tok->variable();
|
||||||
|
if (!var || !var->isMutable())
|
||||||
|
return false; // TODO: Only bailout if function takes argument as non-const reference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
// if the function doesn't have any assignment nor function call,
|
// if the function doesn't have any assignment nor function call,
|
||||||
// it can be a const function..
|
// it can be a const function..
|
||||||
for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) {
|
for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) {
|
||||||
|
@ -2374,6 +2400,18 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto hasOverloadedMemberAccess = [](const Token* end, const Scope* scope) -> bool {
|
||||||
|
if (!end || !scope || !Token::simpleMatch(end->astParent(), "."))
|
||||||
|
return false;
|
||||||
|
auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [](const Function& f) {
|
||||||
|
return f.isConst() && f.name() == "operator.";
|
||||||
|
});
|
||||||
|
if (it == scope->functionList.end() || !it->retType || !it->retType->classScope)
|
||||||
|
return false;
|
||||||
|
const Function* func = it->retType->classScope->findFunction(end, /*requireConst*/ true);
|
||||||
|
return func && func->isConst();
|
||||||
|
};
|
||||||
|
|
||||||
if (end->strAt(1) == "(") {
|
if (end->strAt(1) == "(") {
|
||||||
const Variable *var = lastVarTok->variable();
|
const Variable *var = lastVarTok->variable();
|
||||||
if (!var)
|
if (!var)
|
||||||
|
@ -2389,6 +2427,9 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
|
||||||
;
|
;
|
||||||
else if (var->smartPointerType() && var->smartPointerType()->classScope && isConstMemberFunc(var->smartPointerType()->classScope, end)) {
|
else if (var->smartPointerType() && var->smartPointerType()->classScope && isConstMemberFunc(var->smartPointerType()->classScope, end)) {
|
||||||
;
|
;
|
||||||
|
}
|
||||||
|
else if (hasOverloadedMemberAccess(end, var->typeScope())) {
|
||||||
|
;
|
||||||
} else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end))
|
} else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -2416,8 +2457,8 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression
|
tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression
|
||||||
if (tok1 == end && Token::Match(end->previous(), ". %name% ( !!)"))
|
if (tok1 == end && Token::Match(end->previous(), ". %name% ( !!)") && !checkFuncCall(tok1, scope)) // function call on member
|
||||||
tok1 = tok1->previous(); // check function call
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// streaming: <<
|
// streaming: <<
|
||||||
|
@ -2435,24 +2476,8 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool&
|
||||||
|
|
||||||
// function/constructor call, return init list
|
// function/constructor call, return init list
|
||||||
else if (const Token* funcTok = getFuncTok(tok1)) {
|
else if (const Token* funcTok = getFuncTok(tok1)) {
|
||||||
if (isMemberFunc(scope, funcTok) && (funcTok->strAt(-1) != "." || Token::simpleMatch(funcTok->tokAt(-2), "this ."))) {
|
if (!checkFuncCall(funcTok, scope))
|
||||||
if (!isConstMemberFunc(scope, funcTok))
|
return false;
|
||||||
return false;
|
|
||||||
memberAccessed = true;
|
|
||||||
}
|
|
||||||
// Member variable given as parameter
|
|
||||||
const Token *lpar = funcTok->next();
|
|
||||||
if (Token::simpleMatch(lpar, "( ) ("))
|
|
||||||
lpar = lpar->tokAt(2);
|
|
||||||
for (const Token* tok2 = lpar->next(); tok2 && tok2 != funcTok->next()->link(); tok2 = tok2->next()) {
|
|
||||||
if (tok2->str() == "(")
|
|
||||||
tok2 = tok2->link();
|
|
||||||
else if ((tok2->isName() && isMemberVar(scope, tok2)) || (tok2->isUnaryOp("&") && (tok2 = tok2->astOperand1()))) {
|
|
||||||
const Variable* var = tok2->variable();
|
|
||||||
if (!var || !var->isMutable())
|
|
||||||
return false; // TODO: Only bailout if function takes argument as non-const reference
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) {
|
} else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,7 +452,7 @@ bool CheckMemoryLeakInFunction::test_white_list(const std::string &funcname, con
|
||||||
// a = malloc(10); a = realloc(a, 100);
|
// a = malloc(10); a = realloc(a, 100);
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
void CheckMemoryLeakInFunction::checkReallocUsage()
|
void CheckMemoryLeakInFunction::checkReallocUsage() const
|
||||||
{
|
{
|
||||||
// only check functions
|
// only check functions
|
||||||
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
|
||||||
|
|
|
@ -183,7 +183,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Checking for a memory leak caused by improper realloc usage.
|
* Checking for a memory leak caused by improper realloc usage.
|
||||||
*/
|
*/
|
||||||
void checkReallocUsage();
|
void checkReallocUsage() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Report all possible errors (for the --errorlist) */
|
/** Report all possible errors (for the --errorlist) */
|
||||||
|
|
|
@ -288,7 +288,7 @@ struct ForwardTraversal {
|
||||||
return ft;
|
return ft;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ForwardTraversal> tryForkScope(Token* endBlock, bool isModified = false) {
|
std::vector<ForwardTraversal> tryForkScope(Token* endBlock, bool isModified = false) const {
|
||||||
if (analyzer->updateScope(endBlock, isModified)) {
|
if (analyzer->updateScope(endBlock, isModified)) {
|
||||||
ForwardTraversal ft = fork();
|
ForwardTraversal ft = fork();
|
||||||
return {std::move(ft)};
|
return {std::move(ft)};
|
||||||
|
@ -296,7 +296,7 @@ struct ForwardTraversal {
|
||||||
return std::vector<ForwardTraversal> {};
|
return std::vector<ForwardTraversal> {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ForwardTraversal> tryForkUpdateScope(Token* endBlock, bool isModified = false) {
|
std::vector<ForwardTraversal> tryForkUpdateScope(Token* endBlock, bool isModified = false) const {
|
||||||
std::vector<ForwardTraversal> result = tryForkScope(endBlock, isModified);
|
std::vector<ForwardTraversal> result = tryForkScope(endBlock, isModified);
|
||||||
for (ForwardTraversal& ft : result)
|
for (ForwardTraversal& ft : result)
|
||||||
ft.updateScope(endBlock);
|
ft.updateScope(endBlock);
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct ReverseTraversal {
|
||||||
ValuePtr<Analyzer> analyzer;
|
ValuePtr<Analyzer> analyzer;
|
||||||
const Settings* settings;
|
const Settings* settings;
|
||||||
|
|
||||||
std::pair<bool, bool> evalCond(const Token* tok) {
|
std::pair<bool, bool> evalCond(const Token* tok) const {
|
||||||
std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
|
std::vector<MathLib::bigint> result = analyzer->evaluate(tok);
|
||||||
// TODO: We should convert to bool
|
// TODO: We should convert to bool
|
||||||
const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) {
|
||||||
|
@ -142,7 +142,7 @@ struct ReverseTraversal {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token* isDeadCode(Token* tok, const Token* end = nullptr) {
|
Token* isDeadCode(Token* tok, const Token* end = nullptr) const {
|
||||||
int opSide = 0;
|
int opSide = 0;
|
||||||
for (; tok && tok->astParent(); tok = tok->astParent()) {
|
for (; tok && tok->astParent(); tok = tok->astParent()) {
|
||||||
if (tok == end)
|
if (tok == end)
|
||||||
|
|
|
@ -6221,8 +6221,8 @@ private:
|
||||||
errout.str());
|
errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void const81() { // #11330
|
void const81() {
|
||||||
checkConst("struct A {\n"
|
checkConst("struct A {\n" // #11330
|
||||||
" bool f() const;\n"
|
" bool f() const;\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
"struct S {\n"
|
"struct S {\n"
|
||||||
|
@ -6233,6 +6233,94 @@ private:
|
||||||
"};\n");
|
"};\n");
|
||||||
ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'S::g' can be const.\n",
|
ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'S::g' can be const.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n" // #11499
|
||||||
|
" void f() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
" const T* operator->() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" void g() { p->f(); }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'S::g' can be const.\n",
|
||||||
|
errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n"
|
||||||
|
" void f(int) const;\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
" const T* operator->() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" void g() { p->f(1); }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n"
|
||||||
|
" void f(void*) const;\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
" const T* operator->() const;\n"
|
||||||
|
" P<T>& operator=(P) {\n"
|
||||||
|
" return *this;\n"
|
||||||
|
" }\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" std::vector<S> g() { p->f(nullptr); return {}; }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:14]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n"
|
||||||
|
" void f();\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
" const T* operator->() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" void g() { p->f(); }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n"
|
||||||
|
" void f() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" void g() { p->f(); }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
checkConst("struct A {\n"
|
||||||
|
" void f(int&) const;\n"
|
||||||
|
"};\n"
|
||||||
|
"template<class T>\n"
|
||||||
|
"struct P {\n"
|
||||||
|
" T* operator->();\n"
|
||||||
|
" const T* operator->() const;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" P<A> p;\n"
|
||||||
|
" int i;\n"
|
||||||
|
" void g() { p->f(i); }\n"
|
||||||
|
"};\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void const_handleDefaultParameters() {
|
void const_handleDefaultParameters() {
|
||||||
|
|
Loading…
Reference in New Issue