Add support for C++ attribute nodiscard for functions. (#1269)
This commit is contained in:
parent
14e60210c9
commit
3982c81394
|
@ -194,10 +194,13 @@ void CheckFunctions::checkIgnoredReturnValue()
|
||||||
continue;
|
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());
|
ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function)
|
void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function)
|
||||||
|
|
|
@ -2732,6 +2732,8 @@ void SymbolDatabase::printOut(const char *title) const
|
||||||
std::cout << " constructor ";
|
std::cout << " constructor ";
|
||||||
if (func->isAttributeDestructor())
|
if (func->isAttributeDestructor())
|
||||||
std::cout << " destructor ";
|
std::cout << " destructor ";
|
||||||
|
if (func->isAttributeNodiscard())
|
||||||
|
std::cout << " nodiscard ";
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl;
|
std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl;
|
||||||
std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl;
|
std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl;
|
||||||
|
|
|
@ -747,6 +747,9 @@ public:
|
||||||
bool isAttributeNothrow() const {
|
bool isAttributeNothrow() const {
|
||||||
return tokenDef->isAttributeNothrow();
|
return tokenDef->isAttributeNothrow();
|
||||||
}
|
}
|
||||||
|
bool isAttributeNodiscard() const {
|
||||||
|
return tokenDef->isAttributeNodiscard();
|
||||||
|
}
|
||||||
|
|
||||||
bool hasBody() const {
|
bool hasBody() const {
|
||||||
return getFlag(fHasBody);
|
return getFlag(fHasBody);
|
||||||
|
|
|
@ -413,6 +413,12 @@ public:
|
||||||
void isAttributePacked(const bool value) {
|
void isAttributePacked(const bool value) {
|
||||||
setFlag(fIsAttributePacked, value);
|
setFlag(fIsAttributePacked, value);
|
||||||
}
|
}
|
||||||
|
bool isAttributeNodiscard() const {
|
||||||
|
return getFlag(fIsAttributeNodiscard);
|
||||||
|
}
|
||||||
|
void isAttributeNodiscard(const bool value) {
|
||||||
|
setFlag(fIsAttributeNodiscard, value);
|
||||||
|
}
|
||||||
bool isControlFlowKeyword() const {
|
bool isControlFlowKeyword() const {
|
||||||
return getFlag(fIsControlFlowKeyword);
|
return getFlag(fIsControlFlowKeyword);
|
||||||
}
|
}
|
||||||
|
@ -949,6 +955,7 @@ private:
|
||||||
fIsName = (1 << 20),
|
fIsName = (1 << 20),
|
||||||
fIsLiteral = (1 << 21),
|
fIsLiteral = (1 << 21),
|
||||||
fIsTemplateArg = (1 << 22),
|
fIsTemplateArg = (1 << 22),
|
||||||
|
fIsAttributeNodiscard = (1 << 23), // __attribute__ ((warn_unused_result)), [[nodiscard]]
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int _flags;
|
unsigned int _flags;
|
||||||
|
|
|
@ -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;
|
Token *functok = nullptr;
|
||||||
|
|
||||||
// type func(...) __attribute__((attribute));
|
// type func(...) __attribute__((attribute));
|
||||||
|
@ -8971,6 +8971,8 @@ void Tokenizer::simplifyAttribute()
|
||||||
functok->isAttributeNoreturn(true);
|
functok->isAttributeNoreturn(true);
|
||||||
else if (attribute.find("nothrow") != std::string::npos)
|
else if (attribute.find("nothrow") != std::string::npos)
|
||||||
functok->isAttributeNothrow(true);
|
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()) {
|
for (Token *tok = list.front(); tok; tok = tok->next()) {
|
||||||
if (!tok->link() || !Token::Match(tok, "[ [ %name%"))
|
if (!tok->link() || !Token::Match(tok, "[ [ %name%"))
|
||||||
continue;
|
continue;
|
||||||
if (tok->strAt(2) == "noreturn") {
|
if (Token::Match(tok->tokAt(2), "noreturn|nodiscard")) {
|
||||||
const Token * head = tok->link()->next();
|
const Token * head = tok->link()->next();
|
||||||
while (Token::Match(head, "%name%|::|*|&"))
|
while (Token::Match(head, "%name%|::|*|&"))
|
||||||
head = head->next();
|
head = head->next();
|
||||||
if (head && head->str() == "(" && isFunctionHead(head, "{|;"))
|
if (head && head->str() == "(" && isFunctionHead(head, "{|;")) {
|
||||||
|
if (tok->strAt(2) == "noreturn")
|
||||||
head->previous()->isAttributeNoreturn(true);
|
head->previous()->isAttributeNoreturn(true);
|
||||||
|
else
|
||||||
|
head->previous()->isAttributeNodiscard(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Token::eraseTokens(tok, tok->link()->next());
|
Token::eraseTokens(tok, tok->link()->next());
|
||||||
tok->deleteThis();
|
tok->deleteThis();
|
||||||
|
|
|
@ -926,6 +926,20 @@ private:
|
||||||
check("void foo() {\n" // don't crash
|
check("void foo() {\n" // don't crash
|
||||||
" DEBUG(123)(mystrcmp(a,b))(fd);\n"
|
" DEBUG(123)(mystrcmp(a,b))(fd);\n"
|
||||||
"}", "test.c", &settings2);
|
"}", "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() {
|
void memsetZeroBytes() {
|
||||||
|
|
Loading…
Reference in New Issue