Refactorizations in checkclass.cpp:

- Removed local isVirtual implementation in checkclass.cpp, use Function::isImplicitlyVirtual instead
- Don't bailout when we see C++-style casts in checkConst
- Don't bailout for this pattern "any << member << any"
- Improved/Fixed some test cases (-> #1305)
This commit is contained in:
PKEuS 2012-04-18 18:51:38 +02:00
parent 6a37c36ee8
commit af80344ab7
3 changed files with 51 additions and 93 deletions

View File

@ -552,29 +552,21 @@ void CheckClass::privateFunctions()
if (!whole) if (!whole)
continue; continue;
std::list<const Token *> FuncList; std::list<const Function*> FuncList;
/** @todo embedded class have access to private functions */ /** @todo embedded class have access to private functions */
if (!scope->getNestedNonFunctions()) { if (!scope->getNestedNonFunctions()) {
for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) { for (std::list<Function>::const_iterator func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
// Get private functions.. // Get private functions..
if (func->type == Function::eFunction && func->access == Private) if (func->type == Function::eFunction && func->access == Private)
FuncList.push_back(func->tokenDef); FuncList.push_back(&*func);
} }
} }
// Bailout for overriden virtual functions of base classes // Bailout for overriden virtual functions of base classes
if (!scope->derivedFrom.empty()) { if (!scope->derivedFrom.empty()) {
// All base classes seen?
for (unsigned int i = 0; i < scope->derivedFrom.size() && whole; ++i) {
if (!scope->derivedFrom[i].scope || !scope->derivedFrom[i].scope->classStart)
whole = false;
}
if (!whole)
continue;
// Check virtual functions // Check virtual functions
for (std::list<const Token*>::iterator i = FuncList.begin(); i != FuncList.end();) { for (std::list<const Function*>::iterator i = FuncList.begin(); i != FuncList.end();) {
if (isVirtualFunc(&*scope, *i)) if ((*i)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes
FuncList.erase(i++); FuncList.erase(i++);
else else
++i; ++i;
@ -582,27 +574,28 @@ void CheckClass::privateFunctions()
} }
while (!FuncList.empty()) { while (!FuncList.empty()) {
const std::string& funcName = FuncList.front()->tokenDef->str();
// Check that all private functions are used // Check that all private functions are used
bool used = checkFunctionUsage(FuncList.front()->str(), &*scope); // Usage in this class bool used = checkFunctionUsage(funcName, &*scope); // Usage in this class
// Check in friend classes // Check in friend classes
for (std::list<Scope::FriendInfo>::const_iterator i = scope->friendList.begin(); !used && i != scope->friendList.end(); ++i) for (std::list<Scope::FriendInfo>::const_iterator i = scope->friendList.begin(); !used && i != scope->friendList.end(); ++i)
used = checkFunctionUsage(FuncList.front()->str(), i->scope); used = checkFunctionUsage(funcName, i->scope);
if (!used) { if (!used) {
// Final check; check if the function pointer is used somewhere.. // Final check; check if the function pointer is used somewhere..
const std::string _pattern("return|throw|(|)|,|= &|" + FuncList.front()->str()); const std::string _pattern("return|throw|(|)|,|= &|" + funcName);
// or if the function address is used somewhere... // or if the function address is used somewhere...
// eg. sigc::mem_fun(this, &className::classFunction) // eg. sigc::mem_fun(this, &className::classFunction)
const std::string _pattern2("& " + scope->className + " :: " + FuncList.front()->str()); const std::string _pattern2("& " + scope->className + " :: " + funcName);
const std::string methodAsArgument("(|, " + scope->className + " :: " + FuncList.front()->str() + " ,|)"); const std::string methodAsArgument("(|, " + scope->className + " :: " + funcName + " ,|)");
const std::string methodAssigned("%var% = &| " + scope->className + " :: " + FuncList.front()->str()); const std::string methodAssigned("%var% = &| " + scope->className + " :: " + funcName);
if (!Token::findmatch(_tokenizer->tokens(), _pattern.c_str()) && if (!Token::findmatch(_tokenizer->tokens(), _pattern.c_str()) &&
!Token::findmatch(_tokenizer->tokens(), _pattern2.c_str()) && !Token::findmatch(_tokenizer->tokens(), _pattern2.c_str()) &&
!Token::findmatch(_tokenizer->tokens(), methodAsArgument.c_str()) && !Token::findmatch(_tokenizer->tokens(), methodAsArgument.c_str()) &&
!Token::findmatch(_tokenizer->tokens(), methodAssigned.c_str())) { !Token::findmatch(_tokenizer->tokens(), methodAssigned.c_str())) {
unusedPrivateFunctionError(FuncList.front(), scope->className, FuncList.front()->str()); unusedPrivateFunctionError(FuncList.front()->tokenDef, scope->className, funcName);
} }
} }
@ -1182,7 +1175,7 @@ void CheckClass::checkConst()
// check if base class function is virtual // check if base class function is virtual
if (!scope->derivedFrom.empty()) { if (!scope->derivedFrom.empty()) {
if (isVirtualFunc(&(*scope), func->tokenDef)) if (func->isImplicitlyVirtual(true))
continue; continue;
} }
@ -1333,7 +1326,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func)
} }
// streaming: << // streaming: <<
else if (tok1->str() == "<<" && isMemberVar(scope, tok1->previous())) { else if (tok1->str() == "<<" && isMemberVar(scope, tok1->previous()) && tok1->strAt(-2) != "<<") {
return(false); return(false);
} else if (Token::simpleMatch(tok1->previous(), ") <<") && } else if (Token::simpleMatch(tok1->previous(), ") <<") &&
isMemberVar(scope, tok1->tokAt(-2))) { isMemberVar(scope, tok1->tokAt(-2))) {
@ -1382,7 +1375,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|c_str|if|string|switch|while|catch|for")) { !Token::Match(tok1, "return|if|string|switch|while|catch|for")) {
if (!isConstMemberFunc(scope, tok1)) { if (!isConstMemberFunc(scope, tok1)) {
return(false); return(false);
} }
@ -1393,9 +1386,9 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func)
else if (tok2->isName() && isMemberVar(scope, tok2)) else if (tok2->isName() && isMemberVar(scope, tok2))
return(false); // TODO: Only bailout if function takes argument as non-const reference return(false); // TODO: Only bailout if function takes argument as non-const reference
} }
} else if (Token::Match(tok1, "%var% < %any% > (")) { } 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 ( )") && tok1->varId()) { } 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()) {
// assume all std::*::size() and std::*::empty() are const // assume all std::*::size() and std::*::empty() are const
const Variable *var = symbolDatabase->getVariableFromVarId(tok1->varId()); const Variable *var = symbolDatabase->getVariableFromVarId(tok1->varId());
@ -1421,59 +1414,6 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func)
return(true); return(true);
} }
// check if this function is defined virtual in the base classes
bool CheckClass::isVirtualFunc(const Scope *scope, const Token *functionToken) const
{
// check each base class
for (unsigned int i = 0; i < scope->derivedFrom.size(); ++i) {
// check if base class exists in database
if (scope->derivedFrom[i].scope) {
const Scope *derivedFrom = scope->derivedFrom[i].scope;
std::list<Function>::const_iterator func;
// check if function defined in base class
for (func = derivedFrom->functionList.begin(); func != derivedFrom->functionList.end(); ++func) {
if (func->isVirtual) {
const Token *tok = func->tokenDef;
if (tok->str() == functionToken->str()) {
const Token *temp1 = tok->previous();
const Token *temp2 = functionToken->previous();
bool returnMatch = true;
// check for matching return parameters
while (temp1->str() != "virtual") {
if (temp1->str() != temp2->str()) {
returnMatch = false;
break;
}
temp1 = temp1->previous();
temp2 = temp2->previous();
}
// check for matching function parameters
if (returnMatch && Function::argsMatch(scope, tok->tokAt(2), functionToken->tokAt(2), std::string(""), 0)) {
return true;
}
}
}
}
if (!derivedFrom->derivedFrom.empty()) {
if (isVirtualFunc(derivedFrom, functionToken))
return true;
}
} else {
// unable to find base class so assume it has a virtual function
return true;
}
}
return false;
}
void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname) void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname)
{ {
reportInconclusiveError(tok, Severity::style, "functionConst", reportInconclusiveError(tok, Severity::style, "functionConst",

View File

@ -166,8 +166,6 @@ private:
bool isMemberVar(const Scope *scope, const Token *tok); bool isMemberVar(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);
/** @brief check if this function is virtual in the base classes */
bool isVirtualFunc(const Scope *scope, const Token *functionToken) const;
// constructors helper function // constructors helper function
/** @brief Information about a member variable. Used when checking for uninitialized variables */ /** @brief Information about a member variable. Used when checking for uninitialized variables */

View File

@ -3629,15 +3629,23 @@ private:
"};\n"); "};\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// functions with a function call can't be const.. // functions with a call to a member function can only be const, if that member function is const, too.. (#1305)
checkConst("class foo\n" checkConst("class foo {\n"
"{\n"
"public:\n" "public:\n"
" int x;\n" " int x;\n"
" void a() { x = 1; }\n"
" void b() { a(); }\n" " void b() { a(); }\n"
"};\n"); "};");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkConst("class Fred {\n"
"public:\n"
" int x;\n"
" int a() const { return x; }\n"
" void b() { a(); }\n"
"};");
ASSERT_EQUALS("[test.cpp:5]: (style) 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"
@ -3787,11 +3795,12 @@ private:
"int Fred::setA() { a |= true; }"); "int Fred::setA() { a |= true; }");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// functions with a function call can't be const.. // functions with a function call to a non-const member can't be const.. (#1305)
checkConst("class Fred\n" checkConst("class Fred\n"
"{\n" "{\n"
"public:\n" "public:\n"
" int x;\n" " int x;\n"
" void a() { x = 1; }\n"
" void b();\n" " void b();\n"
"};\n" "};\n"
"void Fred::b() { a(); }"); "void Fred::b() { a(); }");
@ -4053,6 +4062,18 @@ private:
" }\n" " }\n"
"};\n"); "};\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkConst("struct Foo {\n"
" void operator<<(int);\n"
"};\n"
"struct Fred {\n"
" Foo foo;\n"
" void x()\n"
" {\n"
" std::cout << foo << 123;\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:6]: (style) Technically the member function 'Fred::x' can be const.\n", errout.str());
} }
void constoperator3() { void constoperator3() {
@ -4669,15 +4690,6 @@ private:
"};\n"); "};\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// functions with a function call can't be const..
checkConst("class foo\n"
"{\n"
"public:\n"
" unsigned long long int x;\n"
" void b() { a(); }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
// static functions can't be const.. // static functions can't be const..
checkConst("class foo\n" checkConst("class foo\n"
"{\n" "{\n"
@ -4899,6 +4911,14 @@ private:
"};\n" "};\n"
); );
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
checkConst("struct DelayBase {\n"
" float swapSpecificDelays(int index1) {\n"
" return static_cast<float>(delays_[index1]);\n"
" }\n"
" float delays_[4];\n"
"};");
ASSERT_EQUALS("[test.cpp:2]: (style) Technically the member function 'DelayBase::swapSpecificDelays' can be const.\n", errout.str());
} }
void const27() { // ticket #1882 void const27() { // ticket #1882