llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tools-extra
@llvm/pr-subscribers-clang-tidy
Author: Victor Chernyakin (localspook)
<details>
<summary>Changes</summary>
We have several checks that need to test whether an expression is discarded.
Currently, all of them implement bespoke solutions, and all of them are buggy
or incomplete in different and exciting ways, because this is genuinely a
nontrivial
thing to implement. As just one example, `modernize-use-std-print` won't
modernize print statements when they're the target of a label:
```cpp
void f() {
std::printf("^_^");
label:
std::printf(";_;");
}
```
This change improves the situation by centralizing all that logic in one
`isDiscarded` matcher.
It also comes with speedups:
```txt
---User Time--- --System Time-- --User+System-- ---Wall Time---
--- Name ---
Before: 0.7031 ( 1.7%) 0.0938 ( 1.7%) 0.7969 ( 1.7%) 0.7208 ( 1.5%)
modernize-use-std-print
After: 0.0938 ( 0.2%) 0.0156 ( 0.3%) 0.1094 ( 0.2%) 0.0628 ( 0.1%)
modernize-use-std-print
Before: 1.0625 ( 2.5%) 0.0781 ( 1.4%) 1.1406 ( 2.4%) 1.1740 ( 2.5%)
bugprone-standalone-empty
After: 0.4844 ( 1.2%) 0.0312 ( 0.6%) 0.5156 ( 1.1%) 0.5182 ( 1.1%)
bugprone-standalone-empty
Before: 0.5000 ( 1.2%) 0.0938 ( 1.7%) 0.5938 ( 1.3%) 0.4162 ( 0.9%)
bugprone-unused-return-value
After: 0.2656 ( 0.7%) 0.0156 ( 0.3%) 0.2812 ( 0.6%) 0.2741 ( 0.6%)
bugprone-unused-return-value
```
We get these speedups by matching bottom-up instead of top-down, which is more
efficient since, with these checks, the bottom node is rarely matched.
I do measure slight slowdowns for a couple of checks:
```txt
---User Time--- --System Time-- --User+System-- ---Wall Time---
--- Name ---
Before: 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0038 ( 0.0%)
bugprone-throw-keyword-missing
After: 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0000 ( 0.0%) 0.0085 ( 0.0%)
bugprone-throw-keyword-missing
Before: 0.2188 ( 0.5%) 0.0312 ( 0.6%) 0.2500 ( 0.5%) 0.2567 ( 0.5%)
bugprone-unused-raii
After: 0.1250 ( 0.3%) 0.0938 ( 1.8%) 0.2188 ( 0.5%) 0.2767 ( 0.6%)
bugprone-unused-raii
```
These are far outweighed by the speedups above.
---
Patch is 20.03 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/184069.diff
7 Files Affected:
- (modified) clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
(+14-99)
- (modified) clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
(+5-15)
- (modified) clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp (+12-11)
- (modified) clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
(+7-26)
- (modified) clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
(+14-38)
- (modified) clang-tools-extra/clang-tidy/utils/Matchers.h (+57)
- (modified)
clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp (+5)
``````````diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
index 056ae4b80f109..7d787c361c8a5 100644
--- a/clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/StandaloneEmptyCheck.cpp
@@ -7,86 +7,29 @@
//===----------------------------------------------------------------------===//
#include "StandaloneEmptyCheck.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclBase.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/ExprCXX.h"
-#include "clang/AST/Stmt.h"
-#include "clang/AST/Type.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "../utils/Matchers.h"
#include "clang/ASTMatchers/ASTMatchers.h"
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Sema/HeuristicResolver.h"
-#include "llvm/Support/Casting.h"
-namespace clang::tidy::bugprone {
+using namespace clang::ast_matchers;
-using ast_matchers::BoundNodes;
-using ast_matchers::callee;
-using ast_matchers::callExpr;
-using ast_matchers::classTemplateDecl;
-using ast_matchers::cxxMemberCallExpr;
-using ast_matchers::cxxMethodDecl;
-using ast_matchers::expr;
-using ast_matchers::functionDecl;
-using ast_matchers::hasAncestor;
-using ast_matchers::hasName;
-using ast_matchers::hasParent;
-using ast_matchers::ignoringImplicit;
-using ast_matchers::ignoringParenImpCasts;
-using ast_matchers::MatchFinder;
-using ast_matchers::optionally;
-using ast_matchers::returns;
-using ast_matchers::stmt;
-using ast_matchers::stmtExpr;
-using ast_matchers::unless;
-using ast_matchers::voidType;
-
-static const Expr *getCondition(const BoundNodes &Nodes,
- const StringRef NodeId) {
- const auto *If = Nodes.getNodeAs<IfStmt>(NodeId);
- if (If != nullptr)
- return If->getCond();
-
- const auto *For = Nodes.getNodeAs<ForStmt>(NodeId);
- if (For != nullptr)
- return For->getCond();
-
- const auto *While = Nodes.getNodeAs<WhileStmt>(NodeId);
- if (While != nullptr)
- return While->getCond();
-
- const auto *Do = Nodes.getNodeAs<DoStmt>(NodeId);
- if (Do != nullptr)
- return Do->getCond();
-
- const auto *Switch = Nodes.getNodeAs<SwitchStmt>(NodeId);
- if (Switch != nullptr)
- return Switch->getCond();
-
- return nullptr;
-}
+namespace clang::tidy::bugprone {
-void StandaloneEmptyCheck::registerMatchers(ast_matchers::MatchFinder *Finder)
{
+void StandaloneEmptyCheck::registerMatchers(MatchFinder *Finder) {
// Ignore empty calls in a template definition which fall under callExpr
// non-member matcher even if they are methods.
- const auto NonMemberMatcher = expr(ignoringImplicit(ignoringParenImpCasts(
- callExpr(
- hasParent(stmt(optionally(hasParent(stmtExpr().bind("stexpr"))))
- .bind("parent")),
- unless(hasAncestor(classTemplateDecl())),
- callee(functionDecl(hasName("empty"), unless(returns(voidType())))))
- .bind("empty"))));
+ const auto NonMemberMatcher =
+ expr(ignoringParenImpCasts(
+ callExpr(unless(hasAncestor(classTemplateDecl())),
+ callee(functionDecl(hasName("empty"),
+ unless(returns(voidType())))))
+ .bind("empty")),
+ matchers::isDiscarded());
const auto MemberMatcher =
- expr(ignoringImplicit(ignoringParenImpCasts(cxxMemberCallExpr(
- hasParent(stmt(optionally(hasParent(stmtExpr().bind("stexpr"))))
- .bind("parent")),
- callee(cxxMethodDecl(hasName("empty"),
- unless(returns(voidType()))))))))
+ expr(ignoringParenImpCasts(cxxMemberCallExpr(callee(
+ cxxMethodDecl(hasName("empty"), unless(returns(voidType())))))),
+ matchers::isDiscarded())
.bind("empty");
Finder->addMatcher(MemberMatcher, this);
@@ -94,29 +37,8 @@ void
StandaloneEmptyCheck::registerMatchers(ast_matchers::MatchFinder *Finder) {
}
void StandaloneEmptyCheck::check(const MatchFinder::MatchResult &Result) {
- // Skip if the parent node is Expr.
- if (Result.Nodes.getNodeAs<Expr>("parent"))
- return;
-
- const auto *PParentStmtExpr = Result.Nodes.getNodeAs<Expr>("stexpr");
- const auto *ParentCompStmt = Result.Nodes.getNodeAs<CompoundStmt>("parent");
- const auto *ParentCond = getCondition(Result.Nodes, "parent");
- const auto *ParentReturnStmt = Result.Nodes.getNodeAs<ReturnStmt>("parent");
-
if (const auto *MemberCall =
Result.Nodes.getNodeAs<CXXMemberCallExpr>("empty")) {
- // Skip if it's a condition of the parent statement.
- if (ParentCond == MemberCall->getExprStmt())
- return;
- // Skip if it's the last statement in the GNU extension
- // statement expression.
- if (PParentStmtExpr && ParentCompStmt &&
- ParentCompStmt->body_back() == MemberCall->getExprStmt())
- return;
- // Skip if it's a return statement
- if (ParentReturnStmt)
- return;
-
const SourceLocation MemberLoc = MemberCall->getBeginLoc();
const SourceLocation ReplacementLoc = MemberCall->getExprLoc();
const SourceRange ReplacementRange =
@@ -154,13 +76,6 @@ void StandaloneEmptyCheck::check(const
MatchFinder::MatchResult &Result) {
} else if (const auto *NonMemberCall =
Result.Nodes.getNodeAs<CallExpr>("empty")) {
- if (ParentCond == NonMemberCall->getExprStmt())
- return;
- if (PParentStmtExpr && ParentCompStmt &&
- ParentCompStmt->body_back() == NonMemberCall->getExprStmt())
- return;
- if (ParentReturnStmt)
- return;
if (NonMemberCall->getNumArgs() != 1)
return;
diff --git a/clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
index 7cf9696378c62..44f5ceedc6e44 100644
--- a/clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
@@ -7,33 +7,23 @@
//===----------------------------------------------------------------------===//
#include "ThrowKeywordMissingCheck.h"
+#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
-using namespace clang::ast_matchers::internal;
namespace clang::tidy::bugprone {
void ThrowKeywordMissingCheck::registerMatchers(MatchFinder *Finder) {
- const VariadicDynCastAllOfMatcher<Stmt, AttributedStmt> AttributedStmt;
- // Matches an 'expression-statement', as defined in [stmt.expr]/1.
- // Not to be confused with the similarly-named GNU extension, the
- // statement expression.
- const auto ExprStmt = [&](const Matcher<Expr> &InnerMatcher) {
- return expr(hasParent(stmt(anyOf(doStmt(), whileStmt(), forStmt(),
- compoundStmt(), ifStmt(), switchStmt(),
- labelStmt(), AttributedStmt()))),
- InnerMatcher);
- };
-
Finder->addMatcher(
- ExprStmt(
- cxxConstructExpr(hasType(cxxRecordDecl(anyOf(
+ cxxConstructExpr(
+ hasType(cxxRecordDecl(anyOf(
matchesName("[Ee]xception|EXCEPTION"),
hasAnyBase(hasType(hasCanonicalType(recordType(hasDeclaration(
cxxRecordDecl(matchesName("[Ee]xception|EXCEPTION"))
- .bind("base")))))))))))
+ .bind("base"))))))))),
+ matchers::isDiscarded())
.bind("temporary-exception-not-thrown"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp
index 6502fc9bfb89e..b486a454aefe7 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnusedRaiiCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "UnusedRaiiCheck.h"
+#include "../utils/Matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/Lex/Lexer.h"
@@ -26,12 +27,13 @@ void UnusedRaiiCheck::registerMatchers(MatchFinder *Finder)
{
// destroyed.
Finder->addMatcher(
mapAnyOf(cxxConstructExpr, cxxUnresolvedConstructExpr)
- .with(hasParent(compoundStmt().bind("compound")),
- anyOf(hasType(hasCanonicalType(recordType(hasDeclaration(
+ .with(anyOf(hasType(hasCanonicalType(recordType(hasDeclaration(
cxxRecordDecl(hasNonTrivialDestructor()))))),
hasType(hasCanonicalType(templateSpecializationType(
hasDeclaration(classTemplateDecl(has(
- cxxRecordDecl(hasNonTrivialDestructor())))))))))
+ cxxRecordDecl(hasNonTrivialDestructor())))))))),
+ matchers::isDiscarded(),
+ optionally(hasParent(compoundStmt().bind("compound"))))
.bind("expr"),
this);
}
@@ -39,7 +41,7 @@ void UnusedRaiiCheck::registerMatchers(MatchFinder *Finder) {
template <typename T>
static void reportDiagnostic(const DiagnosticBuilder &D, const T *Node,
SourceRange SR, bool DefaultConstruction) {
- const char *Replacement = " give_me_a_name";
+ static constexpr StringRef Replacement = " give_me_a_name";
// If this is a default ctor we have to remove the parens or we'll introduce
a
// most vexing parse.
@@ -63,13 +65,12 @@ void UnusedRaiiCheck::check(const MatchFinder::MatchResult
&Result) {
if (E->getBeginLoc().isMacroID())
return;
- // Don't emit a warning for the last statement in the surrounding compound
- // statement.
- const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound");
- const auto *LastExpr = dyn_cast<Expr>(CS->body_back());
-
- if (LastExpr && E == LastExpr->IgnoreUnlessSpelledInSource())
- return;
+ // Don't emit a warning if this is the last statement in a surrounding
+ // compound statement.
+ if (const auto *CS = Result.Nodes.getNodeAs<CompoundStmt>("compound"))
+ if (const auto *LastExpr = dyn_cast<Expr>(CS->body_back()))
+ if (E == LastExpr->IgnoreUnlessSpelledInSource())
+ return;
// Emit a warning.
auto D = diag(E->getBeginLoc(), "object destroyed immediately after "
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
index 7aee725cae434..334d901b0a423 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp
@@ -171,7 +171,7 @@ void
UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
}
void UnusedReturnValueCheck::registerMatchers(MatchFinder *Finder) {
- auto MatchedDirectCallExpr = expr(
+ auto MatchedDirectCallExpr =
callExpr(callee(functionDecl(
// Don't match copy or move assignment operator.
unless(isAssignmentOverloadedOperator()),
@@ -182,36 +182,17 @@ void UnusedReturnValueCheck::registerMatchers(MatchFinder
*Finder) {
returns(hasCanonicalType(hasDeclaration(
namedDecl(matchers::matchesAnyListedRegexName(
CheckedReturnTypes)))))))))
- .bind("match"));
+ .bind("match");
auto CheckCastToVoid =
AllowCastToVoid ? castExpr(unless(hasCastKind(CK_ToVoid))) : castExpr();
- auto MatchedCallExpr = expr(
- anyOf(MatchedDirectCallExpr,
- explicitCastExpr(unless(cxxFunctionalCastExpr()), CheckCastToVoid,
- hasSourceExpression(MatchedDirectCallExpr))));
-
- 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(callExpr(MatchedDirectCallExpr, matchers::isDiscarded()),
+ this);
Finder->addMatcher(
- stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
- UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
- UnusedInCaseStmt)),
+ explicitCastExpr(unless(cxxFunctionalCastExpr()), CheckCastToVoid,
+ hasSourceExpression(MatchedDirectCallExpr),
+ matchers::isDiscarded()),
this);
}
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
index e92155c822dc7..13efe9539c80c 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStdPrintCheck.cpp
@@ -69,51 +69,27 @@ void UseStdPrintCheck::registerPPCallbacks(const
SourceManager &SM,
this->PP = PP;
}
-static clang::ast_matchers::StatementMatcher unusedReturnValue(
- const clang::ast_matchers::StatementMatcher &MatchedCallExpr) {
- 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));
-
- return stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,
- UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,
- UnusedInCaseStmt));
-}
-
void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {
if (!PrintfLikeFunctions.empty())
Finder->addMatcher(
- unusedReturnValue(
- callExpr(argumentCountAtLeast(1),
- hasArgument(0, stringLiteral(isOrdinary())),
- callee(functionDecl(matchers::matchesAnyListedRegexName(
- PrintfLikeFunctions))
- .bind("func_decl")))
- .bind("printf")),
+ callExpr(argumentCountAtLeast(1),
+ hasArgument(0, stringLiteral(isOrdinary())),
+ callee(functionDecl(matchers::matchesAnyListedRegexName(
+ PrintfLikeFunctions))
+ .bind("func_decl")),
+ matchers::isDiscarded())
+ .bind("printf"),
this);
if (!FprintfLikeFunctions.empty())
Finder->addMatcher(
- unusedReturnValue(
- callExpr(argumentCountAtLeast(2),
- hasArgument(1, stringLiteral(isOrdinary())),
- callee(functionDecl(matchers::matchesAnyListedRegexName(
- FprintfLikeFunctions))
- .bind("func_decl")))
- .bind("fprintf")),
+ callExpr(argumentCountAtLeast(2),
+ hasArgument(1, stringLiteral(isOrdinary())),
+ callee(functionDecl(matchers::matchesAnyListedRegexName(
+ FprintfLikeFunctions))
+ .bind("func_decl")),
+ matchers::isDiscarded())
+ .bind("fprintf"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/utils/Matchers.h
b/clang-tools-extra/clang-tidy/utils/Matchers.h
index 83401e85c8da9..e3b7d612bc63c 100644
--- a/clang-tools-extra/clang-tidy/utils/Matchers.h
+++ b/clang-tools-extra/clang-tidy/utils/Matchers.h
@@ -10,7 +10,9 @@
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H
#include "TypeTraits.h"
+#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/ExprConcepts.h"
+#include "clang/AST/ParentMapContext.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include <optional>
@@ -74,6 +76,61 @@ AST_MATCHER(Expr, hasUnevaluatedContext) {
return false;
}
+AST_MATCHER(Expr, isDiscarded) {
+ const DynTypedNodeList Parents = [&] {
+ const TraversalKindScope _(Finder->getASTContext(),
+ TK_IgnoreUnlessSpelledInSource);
+ return Finder->getASTContext().getParents(Node);
+ }();
+ if (Parents.size() != 1)
+ return false;
+ const DynTypedNode Parent = Parents[0];
+
+ const Expr *const DesiredNode = Node.IgnoreUnlessSpelledInSource();
+ const auto IsCurrentNode = [&](const Stmt *S) {
+ const auto *const AsExpr = dyn_cast_if_present<Expr>(S);
+ return AsExpr && AsExpr->IgnoreUnlessSpelledInSource() == DesiredNode;
+ };
+
+ if (const auto *While = Parent.get<WhileStmt>())
+ return IsCurrentNode(While->getBody());
+
+ if (const auto *For = Parent.get<ForStmt>())
+ return IsCurrentNode(For->getBody()) || IsCurrentNode(For->getInit()) ||
+ IsCurrentNode(For->getInc());
+
+ if (const auto *Do = Parent.get<DoStmt>())
+ return IsCurrentNode(Do->getBody());
+
+ if (const auto *If = Parent.get<IfStmt>())
+ return IsCurrentNode(If->getThen()) || IsCurrentNode(If->getElse());
+
+ if (const auto *ForRange = Parent.get<CXXForRangeStmt>())
+ return IsCurrentNode(ForRange->getBody());
+
+ if (const auto *Switch = Parent.get<SwitchStmt>())
+ return IsCurrentNode(Switch->getBody());
+
+ if (Parent.get<SwitchCase>())
+ return true;
+
+ if (Parent.get<LabelStmt>())
+ return true;
+
+ if (Parent.get<AttributedStmt>())
+ return true;
+
+ if (const auto *Compound = Parent.get<CompoundStmt>()) {
+ // Is this statement the return value of a GNU statement expression?
+ const DynTypedNodeList Grandparents =
+ Finder->getASTContext().getParents(Parent);
+ return !(Grandparents.size() == 1 && Grandparents[0].get<StmtExpr>() &&
+ IsCurrentNode(Compound->body_back()));
+ }
+
+ return false;
+}
+
// 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.
diff --git
a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp
b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp
index 63972cc0fd25e..e202a9d194113 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-std-print.cpp
@@ -184,6 +184,11 @@ int printf_uses_return_value(int choice) {
// CHECK-MESSAGES-NOT: [[@LINE-1]]:6: warning: use 'std::println' instead of
'printf' [modernize-use-std-print]
// CHECK-FIXES-NOT: std::println("GCC statement expression with unused
result {}", i);
+label:
+ printf("Label target %d\n", i);
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: use 'std::println' instead of
'printf' [modernize-use-std-print]
+ // CHECK-FIXES: std::println("Label target {}", i);
+
return printf("Return va...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/184069
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits