Taking care of operation overloading functions (#1108)

This commit is contained in:
Hinterwaeldlers 2018-03-12 10:25:30 +01:00 committed by Daniel Marjamäki
parent 46307df9b6
commit a9b1f4c640
2 changed files with 120 additions and 4 deletions

View File

@ -29,6 +29,7 @@
#include "tokenlist.h"
#include <tinyxml2.h>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <istream>
@ -231,6 +232,54 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
}
static bool isOperatorFunction(const std::string & funcName) {
/* Operator functions are invalid function names for C, so no need to check
* this in here. As result the returned error function might be incorrect.
*
* List of valid operators can be found at:
* http://en.cppreference.com/w/cpp/language/operators
*
* Conversion functions must be a member function (at least for gcc), so no
* need to cover them for unused functions.
*
* To speed up the comparision, not the whole list of operators is used.
* Instead only the character after the operator prefix is checked to be a
* none alpa numeric value, but the '_', to cover function names like
* "operator_unused". In addition the following valid operators are checked:
* - new
* - new[]
* - delete
* - delete[]
*/
const std::string operatorPrefix = "operator";
if (funcName.compare(0, operatorPrefix.length(), operatorPrefix) != 0) {
return false;
}
// Taking care of funcName == "operator", which is no valid operator
if (funcName.length() == operatorPrefix.length()) {
return false;
}
const char firstOperatorChar = funcName[operatorPrefix.length()];
if (firstOperatorChar == '_') {
return false;
}
if (!std::isalnum(firstOperatorChar)) {
return true;
}
const std::vector<std::string> additionalOperators = make_container< std::vector<std::string> >()
<< "new"
<< "new[]"
<< "delete"
<< "delete[]";
return std::find(additionalOperators.begin(), additionalOperators.end(), funcName.substr(operatorPrefix.length())) != additionalOperators.end();;
}
bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings)
@ -245,11 +294,13 @@ bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings
it->first == "if")
continue;
if (!func.usedSameFile) {
if (isOperatorFunction(it->first))
continue;
std::string filename;
if (func.filename != "+")
filename = func.filename;
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
errors = true;
filename = func.filename;
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
errors = true;
} else if (! func.usedOtherFile) {
/** @todo add error message "function is only used in <file> it can be static" */
/*
@ -381,7 +432,7 @@ void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger,
functionName == "if")
continue;
if (calls.find(functionName) == calls.end()) {
if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) {
const Location &loc = decl->second;
unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName);
}

View File

@ -61,6 +61,8 @@ private:
TEST_CASE(lineNumber); // Ticket 3059
TEST_CASE(ignore_declaration); // ignore declaration
TEST_CASE(operatorOverload);
}
void check(const char code[], Settings::PlatformType platform = Settings::Native) {
@ -390,6 +392,69 @@ private:
"void (*list[])(void) = {f}");
ASSERT_EQUALS("", errout.str());
}
void operatorOverload() {
check("class A {\n"
"private:\n"
" friend std::ostream & operator<<(std::ostream &, const A&);\n"
"};\n"
"std::ostream & operator<<(std::ostream &os, const A&) {\n"
" os << \"This is class A\";\n"
"}");
ASSERT_EQUALS("", errout.str());
check("class A{};\n"
"A operator + (const A &, const A &){ return A(); }\n"
"A operator - (const A &, const A &){ return A(); }\n"
"A operator * (const A &, const A &){ return A(); }\n"
"A operator / (const A &, const A &){ return A(); }\n"
"A operator % (const A &, const A &){ return A(); }\n"
"A operator & (const A &, const A &){ return A(); }\n"
"A operator | (const A &, const A &){ return A(); }\n"
"A operator ~ (const A &){ return A(); }\n"
"A operator ! (const A &){ return A(); }\n"
"bool operator < (const A &, const A &){ return true; }\n"
"bool operator > (const A &, const A &){ return true; }\n"
"A operator += (const A &, const A &){ return A(); }\n"
"A operator -= (const A &, const A &){ return A(); }\n"
"A operator *= (const A &, const A &){ return A(); }\n"
"A operator /= (const A &, const A &){ return A(); }\n"
"A operator %= (const A &, const A &){ return A(); }\n"
"A operator &= (const A &, const A &){ return A(); }\n"
"A operator ^= (const A &, const A &){ return A(); }\n"
"A operator |= (const A &, const A &){ return A(); }\n"
"A operator << (const A &, const int){ return A(); }\n"
"A operator >> (const A &, const int){ return A(); }\n"
"A operator <<= (const A &, const int){ return A(); }\n"
"A operator >>= (const A &, const int){ return A(); }\n"
"bool operator == (const A &, const A &){ return true; }\n"
"bool operator != (const A &, const A &){ return true; }\n"
"bool operator <= (const A &, const A &){ return true; }\n"
"bool operator >= (const A &, const A &){ return true; }\n"
"A operator && (const A &, const int){ return A(); }\n"
"A operator || (const A &, const int){ return A(); }\n"
"A operator ++ (const A &, const int){ return A(); }\n"
"A operator ++ (const A &){ return A(); }\n"
"A operator -- (const A &, const int){ return A(); }\n"
"A operator -- (const A &){ return A(); }\n"
"A operator , (const A &, const A &){ return A(); }\n");
ASSERT_EQUALS("", errout.str());
check("class A {\n"
"public:\n"
" static void * operator new(std::size_t);\n"
" static void * operator new[](std::size_t);\n"
"};\n"
"void * A::operator new(std::size_t s) {\n"
" return malloc(s);\n"
"}\n"
"void * A::operator new[](std::size_t s) {\n"
" return malloc(s);\n"
"}");
ASSERT_EQUALS("", errout.str());
}
};
REGISTER_TEST(TestUnusedFunctions)