Skip to content

Commit fdf70d1

Browse files
Taking care of operation overloading functions
1 parent eed9dfb commit fdf70d1

2 files changed

Lines changed: 123 additions & 7 deletions

File tree

lib/checkunusedfunctions.cpp

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,53 @@ void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const char Fi
231231
}
232232

233233

234+
static bool isOperatorFunction(const std::string & funcName) {
235+
/* Operator functions are invalid function names for C, so no need to check
236+
* this in here. As result the returned error function might be incorrect.
237+
*
238+
* List of valid operators can be found at:
239+
* http://en.cppreference.com/w/cpp/language/operators
240+
*
241+
* Conversion functions must be a member function (at least for gcc), so no
242+
* need to cover them for unused functions.
243+
*
244+
* To speed up the comparision, not the whole list of operators is used.
245+
* Instead only the character after the operator prefix is checked to be a
246+
* none alpa numeric value, but the '_', to cover function names like
247+
* "operator_unused". In addition the following valid operators are checked:
248+
* - new
249+
* - new[]
250+
* - delete
251+
* - delete[]
252+
*/
253+
const std::string operatorPrefix = "operator";
254+
if (funcName.compare(0, operatorPrefix.length(), operatorPrefix) != 0) {
255+
return false;
256+
}
257+
258+
const char firstOperatorChar = funcName[operatorPrefix.length()];
259+
if (firstOperatorChar == '_') {
260+
return false;
261+
}
262+
263+
if (!std::isalnum(firstOperatorChar)) {
264+
return true;
265+
}
266+
267+
const std::vector<std::string> additionalOperators = {
268+
"new", "new[]", "delete", "delete[]"
269+
};
270+
const std::string opName = funcName.substr(operatorPrefix.length());
271+
272+
for (std::vector<std::string>::const_iterator curOp = additionalOperators.begin(); curOp != additionalOperators.end(); ++curOp) {
273+
if (*curOp == opName) {
274+
return true;
275+
}
276+
}
277+
278+
return false;
279+
}
280+
234281

235282

236283
bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings& settings)
@@ -245,11 +292,13 @@ bool CheckUnusedFunctions::check(ErrorLogger * const errorLogger, const Settings
245292
it->first == "if")
246293
continue;
247294
if (!func.usedSameFile) {
248-
std::string filename;
249-
if (func.filename != "+")
250-
filename = func.filename;
251-
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
252-
errors = true;
295+
if (!isOperatorFunction(it->first)) {
296+
std::string filename;
297+
if (func.filename != "+")
298+
filename = func.filename;
299+
unusedFunctionError(errorLogger, filename, func.lineNumber, it->first);
300+
errors = true;
301+
}
253302
} else if (! func.usedOtherFile) {
254303
/** @todo add error message "function is only used in <file> it can be static" */
255304
/*
@@ -382,8 +431,10 @@ void CheckUnusedFunctions::analyseWholeProgram(ErrorLogger * const errorLogger,
382431
continue;
383432

384433
if (calls.find(functionName) == calls.end()) {
385-
const Location &loc = decl->second;
386-
unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName);
434+
if (!isOperatorFunction(functionName)) {
435+
const Location &loc = decl->second;
436+
unusedFunctionError(errorLogger, loc.fileName, loc.lineNumber, functionName);
437+
}
387438
}
388439
}
389440
}

test/testunusedfunctions.cpp

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class TestUnusedFunctions : public TestFixture {
6161
TEST_CASE(lineNumber); // Ticket 3059
6262

6363
TEST_CASE(ignore_declaration); // ignore declaration
64+
65+
TEST_CASE(operatorOverload);
6466
}
6567

6668
void check(const char code[], Settings::PlatformType platform = Settings::Native) {
@@ -390,6 +392,69 @@ class TestUnusedFunctions : public TestFixture {
390392
"void (*list[])(void) = {f}");
391393
ASSERT_EQUALS("", errout.str());
392394
}
395+
396+
void operatorOverload() {
397+
check("class A {\n"
398+
"private:\n"
399+
" friend std::ostream & operator<<(std::ostream &, const A&);\n"
400+
"};\n"
401+
"std::ostream & operator<<(std::ostream &os, const A&) {\n"
402+
" os << \"This is class A\";\n"
403+
"}");
404+
ASSERT_EQUALS("", errout.str());
405+
406+
check("class A{};\n"
407+
"A operator + (const A &, const A &){ return A(); }\n"
408+
"A operator - (const A &, const A &){ return A(); }\n"
409+
"A operator * (const A &, const A &){ return A(); }\n"
410+
"A operator / (const A &, const A &){ return A(); }\n"
411+
"A operator % (const A &, const A &){ return A(); }\n"
412+
"A operator & (const A &, const A &){ return A(); }\n"
413+
"A operator | (const A &, const A &){ return A(); }\n"
414+
"A operator ~ (const A &){ return A(); }\n"
415+
"A operator ! (const A &){ return A(); }\n"
416+
"bool operator < (const A &, const A &){ return true; }\n"
417+
"bool operator > (const A &, const A &){ return true; }\n"
418+
"A operator += (const A &, const A &){ return A(); }\n"
419+
"A operator -= (const A &, const A &){ return A(); }\n"
420+
"A operator *= (const A &, const A &){ return A(); }\n"
421+
"A operator /= (const A &, const A &){ return A(); }\n"
422+
"A operator %= (const A &, const A &){ return A(); }\n"
423+
"A operator &= (const A &, const A &){ return A(); }\n"
424+
"A operator ^= (const A &, const A &){ return A(); }\n"
425+
"A operator |= (const A &, const A &){ return A(); }\n"
426+
"A operator << (const A &, const int){ return A(); }\n"
427+
"A operator >> (const A &, const int){ return A(); }\n"
428+
"A operator <<= (const A &, const int){ return A(); }\n"
429+
"A operator >>= (const A &, const int){ return A(); }\n"
430+
"bool operator == (const A &, const A &){ return true; }\n"
431+
"bool operator != (const A &, const A &){ return true; }\n"
432+
"bool operator <= (const A &, const A &){ return true; }\n"
433+
"bool operator >= (const A &, const A &){ return true; }\n"
434+
"A operator && (const A &, const int){ return A(); }\n"
435+
"A operator || (const A &, const int){ return A(); }\n"
436+
"A operator ++ (const A &, const int){ return A(); }\n"
437+
"A operator ++ (const A &){ return A(); }\n"
438+
"A operator -- (const A &, const int){ return A(); }\n"
439+
"A operator -- (const A &){ return A(); }\n"
440+
"A operator , (const A &, const A &){ return A(); }\n");
441+
ASSERT_EQUALS("", errout.str());
442+
443+
444+
check("class A {\n"
445+
"public:\n"
446+
" static void * operator new(std::size_t);\n"
447+
" static void * operator new[](std::size_t);\n"
448+
"};\n"
449+
"void * A::operator new(std::size_t s) {\n"
450+
" return malloc(s);\n"
451+
"}\n"
452+
"void * A::operator new[](std::size_t s) {\n"
453+
" return malloc(s);\n"
454+
"}");
455+
ASSERT_EQUALS("", errout.str());
456+
}
457+
393458
};
394459

395460
REGISTER_TEST(TestUnusedFunctions)

0 commit comments

Comments
 (0)