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" @@ -140,29 +141,8 @@ unless(returns(voidType())), isInstantiatedFrom(hasAnyName(FunVec))))) .bind("match")))); - - auto UnusedInCompoundStmt = - compoundStmt(forEach(MatchedCallExpr), - // The checker can't currently differentiate between the - // return statement and other statements inside GNU statement - // expressions, so disable the checker inside them to avoid - // false positives. - unless(hasParent(stmtExpr()))); - auto UnusedInIfStmt = - ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr))); - auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr)); - auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr)); - auto UnusedInForStmt = - forStmt(eachOf(hasLoopInit(MatchedCallExpr), - hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr))); - auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr)); - auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr)); - Finder->addMatcher( - stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt, - UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt, - UnusedInCaseStmt)), - this); + functionDecl(hasBody(matchers::isValueUnused(MatchedCallExpr))), this); } void UnusedReturnValueCheck::check(const MatchFinder::MatchResult &Result) { diff --git a/clang-tools-extra/clang-tidy/utils/Matchers.h b/clang-tools-extra/clang-tidy/utils/Matchers.h --- a/clang-tools-extra/clang-tidy/utils/Matchers.h +++ b/clang-tools-extra/clang-tidy/utils/Matchers.h @@ -49,6 +49,51 @@ return pointerType(pointee(qualType(isConstQualified()))); } +// Matches the statements in a GNU statement-expression that are not returned +// from it. +AST_MATCHER_P(StmtExpr, hasUnreturning, + clang::ast_matchers::internal::Matcher, matcher) { + const auto compoundStmt = Node.getSubStmt(); + assert(compoundStmt); + + clang::ast_matchers::internal::BoundNodesTreeBuilder result; + bool matched = false; + for (auto stmt = compoundStmt->body_begin(); + stmt + 1 < compoundStmt->body_end(); ++stmt) { + clang::ast_matchers::internal::BoundNodesTreeBuilder builderInner(*Builder); + assert(stmt && *stmt); + if (matcher.matches(**stmt, Finder, &builderInner)) { + result.addMatch(builderInner); + matched = true; + } + } + *Builder = result; + return matched; +} + +// Matches all of the nodes (simmilar to forEach) that match the matcher +// and have return values not used in any statement. +AST_MATCHER_FUNCTION_P(ast_matchers::StatementMatcher, isValueUnused, + ast_matchers::StatementMatcher, Matcher) { + using namespace ast_matchers; + const auto UnusedInCompoundStmt = + compoundStmt(forEach(Matcher), unless(hasParent(stmtExpr()))); + const auto UnusedInGnuExprStmt = stmtExpr(hasUnreturning(Matcher)); + const auto UnusedInIfStmt = + ifStmt(eachOf(hasThen(Matcher), hasElse(Matcher))); + const auto UnusedInWhileStmt = whileStmt(hasBody(Matcher)); + const auto UnusedInDoStmt = doStmt(hasBody(Matcher)); + const auto UnusedInForStmt = forStmt( + eachOf(hasLoopInit(Matcher), hasIncrement(Matcher), hasBody(Matcher))); + const auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(Matcher)); + const auto UnusedInCaseStmt = switchCase(forEach(Matcher)); + const auto Unused = + stmt(anyOf(UnusedInCompoundStmt, UnusedInGnuExprStmt, UnusedInIfStmt, + UnusedInWhileStmt, UnusedInDoStmt, UnusedInForStmt, + UnusedInRangeForStmt, UnusedInCaseStmt)); + return stmt(eachOf(Unused, forEachDescendant(Unused))); +} + // A matcher implementation that matches a list of type name regular expressions // against a NamedDecl. If a regular expression contains the substring "::" // matching will occur against the qualified name, otherwise only the typename.