Improved handling of function calls in const correctness check: Fixed #2702, #2698, #2729.

This commit is contained in:
PKEuS 2012-05-16 11:57:12 -07:00
parent 0bb0fdedc2
commit 77df633904
3 changed files with 50 additions and 7 deletions

View File

@ -1280,6 +1280,36 @@ static unsigned int countParameters(const Token *tok)
return numpar; return numpar;
} }
bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok)
{
unsigned int args = countParameters(tok);
for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
/** @todo we need to look at the argument types when there are overloaded functions
* with the same number of arguments */
if (func->tokenDef->str() == tok->str() && func->argCount() == args) {
return true;
}
}
// not found in this class
if (!scope->derivedFrom.empty()) {
// check each base class
for (unsigned int i = 0; i < scope->derivedFrom.size(); ++i) {
// find the base class
const Scope *derivedFrom = scope->derivedFrom[i].scope;
// find the function in the base class
if (derivedFrom) {
if (isMemberFunc(derivedFrom, tok))
return true;
}
}
}
return false;
}
bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok)
{ {
unsigned int args = countParameters(tok); unsigned int args = countParameters(tok);
@ -1385,7 +1415,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func)
// function call.. // function call..
else if (Token::Match(tok1, "%var% (") && !tok1->isStandardType() && else if (Token::Match(tok1, "%var% (") && !tok1->isStandardType() &&
!Token::Match(tok1, "return|if|string|switch|while|catch|for")) { !Token::Match(tok1, "return|if|string|switch|while|catch|for")) {
if (!isConstMemberFunc(scope, tok1)) { if (isMemberFunc(scope, tok1) && !isConstMemberFunc(scope, tok1)) {
return(false); return(false);
} }
// Member variable given as parameter // Member variable given as parameter
@ -1397,13 +1427,18 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func)
} }
} else if (Token::Match(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { } else if (Token::Match(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) {
return(false); return(false);
} else if (Token::Match(tok1, "%var% . size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|rfind|get_allocator|copy|c_str ( )") && tok1->varId()) { } else if (Token::Match(tok1, "%var% . %var% (")) {
// assume all std::*::size() and std::*::empty() are const if (tok1->varId() && (Token::Match(tok1->tokAt(2), "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(tok1->tokAt(2), "rfind|copy"))) {
const Variable *var = symbolDatabase->getVariableFromVarId(tok1->varId()); const Variable *var = symbolDatabase->getVariableFromVarId(tok1->varId());
if (var && Token::simpleMatch(var->typeStartToken(), "std ::")) // assume all std::*::size() and std::*::empty() are const
tok1 = tok1->tokAt(4); if (var && Token::simpleMatch(var->typeStartToken(), "std ::"))
else tok1 = tok1->next();
else // TODO: Check if the function is const (#2477)
return(false);
} else if (!isMemberVar(scope, tok1)) {
tok1 = tok1->tokAt(2);
} else
return(false); return(false);
} }

View File

@ -164,6 +164,7 @@ private:
// checkConst helper functions // checkConst helper functions
bool isMemberVar(const Scope *scope, const Token *tok); bool isMemberVar(const Scope *scope, const Token *tok);
bool isMemberFunc(const Scope *scope, const Token *tok);
bool isConstMemberFunc(const Scope *scope, const Token *tok); bool isConstMemberFunc(const Scope *scope, const Token *tok);
bool checkConstFunc(const Scope *scope, const Function *func); bool checkConstFunc(const Scope *scope, const Function *func);

View File

@ -2117,6 +2117,13 @@ private:
"};"); "};");
ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout.str()); ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout.str());
checkConst("class Fred {\n"
"public:\n"
" int x;\n"
" void b() { a(); }\n"
"};");
ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout.str());
// static functions can't be const.. // static functions can't be const..
checkConst("class foo\n" checkConst("class foo\n"
"{\n" "{\n"