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 {
18 namespace tidy {
19 namespace performance {
20 
NoAutomaticMoveCheck(StringRef Name,ClangTidyContext * Context)21 NoAutomaticMoveCheck::NoAutomaticMoveCheck(StringRef Name,
22                                            ClangTidyContext *Context)
23     : ClangTidyCheck(Name, Context),
24       AllowedTypes(
25           utils::options::parseStringList(Options.get("AllowedTypes", ""))) {}
26 
registerMatchers(MatchFinder * Finder)27 void NoAutomaticMoveCheck::registerMatchers(MatchFinder *Finder) {
28   const auto ConstLocalVariable =
29       varDecl(hasLocalStorage(), unless(hasType(lValueReferenceType())),
30               hasType(qualType(
31                   isConstQualified(),
32                   hasCanonicalType(matchers::isExpensiveToCopy()),
33                   unless(hasDeclaration(namedDecl(
34                       matchers::matchesAnyListedName(AllowedTypes)))))))
35           .bind("vardecl");
36 
37   // A matcher for a `DstT::DstT(const Src&)` where DstT also has a
38   // `DstT::DstT(Src&&)`.
39   const auto LValueRefCtor = cxxConstructorDecl(
40       hasParameter(0,
41                    hasType(lValueReferenceType(pointee(type().bind("SrcT"))))),
42       ofClass(cxxRecordDecl(hasMethod(cxxConstructorDecl(
43           hasParameter(0, hasType(rValueReferenceType(
44                               pointee(type(equalsBoundNode("SrcT")))))))))));
45 
46   Finder->addMatcher(
47       traverse(TK_AsIs,
48                returnStmt(hasReturnValue(
49                    ignoringElidableConstructorCall(ignoringParenImpCasts(
50                        cxxConstructExpr(
51                            hasDeclaration(LValueRefCtor),
52                            hasArgument(0, ignoringParenImpCasts(declRefExpr(
53                                               to(ConstLocalVariable)))))
54                            .bind("ctor_call")))))),
55       this);
56 }
57 
check(const MatchFinder::MatchResult & Result)58 void NoAutomaticMoveCheck::check(const MatchFinder::MatchResult &Result) {
59   const auto *Var = Result.Nodes.getNodeAs<VarDecl>("vardecl");
60   const auto *CtorCall = Result.Nodes.getNodeAs<Expr>("ctor_call");
61   diag(CtorCall->getExprLoc(), "constness of '%0' prevents automatic move")
62       << Var->getName();
63 }
64 
storeOptions(ClangTidyOptions::OptionMap & Opts)65 void NoAutomaticMoveCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
66   Options.store(Opts, "AllowedTypes",
67                 utils::options::serializeStringList(AllowedTypes));
68 }
69 
70 } // namespace performance
71 } // namespace tidy
72 } // namespace clang
73