[clang-tools-extra] c491c91 - [clang-tidy] Implement CppCoreGuideline F.18
Piotr Zegar via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 12 10:27:32 PDT 2023
Author: Chris Cotter
Date: 2023-03-12T16:58:08Z
New Revision: c491c917023905815d6b4b81f4c5f632b02895c3
URL: https://github.com/llvm/llvm-project/commit/c491c917023905815d6b4b81f4c5f632b02895c3
DIFF: https://github.com/llvm/llvm-project/commit/c491c917023905815d6b4b81f4c5f632b02895c3.diff
LOG: [clang-tidy] Implement CppCoreGuideline F.18
Warn when an rvalue reference function paramter is never moved
from within the function body.
Reviewed By: carlosgalvezp
Differential Revision: https://reviews.llvm.org/D141569
Added:
clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
Modified:
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
index 4210c3ea03fc8..0804675c63949 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -29,6 +29,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
ProTypeStaticCastDowncastCheck.cpp
ProTypeUnionAccessCheck.cpp
ProTypeVarargCheck.cpp
+ RvalueReferenceParamNotMovedCheck.cpp
SlicingCheck.cpp
SpecialMemberFunctionsCheck.cpp
VirtualClassDestructorCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index ac4b8d7b01d7a..5bd9648fe8371 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -38,6 +38,7 @@
#include "ProTypeStaticCastDowncastCheck.h"
#include "ProTypeUnionAccessCheck.h"
#include "ProTypeVarargCheck.h"
+#include "RvalueReferenceParamNotMovedCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
#include "VirtualClassDestructorCheck.h"
@@ -104,6 +105,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
"cppcoreguidelines-pro-type-union-access");
CheckFactories.registerCheck<ProTypeVarargCheck>(
"cppcoreguidelines-pro-type-vararg");
+ CheckFactories.registerCheck<RvalueReferenceParamNotMovedCheck>(
+ "cppcoreguidelines-rvalue-reference-param-not-moved");
CheckFactories.registerCheck<SpecialMemberFunctionsCheck>(
"cppcoreguidelines-special-member-functions");
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
new file mode 100644
index 0000000000000..eb67df05411a0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
@@ -0,0 +1,128 @@
+//===--- RvalueReferenceParamNotMovedCheck.cpp - clang-tidy ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RvalueReferenceParamNotMovedCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::cppcoreguidelines {
+
+namespace {
+AST_MATCHER_P(LambdaExpr, valueCapturesVar, DeclarationMatcher, VarMatcher) {
+ return std::find_if(Node.capture_begin(), Node.capture_end(),
+ [&](const LambdaCapture &Capture) {
+ return Capture.capturesVariable() &&
+ VarMatcher.matches(*Capture.getCapturedVar(),
+ Finder, Builder) &&
+ Capture.getCaptureKind() == LCK_ByCopy;
+ }) != Node.capture_end();
+}
+AST_MATCHER_P2(Stmt, argumentOf, bool, AllowPartialMove, StatementMatcher,
+ Ref) {
+ if (AllowPartialMove) {
+ return stmt(anyOf(Ref, hasDescendant(Ref))).matches(Node, Finder, Builder);
+ } else {
+ return Ref.matches(Node, Finder, Builder);
+ }
+}
+} // namespace
+
+void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
+ auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
+
+ StatementMatcher MoveCallMatcher =
+ callExpr(
+ anyOf(callee(functionDecl(hasName("::std::move"))),
+ callee(unresolvedLookupExpr(hasAnyDeclaration(
+ namedDecl(hasUnderlyingDecl(hasName("::std::move"))))))),
+ argumentCountIs(1),
+ hasArgument(
+ 0, argumentOf(
+ AllowPartialMove,
+ declRefExpr(to(equalsBoundNode("param"))).bind("ref"))),
+ unless(hasAncestor(
+ lambdaExpr(valueCapturesVar(equalsBoundNode("param"))))))
+ .bind("move-call");
+
+ Finder->addMatcher(
+ parmVarDecl(
+ hasType(type(rValueReferenceType())), parmVarDecl().bind("param"),
+ unless(hasType(references(qualType(
+ anyOf(isConstQualified(), substTemplateTypeParmType()))))),
+ optionally(hasType(qualType(references(templateTypeParmType(
+ hasDeclaration(templateTypeParmDecl().bind("template-type"))))))),
+ anyOf(hasAncestor(cxxConstructorDecl(
+ ToParam, isDefinition(), unless(isMoveConstructor()),
+ optionally(hasDescendant(MoveCallMatcher)))),
+ hasAncestor(functionDecl(
+ unless(cxxConstructorDecl()), ToParam,
+ unless(cxxMethodDecl(isMoveAssignmentOperator())),
+ hasBody(optionally(hasDescendant(MoveCallMatcher))))))),
+ this);
+}
+
+void RvalueReferenceParamNotMovedCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+ const auto *TemplateType =
+ Result.Nodes.getNodeAs<TemplateTypeParmDecl>("template-type");
+
+ if (!Param)
+ return;
+
+ if (IgnoreUnnamedParams && Param->getName().empty())
+ return;
+
+ const auto *Function = dyn_cast<FunctionDecl>(Param->getDeclContext());
+ if (!Function)
+ return;
+
+ if (IgnoreNonDeducedTemplateTypes && TemplateType)
+ return;
+
+ if (TemplateType) {
+ if (const FunctionTemplateDecl *FuncTemplate =
+ Function->getDescribedFunctionTemplate()) {
+ const TemplateParameterList *Params =
+ FuncTemplate->getTemplateParameters();
+ if (llvm::is_contained(*Params, TemplateType)) {
+ // Ignore forwarding reference
+ return;
+ }
+ }
+ }
+
+ 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;
+ }
+}
+
+RvalueReferenceParamNotMovedCheck::RvalueReferenceParamNotMovedCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ AllowPartialMove(Options.getLocalOrGlobal("AllowPartialMove", false)),
+ IgnoreUnnamedParams(
+ Options.getLocalOrGlobal("IgnoreUnnamedParams", false)),
+ IgnoreNonDeducedTemplateTypes(
+ Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {}
+
+void RvalueReferenceParamNotMovedCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "AllowPartialMove", AllowPartialMove);
+ Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams);
+ Options.store(Opts, "IgnoreNonDeducedTemplateTypes",
+ IgnoreNonDeducedTemplateTypes);
+}
+
+} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
new file mode 100644
index 0000000000000..d8c3d2bd4ba0e
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
@@ -0,0 +1,39 @@
+//===--- RvalueReferenceParamNotMovedCheck.h - clang-tidy -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RVALUEREFERENCEPARAMNOTMOVEDCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RVALUEREFERENCEPARAMNOTMOVEDCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::cppcoreguidelines {
+
+/// Warns when an rvalue reference function parameter is never moved within
+/// the function body. This check implements CppCoreGuideline F.18.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.html
+class RvalueReferenceParamNotMovedCheck : public ClangTidyCheck {
+public:
+ RvalueReferenceParamNotMovedCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+ const bool AllowPartialMove;
+ const bool IgnoreUnnamedParams;
+ const bool IgnoreNonDeducedTemplateTypes;
+};
+
+} // namespace clang::tidy::cppcoreguidelines
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RVALUEREFERENCEPARAMNOTMOVEDCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 4e861dcd38cfc..c0b2d118ec4b6 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -114,12 +114,6 @@ New checks
Warns when lambda specify a capture default and capture ``this``.
-- New :doc:`llvmlibc-inline-function-decl
- <clang-tidy/checks/llvmlibc/inline-function-decl>` check.
-
- Checks that all implicit and explicit inline functions in header files are
- tagged with the ``LIBC_INLINE`` macro.
-
- New :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines
<clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines>` check.
@@ -127,6 +121,18 @@ New checks
use-after-free errors and suggests avoiding captures or ensuring the lambda
closure object has a guaranteed lifetime.
+- New :doc:`cppcoreguidelines-rvalue-reference-param-not-moved
+ <clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved>` check.
+
+ Warns when an rvalue reference function parameter is never moved within
+ the function body.
+
+- New :doc:`llvmlibc-inline-function-decl
+ <clang-tidy/checks/llvmlibc/inline-function-decl>` check.
+
+ Checks that all implicit and explicit inline functions in header files are
+ tagged with the ``LIBC_INLINE`` macro.
+
New check aliases
^^^^^^^^^^^^^^^^^
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
new file mode 100644
index 0000000000000..f4c58b3734e2f
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
@@ -0,0 +1,73 @@
+.. title:: clang-tidy - cppcoreguidelines-rvalue-reference-param-not-moved
+
+cppcoreguidelines-rvalue-reference-param-not-moved
+==================================================
+
+Warns when an rvalue reference function parameter is never moved within
+the function body.
+
+Rvalue reference parameters indicate a parameter that should be moved with
+``std::move`` from within the function body. Any such parameter that is
+never moved is confusing and potentially indicative of a buggy program.
+
+Example:
+
+.. code-block:: c++
+
+ void logic(std::string&& Input) {
+ std::string Copy(Input); // Oops - forgot to std::move
+ }
+
+Options
+-------
+
+.. option:: AllowPartialMove
+
+ If set to `true`, the check accepts ``std::move`` calls containing any
+ subexpression containing the parameter. CppCoreGuideline F.18 officially
+ mandates that the parameter itself must be moved. Default is `false`.
+
+ .. code-block:: c++
+
+ // 'p' is flagged by this check if and only if AllowPartialMove is false
+ void move_members_of(pair<Obj, Obj>&& p) {
+ pair<Obj, Obj> other;
+ other.first = std::move(p.first);
+ other.second = std::move(p.second);
+ }
+
+ // 'p' is never flagged by this check
+ void move_whole_pair(pair<Obj, Obj>&& p) {
+ pair<Obj, Obj> other = std::move(p);
+ }
+
+.. option:: IgnoreUnnamedParams
+
+ If set to `true`, the check ignores unnamed rvalue reference parameters.
+ Default is `false`.
+
+.. option:: IgnoreNonDeducedTemplateTypes
+
+ If set to `true`, the check ignores non-deduced template type rvalue
+ reference parameters. Default is `false`.
+
+ .. code-block:: c++
+
+ template <class T>
+ struct SomeClass {
+ // Below, 'T' is not deduced and 'T&&' is an rvalue reference type.
+ // This will be flagged if and only if IgnoreNonDeducedTemplateTypes is
+ // false. One suggested fix would be to specialize the class for 'T' and
+ // 'T&' separately (e.g., see std::future), or allow only one of 'T' or
+ // 'T&' instantiations of SomeClass (e.g., see std::optional).
+ SomeClass(T&& t) { }
+ };
+
+ // Never flagged, since 'T' is a forwarding reference in a deduced context
+ template <class T>
+ void forwarding_ref(T&& t) {
+ T other = std::forward<T>(t);
+ }
+
+This check implements
+`CppCoreGuideline F.18 <http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#f18-for-will-move-from-parameters-pass-by-x-and-stdmove-the-parameter>`_.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 6385b754d3b7d..1919458cfc87c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -203,6 +203,7 @@ Clang-Tidy Checks
`cppcoreguidelines-pro-type-static-cast-downcast <cppcoreguidelines/pro-type-static-cast-downcast.html>`_, "Yes"
`cppcoreguidelines-pro-type-union-access <cppcoreguidelines/pro-type-union-access.html>`_,
`cppcoreguidelines-pro-type-vararg <cppcoreguidelines/pro-type-vararg.html>`_,
+ `cppcoreguidelines-rvalue-reference-param-not-moved <cppcoreguidelines/rvalue-reference-param-not-moved.html>`_,
`cppcoreguidelines-slicing <cppcoreguidelines/slicing.html>`_,
`cppcoreguidelines-special-member-functions <cppcoreguidelines/special-member-functions.html>`_,
`cppcoreguidelines-virtual-class-destructor <cppcoreguidelines/virtual-class-destructor.html>`_, "Yes"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
new file mode 100644
index 0000000000000..9ee26e851bfc2
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
@@ -0,0 +1,328 @@
+// RUN: %check_clang_tidy -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes, value: true}]}" -- -fno-delayed-template-parsing
+// RUN: %check_clang_tidy -check-suffix=,CXX14 -std=c++14 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes, value: true}]}" -- -fno-delayed-template-parsing
+// RUN: %check_clang_tidy -check-suffix=,NOSUBEXPR -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove, value: false},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes, value: true}]}" -- -fno-delayed-template-parsing
+// RUN: %check_clang_tidy -check-suffix=,UNNAMED -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: false},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes, value: true}]}" -- -fno-delayed-template-parsing
+// RUN: %check_clang_tidy -check-suffix=,NONDEDUCED -std=c++11 %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- \
+// RUN: -config="{CheckOptions: [{key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams, value: true},{key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreNonDeducedTemplateTypes, value: false}]}" -- -fno-delayed-template-parsing
+
+// NOLINTBEGIN
+namespace std {
+template <typename>
+struct remove_reference;
+
+template <typename _Tp> struct remove_reference { typedef _Tp type; };
+template <typename _Tp> struct remove_reference<_Tp&> { typedef _Tp type; };
+template <typename _Tp> struct remove_reference<_Tp&&> { typedef _Tp type; };
+
+template <typename _Tp>
+constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept;
+
+template <typename _Tp>
+constexpr _Tp &&
+forward(typename remove_reference<_Tp>::type &__t) noexcept;
+
+}
+// NOLINTEND
+
+struct Obj {
+ Obj();
+ Obj(const Obj&);
+ Obj& operator=(const Obj&);
+ Obj(Obj&&);
+ Obj& operator=(Obj&&);
+ void member() const;
+};
+
+void consumes_object(Obj);
+
+void never_moves_param(Obj&& o) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ o.member();
+}
+
+void just_a_declaration(Obj&& o);
+
+void copies_object(Obj&& o) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ Obj copy = o;
+}
+
+template <typename T>
+void never_moves_param_template(Obj&& o, T t) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ o.member();
+}
+
+void never_moves_params(Obj&& o1, Obj&& o2) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ // CHECK-MESSAGES: :[[@LINE-2]]:41: warning: rvalue reference parameter 'o2' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+}
+
+void never_moves_some_params(Obj&& o1, Obj&& o2) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+ Obj other{std::move(o2)};
+}
+
+void never_moves_unnamed(Obj&&) {}
+// CHECK-MESSAGES-UNNAMED: :[[@LINE-1]]:31: warning: rvalue reference parameter '' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+void never_moves_mixed(Obj o1, Obj&& o2) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: rvalue reference parameter 'o2' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+}
+
+void lambda_captures_parameter_as_value(Obj&& o) {
+ auto f = [o]() {
+ consumes_object(std::move(o));
+ };
+ // CHECK-MESSAGES: :[[@LINE-4]]:47: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+}
+
+void lambda_captures_parameter_as_value_nested(Obj&& o) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ auto f = [&o]() {
+ auto f_nested = [o]() {
+ consumes_object(std::move(o));
+ };
+ };
+ auto f2 = [o]() {
+ auto f_nested = [&o]() {
+ consumes_object(std::move(o));
+ };
+ };
+ auto f3 = [o]() {
+ auto f_nested = [&o]() {
+ auto f_nested_inner = [&o]() {
+ consumes_object(std::move(o));
+ };
+ };
+ };
+ auto f4 = [&o]() {
+ auto f_nested = [&o]() {
+ auto f_nested_inner = [o]() {
+ consumes_object(std::move(o));
+ };
+ };
+ };
+}
+
+void misc_lambda_checks() {
+ auto never_moves = [](Obj&& o1) {
+ Obj other{o1};
+ };
+ // CHECK-MESSAGES: :[[@LINE-3]]:31: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+#if __cplusplus >= 201402L
+ auto never_moves_with_auto_param = [](Obj&& o1, auto& v) {
+ Obj other{o1};
+ };
+ // CHECK-MESSAGES-CXX14: :[[@LINE-3]]:47: warning: rvalue reference parameter 'o1' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+#endif
+}
+
+template <typename T>
+void forwarding_ref(T&& t) {
+ t.member();
+}
+
+template <typename T>
+void forwarding_ref_forwarded(T&& t) {
+ forwarding_ref(std::forward<T>(t));
+}
+
+template <typename... Ts>
+void type_pack(Ts&&... ts) {
+ (forwarding_ref(std::forward<Ts>(ts)), ...);
+}
+
+void call_forwarding_functions() {
+ Obj o;
+
+ forwarding_ref(Obj{});
+ type_pack(Obj{});
+ type_pack(Obj{}, o);
+ type_pack(Obj{}, Obj{});
+}
+
+void moves_parameter(Obj&& o) {
+ Obj moved = std::move(o);
+}
+
+void moves_parameter_extra_parens(Obj&& o) {
+ Obj moved = std::move((o));
+}
+
+template <typename T1, typename T2>
+struct mypair {
+ T1 first;
+ T2 second;
+};
+
+void moves_member_of_parameter(mypair<Obj, Obj>&& pair) {
+ // CHECK-MESSAGES-NOSUBEXPR: :[[@LINE-1]]:51: warning: rvalue reference parameter 'pair' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ Obj a = std::move(pair.first);
+ Obj b = std::move(pair.second);
+}
+
+template <typename T>
+struct myoptional {
+ T& operator*() &;
+ T&& operator*() &&;
+};
+
+void moves_deref_optional(myoptional<Obj>&& opt) {
+ // CHECK-MESSAGES-NOSUBEXPR: :[[@LINE-1]]:45: warning: rvalue reference parameter 'opt' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ Obj other = std::move(*opt);
+}
+
+void moves_optional_then_deref_resulting_rvalue(myoptional<Obj>&& opt) {
+ Obj other = *std::move(opt);
+}
+
+void pass_by_lvalue_reference(Obj& o) {
+ o.member();
+}
+
+void pass_by_value(Obj o) {
+ o.member();
+}
+
+void pass_by_const_lvalue_reference(const Obj& o) {
+ o.member();
+}
+
+void pass_by_const_lvalue_reference(const Obj&& o) {
+ o.member();
+}
+
+void lambda_captures_parameter_as_reference(Obj&& o) {
+ auto f = [&o]() {
+ consumes_object(std::move(o));
+ };
+}
+
+void lambda_captures_parameter_as_reference_nested(Obj&& o) {
+ auto f = [&o]() {
+ auto f_nested = [&o]() {
+ auto f_nested2 = [&o]() {
+ consumes_object(std::move(o));
+ };
+ };
+ };
+}
+
+#if __cplusplus >= 201402L
+void lambda_captures_parameter_generalized(Obj&& o) {
+ auto f = [o = std::move(o)]() {
+ consumes_object(std::move(o));
+ };
+}
+#endif
+
+void negative_lambda_checks() {
+ auto never_moves_nested = [](Obj&& o1) {
+ auto nested = [&]() {
+ Obj other{std::move(o1)};
+ };
+ };
+
+#if __cplusplus >= 201402L
+ auto auto_lvalue_ref_param = [](auto& o1) {
+ Obj other{o1};
+ };
+
+ auto auto_forwarding_ref_param = [](auto&& o1) {
+ Obj other{o1};
+ };
+
+ auto does_move_auto_rvalue_ref_param = [](auto&& o1) {
+ Obj other{std::forward(o1)};
+ };
+#endif
+
+ auto does_move = [](Obj&& o1) {
+ Obj other{std::move(o1)};
+ };
+
+ auto not_rvalue_ref = [](Obj& o1) {
+ Obj other{std::move(o1)};
+ };
+
+ Obj local;
+ auto captures = [local]() { };
+}
+
+struct AClass {
+ void member_with_lambda_no_move(Obj&& o) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: rvalue reference parameter 'o' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ auto captures_this = [=, this]() {
+ Obj other = std::move(o);
+ };
+ }
+ void member_with_lambda_that_moves(Obj&& o) {
+ auto captures_this = [&, this]() {
+ Obj other = std::move(o);
+ };
+ }
+};
+
+void useless_move(Obj&& o) {
+ // FIXME - The object is not actually moved from - this should probably be
+ // flagged by *some* check. Which one?
+ std::move(o);
+}
+
+template <typename>
+class TemplatedClass;
+
+template <typename T>
+void unresolved_lookup(TemplatedClass<T>&& o) {
+ TemplatedClass<T> moved = std::move(o);
+}
+
+struct DefinesMove {
+ DefinesMove(DefinesMove&& rhs) : o(std::move(rhs.o)) { }
+ DefinesMove& operator=(DefinesMove&& rhs) {
+ if (this != &rhs) {
+ o = std::move(rhs.o);
+ }
+ return *this;
+ }
+ Obj o;
+};
+
+struct DeclaresMove {
+ DeclaresMove(DeclaresMove&& rhs);
+ DeclaresMove& operator=(DeclaresMove&& rhs);
+};
+
+struct AnotherObj {
+ AnotherObj(Obj&& o) : o(std::move(o)) {}
+ AnotherObj(Obj&& o, int) { o = std::move(o); }
+ Obj o;
+};
+
+template <class T>
+struct AClassTemplate {
+ AClassTemplate(T&& t) {}
+ // CHECK-MESSAGES-NONDEDUCED: :[[@LINE-1]]:22: warning: rvalue reference parameter 't' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+ void moves(T&& t) {
+ T other = std::move(t);
+ }
+ void never_moves(T&& t) {}
+ // CHECK-MESSAGES-NONDEDUCED: :[[@LINE-1]]:24: warning: rvalue reference parameter 't' is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+};
+
+void instantiate_a_class_template() {
+ Obj o;
+ AClassTemplate<Obj> withObj{std::move(o)};
+ withObj.never_moves(std::move(o));
+
+ AClassTemplate<Obj&> withObjRef(o);
+ withObjRef.never_moves(o);
+}
More information about the cfe-commits
mailing list