[clang-tools-extra] [clang-tidy] Fixed missing-std-forward false positives in lambdas (PR #231260)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 12 12:02:47 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-tidy
Author: GopinathBalaji
<details>
<summary>Changes</summary>
`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
---
Full diff: https://github.com/llvm/llvm-project/pull/231260.diff
4 Files Affected:
- (modified) clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.cpp (+5-7)
- (modified) clang-tools-extra/clang-tidy/cppcoreguidelines/MissingStdForwardCheck.h (+1-1)
- (modified) clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward-cxx20.cpp (+6)
- (modified) clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/missing-std-forward.cpp (+108-1)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/231260
More information about the cfe-commits
mailing list