https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/178651
>From 4bf9b5fa2be82ad2ce454a86bfae26cc3747e4d5 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Wed, 28 Jan 2026 23:39:33 +0800 Subject: [PATCH 1/3] [clang-tidy] Fix FP/FN in cppcoreguidelines-missing-std-forward --- .../MissingStdForwardCheck.cpp | 20 ++++---- clang-tools-extra/docs/ReleaseNotes.rst | 8 ++++ .../cppcoreguidelines/missing-std-forward.cpp | 46 +++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp index d1d81d510c8fb..357a5e46df823 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp @@ -106,20 +106,22 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { varDecl(hasInitializer(ignoringParenImpCasts(equalsBoundNode("call")))))); auto CapturedInLambda = hasDeclContext(cxxRecordDecl( - isLambda(), - hasParent(lambdaExpr(forCallable(equalsBoundNode("func")), - anyOf(CapturedInCaptureList, CapturedInBody))))); + isLambda(), hasParent(lambdaExpr( + anyOf(CapturedInCaptureList, CapturedInBody), + hasAncestor(functionDecl(equalsBoundNode("func"))))))); auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param"))); auto ForwardCallMatcher = callExpr( callExpr().bind("call"), argumentCountIs(1), - hasArgument(0, declRefExpr(to(varDecl().bind("var")))), - forCallable( - anyOf(allOf(equalsBoundNode("func"), - functionDecl(hasAnyParameter(parmVarDecl(allOf( - equalsBoundNode("param"), equalsBoundNode("var")))))), - CapturedInLambda)), + hasArgument( + 0, declRefExpr(to( + varDecl(anyOf(equalsBoundNode("param"), + hasSameNameAsBoundNode("param"), + hasInitializer(ignoringParenImpCasts( + declRefExpr(to(equalsBoundNode("param"))))))) + .bind("var")))), + forCallable(anyOf(equalsBoundNode("func"), CapturedInLambda)), callee(unresolvedLookupExpr(hasAnyDeclaration( namedDecl(hasUnderlyingDecl(hasName(ForwardFunction)))))), diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 754880bd1a381..6faf18fc96b55 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -136,6 +136,14 @@ Changes in existing checks the invalidating function in the warning message when a custom invalidation function is used (via the `InvalidationFunctions` option). +- Improved :doc:`cppcoreguidelines-missing-std-forward + <clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check by: + + - Correctly handling forwarding in deeply nested lambdas. + + - Fixing a false negative when multiple parameters are used in a lambda and + only some of them are forwarded. + - Improved :doc:`llvm-use-ranges <clang-tidy/checks/llvm/use-ranges>` check by adding support for the following algorithms: ``std::accumulate``, ``std::replace_copy``, and diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp index 98c592db7ce22..47e2977d6fc12 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp @@ -105,6 +105,26 @@ void foo(X &&x, Y &&y) { use(y); } +template <typename T> +void nested_but_no_forward(T &&arg) { + // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: forwarding reference parameter 'arg' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + [&]() + { + [&]() + { consumes_all(arg); }(); + }(); +} + +template <typename T, typename U> +void nested_forward_only_one(T &&arg1, U &&arg2) { + // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: forwarding reference parameter 'arg2' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + [&]() + { + [&]() + { consumes_all(std::forward<T>(arg1)); }(); + }(); +} + } // namespace positive_cases namespace negative_cases { @@ -182,6 +202,32 @@ void lambda_value_reference_auxiliary_var(T&& t) { [&x = t]() { T other = std::forward<T>(x); }; } +template <typename T> +void nested_forward(T &&arg) { + [&]() + { + [&]() + { consumes_all(std::forward<T>(arg)); }(); + }(); +} + +template <typename T> +void triple_nested_forward(T &&arg) { + [&]() + { + [&]() + { + [&]() + { consumes_all(std::forward<T>(arg)); }(); + }(); + }(); +} + +template <class T> +void lambda_renamed_capture(T&& t) { + [&a = t]() { consumes_all(std::forward<T>(a)); }; +} + } // namespace negative_cases namespace deleted_functions { >From 9a8af31a0d8563e9b89ffeccc08485d68fc5eea7 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Thu, 12 Feb 2026 00:59:43 +0800 Subject: [PATCH 2/3] ~ --- .../checkers/cppcoreguidelines/missing-std-forward.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp index 47e2977d6fc12..e5f3421cb6b46 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp @@ -223,11 +223,6 @@ void triple_nested_forward(T &&arg) { }(); } -template <class T> -void lambda_renamed_capture(T&& t) { - [&a = t]() { consumes_all(std::forward<T>(a)); }; -} - } // namespace negative_cases namespace deleted_functions { >From 3408527d2b6421c174fe8dbff0dc4ab89ebe7ee9 Mon Sep 17 00:00:00 2001 From: mtx <[email protected]> Date: Fri, 20 Feb 2026 00:55:55 +0800 Subject: [PATCH 3/3] hopefully better..? --- .../MissingStdForwardCheck.cpp | 62 ++++++++++++------- .../cppcoreguidelines/missing-std-forward.cpp | 28 +++++++++ 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp index 357a5e46df823..0ffcbc9d4cccd 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp @@ -85,21 +85,41 @@ AST_MATCHER(VarDecl, hasIdentifier) { return ID != nullptr && !ID->isPlaceholder(); } +AST_MATCHER_P(ValueDecl, refersToBoundParm, std::string, ParamID) { + return Builder->removeBindings( + [&](const ast_matchers::internal::BoundNodesMap &Nodes) { + const auto *Param = Nodes.getNodeAs<ParmVarDecl>(ParamID); + if (!Param) + return true; + + for (const ValueDecl *V = &Node; V;) { + if (V == Param) + return false; + + const auto *VD = dyn_cast<VarDecl>(V); + const Expr *Init = (VD && VD->getType()->isReferenceType()) + ? VD->getInit() + : nullptr; + const auto *DRE = + Init ? dyn_cast<DeclRefExpr>(Init->IgnoreParenImpCasts()) + : nullptr; + V = DRE ? DRE->getDecl() : nullptr; + } + return true; + }); +} + } // namespace void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { - auto RefToParmImplicit = allOf( - equalsBoundNode("var"), hasInitializer(ignoringParenImpCasts( - declRefExpr(to(equalsBoundNode("param")))))); - auto RefToParm = capturesVar( - varDecl(anyOf(hasSameNameAsBoundNode("param"), RefToParmImplicit))); + auto CapturedVar = varDecl( + anyOf(refersToBoundParm("param"), hasSameNameAsBoundNode("param"))); auto CaptureInRef = allOf(hasCaptureDefaultKind(LambdaCaptureDefault::LCD_ByRef), - unless(hasAnyCapture( - capturesVar(varDecl(hasSameNameAsBoundNode("param")))))); - auto CaptureByRefExplicit = hasAnyCapture( - allOf(hasCaptureKind(LambdaCaptureKind::LCK_ByRef), RefToParm)); + unless(hasAnyCapture(capturesVar(CapturedVar)))); + auto CaptureByRefExplicit = hasAnyCapture(allOf( + hasCaptureKind(LambdaCaptureKind::LCK_ByRef), capturesVar(CapturedVar))); auto CapturedInBody = lambdaExpr(anyOf(CaptureInRef, CaptureByRefExplicit)); auto CapturedInCaptureList = hasAnyCapture(capturesVar( @@ -112,21 +132,15 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param"))); - auto ForwardCallMatcher = callExpr( - callExpr().bind("call"), argumentCountIs(1), - hasArgument( - 0, declRefExpr(to( - varDecl(anyOf(equalsBoundNode("param"), - hasSameNameAsBoundNode("param"), - hasInitializer(ignoringParenImpCasts( - declRefExpr(to(equalsBoundNode("param"))))))) - .bind("var")))), - forCallable(anyOf(equalsBoundNode("func"), CapturedInLambda)), - callee(unresolvedLookupExpr(hasAnyDeclaration( - namedDecl(hasUnderlyingDecl(hasName(ForwardFunction)))))), - - unless(anyOf(hasAncestor(typeLoc()), - hasAncestor(expr(hasUnevaluatedContext()))))); + auto ForwardCallMatcher = + callExpr(callExpr().bind("call"), argumentCountIs(1), + hasArgument(0, declRefExpr(to(CapturedVar)).bind("var")), + forCallable(anyOf(equalsBoundNode("func"), CapturedInLambda)), + callee(unresolvedLookupExpr(hasAnyDeclaration( + namedDecl(hasUnderlyingDecl(hasName(ForwardFunction)))))), + + unless(anyOf(hasAncestor(typeLoc()), + hasAncestor(expr(hasUnevaluatedContext()))))); Finder->addMatcher( parmVarDecl( diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp index e5f3421cb6b46..bbbd6fa1c9686 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp @@ -125,6 +125,16 @@ void nested_forward_only_one(T &&arg1, U &&arg2) { }(); } +template <typename T> +void nested_rename_no_forward(T &&t) { + // CHECK-MESSAGES: :[[@LINE-1]]:35: warning: forwarding reference parameter 't' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + [&x = t]() { + [&y = x]() { + (void)y; + }(); + }(); +} + } // namespace positive_cases namespace negative_cases { @@ -202,6 +212,24 @@ void lambda_value_reference_auxiliary_var(T&& t) { [&x = t]() { T other = std::forward<T>(x); }; } +template <class T> +void lambda_multi_level_rename(T &&t) { + [&x = t]() { + [&y = x]() { + T other = std::forward<T>(y); + }(); + }(); +} + +template <class T> +void lambda_implicit_and_rename(T &&t) { + [&]() { + [&y = t]() { + T other = std::forward<T>(y); + }(); + }(); +} + template <typename T> void nested_forward(T &&arg) { [&]() _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
