xref: /llvm-project/clang-tools-extra/clang-tidy/performance/NoAutomaticMoveCheck.cpp (revision 9182c679dde7cb6480e66b9231a53d43ad03908b)
1 //===--- NoAutomaticMoveCheck.cpp - clang-tidy ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NoAutomaticMoveCheck.h"
10 #include "../utils/Matchers.h"
11 #include "../utils/OptionsUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 
15 using namespace clang::ast_matchers;
16 
17 namespace clang::tidy::performance {
18 
19 namespace {
20 
AST_MATCHER(VarDecl,isNRVOVariable)21 AST_MATCHER(VarDecl, isNRVOVariable) { return Node.isNRVOVariable(); }
22 
23 } // namespace
24 
NoAutomaticMoveCheck(StringRef Name,ClangTidyContext * Context)25 NoAutomaticMoveCheck::NoAutomaticMoveCheck(StringRef Name,
26                                            ClangTidyContext *Context)
27     : ClangTidyCheck(Name, Context),
28       AllowedTypes(
29           utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
30 
registerMatchers(MatchFinder * Finder)31 void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
32   const auto NonNrvoConstLocalVariable =
33       varDecl(hasLocalStorage(), unless(hasType(lValueReferenceType())),
34               unless(isNRVOVariable()),
35               hasType(qualType(
36                   isConstQualified(),
37                   hasCanonicalType(matchers::isExpensiveToCopy()),
38                   unless(hasDeclaration(namedDecl(
39                       matchers::matchesAnyListedName(AllowedTypes)))))))
40           .bind("vardecl");
41 
42   // A matcher for a `DstT::DstT(const Src&)` where DstT also has a
43   // `DstT::DstT(Src&&)`.
44   const auto LValueRefCtor = cxxConstructorDecl(
45       hasParameter(0,
46                    hasType(lValueReferenceType(pointee(type().bind("SrcT"))))),
47       ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl(
48           hasParameter(0, hasType(rValueReferenceType(
49                               pointee(type(equalsBoundNode("SrcT")))))))))));
50 
51   // A matcher for `DstT::DstT(const Src&&)`, which typically comes from an
52   // instantiation of `template <typename U> DstT::DstT(U&&)`.
53   const auto ConstRefRefCtor = cxxConstructorDecl(
54       parameterCountIs(1),
55       hasParameter(0,
56                    hasType(rValueReferenceType(pointee(isConstQualified())))));
57 
58   Finder->addMatcher(
59       traverse(
60           TK_AsIs,
61           returnStmt(hasReturnValue(
62               ignoringElidableConstructorCall(ignoringParenImpCasts(
63                   cxxConstructExpr(
64                       hasDeclaration(anyOf(LValueRefCtor, ConstRefRefCtor)),
65                       hasArgument(0, ignoringParenImpCasts(declRefExpr(
66                                          to(NonNrvoConstLocalVariable)))))
67                       .bind("ctor_call")))))),
68       this);
69 }
70 
check(const MatchFinder::MatchResult & Result)71 void NoAutomaticMoveCheck::check(const MatchFinder::MatchResult &Result) {
72   const auto *Var = Result.Nodes.getNodeAs<VarDecl>("vardecl");
73   const auto *CtorCall = Result.Nodes.getNodeAs<Expr>("ctor_call");
74   diag(CtorCall->getExprLoc(), "constness of '%0' prevents automatic move")
75       << Var->getName();
76 }
77 
storeOptions(ClangTidyOptions::OptionMap & Opts)78 void NoAutomaticMoveCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
79   Options.store(Opts, "AllowedTypes",
80                 utils::options::serializeStringList(AllowedTypes));
81 }
82 
83 } // namespace clang::tidy::performance
84