From 3982c813940def294ee4e99a1cebe4b7df98c71c Mon Sep 17 00:00:00 2001 From: IOBYTE Date: Tue, 29 May 2018 15:43:56 -0400 Subject: [PATCH] Add support for C++ attribute nodiscard for functions. (#1269) --- lib/checkfunctions.cpp | 5 ++++- lib/symboldatabase.cpp | 2 ++ lib/symboldatabase.h | 3 +++ lib/token.h | 7 +++++++ lib/tokenize.cpp | 14 ++++++++++---- test/testfunctions.cpp | 14 ++++++++++++++ 6 files changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index 83ed8b1f1..450760ff5 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -194,8 +194,11 @@ void CheckFunctions::checkIgnoredReturnValue() continue; } - if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && _settings->library.isUseRetVal(tok) && !WRONG_DATA(!tok->next()->astOperand1(), tok)) + if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && + (_settings->library.isUseRetVal(tok) || (tok->function() && tok->function()->isAttributeNodiscard())) && + !WRONG_DATA(!tok->next()->astOperand1(), tok)) { ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString()); + } } } } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 5cc18a65a..6a513eff7 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2732,6 +2732,8 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " constructor "; if (func->isAttributeDestructor()) std::cout << " destructor "; + if (func->isAttributeNodiscard()) + std::cout << " nodiscard "; std::cout << std::endl; std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl; std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 25fc5fffe..300957297 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -747,6 +747,9 @@ public: bool isAttributeNothrow() const { return tokenDef->isAttributeNothrow(); } + bool isAttributeNodiscard() const { + return tokenDef->isAttributeNodiscard(); + } bool hasBody() const { return getFlag(fHasBody); diff --git a/lib/token.h b/lib/token.h index 73d9e1c78..ba09b46f3 100644 --- a/lib/token.h +++ b/lib/token.h @@ -413,6 +413,12 @@ public: void isAttributePacked(const bool value) { setFlag(fIsAttributePacked, value); } + bool isAttributeNodiscard() const { + return getFlag(fIsAttributeNodiscard); + } + void isAttributeNodiscard(const bool value) { + setFlag(fIsAttributeNodiscard, value); + } bool isControlFlowKeyword() const { return getFlag(fIsControlFlowKeyword); } @@ -949,6 +955,7 @@ private: fIsName = (1 << 20), fIsLiteral = (1 << 21), fIsTemplateArg = (1 << 22), + fIsAttributeNodiscard = (1 << 23), // __attribute__ ((warn_unused_result)), [[nodiscard]] }; unsigned int _flags; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 3ed1f464b..45639ce0e 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -8945,7 +8945,7 @@ void Tokenizer::simplifyAttribute() } } - else if (Token::Match(tok->tokAt(2), "( pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__ )")) { + else if (Token::Match(tok->tokAt(2), "( pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__|warn_unused_result )")) { Token *functok = nullptr; // type func(...) __attribute__((attribute)); @@ -8971,6 +8971,8 @@ void Tokenizer::simplifyAttribute() functok->isAttributeNoreturn(true); else if (attribute.find("nothrow") != std::string::npos) functok->isAttributeNothrow(true); + else if (attribute.find("warn_unused_result") != std::string::npos) + functok->isAttributeNodiscard(true); } } @@ -8992,12 +8994,16 @@ void Tokenizer::simplifyCPPAttribute() for (Token *tok = list.front(); tok; tok = tok->next()) { if (!tok->link() || !Token::Match(tok, "[ [ %name%")) continue; - if (tok->strAt(2) == "noreturn") { + if (Token::Match(tok->tokAt(2), "noreturn|nodiscard")) { const Token * head = tok->link()->next(); while (Token::Match(head, "%name%|::|*|&")) head = head->next(); - if (head && head->str() == "(" && isFunctionHead(head, "{|;")) - head->previous()->isAttributeNoreturn(true); + if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { + if (tok->strAt(2) == "noreturn") + head->previous()->isAttributeNoreturn(true); + else + head->previous()->isAttributeNodiscard(true); + } } Token::eraseTokens(tok, tok->link()->next()); tok->deleteThis(); diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index 39a28200f..e2308a3c8 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -926,6 +926,20 @@ private: check("void foo() {\n" // don't crash " DEBUG(123)(mystrcmp(a,b))(fd);\n" "}", "test.c", &settings2); + check("struct teststruct {\n" + " int testfunc1() __attribute__ ((warn_unused_result)) { return 1; }\n" + " [[nodiscard]] int testfunc2() { return 1; }\n" + " void foo() { testfunc1(); testfunc2(); }\n" + "};\n" + "int main() {\n" + " teststruct TestStruct1;\n" + " TestStruct1.testfunc1();\n" + " TestStruct1.testfunc2();\n" + "}", "test.cpp", &settings2); + ASSERT_EQUALS("[test.cpp:4]: (warning) Return value of function testfunc1() is not used.\n" + "[test.cpp:4]: (warning) Return value of function testfunc2() is not used.\n" + "[test.cpp:8]: (warning) Return value of function TestStruct1.testfunc1() is not used.\n" + "[test.cpp:9]: (warning) Return value of function TestStruct1.testfunc2() is not used.\n", errout.str()); } void memsetZeroBytes() {