ccotter updated this revision to Diff 491125.
ccotter added a comment.
- Fix windows, fix crash
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D141569/new/
https://reviews.llvm.org/D141569
Files:
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/rvalue-reference-param-not-moved.cpp
@@ -0,0 +1,244 @@
+// RUN: %check_clang_tidy -std=c++14-or-later %s cppcoreguidelines-rvalue-reference-param-not-moved %t -- -- -fno-delayed-template-parsing
+
+// NOLINTBEGIN
+namespace std {
+template <typename>
+struct remove_reference;
+
+template <typename _Tp>
+struct remove_reference {
+ 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]]:24: warning: rvalue reference parameter is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ o.member();
+}
+
+void copies_object(Obj&& o) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: rvalue reference parameter 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]]:33: warning: rvalue reference parameter 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]]:25: warning: rvalue reference parameter is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+ // CHECK-MESSAGES: :[[@LINE-2]]:35: warning: rvalue reference parameter 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]]:30: warning: rvalue reference parameter is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+ Obj other{std::move(o2)};
+}
+
+void never_moves_mixed(Obj o1, Obj&& o2) {
+ // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: rvalue reference parameter 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]]:41: warning: rvalue reference parameter 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]]:48: warning: rvalue reference parameter 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]]:25: warning: rvalue reference parameter is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+
+ auto never_moves_with_auto_param = [](Obj&& o1, auto& v) {
+ Obj other{o1};
+ };
+ // CHECK-MESSAGES: :[[@LINE-3]]:41: warning: rvalue reference parameter is never moved from inside the function body [cppcoreguidelines-rvalue-reference-param-not-moved]
+}
+
+// Negative tests - below functions do not violate the guideline
+
+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 good1(Obj&& o) {
+ Obj moved = std::move(o);
+ moved.member();
+}
+
+void good2(Obj& o) {
+ o.member();
+}
+
+void good3(Obj o) {
+ o.member();
+}
+
+void good4(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));
+ };
+ };
+ };
+}
+
+void lambda_captures_parameter_generalized(Obj&& o) {
+ auto f = [o = std::move(o)]() {
+ consumes_object(std::move(o));
+ };
+}
+
+void negative_lambda_checks() {
+ auto never_moves_nested = [](Obj&& o1) {
+ auto nested = [&]() {
+ Obj other{std::move(o1)};
+ };
+ };
+
+ auto auto_lvalue_ref_param = [](auto& o1) {
+ Obj other{o1};
+ };
+
+ auto auto_forwarding_ref_param = [](auto&& o1) {
+ Obj other{o1};
+ };
+
+ auto does_move = [](Obj&& o1) {
+ Obj other{std::move(o1)};
+ };
+
+ auto does_move_auto_rvalue_ref_param = [](auto&& o1) {
+ Obj other{std::forward(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]]:35: warning: rvalue reference parameter 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 AnotherObj {
+ AnotherObj(Obj&& o) : o(std::move(o)) {}
+ AnotherObj(Obj&& o, int) { o = std::move(o); }
+ Obj o;
+};
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -200,6 +200,7 @@
`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"
Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/rvalue-reference-param-not-moved.rst
@@ -0,0 +1,22 @@
+.. 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.
+
+Examples:
+
+.. code-block:: c++
+
+ void logic(std::string&& Input) {
+ std::string Copy(Input); // Oops - forgot to std::move
+ }
+
+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>`_.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -126,6 +126,12 @@
Warns on coroutines that accept reference parameters.
+- 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:`misc-use-anonymous-namespace
<clang-tidy/checks/misc/use-anonymous-namespace>` check.
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.h
@@ -0,0 +1,38 @@
+//===--- 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 {
+namespace tidy {
+namespace 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)
+ : ClangTidyCheck(Name, 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;
+ }
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RVALUEREFERENCEPARAMNOTMOVEDCHECK_H
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/RvalueReferenceParamNotMovedCheck.cpp
@@ -0,0 +1,146 @@
+//===--- 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"
+
+#include <queue>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cppcoreguidelines {
+
+void RvalueReferenceParamNotMovedCheck::registerMatchers(MatchFinder *Finder) {
+ auto ToParam = hasAnyParameter(parmVarDecl(equalsBoundNode("param")));
+ Finder->addMatcher(
+ parmVarDecl(allOf(
+ parmVarDecl(hasType(type(rValueReferenceType()))).bind("param"),
+ parmVarDecl(
+ equalsBoundNode("param"),
+ unless(hasType(
+ qualType(references(templateTypeParmType(
+ hasDeclaration(templateTypeParmDecl()))),
+ unless(references(qualType(isConstQualified())))))),
+ anyOf(hasAncestor(compoundStmt(hasParent(
+ lambdaExpr(has(cxxRecordDecl(has(cxxMethodDecl(
+ ToParam, hasName("operator()"))))))
+ .bind("containing-lambda")))),
+ hasAncestor(cxxConstructorDecl(isDefinition(), ToParam,
+ unless(isMoveConstructor()),
+ isDefinition(), ToParam)
+ .bind("containing-ctor")),
+ hasAncestor(
+ functionDecl(
+ isDefinition(), ToParam,
+ unless(cxxConstructorDecl(isMoveConstructor())),
+ unless(cxxMethodDecl(isMoveAssignmentOperator())))
+ .bind("containing-func")))))),
+ this);
+}
+
+static bool isValueCapturedByAnyLambda(ASTContext &Context,
+ DynTypedNode ContainingNode,
+ const Expr *StartingExpr,
+ const Decl *Param) {
+ std::queue<DynTypedNode> ToVisit;
+ ToVisit.push(DynTypedNode::create(*StartingExpr));
+ while (!ToVisit.empty()) {
+ DynTypedNode At = ToVisit.front();
+ ToVisit.pop();
+
+ if (At == ContainingNode) {
+ return false;
+ }
+ if (const auto *Lambda = At.get<LambdaExpr>()) {
+ bool ParamIsValueCaptured =
+ std::find_if(Lambda->capture_begin(), Lambda->capture_end(),
+ [&](const LambdaCapture &Capture) {
+ return Capture.capturesVariable() &&
+ Capture.getCapturedVar() == Param &&
+ Capture.getCaptureKind() == LCK_ByCopy;
+ }) != Lambda->capture_end();
+ if (ParamIsValueCaptured) {
+ return true;
+ }
+ }
+
+ DynTypedNodeList Parents = Context.getParents(At);
+ std::for_each(Parents.begin(), Parents.end(),
+ [&](const DynTypedNode &Node) { ToVisit.push(Node); });
+ if (Parents.empty()) {
+ return false;
+ }
+ }
+ return false;
+}
+
+void RvalueReferenceParamNotMovedCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Param = Result.Nodes.getNodeAs<ParmVarDecl>("param");
+ const auto *ContainingLambda =
+ Result.Nodes.getNodeAs<LambdaExpr>("containing-lambda");
+ const auto *ContainingCtor =
+ Result.Nodes.getNodeAs<FunctionDecl>("containing-ctor");
+ const auto *ContainingFunc =
+ Result.Nodes.getNodeAs<FunctionDecl>("containing-func");
+
+ StatementMatcher MoveCallMatcher =
+ callExpr(anyOf(callee(functionDecl(hasName("::std::move"))),
+ callee(unresolvedLookupExpr(hasAnyDeclaration(namedDecl(
+ hasUnderlyingDecl(hasName("::std::move"))))))),
+ argumentCountIs(1),
+ hasArgument(0, declRefExpr(to(equalsNode(Param))).bind("ref")));
+
+ DynTypedNode ContainingNode;
+
+ SmallVector<BoundNodes, 1> Matches;
+ if (ContainingLambda) {
+ if (!ContainingLambda->getBody())
+ return;
+ ContainingNode = DynTypedNode::create(*ContainingLambda);
+ Matches = match(findAll(MoveCallMatcher), *ContainingLambda->getBody(),
+ *Result.Context);
+ } else if (ContainingCtor) {
+ ContainingNode = DynTypedNode::create(*ContainingCtor);
+ Matches = match(findAll(cxxConstructorDecl(hasDescendant(MoveCallMatcher))),
+ *ContainingCtor, *Result.Context);
+ } else if (ContainingFunc) {
+ if (!ContainingFunc->getBody())
+ return;
+ ContainingNode = DynTypedNode::create(*ContainingFunc);
+ Matches = match(findAll(MoveCallMatcher), *ContainingFunc->getBody(),
+ *Result.Context);
+ } else {
+ return;
+ }
+
+ int MoveExprs = Matches.size();
+ for (const BoundNodes &Match : Matches) {
+ // The DeclRefExprs of non-initializer value captured variables refer to
+ // the original variable declaration in the AST. In such cases, we exclude
+ // those DeclRefExprs since they are not actually moving the original
+ // variable.
+ if (isValueCapturedByAnyLambda(*Result.Context, ContainingNode,
+ Match.getNodeAs<DeclRefExpr>("ref"),
+ Param)) {
+ --MoveExprs;
+ }
+ }
+
+ if (MoveExprs == 0) {
+ diag(Param->getBeginLoc(), "rvalue reference parameter is never moved from "
+ "inside the function body");
+ }
+}
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -36,6 +36,7 @@
#include "ProTypeStaticCastDowncastCheck.h"
#include "ProTypeUnionAccessCheck.h"
#include "ProTypeVarargCheck.h"
+#include "RvalueReferenceParamNotMovedCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
#include "VirtualClassDestructorCheck.h"
@@ -99,6 +100,8 @@
"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");
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -27,6 +27,7 @@
ProTypeStaticCastDowncastCheck.cpp
ProTypeUnionAccessCheck.cpp
ProTypeVarargCheck.cpp
+ RvalueReferenceParamNotMovedCheck.cpp
SlicingCheck.cpp
SpecialMemberFunctionsCheck.cpp
VirtualClassDestructorCheck.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits