Partial fix for #13 - detect simple cases of member functions that can be const
This commit is contained in:
parent
037ecffc34
commit
35c303943f
|
@ -1398,6 +1398,85 @@ void CheckClass::thisSubtraction()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void CheckClass::checkConst()
|
||||
{
|
||||
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||
{
|
||||
if (Token::Match(tok, "class %var% :|{"))
|
||||
{
|
||||
// get class name..
|
||||
const std::string classname(tok->strAt(1));
|
||||
|
||||
// goto initial {'
|
||||
while (tok && tok->str() != "{")
|
||||
tok = tok->next();
|
||||
if (!tok)
|
||||
break;
|
||||
|
||||
// parse in this class definition to see if there are any simple getter functions
|
||||
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next())
|
||||
{
|
||||
if (tok2->str() == "{")
|
||||
tok2 = tok2->link();
|
||||
else if (tok2->str() == "}")
|
||||
break;
|
||||
|
||||
// member function?
|
||||
if (Token::Match(tok2, "[;}] %type% %var% ("))
|
||||
{
|
||||
// get function name
|
||||
const std::string functionName(tok2->strAt(2));
|
||||
|
||||
// goto the ')'
|
||||
tok2 = tok2->tokAt(3)->link();
|
||||
if (!tok2)
|
||||
break;
|
||||
|
||||
// is this function implemented inline?
|
||||
if (Token::simpleMatch(tok2, ") {"))
|
||||
{
|
||||
// if the function doesn't have any assignment nor function call,
|
||||
// it can be a const function..
|
||||
unsigned int indentlevel = 0;
|
||||
bool isconst = true;
|
||||
for (const Token *tok3 = tok2; tok3; tok3 = tok3->next())
|
||||
{
|
||||
if (tok3->str() == "{")
|
||||
++indentlevel;
|
||||
else if (tok3->str() == "}")
|
||||
{
|
||||
if (indentlevel <= 1)
|
||||
break;
|
||||
--indentlevel;
|
||||
}
|
||||
else if (tok3->str() == "=" ||
|
||||
Token::Match(tok, "%var% ("))
|
||||
{
|
||||
isconst = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// nothing non-const was found. write error..
|
||||
if (isconst)
|
||||
checkConstError(tok2, classname, functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname)
|
||||
{
|
||||
reportError(tok, Severity::style, "functionConst", "The function '" + classname + "::" + funcname + "' can be const");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CheckClass::noConstructorError(const Token *tok, const std::string &classname, bool isStruct)
|
||||
{
|
||||
reportError(tok, Severity::style, "noConstructor", "The " + std::string(isStruct ? "struct" : "class") + " '" + classname + "' has no constructor. Member variables not initialized.");
|
||||
|
|
|
@ -96,6 +96,9 @@ public:
|
|||
|
||||
/** @brief warn for "this-x". The indented code may be "this->x" */
|
||||
void thisSubtraction();
|
||||
|
||||
/** @brief can member function be const? */
|
||||
void checkConst();
|
||||
private:
|
||||
|
||||
/** @brief Information about a member variable. Used when checking for uninitialized variables */
|
||||
|
@ -161,6 +164,8 @@ private:
|
|||
void operatorEqRetRefThisError(const Token *tok);
|
||||
void operatorEqToSelfError(const Token *tok);
|
||||
|
||||
void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname);
|
||||
|
||||
void getErrorMessages()
|
||||
{
|
||||
noConstructorError(0, "classname", false);
|
||||
|
@ -174,6 +179,7 @@ private:
|
|||
thisSubtractionError(0);
|
||||
operatorEqRetRefThisError(0);
|
||||
operatorEqToSelfError(0);
|
||||
checkConstError(0, "class", "function");
|
||||
}
|
||||
|
||||
std::string name() const
|
||||
|
@ -189,7 +195,8 @@ private:
|
|||
"* [[CheckMemset|Warn if memset, memcpy etc are used on a class]]\n"
|
||||
"* If it's a base class, check that the destructor is virtual\n"
|
||||
"* The operator= should return a constant reference to itself\n"
|
||||
"* Are there unused private functions\n";
|
||||
"* Are there unused private functions\n"
|
||||
"* Constness for member functions\n";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -78,6 +78,9 @@ private:
|
|||
TEST_CASE(memsetOnClass);
|
||||
|
||||
TEST_CASE(this_subtraction); // warn about "this-x"
|
||||
|
||||
// can member function be made const
|
||||
TEST_CASE(const1);
|
||||
}
|
||||
|
||||
// Check the operator Equal
|
||||
|
@ -1501,6 +1504,32 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:2]: (possible style) Suspicious pointer subtraction\n"
|
||||
"[test.cpp:3]: (possible style) Suspicious pointer subtraction\n", errout.str());
|
||||
}
|
||||
|
||||
void checkConst(const char code[])
|
||||
{
|
||||
// Tokenize..
|
||||
Tokenizer tokenizer;
|
||||
std::istringstream istr(code);
|
||||
tokenizer.tokenize(istr, "test.cpp");
|
||||
tokenizer.simplifyTokenList();
|
||||
|
||||
// Clear the error log
|
||||
errout.str("");
|
||||
|
||||
// Check..
|
||||
Settings settings;
|
||||
CheckClass checkClass(&tokenizer, &settings, this);
|
||||
checkClass.checkConst();
|
||||
}
|
||||
|
||||
void const1()
|
||||
{
|
||||
checkConst("class Fred {\n"
|
||||
" int a;\n"
|
||||
" int getA() { return a; }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (style) The function 'Fred::getA' can be const\n", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestClass)
|
||||
|
|
Loading…
Reference in New Issue