https://github.com/GopinathBalaji created https://github.com/llvm/llvm-project/pull/231260
`cppcoreguidelines-missing-std-forward` warns that a generic lambda's `auto&&` parameter is never forwarded even when its body calls `std::forward<decltype(x)>(x)`. My change uses `TK_AsIs` traversal and associates each parameter with its owning function through `hasDeclContext`, allowing the check to recognize forwarding inside implicit lambda call operators. Template instantiations are excluded to avoid diagnosing instantiated copies. The regression test coverage includes: generic lambdas in C++14 and later, explicitly templated C++20 lambdas, nested lambdas and function templates, reference and value captures, unevaluated contexts, and forwarding only one of multiple parameters. Unforwarded parameters continue to warn. ### Validation: - All `missing-std-forward` lit tests pass. - The original reproducer no longer emits the false positive. Assisted-by: GPT 6.1 Sol Fixes #228393 >From 67bb6e96c311998e6e6103a20ab547b421f10b9b Mon Sep 17 00:00:00 2001 From: Gopinath Balaji <[email protected]> Date: Mon, 12 Oct 2026 13:24:44 -0500 Subject: [PATCH] [clang-tidy] Fixed missing-std-forward false positives in lambdas Recognize forwarding inside generic lambda call operators by using TK_AsIs traversal and matching each parameter's owing function through its declaration context. Exclude template instantiations to avoid diagnosing instantiated copies. Also added regression coverage for generic and explicitly templated lambdas, nested callables, captures, unevaluated contexts, and parameter-specific forwarding. Fixes #228393 --- .../MissingStdForwardCheck.cpp | 12 +- .../MissingStdForwardCheck.h | 2 +- .../missing-std-forward-cxx20.cpp | 6 + .../cppcoreguidelines/missing-std-forward.cpp | 109 +++++++++++++++++- 4 files changed, 120 insertions(+), 9 deletions(-) diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp index 0cae9e232cd50..25aa86f86cf96 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp @@ -136,13 +136,11 @@ void MissingStdForwardCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( parmVarDecl( parmVarDecl().bind("param"), hasIdentifier(), - unless(hasAttr(attr::Kind::Unused)), isTemplateTypeParameter(), - hasAncestor(functionDecl().bind("func")), - hasAncestor(functionDecl( - isDefinition(), equalsBoundNode("func"), ToParam, - unless(anyOf( - isDeleted(), - traverse(TK_AsIs, hasDescendant(ForwardCallMatcher))))))), + unless(hasAttr(attr::Kind::Unused)), unless(isInstantiated()), + isTemplateTypeParameter(), + hasDeclContext(functionDecl( + functionDecl().bind("func"), isDefinition(), ToParam, + unless(anyOf(isDeleted(), hasDescendant(ForwardCallMatcher)))))), this); } diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h index 2dd13e2124d74..c8ccfc1864628 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h @@ -28,7 +28,7 @@ class MissingStdForwardCheck : public ClangTidyCheck { return LangOpts.CPlusPlus11; } std::optional<TraversalKind> getCheckTraversalKind() const override { - return TK_IgnoreUnlessSpelledInSource; + return TK_AsIs; } void storeOptions(ClangTidyOptions::OptionMap &Opts) override; diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-cxx20.cpp index cbf3def7800bb..a6c53a51b20bb 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-cxx20.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-cxx20.cpp @@ -22,3 +22,9 @@ void mixed_parameters(T &&t, auto &&u) { (void)t; (void)u; } + +void explicitly_templated_lambda_forwards() { + auto forwards = []<typename T>(T &&x) { + (void)std::forward<T>(x); + }; +} 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 136e4af7c8ee5..d4dbe7310c604 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 @@ -1,4 +1,5 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-missing-std-forward %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++11 %s cppcoreguidelines-missing-std-forward %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++14-or-later -check-suffixes=,CXX14 %s cppcoreguidelines-missing-std-forward %t -- -- -fno-delayed-template-parsing #include <utility> @@ -279,3 +280,109 @@ template<typename F> void unused_argument3(F&& _) {} } // namespace unused_arguments + +#if __cplusplus >= 201402L + +namespace generic_lambdas { + +void forwards_parameter() { + auto lambda = [](auto &&x) { + (void)std::forward<decltype(x)>(x); + }; +} + +void does_not_forward_parameter() { + auto lambda = [](auto &&x) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:27: warning: forwarding reference parameter 'x' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (void)x; + }; +} + +void forwards_parameter_by_reference_capture() { + auto lambda = [](auto &&x) { + [&x]() { + (void)std::forward<decltype(x)>(x); + }(); + }; +} + +void does_not_forward_parameter_by_value_capture() { + auto lambda = [](auto &&x) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:27: warning: forwarding reference parameter 'x' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + [x]() { + (void)std::forward<decltype(x)>(x); + }(); + }; +} + +template <typename T> +void forwards_parameter_in_template() { + auto lambda = [](auto &&x) { + (void)std::forward<decltype(x)>(x); + }; +} + +void forwards_only_one_parameter() { + auto lambda = [](auto &&x, auto &&y) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:37: warning: forwarding reference parameter 'y' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (void)std::forward<decltype(x)>(x); + (void)y; + }; +} + +void forwards_only_inner_lambda_parameter() { + auto outer = [](auto &&x) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:26: warning: forwarding reference parameter 'x' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (void)x; + auto inner = [](auto &&x) { + (void)std::forward<decltype(x)>(x); + }; + }; +} + +// decltype examines a type; it does not execute the forwarding expression. +void does_not_forward_in_decltype() { + auto lambda = [](auto &&x) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:27: warning: forwarding reference parameter 'x' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + using ForwardedType = decltype(std::forward<decltype(x)>(x)); + }; +} + +// sizeof does not execute this forwarding expression either. +void does_not_forward_in_sizeof() { + auto lambda = [](auto &&x) { + // CHECK-MESSAGES-CXX14: :[[@LINE-1]]:27: warning: forwarding reference parameter 'x' is never forwarded inside the function body [cppcoreguidelines-missing-std-forward] + (void)sizeof(std::forward<decltype(x)>(x)); + }; +} + +// Instantiating the lambda for lvalues and rvalues must not introduce warnings. +void forwards_instantiated_parameter() { + auto lambda = [](auto &&x) { + (void)std::forward<decltype(x)>(x); + }; + + int value = 0; + lambda(value); + lambda(0); +} + +// const auto&& is not a forwarding reference. +void const_rvalue_reference_parameter() { + auto lambda = [](const auto &&x) { + (void)x; + }; +} + +template <typename T> +void forwards_outer_and_inner_parameters(T &&x) { + auto inner = [](auto &&y) { + (void)std::forward<decltype(y)>(y); + }; + + (void)std::forward<T>(x); +} + +} // namespace generic_lambdas + +#endif // __cplusplus >= 201402L _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
