diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.h --- a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.h @@ -27,6 +27,7 @@ private: std::string CheckedFunctions; + std::string CheckedReturnTypes; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp @@ -28,6 +28,17 @@ Finder, Builder); } +// Checks if the function return type is in CheckedReturnedTypes +AST_MATCHER_P(FunctionDecl, hasAnyReturnType, std::vector, + RetTypes) { + auto FuncRetType = Node.getReturnType().getAsString(); + for (const auto RetType : RetTypes) { + if (RetType == FuncRetType) { + return true; + } + } + return false; +} } // namespace UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name, @@ -124,19 +135,28 @@ "::sigismember;" "::strcasecmp;" "::strsignal;" - "::ttyname")) {} + "::ttyname")), + CheckedReturnTypes(Options.get("CheckedReturnTypes", + "std::error_code;" + "std::expected;" + "boost::system::error_code;" + "abseil::Status")) {} void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "CheckedFunctions", CheckedFunctions); + Options.store(Opts, "CheckedReturnTypes", CheckedReturnTypes); } void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) { auto FunVec = utils::options::parseStringList(CheckedFunctions); + auto RetTypeVec = utils::options::parseStringList(CheckedReturnTypes); + auto MatchedCallExpr = expr(ignoringImplicit(ignoringParenImpCasts( callExpr(callee(functionDecl( // Don't match void overloads of checked functions. unless(returns(voidType())), - isInstantiatedFrom(hasAnyName(FunVec))))) + anyOf(isInstantiatedFrom(hasAnyName(FunVec)), + hasAnyReturnType(RetTypeVec))))) .bind("match")))); auto UnusedInCompoundStmt = diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -391,6 +391,9 @@ `: warn on ``const &&`` constructors. +- Extend ``bugprone-unused-return-value`` check to check for all functions + with specified return types using the ``CheckedReturnTypes`` option. + Removed checks ^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-return-value.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-return-value.rst --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-return-value.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-return-value.rst @@ -46,5 +46,11 @@ return value often indicates that the programmer confused the function with ``clear()``. +.. option:: CheckedReturnTypes + + Semicolon-separated list of function return types to check. + By default the following function return types are checked: + ``std::error_code std::expected boost::system::error_code abseil::Status`` + `cert-err33-c <../cert/err33-c.html>`_ is an alias of this check that checks a fixed and large set of standard library functions. diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-return-value.cpp @@ -52,6 +52,9 @@ bool empty() const noexcept; }; +class error_code { +}; + // the check should be able to match std lib calls even if the functions are // declared inside inline namespaces inline namespace v1 { @@ -72,6 +75,10 @@ void useFuture(const std::future &fut); +std::error_code errorFunc() { + return std::error_code(); +} + void warning() { std::async(increment, 42); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used @@ -185,6 +192,10 @@ // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning } + + errorFunc(); + // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used + // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning } void noWarning() { @@ -209,6 +220,8 @@ std::vector VecNoWarning; auto VecEmptyRetval = VecNoWarning.empty(); + (void) errorFunc(); + // test using the return value in different kinds of expressions useFuture(std::async(increment, 42)); std::launder(&FNoWarning)->f();