[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