diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -28,6 +28,7 @@ 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 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -37,6 +37,7 @@ #include "ProTypeStaticCastDowncastCheck.h" #include "ProTypeUnionAccessCheck.h" #include "ProTypeVarargCheck.h" +#include "RvalueReferenceParamNotMovedCheck.h" #include "SlicingCheck.h" #include "SpecialMemberFunctionsCheck.h" #include "VirtualClassDestructorCheck.h" @@ -101,6 +102,8 @@ "cppcoreguidelines-pro-type-union-access"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-vararg"); + CheckFactories.registerCheck( + "cppcoreguidelines-rvalue-reference-param-not-moved"); CheckFactories.registerCheck( "cppcoreguidelines-special-member-functions"); CheckFactories.registerCheck("cppcoreguidelines-slicing"); diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h new file mode 100644 --- /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 AllowAnySubExpr; + 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/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp @@ -0,0 +1,127 @@ +//===--- 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, AllowAnySubExpr, StatementMatcher, Ref) { + if (AllowAnySubExpr) { + 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( + AllowAnySubExpr, + 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("param"); + const auto *TemplateType = + Result.Nodes.getNodeAs("template-type"); + + if (!Param) + return; + + if (IgnoreUnnamedParams && Param->getName().empty()) + return; + + const auto *Function = dyn_cast(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("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), + AllowAnySubExpr(Options.getLocalOrGlobal("AllowAnySubExpr", false)), + IgnoreUnnamedParams( + Options.getLocalOrGlobal("IgnoreUnnamedParams", false)), + IgnoreNonDeducedTemplateTypes( + Options.getLocalOrGlobal("IgnoreNonDeducedTemplateTypes", false)) {} + +void RvalueReferenceParamNotMovedCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowAnySubExpr", AllowAnySubExpr); + Options.store(Opts, "IgnoreUnnamedParams", IgnoreUnnamedParams); + Options.store(Opts, "IgnoreNonDeducedTemplateTypes", + IgnoreNonDeducedTemplateTypes); +} + +} // namespace clang::tidy::cppcoreguidelines diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -110,6 +110,12 @@ Warns when lambda specify a capture default and capture ``this``. +- New :doc:`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 ` check. 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 --- /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:: AllowAnyMoveExpr + + 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 AllowAnyMoveExpr is false + void move_members_of(pair&& p) { + pair 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&& p) { + pair 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 + 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 + void forwarding_ref(T&& t) { + T other = std::forward(t); + } + +This check implements +`CppCoreGuideline F.18 `_. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -202,6 +202,7 @@ `cppcoreguidelines-pro-type-static-cast-downcast `_, "Yes" `cppcoreguidelines-pro-type-union-access `_, `cppcoreguidelines-pro-type-vararg `_, + `cppcoreguidelines-rvalue-reference-param-not-moved `_, `cppcoreguidelines-slicing `_, `cppcoreguidelines-special-member-functions `_, `cppcoreguidelines-virtual-class-destructor `_, "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 --- /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.AllowAnySubExpr, 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.AllowAnySubExpr, 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.AllowAnySubExpr, 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.AllowAnySubExpr, 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.AllowAnySubExpr, 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 +struct remove_reference; + +template struct remove_reference { typedef _Tp type; }; +template struct remove_reference<_Tp&> { typedef _Tp type; }; +template struct remove_reference<_Tp&&> { typedef _Tp type; }; + +template +constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept; + +template +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 +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 +void forwarding_ref(T&& t) { + t.member(); +} + +template +void forwarding_ref_forwarded(T&& t) { + forwarding_ref(std::forward(t)); +} + +template +void type_pack(Ts&&... ts) { + (forwarding_ref(std::forward(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 +struct mypair { + T1 first; + T2 second; +}; + +void moves_member_of_parameter(mypair&& 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 +struct myoptional { + T& operator*() &; + T&& operator*() &&; +}; + +void moves_deref_optional(myoptional&& 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&& 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 +class TemplatedClass; + +template +void unresolved_lookup(TemplatedClass&& o) { + TemplatedClass 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 +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 withObj{std::move(o)}; + withObj.never_moves(std::move(o)); + + AClassTemplate withObjRef(o); + withObjRef.never_moves(o); +}