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; + const std::vector 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 @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "UnusedReturnValueCheck.h" +#include "../utils/Matchers.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" @@ -27,7 +28,6 @@ return InnerMatcher.matches(InstantiatedFrom ? *InstantiatedFrom : Node, Finder, Builder); } - } // namespace UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name, @@ -124,19 +124,30 @@ "::sigismember;" "::strcasecmp;" "::strsignal;" - "::ttyname")) {} + "::ttyname")), + CheckedReturnTypes(utils::options::parseStringList( + 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", + utils::options::serializeStringList(CheckedReturnTypes)); } void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) { auto FunVec = utils::options::parseStringList(CheckedFunctions); + 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)), + returns(hasCanonicalType(hasDeclaration( + namedDecl(matchers::matchesAnyListedName( + CheckedReturnTypes))))))))) .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 @@ -248,6 +248,10 @@ constructor initializers. Correctly handle constructor arguments as being sequenced when constructor call is written as list-initialization. +- Extend :doc:`bugprone-unused-return-value + ` check to check for all functions + with specified return types using the ``CheckedReturnTypes`` option. + - Deprecated :doc:`cert-dcl21-cpp ` check. 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();