[clang-tools-extra] [clang-tidy] Add AllowImplicitMove option to rvalue-reference-param-not-moved (PR #190541)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 5 06:20:20 PDT 2026
https://github.com/vbvictor updated https://github.com/llvm/llvm-project/pull/190541
>From f52b2056ba5a654387fc56b28093f550a9c97030 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 5 Apr 2026 20:43:07 +0300
Subject: [PATCH 1/5] [clang-tidy] Add AllowImplicitMove option to
rvalue-reference-param-not-moved
---
.../RvalueReferenceParamNotMovedCheck.cpp | 40 +++++--
.../RvalueReferenceParamNotMovedCheck.h | 1 +
clang-tools-extra/docs/ReleaseNotes.rst | 11 +-
.../rvalue-reference-param-not-moved.rst | 12 ++
...rvalue-reference-param-not-moved-cxx20.cpp | 103 ++++++++++++++++++
5 files changed, 154 insertions(+), 13 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
index 5aa6cbeff53d8..2934e97143d4e 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
@@ -54,6 +54,21 @@ void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
hasAncestor(expr(hasUnevaluatedContext())))))
.bind("move-call");
+ // P1825R0 (C++20): returning a named rvalue reference parameter by name
+ // performs an implicit move, which is equivalent to ``std::move(param)``
+ const StatementMatcher ImplicitMoveReturnMatcher = traverse(
+ TK_IgnoreUnlessSpelledInSource,
+ returnStmt(hasReturnValue(ignoringParens(
+ declRefExpr(to(equalsBoundNode("param"))).bind("ref"))))
+ .bind("implicit-move-return"));
+
+ const bool EnableImplicitMove =
+ AllowImplicitMove && getLangOpts().CPlusPlus20;
+
+ const StatementMatcher UsageMatcher = stmt(
+ anyOf(MoveCallMatcher, EnableImplicitMove ? ImplicitMoveReturnMatcher
+ : stmt(unless(anything()))));
+
Finder->addMatcher(
parmVarDecl(
hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),
@@ -68,10 +83,10 @@ void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
unless(cxxConstructorDecl(isMoveConstructor())),
unless(cxxMethodDecl(isMoveAssignmentOperator())), ToParam,
anyOf(cxxConstructorDecl(
- optionally(hasDescendant(MoveCallMatcher))),
- functionDecl(unless(cxxConstructorDecl()),
- optionally(hasBody(
- hasDescendant(MoveCallMatcher))))))
+ optionally(hasDescendant(UsageMatcher))),
+ functionDecl(
+ unless(cxxConstructorDecl()),
+ optionally(hasBody(hasDescendant(UsageMatcher))))))
.bind("func"))),
this);
}
@@ -108,12 +123,15 @@ void RvalueReferenceParamNotMovedCheck::check(
}
const auto *MoveCall = Result.Nodes.getNodeAs<CallExpr>("move-call");
- if (!MoveCall) {
- diag(Param->getLocation(),
- "rvalue reference parameter %0 is never moved from "
- "inside the function body")
- << Param;
- }
+ const auto *ImplicitMoveReturn =
+ Result.Nodes.getNodeAs<ReturnStmt>("implicit-move-return");
+ if (MoveCall || ImplicitMoveReturn)
+ return;
+
+ diag(Param->getLocation(),
+ "rvalue reference parameter %0 is never moved from "
+ "inside the function body")
+ << Param;
}
RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(
@@ -123,6 +141,7 @@ RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(
IgnoreUnnamedParams(Options.get("IgnoreUnnamedParams", false)),
IgnoreNonDeducedTemplateTypes(
Options.get("IgnoreNonDeducedTemplateTypes", false)),
+ AllowImplicitMove(Options.get("AllowImplicitMove", false)),
MoveFunction(Options.get("MoveFunction", "::std::move")) {}
void RvalueReferenceParamNotMovedCheck::storeOptions(
@@ -131,6 +150,7 @@ void RvalueReferenceParamNotMovedCheck::storeOptions(
Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams);
Options.store(Opts, "IgnoreNonDeducedTemplateTypes",
IgnoreNonDeducedTemplateTypes);
+ Options.store(Opts, "AllowImplicitMove", AllowImplicitMove);
Options.store(Opts, "MoveFunction", MoveFunction);
}
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
index 9fec58fb86036..360b1fc76a830 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
@@ -32,6 +32,7 @@ class RvalueReferenceParamNotMovedCheck : public ClangTidyCheck {
const bool AllowPartialMove;
const bool IgnoreUnnamedParams;
const bool IgnoreNonDeducedTemplateTypes;
+ const bool AllowImplicitMove;
const StringRef MoveFunction;
};
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index cfa9e4ed18b4b..ea533238cb7a5 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -502,9 +502,14 @@ Changes in existing checks
detail.
- Improved :doc:`cppcoreguidelines-rvalue-reference-param-not-moved
- <clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved>` check
- by fixing a false positive on implicitly generated functions such as
- inherited constructors.
+ <clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved>` check:
+
+ - Fixed a false positive on implicitly generated functions such as
+ inherited constructors.
+
+ - Added `AllowImplicitMove` option. When enabled and compiling as C++20
+ or later, the check don't warn when an rvalue reference parameter is returned
+ without an explicit ``std::move``.
- Improved :doc:`cppcoreguidelines-use-enum-class
<clang-tidy/checks/cppcoreguidelines/use-enum-class>` check by adding the
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
index 2fea9f16b3bb0..2698b0ac40c10 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
@@ -79,6 +79,18 @@ Options
T other = std::forward<T>(t);
}
+.. option:: AllowImplicitMove
+
+ If set to `true`, the check recognizes C++20 implicit move ``return param;``
+ where ``param`` is a rvalue reference parameter. This option only
+ takes effect when compiled with C++20 or later. Default is `false`.
+
+ .. code-block:: c++
+
+ A f(A&& a) {
+ return a; // no warning with AllowImplicitMove = true
+ }
+
.. option:: MoveFunction
Specify the function used for moving. Default is `::std::move`.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
new file mode 100644
index 0000000000000..7b485887f9446
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
@@ -0,0 +1,103 @@
+// RUN: %check_clang_tidy -check-suffix=ALLOW-CXX20 -std=c++20-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowImplicitMove: true}}"
+// RUN: %check_clang_tidy -check-suffix=ALLOW-PRE-CXX20 -std=c++11,c++14,c++17 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowImplicitMove: true}}"
+// RUN: %check_clang_tidy -check-suffix=DEFAULT -std=c++11-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t
+
+#include <utility>
+
+struct S {
+ S();
+ S(const S&);
+ S(S&&) noexcept;
+ S& operator=(const S&);
+ S& operator=(S&&) noexcept;
+};
+
+int intImplicitReturn(int&& x) {
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:29: warning: rvalue reference parameter 'x' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:29: warning: rvalue reference parameter 'x' is never moved from inside the function body
+ return x;
+}
+
+S classImplicitReturn(S&& s) {
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:27: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:27: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return s;
+}
+
+S classImplicitReturnParens(S&& s) {
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:33: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:33: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return (s);
+}
+
+S explicitMoveReturn(S&& s) {
+ return std::move(s);
+}
+
+S notMovedOrReturned(S&& s) {
+ // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ S copy = s;
+ return copy;
+}
+
+S SomePathsReturnParam(S&& s, bool cond) {
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:28: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:28: warning: rvalue reference parameter 's' is never moved from inside the function body
+ if (cond)
+ return s;
+ return S();
+}
+
+S NoPathReturnsParam(S&& s, bool cond) {
+ // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ if (cond)
+ return S();
+ S copy = s;
+ return copy;
+}
+
+S TwoParamsBothMoved(S&& a, S&& b, bool cond) {
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:33: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-4]]:33: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ if (cond)
+ return a;
+ return b;
+}
+
+S TwoParamsOnlyOneMoved(S&& a, S&& b) {
+ // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:29: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-3]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-4]]:29: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-5]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ (void)b;
+ return a;
+}
+
+struct A {
+ A();
+ A(const A&, int);
+ A(A&&) noexcept;
+};
+
+A explicitCtorWithParamArg(A&& param) {
+ // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
+ return A(param, 10);
+}
+
+S explicitCtorCall(S&& s) {
+ // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return S(s);
+}
>From 65640291e90ed68267f80d273af9bf82da0a062b Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 13 Apr 2026 19:32:08 +0300
Subject: [PATCH 2/5] !
---
.../RvalueReferenceParamNotMovedCheck.cpp | 9 +-
...ference-param-not-moved-allow-implicit.cpp | 89 +++++++++++++++
...rvalue-reference-param-not-moved-cxx20.cpp | 103 ------------------
3 files changed, 92 insertions(+), 109 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-allow-implicit.cpp
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
index 2934e97143d4e..44b1eb3fa169e 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
@@ -54,7 +54,7 @@ void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
hasAncestor(expr(hasUnevaluatedContext())))))
.bind("move-call");
- // P1825R0 (C++20): returning a named rvalue reference parameter by name
+ // P1825R0: returning a named rvalue reference parameter by name
// performs an implicit move, which is equivalent to ``std::move(param)``
const StatementMatcher ImplicitMoveReturnMatcher = traverse(
TK_IgnoreUnlessSpelledInSource,
@@ -62,12 +62,9 @@ void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
declRefExpr(to(equalsBoundNode("param"))).bind("ref"))))
.bind("implicit-move-return"));
- const bool EnableImplicitMove =
- AllowImplicitMove && getLangOpts().CPlusPlus20;
-
const StatementMatcher UsageMatcher = stmt(
- anyOf(MoveCallMatcher, EnableImplicitMove ? ImplicitMoveReturnMatcher
- : stmt(unless(anything()))));
+ anyOf(MoveCallMatcher, AllowImplicitMove ? ImplicitMoveReturnMatcher
+ : stmt(unless(anything()))));
Finder->addMatcher(
parmVarDecl(
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-allow-implicit.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-allow-implicit.cpp
new file mode 100644
index 0000000000000..190a6e0222e86
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-allow-implicit.cpp
@@ -0,0 +1,89 @@
+// RUN: %check_clang_tidy -check-suffix=ALLOW -std=c++11-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowImplicitMove: true}}"
+// RUN: %check_clang_tidy -check-suffix=DEFAULT -std=c++11-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t
+
+#include <utility>
+
+struct S {
+ S();
+ S(const S&);
+ S(S&&) noexcept;
+ S& operator=(const S&);
+ S& operator=(S&&) noexcept;
+};
+
+int intImplicitReturn(int&& x) {
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:29: warning: rvalue reference parameter 'x' is never moved from inside the function body
+ return x;
+}
+
+S classImplicitReturn(S&& s) {
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:27: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return s;
+}
+
+S classImplicitReturnParens(S&& s) {
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:33: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return (s);
+}
+
+S explicitMoveReturn(S&& s) {
+ return std::move(s);
+}
+
+S notMovedOrReturned(S&& s) {
+ // CHECK-MESSAGES-ALLOW: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ S copy = s;
+ return copy;
+}
+
+S SomePathsReturnParam(S&& s, bool cond) {
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:28: warning: rvalue reference parameter 's' is never moved from inside the function body
+ if (cond)
+ return s;
+ return S();
+}
+
+S NoPathReturnsParam(S&& s, bool cond) {
+ // CHECK-MESSAGES-ALLOW: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
+ if (cond)
+ return S();
+ S copy = s;
+ return copy;
+}
+
+S TwoParamsBothMoved(S&& a, S&& b, bool cond) {
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:26: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:33: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ if (cond)
+ return a;
+ return b;
+}
+
+S TwoParamsOnlyOneMoved(S&& a, S&& b) {
+ // CHECK-MESSAGES-ALLOW: :[[@LINE-1]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:29: warning: rvalue reference parameter 'a' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
+ (void)b;
+ return a;
+}
+
+struct A {
+ A();
+ A(const A&, int);
+ A(A&&) noexcept;
+};
+
+A explicitCtorWithParamArg(A&& param) {
+ // CHECK-MESSAGES-ALLOW: :[[@LINE-1]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
+ return A(param, 10);
+}
+
+S explicitCtorCall(S&& s) {
+ // CHECK-MESSAGES-ALLOW: :[[@LINE-1]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
+ return S(s);
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
deleted file mode 100644
index 7b485887f9446..0000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved-cxx20.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-// RUN: %check_clang_tidy -check-suffix=ALLOW-CXX20 -std=c++20-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
-// RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowImplicitMove: true}}"
-// RUN: %check_clang_tidy -check-suffix=ALLOW-PRE-CXX20 -std=c++11,c++14,c++17 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
-// RUN: -config="{CheckOptions: {cppcoreguidelines-rvalue-reference-param-not-moved.AllowImplicitMove: true}}"
-// RUN: %check_clang_tidy -check-suffix=DEFAULT -std=c++11-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t
-
-#include <utility>
-
-struct S {
- S();
- S(const S&);
- S(S&&) noexcept;
- S& operator=(const S&);
- S& operator=(S&&) noexcept;
-};
-
-int intImplicitReturn(int&& x) {
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:29: warning: rvalue reference parameter 'x' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:29: warning: rvalue reference parameter 'x' is never moved from inside the function body
- return x;
-}
-
-S classImplicitReturn(S&& s) {
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:27: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:27: warning: rvalue reference parameter 's' is never moved from inside the function body
- return s;
-}
-
-S classImplicitReturnParens(S&& s) {
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:33: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:33: warning: rvalue reference parameter 's' is never moved from inside the function body
- return (s);
-}
-
-S explicitMoveReturn(S&& s) {
- return std::move(s);
-}
-
-S notMovedOrReturned(S&& s) {
- // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- S copy = s;
- return copy;
-}
-
-S SomePathsReturnParam(S&& s, bool cond) {
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:28: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-2]]:28: warning: rvalue reference parameter 's' is never moved from inside the function body
- if (cond)
- return s;
- return S();
-}
-
-S NoPathReturnsParam(S&& s, bool cond) {
- // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 's' is never moved from inside the function body
- if (cond)
- return S();
- S copy = s;
- return copy;
-}
-
-S TwoParamsBothMoved(S&& a, S&& b, bool cond) {
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-1]]:26: warning: rvalue reference parameter 'a' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:33: warning: rvalue reference parameter 'b' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:26: warning: rvalue reference parameter 'a' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-4]]:33: warning: rvalue reference parameter 'b' is never moved from inside the function body
- if (cond)
- return a;
- return b;
-}
-
-S TwoParamsOnlyOneMoved(S&& a, S&& b) {
- // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:29: warning: rvalue reference parameter 'a' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-3]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-4]]:29: warning: rvalue reference parameter 'a' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-5]]:36: warning: rvalue reference parameter 'b' is never moved from inside the function body
- (void)b;
- return a;
-}
-
-struct A {
- A();
- A(const A&, int);
- A(A&&) noexcept;
-};
-
-A explicitCtorWithParamArg(A&& param) {
- // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:32: warning: rvalue reference parameter 'param' is never moved from inside the function body
- return A(param, 10);
-}
-
-S explicitCtorCall(S&& s) {
- // CHECK-MESSAGES-ALLOW-CXX20: :[[@LINE-1]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-ALLOW-PRE-CXX20: :[[@LINE-2]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
- // CHECK-MESSAGES-DEFAULT: :[[@LINE-3]]:24: warning: rvalue reference parameter 's' is never moved from inside the function body
- return S(s);
-}
>From 090a56590f3db6d40239ea4a2eb9a16f5b61fbf9 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Mon, 13 Apr 2026 19:34:35 +0300
Subject: [PATCH 3/5] fix doc
---
.../rvalue-reference-param-not-moved.rst | 23 +++++++++----------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
index 2698b0ac40c10..ee60f6c0179a9 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
@@ -51,6 +51,17 @@ Options
pair<Obj, Obj> other = std::move(p);
}
+.. option:: AllowImplicitMove
+
+ If set to `true`, the check recognizes implicit move ``return param;``
+ where ``param`` is a rvalue reference parameter. Default is `false`.
+
+ .. code-block:: c++
+
+ A f(A&& a) {
+ return a; // no warning with AllowImplicitMove = true
+ }
+
.. option:: IgnoreUnnamedParams
If set to `true`, the check ignores unnamed rvalue reference parameters.
@@ -79,18 +90,6 @@ Options
T other = std::forward<T>(t);
}
-.. option:: AllowImplicitMove
-
- If set to `true`, the check recognizes C++20 implicit move ``return param;``
- where ``param`` is a rvalue reference parameter. This option only
- takes effect when compiled with C++20 or later. Default is `false`.
-
- .. code-block:: c++
-
- A f(A&& a) {
- return a; // no warning with AllowImplicitMove = true
- }
-
.. option:: MoveFunction
Specify the function used for moving. Default is `::std::move`.
>From 29479dc2d0dec4391d9bdc6cbe9e41391ee24a89 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 10 May 2026 20:31:25 +0300
Subject: [PATCH 4/5] ~
---
clang-tools-extra/docs/ReleaseNotes.rst | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ea533238cb7a5..640c376c35f2c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -507,9 +507,8 @@ Changes in existing checks
- Fixed a false positive on implicitly generated functions such as
inherited constructors.
- - Added `AllowImplicitMove` option. When enabled and compiling as C++20
- or later, the check don't warn when an rvalue reference parameter is returned
- without an explicit ``std::move``.
+ - Added `AllowImplicitMove` option to not warn when an rvalue reference
+ parameter is returned without an explicit ``std::move``.
- Improved :doc:`cppcoreguidelines-use-enum-class
<clang-tidy/checks/cppcoreguidelines/use-enum-class>` check by adding the
>From dea03dde9c73addf69fda381d5db1ddcb2027953 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Sun, 10 May 2026 20:32:14 +0300
Subject: [PATCH 5/5] ~
---
clang-tools-extra/docs/ReleaseNotes.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 640c376c35f2c..e400881d4c9b0 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -504,11 +504,11 @@ Changes in existing checks
- Improved :doc:`cppcoreguidelines-rvalue-reference-param-not-moved
<clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved>` check:
- - Fixed a false positive on implicitly generated functions such as
- inherited constructors.
+ - Fixed a false positive on implicitly generated functions such as
+ inherited constructors.
- - Added `AllowImplicitMove` option to not warn when an rvalue reference
- parameter is returned without an explicit ``std::move``.
+ - Added `AllowImplicitMove` option to not warn when an rvalue reference
+ parameter is returned without an explicit ``std::move``.
- Improved :doc:`cppcoreguidelines-use-enum-class
<clang-tidy/checks/cppcoreguidelines/use-enum-class>` check by adding the
More information about the cfe-commits
mailing list