diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index cbcd70d2e..38eabcb82 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -29,6 +29,7 @@ #include "tokenlist.h" #include +#include #include #include #include @@ -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 additionalOperators = make_container< std::vector >() + << "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 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); } diff --git a/test/testunusedfunctions.cpp b/test/testunusedfunctions.cpp index 275ce089b..d178ae6cb 100644 --- a/test/testunusedfunctions.cpp +++ b/test/testunusedfunctions.cpp @@ -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)