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)
|
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.");
|
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" */
|
/** @brief warn for "this-x". The indented code may be "this->x" */
|
||||||
void thisSubtraction();
|
void thisSubtraction();
|
||||||
|
|
||||||
|
/** @brief can member function be const? */
|
||||||
|
void checkConst();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** @brief Information about a member variable. Used when checking for uninitialized variables */
|
/** @brief Information about a member variable. Used when checking for uninitialized variables */
|
||||||
|
@ -161,6 +164,8 @@ private:
|
||||||
void operatorEqRetRefThisError(const Token *tok);
|
void operatorEqRetRefThisError(const Token *tok);
|
||||||
void operatorEqToSelfError(const Token *tok);
|
void operatorEqToSelfError(const Token *tok);
|
||||||
|
|
||||||
|
void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname);
|
||||||
|
|
||||||
void getErrorMessages()
|
void getErrorMessages()
|
||||||
{
|
{
|
||||||
noConstructorError(0, "classname", false);
|
noConstructorError(0, "classname", false);
|
||||||
|
@ -174,6 +179,7 @@ private:
|
||||||
thisSubtractionError(0);
|
thisSubtractionError(0);
|
||||||
operatorEqRetRefThisError(0);
|
operatorEqRetRefThisError(0);
|
||||||
operatorEqToSelfError(0);
|
operatorEqToSelfError(0);
|
||||||
|
checkConstError(0, "class", "function");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string name() const
|
std::string name() const
|
||||||
|
@ -189,7 +195,8 @@ private:
|
||||||
"* [[CheckMemset|Warn if memset, memcpy etc are used on a class]]\n"
|
"* [[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"
|
"* If it's a base class, check that the destructor is virtual\n"
|
||||||
"* The operator= should return a constant reference to itself\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(memsetOnClass);
|
||||||
|
|
||||||
TEST_CASE(this_subtraction); // warn about "this-x"
|
TEST_CASE(this_subtraction); // warn about "this-x"
|
||||||
|
|
||||||
|
// can member function be made const
|
||||||
|
TEST_CASE(const1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the operator Equal
|
// Check the operator Equal
|
||||||
|
@ -1501,6 +1504,32 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:2]: (possible style) Suspicious pointer subtraction\n"
|
ASSERT_EQUALS("[test.cpp:2]: (possible style) Suspicious pointer subtraction\n"
|
||||||
"[test.cpp:3]: (possible style) Suspicious pointer subtraction\n", errout.str());
|
"[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)
|
REGISTER_TEST(TestClass)
|
||||||
|
|
Loading…
Reference in New Issue