Add support for C++ attribute nodiscard for functions. (#1269)

This commit is contained in:
IOBYTE 2018-05-29 15:43:56 -04:00 committed by Daniel Marjamäki
parent 14e60210c9
commit 3982c81394
6 changed files with 40 additions and 5 deletions

View File

@ -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());
}
}
}
}

View File

@ -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;

View File

@ -747,6 +747,9 @@ public:
bool isAttributeNothrow() const {
return tokenDef->isAttributeNothrow();
}
bool isAttributeNodiscard() const {
return tokenDef->isAttributeNodiscard();
}
bool hasBody() const {
return getFlag(fHasBody);

View File

@ -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;

View File

@ -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();

View File

@ -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() {