From 82c09f243bb946b61463c94700e8dbe9d458f6ac Mon Sep 17 00:00:00 2001 From: shaneasd Date: Mon, 13 Apr 2020 02:35:54 +0800 Subject: [PATCH] Maybeunusedsupport (#2570) * Add rudimentary support for [[maybe_unused]] * Add more test cases. use the symboldatabase rather than reparsing. Fix travis error. * test review actions * change var to usage._var --- lib/checkunusedvar.cpp | 11 +++--- lib/symboldatabase.cpp | 4 +++ lib/symboldatabase.h | 7 +++- lib/token.h | 7 ++++ lib/tokenize.cpp | 5 +++ test/testunusedvar.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 5 deletions(-) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index cc53cee05..e593636d8 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1246,7 +1246,8 @@ void CheckUnusedVar::checkFunctionVariableUsage() } // warn - unreadVariableError(tok, expr->expressionString(), false); + if(!expr->variable() || !expr->variable()->isMaybeUnused()) + unreadVariableError(tok, expr->expressionString(), false); } } @@ -1281,9 +1282,11 @@ void CheckUnusedVar::checkFunctionVariableUsage() allocatedButUnusedVariableError(usage._lastAccess, varname); // variable has not been written, read, or modified - else if (usage.unused() && !usage._modified) - unusedVariableError(usage._var->nameToken(), varname); - + else if (usage.unused() && !usage._modified) { + if (!usage._var->isMaybeUnused()) { + unusedVariableError(usage._var->nameToken(), varname); + } + } // variable has not been written but has been modified else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) unassignedVariableError(usage._var->nameToken(), varname); diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 7d7c6a69d..4664b2b0a 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1889,6 +1889,10 @@ void Variable::evaluate(const Settings* settings) setFlag(fIsReference, true); // Set also fIsReference } + if (tok->isMaybeUnused()) { + setFlag(fIsMaybeUnused, true); + } + if (tok->str() == "<" && tok->link()) tok = tok->link(); else diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 8fdbc20f7..2d634af7a 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -193,7 +193,8 @@ class CPPCHECKLIB Variable { fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */ fIsFloatType = (1 << 12), /** @brief Floating point type */ fIsVolatile = (1 << 13), /** @brief volatile */ - fIsSmartPointer = (1 << 14) /** @brief std::shared_ptr|unique_ptr */ + fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */ + fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */ }; /** @@ -618,6 +619,10 @@ public: return type() && type()->isEnumType(); } + bool isMaybeUnused() const { + return getFlag(fIsMaybeUnused); + } + const ValueType *valueType() const { return mValueType; } diff --git a/lib/token.h b/lib/token.h index 074cc1595..9a9485001 100644 --- a/lib/token.h +++ b/lib/token.h @@ -541,6 +541,12 @@ public: void isAttributeNodiscard(const bool value) { setFlag(fIsAttributeNodiscard, value); } + bool isMaybeUnused() const { + return getFlag(fIsMaybeUnused); + } + void isMaybeUnused(const bool value) { + setFlag(fIsMaybeUnused, value); + } void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { mImpl->setCppcheckAttribute(type, value); } @@ -1155,6 +1161,7 @@ private: fIncompleteVar = (1 << 25), fConstexpr = (1 << 26), fExternC = (1 << 27), + fIsMaybeUnused = (1 << 28), // [[maybe_unsed]] }; Token::Type mTokType; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 82730bf4f..7bd0daa71 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -10246,6 +10246,11 @@ void Tokenizer::simplifyCPPAttribute() else head->previous()->isAttributeNodiscard(true); } + } else if (Token::simpleMatch(tok->tokAt(2), "maybe_unused")) { + Token* head = tok->tokAt(5); + while (isCPPAttribute(head)) + head = head->tokAt(5); + head->isMaybeUnused(true); } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { const Token *vartok = tok->tokAt(4); if (vartok->str() == ":") diff --git a/test/testunusedvar.cpp b/test/testunusedvar.cpp index 383df1f02..b9afc1bcb 100644 --- a/test/testunusedvar.cpp +++ b/test/testunusedvar.cpp @@ -148,6 +148,7 @@ private: TEST_CASE(localvarconst1); TEST_CASE(localvarconst2); TEST_CASE(localvarreturn); // ticket #9167 + TEST_CASE(localvarmaybeunused); TEST_CASE(localvarthrow); // ticket #3687 @@ -4160,6 +4161,87 @@ private: ASSERT_EQUALS("", errout.str()); } + void localvarmaybeunused() { + functionVariableUsage("int main() {\n" + " [[maybe_unused]] int x;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("[[nodiscard]] int getX() { return 4; }\n" + "int main() {\n" + " [[maybe_unused]] int x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("[[nodiscard]] int getX() { return 4; }\n" + "int main() {\n" + " [[maybe_unused]] int x = getX();\n" + " x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("[[nodiscard]] int getX() { return 4; }\n" + "int main() {\n" + " [[maybe_unused]] int x = getX();\n" + " x = getX();\n" + " std::cout << x;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] const int x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] const int& x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] const int* x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] int& x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] int* x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] auto x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] auto&& x = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] int x[] = getX();\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] constexpr volatile static int x = 1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("[[maybe_unused]] inline int x = 1;"); + ASSERT_EQUALS("", errout.str()); + + functionVariableUsage("int main() {\n" + " [[maybe_unused]] [[anotherattribute]] const int* = 1;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void localvarthrow() { // ticket #3687 functionVariableUsage("void foo() {\n" " try {}"